}
-void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
+void RelocInfo::apply(intptr_t delta) {
if (RelocInfo::IsInternalReference(rmode_)) {
// absolute code pointer inside code object moves with the code object.
int32_t* p = reinterpret_cast<int32_t*>(pc_);
}
-Address RelocInfo::call_address() {
+Address RelocInfo::debug_call_address() {
// The 2 instructions offset assumes patched debug break slot or return
// sequence.
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return Memory::Address_at(pc_ + 2 * Assembler::kInstrSize);
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ return Memory::Address_at(pc_ + Assembler::kPatchDebugBreakSlotAddressOffset);
}
-void RelocInfo::set_call_address(Address target) {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- Memory::Address_at(pc_ + 2 * Assembler::kInstrSize) = target;
+void RelocInfo::set_debug_call_address(Address target) {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ Memory::Address_at(pc_ + Assembler::kPatchDebugBreakSlotAddressOffset) =
+ target;
if (host() != NULL) {
Object* target_code = Code::GetCodeFromTargetAddress(target);
host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
}
-Object* RelocInfo::call_object() {
- return *call_object_address();
-}
-
-
-void RelocInfo::set_call_object(Object* target) {
- *call_object_address() = target;
-}
-
-
-Object** RelocInfo::call_object_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
-}
-
-
void RelocInfo::WipeOut() {
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
visitor->VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
- } else if (((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence())) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence() &&
isolate->debug()->has_break_points()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
} else if (heap->isolate()->debug()->has_break_points() &&
- ((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence()))) {
+ RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
StaticVisitor::VisitRuntimeEntry(this);
}
-Address Assembler::break_address_from_return_address(Address pc) {
- return pc - Assembler::kPatchDebugBreakSlotReturnOffset;
-}
-
-
Address Assembler::return_address_from_call_start(Address pc) {
if (IsLdrPcImmediateOffset(Memory::int32_at(pc)) |
IsLdrPpImmediateOffset(Memory::int32_at(pc))) {
// in the instruction stream that the call will return from.
INLINE(static Address return_address_from_call_start(Address pc));
- // Return the code target address of the patch debug break slot
- INLINE(static Address break_address_from_return_address(Address pc));
-
// This sets the branch destination (which is in the constant pool on ARM).
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
// Size of an instruction.
static const int kInstrSize = sizeof(Instr);
- // Distance between start of patched return sequence and the emitted address
- // to jump to.
- // Patched return sequence is:
- // ldr ip, [pc, #0] @ emited address and start
- // blx ip
- static const int kPatchReturnSequenceAddressOffset = 0 * kInstrSize;
-
// Distance between start of patched debug break slot and the emitted address
// to jump to.
// Patched debug break slot code is:
// ldr ip, [pc, #0] @ emited address and start
// blx ip
- static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize;
-
- static const int kPatchDebugBreakSlotReturnOffset = 2 * kInstrSize;
+ static const int kPatchDebugBreakSlotAddressOffset = 2 * kInstrSize;
// Difference between address of current opcode and value read from pc
// register.
static const int kPcLoadDelta = 8;
- static const int kJSReturnSequenceInstructions = 4;
- static const int kJSReturnSequenceLength =
- kJSReturnSequenceInstructions * kInstrSize;
- static const int kDebugBreakSlotInstructions = 3;
+ static const int kDebugBreakSlotInstructions = 4;
static const int kDebugBreakSlotLength =
kDebugBreakSlotInstructions * kInstrSize;
// Debugging
- // Mark address of the ExitJSFrame code.
- void RecordJSReturn();
-
// Mark generator continuation.
void RecordGeneratorContinuation();
// Mark address of a debug break slot.
- void RecordDebugBreakSlot();
- void RecordDebugBreakSlotForCall(int argc);
- void RecordDebugBreakSlotForConstructCall();
+ void RecordDebugBreakSlot(RelocInfo::Mode mode, int argc = 0);
// Record the AST id of the CallIC being compiled, so that it can be placed
// in the relocation information.
namespace v8 {
namespace internal {
-void BreakLocation::SetDebugBreakAtReturn() {
- // Patch the code changing the return from JS function sequence from
- // mov sp, fp
- // ldmia sp!, {fp, lr}
- // add sp, sp, #4
- // bx lr
- // to a call to the debug break return code.
- // ldr ip, [pc, #0]
- // blx ip
- // <debug break return code entry point address>
- // bkpt 0
- CodePatcher patcher(pc(), Assembler::kJSReturnSequenceInstructions);
- patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
- patcher.masm()->blx(v8::internal::ip);
- patcher.Emit(
- debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry());
- patcher.masm()->bkpt(0);
+#define __ ACCESS_MASM(masm)
+
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_size));
}
-void BreakLocation::SetDebugBreakAtSlot() {
- DCHECK(IsDebugBreakSlot());
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the constant pool in the debug break slot code.
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
// Patch the code changing the debug break slot code from
// mov r2, r2
// mov r2, r2
// mov r2, r2
+ // mov r2, r2
// to a call to the debug break slot code.
// ldr ip, [pc, #0]
- // blx ip
+ // b skip
// <debug break slot code entry point address>
- CodePatcher patcher(pc(), Assembler::kDebugBreakSlotInstructions);
- patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
- patcher.masm()->blx(v8::internal::ip);
- patcher.Emit(
- debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry());
+ // skip:
+ // blx ip
+ Label skip_constant;
+ patcher.masm()->ldr(ip, MemOperand(v8::internal::pc, 0));
+ patcher.masm()->b(&skip_constant);
+ patcher.Emit(code->entry());
+ patcher.masm()->bind(&skip_constant);
+ patcher.masm()->blx(ip);
}
-#define __ ACCESS_MASM(masm)
-
-
-static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList object_regs) {
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
{
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
__ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(ip);
- // Store the registers containing live values on the expression stack to
- // make sure that these are correctly updated during GC. Non object values
- // are stored as a smi causing it to be untouched by GC.
- DCHECK((object_regs & ~kJSCallerSaved) == 0);
- if (object_regs != 0) {
- __ stm(db_w, sp, object_regs);
- }
+ if (mode == SAVE_RESULT_REGISTER) __ push(r0);
-#ifdef DEBUG
- __ RecordComment("// Calling from debug break to runtime - come in - over");
-#endif
__ mov(r0, Operand::Zero()); // no arguments
__ mov(r1, Operand(ExternalReference::debug_break(masm->isolate())));
CEntryStub ceb(masm->isolate(), 1);
__ CallStub(&ceb);
- // Restore the register values from the expression stack.
- if (object_regs != 0) {
- __ ldm(ia_w, sp, object_regs);
- }
-
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- Register reg = {r};
- if (FLAG_debug_code && ((object_regs & (1 << r)) == 0)) {
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = {JSCallerSavedCode(i)};
__ mov(reg, Operand(kDebugZapValue));
}
}
+ if (mode == SAVE_RESULT_REGISTER) __ pop(r0);
+
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
}
-void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
- // In places other than IC call sites it is expected that r0 is TOS which
- // is an object - this is not generally the case so this should be used with
- // care.
- Generate_DebugBreakCallHelper(masm, r0.bit());
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm,
- DebugCodegen::SlotLocation location,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the constant pool in the debug break slot code.
- Assembler::BlockConstPoolScope block_const_pool(masm);
- Label check_codesize;
- __ bind(&check_codesize);
- RecordRelocInfo(masm, location, call_argc);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(MacroAssembler::DEBUG_BREAK_NOP);
- }
- DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
- masm->InstructionsGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
- // In the places where a debug break slot is inserted no registers can contain
- // object pointers.
- Generate_DebugBreakCallHelper(masm, 0);
-}
-
-
void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
__ Ret();
}
EmitProfilingCounterReset();
__ bind(&ok);
-#ifdef DEBUG
- // Add a label for checking the size of the code used for returning.
- Label check_exit_codesize;
- __ bind(&check_exit_codesize);
-#endif
// Make sure that the constant pool is not emitted inside of the return
// sequence.
{ Assembler::BlockConstPoolScope block_const_pool(masm_);
SetReturnPosition(function());
// TODO(svenpanne) The code below is sometimes 4 words, sometimes 5!
PredictableCodeSizeScope predictable(masm_, -1);
- __ RecordJSReturn();
int no_frame_start = __ LeaveFrame(StackFrame::JAVA_SCRIPT);
{ ConstantPoolUnavailableScope constant_pool_unavailable(masm_);
__ add(sp, sp, Operand(sp_delta));
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
}
-
-#ifdef DEBUG
- // Check that the size of the code used for returning is large enough
- // for the debugger's requirements.
- DCHECK(Assembler::kJSReturnSequenceInstructions <=
- masm_->InstructionsGeneratedSince(&check_exit_codesize));
-#endif
}
}
CodePatcher(byte* address,
int instructions,
FlushICache flush_cache = FLUSH);
- virtual ~CodePatcher();
+ ~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }
bool CpuFeatures::SupportsCrankshaft() { return true; }
-void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
+void RelocInfo::apply(intptr_t delta) {
// On arm64 only internal references need extra work.
DCHECK(RelocInfo::IsInternalReference(rmode_));
}
-Address Assembler::break_address_from_return_address(Address pc) {
- return pc - Assembler::kPatchDebugBreakSlotReturnOffset;
-}
-
-
Address Assembler::return_address_from_call_start(Address pc) {
// The call, generated by MacroAssembler::Call, is one of two possible
// sequences:
}
-Address RelocInfo::call_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+Address RelocInfo::debug_call_address() {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
// For the above sequences the Relocinfo points to the load literal loading
// the call address.
+ STATIC_ASSERT(Assembler::kPatchDebugBreakSlotAddressOffset == 0);
return Assembler::target_address_at(pc_, host_);
}
-void RelocInfo::set_call_address(Address target) {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+void RelocInfo::set_debug_call_address(Address target) {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ STATIC_ASSERT(Assembler::kPatchDebugBreakSlotAddressOffset == 0);
Assembler::set_target_address_at(pc_, host_, target);
if (host() != NULL) {
Object* target_code = Code::GetCodeFromTargetAddress(target);
// The sequence must be:
// ldr ip0, [pc, #offset]
// blr ip0
- // See arm64/debug-arm64.cc BreakLocation::SetDebugBreakAtReturn().
+ // See arm64/debug-arm64.cc DebugCodegen::PatchDebugBreakSlot
Instruction* i1 = reinterpret_cast<Instruction*>(pc_);
Instruction* i2 = i1->following();
return i1->IsLdrLiteralX() && (i1->Rt() == kIp0Code) &&
visitor->VisitExternalReference(this);
} else if (mode == RelocInfo::INTERNAL_REFERENCE) {
visitor->VisitInternalReference(this);
- } else if (((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence())) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence() &&
isolate->debug()->has_break_points()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
} else if (mode == RelocInfo::INTERNAL_REFERENCE) {
StaticVisitor::VisitInternalReference(this);
} else if (heap->isolate()->debug()->has_break_points() &&
- ((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence()))) {
+ RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
StaticVisitor::VisitRuntimeEntry(this);
void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
// We do not try to reuse pool constants.
RelocInfo rinfo(reinterpret_cast<byte*>(pc_), rmode, data, NULL);
- if (((rmode >= RelocInfo::JS_RETURN) &&
+ if (((rmode >= RelocInfo::COMMENT) &&
(rmode <= RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL)) ||
(rmode == RelocInfo::INTERNAL_REFERENCE) ||
(rmode == RelocInfo::CONST_POOL) || (rmode == RelocInfo::VENEER_POOL) ||
(rmode == RelocInfo::DEOPT_REASON) ||
(rmode == RelocInfo::GENERATOR_CONTINUATION)) {
// Adjust code for new modes.
- DCHECK(RelocInfo::IsDebugBreakSlot(rmode) || RelocInfo::IsJSReturn(rmode) ||
- RelocInfo::IsComment(rmode) || RelocInfo::IsDeoptReason(rmode) ||
- RelocInfo::IsPosition(rmode) ||
+ DCHECK(RelocInfo::IsDebugBreakSlot(rmode) || RelocInfo::IsComment(rmode) ||
+ RelocInfo::IsDeoptReason(rmode) || RelocInfo::IsPosition(rmode) ||
RelocInfo::IsInternalReference(rmode) ||
RelocInfo::IsConstPool(rmode) || RelocInfo::IsVeneerPool(rmode) ||
RelocInfo::IsGeneratorContinuation(rmode));
// instruction stream that call will return from.
inline static Address return_address_from_call_start(Address pc);
- // Return the code target address of the patch debug break slot
- inline static Address break_address_from_return_address(Address pc);
-
// This sets the branch destination (which is in the constant pool on ARM).
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
return SizeOfCodeGeneratedSince(label) / kInstructionSize;
}
- // Number of instructions generated for the return sequence in
- // FullCodeGenerator::EmitReturnSequence.
- static const int kJSReturnSequenceInstructions = 7;
- static const int kJSReturnSequenceLength =
- kJSReturnSequenceInstructions * kInstructionSize;
- // Distance between start of patched return sequence and the emitted address
- // to jump to.
- static const int kPatchReturnSequenceAddressOffset = 0;
static const int kPatchDebugBreakSlotAddressOffset = 0;
// Number of instructions necessary to be able to later patch it to a call.
- // See DebugCodegen::GenerateSlot() and
- // BreakLocation::SetDebugBreakAtSlot().
- static const int kDebugBreakSlotInstructions = 4;
+ static const int kDebugBreakSlotInstructions = 5;
static const int kDebugBreakSlotLength =
kDebugBreakSlotInstructions * kInstructionSize;
- static const int kPatchDebugBreakSlotReturnOffset = 2 * kInstructionSize;
-
// Prevent contant pool emission until EndBlockConstPool is called.
// Call to this function can be nested but must be followed by an equal
// number of call to EndBlockConstpool.
int buffer_space() const;
- // Mark address of the ExitJSFrame code.
- void RecordJSReturn();
-
// Mark generator continuation.
void RecordGeneratorContinuation();
// Mark address of a debug break slot.
- void RecordDebugBreakSlot();
- void RecordDebugBreakSlotForCall(int argc);
- void RecordDebugBreakSlotForConstructCall();
+ void RecordDebugBreakSlot(RelocInfo::Mode mode, int argc = 0);
// Record the emission of a constant pool.
//
namespace v8 {
namespace internal {
-
#define __ ACCESS_MASM(masm)
-void BreakLocation::SetDebugBreakAtReturn() {
- // Patch the code emitted by FullCodeGenerator::EmitReturnSequence, changing
- // the return from JS function sequence from
- // mov sp, fp
- // ldp fp, lr, [sp] #16
- // lrd ip0, [pc, #(3 * kInstructionSize)]
- // add sp, sp, ip0
- // ret
- // <number of paramters ...
- // ... plus one (64 bits)>
- // to a call to the debug break return code.
- // ldr ip0, [pc, #(3 * kInstructionSize)]
- // blr ip0
- // hlt kHltBadCode @ code should not return, catch if it does.
- // <debug break return code ...
- // ... entry point address (64 bits)>
-
- // The patching code must not overflow the space occupied by the return
- // sequence.
- STATIC_ASSERT(Assembler::kJSReturnSequenceInstructions >= 5);
- PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc()), 5);
- byte* entry =
- debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry();
-
- // The first instruction of a patched return sequence must be a load literal
- // loading the address of the debug break return code.
- patcher.ldr_pcrel(ip0, (3 * kInstructionSize) >> kLoadLiteralScaleLog2);
- // TODO(all): check the following is correct.
- // The debug break return code will push a frame and call statically compiled
- // code. By using blr, even though control will not return after the branch,
- // this call site will be registered in the frame (lr being saved as the pc
- // of the next instruction to execute for this frame). The debugger can now
- // iterate on the frames to find call to debug break return code.
- patcher.blr(ip0);
- patcher.hlt(kHltBadCode);
- patcher.dc64(reinterpret_cast<int64_t>(entry));
+void EmitDebugBreakSlot(Assembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(Assembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ static_cast<int>(masm->InstructionsGeneratedSince(&check_size)));
+}
+
+
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the constant pool in the debug break slot code.
+ InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc),
+ Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(&patcher);
}
-void BreakLocation::SetDebugBreakAtSlot() {
- DCHECK(IsDebugBreakSlot());
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc),
+ Assembler::kDebugBreakSlotInstructions);
// Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug
// break slot code from
// mov x0, x0 @ nop DEBUG_BREAK_NOP
// mov x0, x0 @ nop DEBUG_BREAK_NOP
// mov x0, x0 @ nop DEBUG_BREAK_NOP
// mov x0, x0 @ nop DEBUG_BREAK_NOP
+ // mov x0, x0 @ nop DEBUG_BREAK_NOP
// to a call to the debug slot code.
// ldr ip0, [pc, #(2 * kInstructionSize)]
// blr ip0
- // <debug break slot code ...
- // ... entry point address (64 bits)>
-
- // TODO(all): consider adding a hlt instruction after the blr as we don't
- // expect control to return here. This implies increasing
- // kDebugBreakSlotInstructions to 5 instructions.
-
- // The patching code must not overflow the space occupied by the return
- // sequence.
- STATIC_ASSERT(Assembler::kDebugBreakSlotInstructions >= 4);
- PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc()), 4);
- byte* entry =
- debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry();
+ // b skip
+ // <debug break slot code entry point address (64 bits)>
+ // skip:
+ Label skip_constant;
// The first instruction of a patched debug break slot must be a load literal
// loading the address of the debug break slot code.
patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2);
+ patcher.b(&skip_constant);
+ patcher.dc64(reinterpret_cast<int64_t>(code->entry()));
+ patcher.bind(&skip_constant);
// TODO(all): check the following is correct.
// The debug break slot code will push a frame and call statically compiled
- // code. By using blr, event hough control will not return after the branch,
- // this call site will be registered in the frame (lr being saved as the pc
- // of the next instruction to execute for this frame). The debugger can now
- // iterate on the frames to find call to debug break slot code.
+ // code. By using blr, this call site will be registered in the frame.
+ // The debugger can now iterate on the frames to find this call.
patcher.blr(ip0);
- patcher.dc64(reinterpret_cast<int64_t>(entry));
}
-static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList object_regs) {
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
Register scratch = x10;
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
__ Push(scratch);
- // Any live values (object_regs and non_object_regs) in caller-saved
- // registers (or lr) need to be stored on the stack so that their values are
- // safely preserved for a call into C code.
- //
- // Also:
- // * object_regs may be modified during the C code by the garbage
- // collector. Every object register must be a valid tagged pointer or
- // SMI.
- //
- // * non_object_regs will be converted to SMIs so that the garbage
- // collector doesn't try to interpret them as pointers.
- //
- // TODO(jbramley): Why can't this handle callee-saved registers?
- DCHECK((~kCallerSaved.list() & object_regs) == 0);
- DCHECK((scratch.Bit() & object_regs) == 0);
- DCHECK((masm->TmpList()->list() & object_regs) == 0);
- STATIC_ASSERT(kSmiValueSize == 32);
-
- if (object_regs != 0) {
- __ PushXRegList(object_regs);
- }
+ if (mode == SAVE_RESULT_REGISTER) __ Push(x0);
-#ifdef DEBUG
- __ RecordComment("// Calling from debug break to runtime - come in - over");
-#endif
__ Mov(x0, 0); // No arguments.
__ Mov(x1, ExternalReference::debug_break(masm->isolate()));
CEntryStub stub(masm->isolate(), 1);
__ CallStub(&stub);
- // Restore the register values from the expression stack.
- if (object_regs != 0) {
- __ PopXRegList(object_regs);
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = Register::XRegFromCode(JSCallerSavedCode(i));
+ __ Mov(reg, Operand(kDebugZapValue));
+ }
}
+ // Restore the register values from the expression stack.
+ if (mode == SAVE_RESULT_REGISTER) __ Pop(x0);
+
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
}
-void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
- // In places other than IC call sites it is expected that r0 is TOS which
- // is an object - this is not generally the case so this should be used with
- // care.
- Generate_DebugBreakCallHelper(masm, x0.Bit());
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm,
- DebugCodegen::SlotLocation location,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the constant pool in the debug break slot code.
- InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions);
- RecordRelocInfo(masm, location, call_argc);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(Assembler::DEBUG_BREAK_NOP);
- }
-}
-
-
-void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
- // In the places where a debug break slot is inserted no registers can contain
- // object pointers.
- Generate_DebugBreakCallHelper(masm, 0);
-}
-
-
void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
__ Ret();
}
EmitProfilingCounterReset();
__ Bind(&ok);
- // Make sure that the constant pool is not emitted inside of the return
- // sequence. This sequence can get patched when the debugger is used. See
- // debug-arm64.cc:BreakLocation::SetDebugBreakAtReturn().
- {
- InstructionAccurateScope scope(masm_,
- Assembler::kJSReturnSequenceInstructions);
- SetReturnPosition(function());
- __ RecordJSReturn();
- // This code is generated using Assembler methods rather than Macro
- // Assembler methods because it will be patched later on, and so the size
- // of the generated code must be consistent.
- const Register& current_sp = __ StackPointer();
- // Nothing ensures 16 bytes alignment here.
- DCHECK(!current_sp.Is(csp));
- __ mov(current_sp, fp);
- int no_frame_start = masm_->pc_offset();
- __ ldp(fp, lr, MemOperand(current_sp, 2 * kXRegSize, PostIndex));
- // Drop the arguments and receiver and return.
- // TODO(all): This implementation is overkill as it supports 2**31+1
- // arguments, consider how to improve it without creating a security
- // hole.
- __ ldr_pcrel(ip0, (3 * kInstructionSize) >> kLoadLiteralScaleLog2);
- __ add(current_sp, current_sp, ip0);
- __ ret();
- int32_t arg_count = info_->scope()->num_parameters() + 1;
- __ dc64(kXRegSize * arg_count);
- info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
- }
+ SetReturnPosition(function());
+ const Register& current_sp = __ StackPointer();
+ // Nothing ensures 16 bytes alignment here.
+ DCHECK(!current_sp.Is(csp));
+ __ Mov(current_sp, fp);
+ int no_frame_start = masm_->pc_offset();
+ __ Ldp(fp, lr, MemOperand(current_sp, 2 * kXRegSize, PostIndex));
+ // Drop the arguments and receiver and return.
+ // TODO(all): This implementation is overkill as it supports 2**31+1
+ // arguments, consider how to improve it without creating a security
+ // hole.
+ __ ldr_pcrel(ip0, (3 * kInstructionSize) >> kLoadLiteralScaleLog2);
+ __ Add(current_sp, current_sp, ip0);
+ __ Ret();
+ int32_t arg_count = info_->scope()->num_parameters() + 1;
+ __ dc64(kXRegSize * arg_count);
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
}
return "property cell";
case RUNTIME_ENTRY:
return "runtime entry";
- case JS_RETURN:
- return "js return";
case COMMENT:
return "comment";
case POSITION:
return "veneer pool";
case DEBUG_BREAK_SLOT_AT_POSITION:
return "debug break slot at position";
+ case DEBUG_BREAK_SLOT_AT_RETURN:
+ return "debug break slot at return";
case DEBUG_BREAK_SLOT_AT_CALL:
return "debug break slot at call";
case DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL:
break;
}
case RUNTIME_ENTRY:
- case JS_RETURN:
case COMMENT:
case POSITION:
case STATEMENT_POSITION:
case CONST_POOL:
case VENEER_POOL:
case DEBUG_BREAK_SLOT_AT_POSITION:
+ case DEBUG_BREAK_SLOT_AT_RETURN:
case DEBUG_BREAK_SLOT_AT_CALL:
case DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL:
case GENERATOR_CONTINUATION:
}
-void Assembler::RecordJSReturn() {
- positions_recorder()->WriteRecordedPositions();
- EnsureSpace ensure_space(this);
- RecordRelocInfo(RelocInfo::JS_RETURN);
-}
-
-
void Assembler::RecordGeneratorContinuation() {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::GENERATOR_CONTINUATION);
}
-void Assembler::RecordDebugBreakSlot() {
- EnsureSpace ensure_space(this);
- RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
-}
-
-
-void Assembler::RecordDebugBreakSlotForCall(int argc) {
- EnsureSpace ensure_space(this);
- intptr_t data = static_cast<intptr_t>(argc);
- RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL, data);
-}
-
-
-void Assembler::RecordDebugBreakSlotForConstructCall() {
+void Assembler::RecordDebugBreakSlot(RelocInfo::Mode mode, int call_argc) {
EnsureSpace ensure_space(this);
- RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL);
+ DCHECK(RelocInfo::IsDebugBreakSlot(mode));
+ intptr_t data = static_cast<intptr_t>(call_argc);
+ RecordRelocInfo(mode, data);
}
// Everything after runtime_entry (inclusive) is not GC'ed.
RUNTIME_ENTRY,
- JS_RETURN, // Marks start of the ExitJSFrame code.
COMMENT,
POSITION, // See comment for kNoPosition above.
STATEMENT_POSITION, // See comment for kNoPosition above.
// Additional code inserted for debug break slot.
DEBUG_BREAK_SLOT_AT_POSITION,
+ DEBUG_BREAK_SLOT_AT_RETURN,
DEBUG_BREAK_SLOT_AT_CALL,
DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL,
static inline bool IsGCRelocMode(Mode mode) {
return mode <= LAST_GCED_ENUM;
}
- static inline bool IsJSReturn(Mode mode) {
- return mode == JS_RETURN;
- }
static inline bool IsComment(Mode mode) {
return mode == COMMENT;
}
return mode == INTERNAL_REFERENCE_ENCODED;
}
static inline bool IsDebugBreakSlot(Mode mode) {
- return IsDebugBreakSlotAtPosition(mode) || IsDebugBreakSlotAtCall(mode) ||
+ return IsDebugBreakSlotAtPosition(mode) || IsDebugBreakSlotAtReturn(mode) ||
+ IsDebugBreakSlotAtCall(mode) ||
IsDebugBreakSlotAtConstructCall(mode);
}
static inline bool IsDebugBreakSlotAtPosition(Mode mode) {
return mode == DEBUG_BREAK_SLOT_AT_POSITION;
}
+ static inline bool IsDebugBreakSlotAtReturn(Mode mode) {
+ return mode == DEBUG_BREAK_SLOT_AT_RETURN;
+ }
static inline bool IsDebugBreakSlotAtCall(Mode mode) {
return mode == DEBUG_BREAK_SLOT_AT_CALL;
}
Code* host() const { return host_; }
void set_host(Code* host) { host_ = host; }
- // Apply a relocation by delta bytes
- INLINE(void apply(intptr_t delta,
- ICacheFlushMode icache_flush_mode =
- FLUSH_ICACHE_IF_NEEDED));
+ // Apply a relocation by delta bytes. When the code object is moved, PC
+ // relative addresses have to be updated as well as absolute addresses
+ // inside the code (internal references).
+ // Do not forget to flush the icache afterwards!
+ INLINE(void apply(intptr_t delta));
// Is the pointer this relocation info refers to coded like a plain pointer
// or is it strange in some way (e.g. relative or patched into a series of
// Read/modify the address of a call instruction. This is used to relocate
// the break points where straight-line code is patched with a call
// instruction.
- INLINE(Address call_address());
- INLINE(void set_call_address(Address target));
- INLINE(Object* call_object());
- INLINE(void set_call_object(Object* target));
- INLINE(Object** call_object_address());
+ INLINE(Address debug_call_address());
+ INLINE(void set_debug_call_address(Address target));
// Wipe out a relocation to a fixed value, used for making snapshots
// reproducible.
static const int kDataMask =
(1 << CODE_TARGET_WITH_ID) | kPositionMask | (1 << COMMENT);
static const int kDebugBreakSlotMask =
- 1 << DEBUG_BREAK_SLOT_AT_POSITION | 1 << DEBUG_BREAK_SLOT_AT_CALL |
- 1 << DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL;
- static const int kApplyMask; // Modes affected by apply. Depends on arch.
+ 1 << DEBUG_BREAK_SLOT_AT_POSITION | 1 << DEBUG_BREAK_SLOT_AT_RETURN |
+ 1 << DEBUG_BREAK_SLOT_AT_CALL | 1 << DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL;
+ static const int kApplyMask; // Modes affected by apply. Depends on arch.
private:
// On ARM, note that pc_ is the address of the constant pool entry
static void Generate_Return_DebugBreak(MacroAssembler* masm) {
- DebugCodegen::GenerateReturnDebugBreak(masm);
+ DebugCodegen::GenerateDebugBreakStub(masm,
+ DebugCodegen::SAVE_RESULT_REGISTER);
}
static void Generate_Slot_DebugBreak(MacroAssembler* masm) {
- DebugCodegen::GenerateSlotDebugBreak(masm);
+ DebugCodegen::GenerateDebugBreakStub(masm,
+ DebugCodegen::IGNORE_RESULT_REGISTER);
}
BreakLocation::BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo,
- RelocInfo* original_rinfo, int position,
- int statement_position)
+ int position, int statement_position)
: debug_info_(debug_info),
pc_offset_(static_cast<int>(rinfo->pc() - debug_info->code()->entry())),
- original_pc_offset_(static_cast<int>(
- original_rinfo->pc() - debug_info->original_code()->entry())),
rmode_(rinfo->rmode()),
- original_rmode_(original_rinfo->rmode()),
data_(rinfo->data()),
- original_data_(original_rinfo->data()),
position_(position),
statement_position_(statement_position) {}
BreakLocatorType type)
: debug_info_(debug_info),
reloc_iterator_(debug_info->code(), GetModeMask(type)),
- reloc_iterator_original_(debug_info->original_code(), GetModeMask(type)),
break_index_(-1),
position_(1),
statement_position_(1) {
- if (!RinfoDone()) Next();
+ if (!Done()) Next();
}
int mask = 0;
mask |= RelocInfo::ModeMask(RelocInfo::POSITION);
mask |= RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION);
- mask |= RelocInfo::ModeMask(RelocInfo::JS_RETURN);
+ mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL);
mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL);
if (type == ALL_BREAK_LOCATIONS) {
void BreakLocation::Iterator::Next() {
DisallowHeapAllocation no_gc;
- DCHECK(!RinfoDone());
+ DCHECK(!Done());
// Iterate through reloc info for code and original code stopping at each
// breakable code target.
bool first = break_index_ == -1;
- while (!RinfoDone()) {
- if (!first) RinfoNext();
+ while (!Done()) {
+ if (!first) reloc_iterator_.next();
first = false;
- if (RinfoDone()) return;
+ if (Done()) return;
// Whenever a statement position or (plain) position is passed update the
// current value of these.
continue;
}
- if (RelocInfo::IsJSReturn(rmode())) {
+ DCHECK(RelocInfo::IsDebugBreakSlot(rmode()) ||
+ RelocInfo::IsDebuggerStatement(rmode()));
+
+ if (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) {
// Set the positions to the end of the function.
if (debug_info_->shared()->HasSourceCode()) {
position_ = debug_info_->shared()->end_position() -
position_ = 0;
}
statement_position_ = position_;
- break;
}
- DCHECK(RelocInfo::IsDebugBreakSlot(rmode()) ||
- RelocInfo::IsDebuggerStatement(rmode()));
break;
}
break_index_++;
// the address.
BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info,
BreakLocatorType type, Address pc) {
+ DCHECK(debug_info->code()->has_debug_break_slots());
Iterator it(debug_info, type);
it.SkipTo(BreakIndexFromAddress(debug_info, type, pc));
return it.GetBreakLocation();
void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info,
BreakLocatorType type, Address pc,
List<BreakLocation>* result_out) {
+ DCHECK(debug_info->code()->has_debug_break_slots());
int break_index = BreakIndexFromAddress(debug_info, type, pc);
Iterator it(debug_info, type);
it.SkipTo(break_index);
BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info,
BreakLocatorType type, int position,
BreakPositionAlignment alignment) {
+ DCHECK(debug_info->code()->has_debug_break_slots());
// Run through all break points to locate the one closest to the source
// position.
int closest_break = 0;
// handler as the handler and the function is the same.
if (IsDebugBreak()) return;
- if (IsExit()) {
- // Patch the frame exit code with a break point.
- SetDebugBreakAtReturn();
- } else if (IsDebugBreakSlot()) {
- // Patch the code in the break slot.
- SetDebugBreakAtSlot();
- }
+ DCHECK(IsDebugBreakSlot());
+ Builtins* builtins = debug_info_->GetIsolate()->builtins();
+ Handle<Code> target =
+ IsReturn() ? builtins->Return_DebugBreak() : builtins->Slot_DebugBreak();
+ DebugCodegen::PatchDebugBreakSlot(pc(), target);
DCHECK(IsDebugBreak());
}
// Debugger statement always calls debugger. No need to modify it.
if (IsDebuggerStatement()) return;
- if (IsExit()) {
- // Restore the frame exit code with a break point.
- RestoreFromOriginal(Assembler::kJSReturnSequenceLength);
- } else if (IsDebugBreakSlot()) {
- // Restore the code in the break slot.
- RestoreFromOriginal(Assembler::kDebugBreakSlotLength);
- }
+ DCHECK(IsDebugBreakSlot());
+ DebugCodegen::ClearDebugBreakSlot(pc());
DCHECK(!IsDebugBreak());
}
-void BreakLocation::RestoreFromOriginal(int length_in_bytes) {
- memcpy(pc(), original_pc(), length_in_bytes);
- CpuFeatures::FlushICache(pc(), length_in_bytes);
-}
-
-
bool BreakLocation::IsStepInLocation() const {
return IsConstructCall() || IsCall();
}
bool BreakLocation::IsDebugBreak() const {
- if (IsExit()) {
- return rinfo().IsPatchedReturnSequence();
- } else if (IsDebugBreakSlot()) {
+ if (IsDebugBreakSlot()) {
return rinfo().IsPatchedDebugBreakSlotSequence();
}
return false;
}
-bool BreakLocation::Iterator::RinfoDone() const {
- DCHECK(reloc_iterator_.done() == reloc_iterator_original_.done());
- return reloc_iterator_.done();
-}
-
-
-void BreakLocation::Iterator::RinfoNext() {
- reloc_iterator_.next();
- reloc_iterator_original_.next();
-#ifdef DEBUG
- DCHECK(reloc_iterator_.done() == reloc_iterator_original_.done());
- DCHECK(reloc_iterator_.done() || rmode() == original_rinfo()->rmode());
-#endif
-}
-
-
// Threading support.
void Debug::ThreadInit() {
thread_local_.break_count_ = 0;
}
// If this is the last break code target step out is the only possibility.
- if (location.IsExit() || step_action == StepOut) {
+ if (location.IsReturn() || step_action == StepOut) {
if (step_action == StepOut) {
// Skip step_count frames starting with the current one.
while (step_count-- > 0 && !frames_it.done()) {
frames_it.Advance();
}
} else {
- DCHECK(location.IsExit());
+ DCHECK(location.IsReturn());
frames_it.Advance();
}
// Skip native and extension functions on the stack.
// statement is hit.
if (step_action == StepNext || step_action == StepIn) {
// Never continue if returning from function.
- if (break_location->IsExit()) return false;
+ if (break_location->IsReturn()) return false;
// Continue if we are still on the same frame and in the same statement.
int current_statement_position =
if (LiveEdit::SetAfterBreakTarget(this)) return; // LiveEdit did the job.
- HandleScope scope(isolate_);
- PrepareForBreakPoints();
-
- // Get the executing function in which the debug break occurred.
- Handle<JSFunction> function(JSFunction::cast(frame->function()));
- Handle<SharedFunctionInfo> shared(function->shared());
- if (!EnsureDebugInfo(shared, function)) {
- // Return if we failed to retrieve the debug info.
- return;
- }
- Handle<DebugInfo> debug_info = GetDebugInfo(shared);
- Handle<Code> code(debug_info->code());
- Handle<Code> original_code(debug_info->original_code());
-#ifdef DEBUG
- // Get the code which is actually executing.
- Handle<Code> frame_code(frame->LookupCode());
- DCHECK(frame_code.is_identical_to(code));
-#endif
-
- // Find the call address in the running code. This address holds the call to
- // either a DebugBreakXXX or to the debug break return entry code if the
- // break point is still active after processing the break point.
- Address addr = Assembler::break_address_from_return_address(frame->pc());
-
- // Check if the location is at JS exit or debug break slot.
- bool at_js_return = false;
- bool break_at_js_return_active = false;
- bool at_debug_break_slot = false;
- RelocIterator it(debug_info->code());
- while (!it.done() && !at_js_return && !at_debug_break_slot) {
- if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
- at_js_return = (it.rinfo()->pc() ==
- addr - Assembler::kPatchReturnSequenceAddressOffset);
- break_at_js_return_active = it.rinfo()->IsPatchedReturnSequence();
- }
- if (RelocInfo::IsDebugBreakSlot(it.rinfo()->rmode())) {
- at_debug_break_slot = (it.rinfo()->pc() ==
- addr - Assembler::kPatchDebugBreakSlotAddressOffset);
- }
- it.next();
- }
-
- // Handle the jump to continue execution after break point depending on the
- // break location.
- if (at_js_return) {
- // If the break point at return is still active jump to the corresponding
- // place in the original code. If not the break point was removed during
- // break point processing.
- if (break_at_js_return_active) {
- addr += original_code->instruction_start() - code->instruction_start();
- }
-
- // Move back to where the call instruction sequence started.
- after_break_target_ = addr - Assembler::kPatchReturnSequenceAddressOffset;
- } else if (at_debug_break_slot) {
- // Address of where the debug break slot starts.
- addr = addr - Assembler::kPatchDebugBreakSlotAddressOffset;
-
- // Continue just after the slot.
- after_break_target_ = addr + Assembler::kDebugBreakSlotLength;
- }
+ // Continue just after the slot.
+ after_break_target_ = frame->pc();
}
// If there are no break points this cannot be break at return, as
// the debugger statement and stack guard debug break cannot be at
// return.
- if (!has_break_points_) {
- return false;
- }
+ if (!has_break_points_) return false;
PrepareForBreakPoints();
DCHECK(frame_code.is_identical_to(code));
#endif
- // Find the call address in the running code.
- Address addr = Assembler::break_address_from_return_address(frame->pc());
-
- // Check if the location is at JS return.
- RelocIterator it(debug_info->code());
- while (!it.done()) {
- if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
- return (it.rinfo()->pc() ==
- addr - Assembler::kPatchReturnSequenceAddressOffset);
- }
- it.next();
+ // Find the reloc info matching the start of the debug break slot.
+ Address slot_pc = frame->pc() - Assembler::kDebugBreakSlotLength;
+ int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
+ for (RelocIterator it(debug_info->code(), mask); !it.done(); it.next()) {
+ if (it.rinfo()->pc() == slot_pc) return true;
}
return false;
}
queue_.Clear();
}
-
-void DebugCodegen::RecordRelocInfo(MacroAssembler* masm,
- DebugCodegen::SlotLocation location,
- int call_argc) {
- switch (location) {
- case PLAIN_DEBUG_BREAK:
- masm->RecordDebugBreakSlot();
- return;
- case DEBUG_BREAK_AT_CALL:
- DCHECK_LE(0, call_argc);
- masm->RecordDebugBreakSlotForCall(call_argc);
- return;
- case DEBUG_BREAK_AT_CONSTRUCT_CALL:
- masm->RecordDebugBreakSlotForConstructCall();
- return;
- }
-}
-
} // namespace internal
} // namespace v8
BreakPositionAlignment alignment);
bool IsDebugBreak() const;
- inline bool IsExit() const { return RelocInfo::IsJSReturn(rmode_); }
+
+ inline bool IsReturn() const {
+ return RelocInfo::IsDebugBreakSlotAtReturn(rmode_);
+ }
inline bool IsCall() const {
return RelocInfo::IsDebugBreakSlotAtCall(rmode_);
}
inline int statement_position() const { return statement_position_; }
inline Address pc() const { return code()->entry() + pc_offset_; }
- inline Address original_pc() const {
- return debug_info_->original_code()->entry() + original_pc_offset_;
- }
inline RelocInfo::Mode rmode() const { return rmode_; }
inline Code* code() const { return debug_info_->code(); }
private:
- BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo,
- RelocInfo* original_rinfo, int position,
+ BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo, int position,
int statement_position);
class Iterator {
Iterator(Handle<DebugInfo> debug_info, BreakLocatorType type);
BreakLocation GetBreakLocation() {
- return BreakLocation(debug_info_, rinfo(), original_rinfo(), position(),
+ return BreakLocation(debug_info_, rinfo(), position(),
statement_position());
}
- inline bool Done() const { return RinfoDone(); }
+ inline bool Done() const { return reloc_iterator_.done(); }
void Next();
void SkipTo(int count) {
inline RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); }
inline RelocInfo* rinfo() { return reloc_iterator_.rinfo(); }
- inline RelocInfo* original_rinfo() {
- return reloc_iterator_original_.rinfo();
- }
-
inline Address pc() { return rinfo()->pc(); }
int break_index() const { return break_index_; }
inline int position() const { return position_; }
private:
static int GetModeMask(BreakLocatorType type);
- bool RinfoDone() const;
- void RinfoNext();
-
Handle<DebugInfo> debug_info_;
RelocIterator reloc_iterator_;
- RelocIterator reloc_iterator_original_;
int break_index_;
int position_;
int statement_position_;
static int BreakIndexFromAddress(Handle<DebugInfo> debug_info,
BreakLocatorType type, Address pc);
- void ClearDebugBreak();
- void RestoreFromOriginal(int length_in_bytes);
-
void SetDebugBreak();
- void SetDebugBreakAtReturn();
- void SetDebugBreakAtSlot();
+ void ClearDebugBreak();
inline bool IsDebuggerStatement() const {
return RelocInfo::IsDebuggerStatement(rmode_);
Handle<DebugInfo> debug_info_;
int pc_offset_;
- int original_pc_offset_;
RelocInfo::Mode rmode_;
- RelocInfo::Mode original_rmode_;
intptr_t data_;
- intptr_t original_data_;
int position_;
int statement_position_;
};
// Code generator routines.
class DebugCodegen : public AllStatic {
public:
- enum SlotLocation {
- PLAIN_DEBUG_BREAK,
- DEBUG_BREAK_AT_CALL,
- DEBUG_BREAK_AT_CONSTRUCT_CALL
+ enum DebugBreakCallHelperMode {
+ SAVE_RESULT_REGISTER,
+ IGNORE_RESULT_REGISTER
};
- static void GenerateSlot(MacroAssembler* masm, SlotLocation location,
- int call_argc = -1);
- static void GenerateReturnDebugBreak(MacroAssembler* masm);
- static void GenerateSlotDebugBreak(MacroAssembler* masm);
+ static void GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode);
+
static void GeneratePlainReturnLiveEdit(MacroAssembler* masm);
// FrameDropper is a code replacement for a JavaScript frame with possibly
// called, it only gets returned to.
static void GenerateFrameDropperLiveEdit(MacroAssembler* masm);
- private:
- static void RecordRelocInfo(MacroAssembler* masm, SlotLocation location,
- int call_argc);
+
+ static void GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc = -1);
+
+ static void PatchDebugBreakSlot(Address pc, Handle<Code> code);
+ static void ClearDebugBreakSlot(Address pc);
};
Handle<DebugInfo> Factory::NewDebugInfo(Handle<SharedFunctionInfo> shared) {
- // Get the original code of the function.
- Handle<Code> code(shared->code());
-
- // Create a copy of the code before allocating the debug info object to avoid
- // allocation while setting up the debug info object.
- Handle<Code> original_code(*Factory::CopyCode(code));
-
// Allocate initial fixed array for active break points before allocating the
// debug info object to avoid allocation while setting up the debug info
// object.
Handle<DebugInfo> debug_info =
Handle<DebugInfo>::cast(NewStruct(DEBUG_INFO_TYPE));
debug_info->set_shared(*shared);
- debug_info->set_original_code(*original_code);
- debug_info->set_code(*code);
+ debug_info->set_code(shared->code());
debug_info->set_break_points(*break_points);
// Link debug info to function.
void FullCodeGenerator::SetReturnPosition(FunctionLiteral* fun) {
RecordStatementPosition(masm_, fun->end_position() - 1);
+ if (info_->is_debug()) {
+ // Always emit a debug break slot before a return.
+ DebugCodegen::GenerateSlot(masm_, RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN);
+ }
}
bool recorded = RecordStatementPosition(masm_, stmt->position());
if (recorded && insert_break == INSERT_BREAK && info_->is_debug() &&
!stmt->IsDebuggerStatement()) {
- DebugCodegen::GenerateSlot(masm_, DebugCodegen::PLAIN_DEBUG_BREAK);
+ DebugCodegen::GenerateSlot(masm_, RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
}
}
if (expr->position() == RelocInfo::kNoPosition) return;
bool recorded = RecordPosition(masm_, expr->position());
if (recorded && insert_break == INSERT_BREAK && info_->is_debug()) {
- DebugCodegen::GenerateSlot(masm_, DebugCodegen::PLAIN_DEBUG_BREAK);
+ DebugCodegen::GenerateSlot(masm_, RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
}
}
if (expr->position() == RelocInfo::kNoPosition) return;
bool recorded = RecordStatementPosition(masm_, expr->position());
if (recorded && info_->is_debug()) {
- DebugCodegen::GenerateSlot(masm_, DebugCodegen::PLAIN_DEBUG_BREAK);
+ DebugCodegen::GenerateSlot(masm_, RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
}
}
RecordPosition(masm_, expr->position());
if (info_->is_debug()) {
// Always emit a debug break slot before a call.
- DebugCodegen::GenerateSlot(masm_, DebugCodegen::DEBUG_BREAK_AT_CALL, argc);
+ DebugCodegen::GenerateSlot(masm_, RelocInfo::DEBUG_BREAK_SLOT_AT_CALL,
+ argc);
}
}
if (info_->is_debug()) {
// Always emit a debug break slot before a construct call.
DebugCodegen::GenerateSlot(masm_,
- DebugCodegen::DEBUG_BREAK_AT_CONSTRUCT_CALL);
+ RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL);
}
}
}
void VisitDebugTarget(RelocInfo* rinfo) {
- DCHECK((RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
- rinfo->IsPatchedDebugBreakSlotSequence()));
- Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
+ DCHECK(RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence());
+ Object* target =
+ Code::GetCodeFromTargetAddress(rinfo->debug_call_address());
VisitPointer(&target);
- rinfo->set_call_address(Code::cast(target)->instruction_start());
+ rinfo->set_debug_call_address(Code::cast(target)->instruction_start());
}
static inline void UpdateSlot(Heap* heap, Object** slot) {
if (rinfo.IsPatchedDebugBreakSlotSequence()) rinfo.Visit(isolate, v);
break;
}
- case SlotsBuffer::JS_RETURN_SLOT: {
- RelocInfo rinfo(addr, RelocInfo::JS_RETURN, 0, NULL);
- if (rinfo.IsPatchedReturnSequence()) rinfo.Visit(isolate, v);
- break;
- }
case SlotsBuffer::EMBEDDED_OBJECT_SLOT: {
RelocInfo rinfo(addr, RelocInfo::EMBEDDED_OBJECT, 0, NULL);
rinfo.Visit(isolate, v);
return SlotsBuffer::EMBEDDED_OBJECT_SLOT;
} else if (RelocInfo::IsDebugBreakSlot(rmode)) {
return SlotsBuffer::DEBUG_TARGET_SLOT;
- } else if (RelocInfo::IsJSReturn(rmode)) {
- return SlotsBuffer::JS_RETURN_SLOT;
}
UNREACHABLE();
return SlotsBuffer::NUMBER_OF_SLOT_TYPES;
CODE_TARGET_SLOT,
CODE_ENTRY_SLOT,
DEBUG_TARGET_SLOT,
- JS_RETURN_SLOT,
NUMBER_OF_SLOT_TYPES
};
return "CODE_ENTRY_SLOT";
case DEBUG_TARGET_SLOT:
return "DEBUG_TARGET_SLOT";
- case JS_RETURN_SLOT:
- return "JS_RETURN_SLOT";
case NUMBER_OF_SLOT_TYPES:
return "NUMBER_OF_SLOT_TYPES";
}
template <typename StaticVisitor>
void StaticMarkingVisitor<StaticVisitor>::VisitDebugTarget(Heap* heap,
RelocInfo* rinfo) {
- DCHECK((RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
- rinfo->IsPatchedDebugBreakSlotSequence()));
- Code* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
+ DCHECK(RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence());
+ Code* target = Code::GetCodeFromTargetAddress(rinfo->debug_call_address());
heap->mark_compact_collector()->RecordRelocSlot(rinfo, target);
StaticVisitor::MarkObject(heap, target);
}
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
- RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) |
RelocInfo::kDebugBreakSlotMask;
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
- RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) |
RelocInfo::kDebugBreakSlotMask;
// The modes possibly affected by apply must be in kApplyMask.
-void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
- bool flush_icache = icache_flush_mode != SKIP_ICACHE_FLUSH;
+void RelocInfo::apply(intptr_t delta) {
if (IsRuntimeEntry(rmode_) || IsCodeTarget(rmode_)) {
int32_t* p = reinterpret_cast<int32_t*>(pc_);
*p -= delta; // Relocate entry.
- if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t));
} else if (IsCodeAgeSequence(rmode_)) {
if (*pc_ == kCallOpcode) {
int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
*p -= delta; // Relocate entry.
- if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t));
}
- } else if (IsJSReturn(rmode_) && IsPatchedReturnSequence()) {
- // Special handling of js_return when a break point is set (call
- // instruction has been inserted).
- int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
- *p -= delta; // Relocate entry.
- if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t));
} else if (IsDebugBreakSlot(rmode_) && IsPatchedDebugBreakSlotSequence()) {
// Special handling of a debug break slot when a break point is set (call
// instruction has been inserted).
- int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
+ int32_t* p = reinterpret_cast<int32_t*>(
+ pc_ + Assembler::kPatchDebugBreakSlotAddressOffset);
*p -= delta; // Relocate entry.
- if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t));
} else if (IsInternalReference(rmode_)) {
// absolute code pointer inside code object moves with the code object.
int32_t* p = reinterpret_cast<int32_t*>(pc_);
*p += delta; // Relocate entry.
- if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t));
}
}
}
-Address RelocInfo::call_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return Assembler::target_address_at(pc_ + 1, host_);
+Address RelocInfo::debug_call_address() {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset;
+ return Assembler::target_address_at(location, host_);
}
-void RelocInfo::set_call_address(Address target) {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- Assembler::set_target_address_at(pc_ + 1, host_, target);
+void RelocInfo::set_debug_call_address(Address target) {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset;
+ Assembler::set_target_address_at(location, host_, target);
if (host() != NULL) {
Object* target_code = Code::GetCodeFromTargetAddress(target);
host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
}
-Object* RelocInfo::call_object() {
- return *call_object_address();
-}
-
-
-void RelocInfo::set_call_object(Object* target) {
- *call_object_address() = target;
-}
-
-
-Object** RelocInfo::call_object_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return reinterpret_cast<Object**>(pc_ + 1);
-}
-
-
void RelocInfo::WipeOut() {
if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) ||
IsInternalReference(rmode_)) {
visitor->VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
- } else if (((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence())) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence() &&
isolate->debug()->has_break_points()) {
visitor->VisitDebugTarget(this);
} else if (IsRuntimeEntry(mode)) {
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
} else if (heap->isolate()->debug()->has_break_points() &&
- ((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence()))) {
+ RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (IsRuntimeEntry(mode)) {
StaticVisitor::VisitRuntimeEntry(this);
}
-Address Assembler::break_address_from_return_address(Address pc) {
- return pc - Assembler::kPatchDebugBreakSlotReturnOffset;
-}
-
-
Displacement Assembler::disp_at(Label* L) {
return Displacement(long_at(L->pos()));
}
const int RelocInfo::kApplyMask =
RelocInfo::kCodeTargetMask | 1 << RelocInfo::RUNTIME_ENTRY |
- 1 << RelocInfo::JS_RETURN | 1 << RelocInfo::INTERNAL_REFERENCE |
- 1 << RelocInfo::CODE_AGE_SEQUENCE | RelocInfo::kDebugBreakSlotMask;
+ 1 << RelocInfo::INTERNAL_REFERENCE | 1 << RelocInfo::CODE_AGE_SEQUENCE |
+ RelocInfo::kDebugBreakSlotMask;
bool RelocInfo::IsCodedSpecially() {
// of that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
- // Return the code target address of the patch debug break slot
- inline static Address break_address_from_return_address(Address pc);
-
// This sets the branch destination (which is in the instruction on x86).
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
// Distance between the address of the code target in the call instruction
// and the return address
static const int kCallTargetAddressOffset = kPointerSize;
- // Distance between start of patched return sequence and the emitted address
- // to jump to.
- static const int kPatchReturnSequenceAddressOffset = 1; // JMP imm32.
-
- // Distance between start of patched debug break slot and the emitted address
- // to jump to.
- static const int kPatchDebugBreakSlotAddressOffset = 1; // JMP imm32.
static const int kCallInstructionLength = 5;
- static const int kPatchDebugBreakSlotReturnOffset = kPointerSize;
- static const int kJSReturnSequenceLength = 6;
// The debug break slot must be able to contain a call instruction.
static const int kDebugBreakSlotLength = kCallInstructionLength;
+ // Distance between start of patched debug break slot and the emitted address
+ // to jump to.
+ static const int kPatchDebugBreakSlotAddressOffset = 1; // JMP imm32.
+
// One byte opcode for test al, 0xXX.
static const byte kTestAlByte = 0xA8;
// One byte opcode for nop.
return pc_offset() - label->pos();
}
- // Mark address of the ExitJSFrame code.
- void RecordJSReturn();
-
// Mark generator continuation.
void RecordGeneratorContinuation();
// Mark address of a debug break slot.
- void RecordDebugBreakSlot();
- void RecordDebugBreakSlotForCall(int argc);
- void RecordDebugBreakSlotForConstructCall();
+ void RecordDebugBreakSlot(RelocInfo::Mode mode, int argc = 0);
// Record a comment relocation entry that can be used by a disassembler.
// Use --code-comments to enable.
namespace v8 {
namespace internal {
+#define __ ACCESS_MASM(masm)
-// Patch the code at the current PC with a call to the target address.
-// Additional guard int3 instructions can be added if required.
-void PatchCodeWithCall(Address pc, Address target, int guard_bytes) {
- // Call instruction takes up 5 bytes and int3 takes up one byte.
- static const int kCallCodeSize = 5;
- int code_size = kCallCodeSize + guard_bytes;
-
- // Create a code patcher.
- CodePatcher patcher(pc, code_size);
-// Add a label for checking the size of the code used for returning.
-#ifdef DEBUG
+void EmitDebugBreakSlot(MacroAssembler* masm) {
Label check_codesize;
- patcher.masm()->bind(&check_codesize);
-#endif
-
- // Patch the code.
- patcher.masm()->call(target, RelocInfo::NONE32);
-
- // Check that the size of the code generated is as expected.
- DCHECK_EQ(kCallCodeSize,
- patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
+ __ bind(&check_codesize);
+ __ Nop(Assembler::kDebugBreakSlotLength);
+ DCHECK_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
- // Add the requested number of int3 instructions after the call.
- DCHECK_GE(guard_bytes, 0);
- for (int i = 0; i < guard_bytes; i++) {
- patcher.masm()->int3();
- }
- CpuFeatures::FlushICache(pc, code_size);
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction.
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
}
-// Patch the JS frame exit code with a debug break call. See
-// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-ia32.cc
-// for the precise return instructions sequence.
-void BreakLocation::SetDebugBreakAtReturn() {
- DCHECK(Assembler::kJSReturnSequenceLength >=
- Assembler::kCallInstructionLength);
- PatchCodeWithCall(
- pc(), debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
- Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
+ EmitDebugBreakSlot(patcher.masm());
}
-void BreakLocation::SetDebugBreakAtSlot() {
- DCHECK(IsDebugBreakSlot());
- Isolate* isolate = debug_info_->GetIsolate();
- PatchCodeWithCall(
- pc(), isolate->builtins()->Slot_DebugBreak()->entry(),
- Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ static const int kSize = Assembler::kDebugBreakSlotLength;
+ CodePatcher patcher(pc, kSize);
+
+ // Add a label for checking the size of the code used for returning.
+ Label check_codesize;
+ patcher.masm()->bind(&check_codesize);
+ patcher.masm()->call(code->entry(), RelocInfo::NONE32);
+ // Check that the size of the code generated is as expected.
+ DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
}
-#define __ ACCESS_MASM(masm)
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
-static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList object_regs) {
// Enter an internal frame.
{
FrameScope scope(masm, StackFrame::INTERNAL);
}
__ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
- // Store the registers containing live values on the expression stack to
- // make sure that these are correctly updated during GC. Non object values
- // are stored as a smi causing it to be untouched by GC.
- DCHECK((object_regs & ~kJSCallerSaved) == 0);
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- Register reg = { r };
- if ((object_regs & (1 << r)) != 0) {
- __ push(reg);
- }
- }
+ if (mode == SAVE_RESULT_REGISTER) __ push(eax);
-#ifdef DEBUG
- __ RecordComment("// Calling from debug break to runtime - come in - over");
-#endif
__ Move(eax, Immediate(0)); // No arguments.
__ mov(ebx, Immediate(ExternalReference::debug_break(masm->isolate())));
CEntryStub ceb(masm->isolate(), 1);
__ CallStub(&ceb);
- // Automatically find register that could be used after register restore.
- // We need one register for padding skip instructions.
- Register unused_reg = { -1 };
-
- // Restore the register values containing object pointers from the
- // expression stack.
- for (int i = kNumJSCallerSaved; --i >= 0;) {
- int r = JSCallerSavedCode(i);
- Register reg = { r };
- if (FLAG_debug_code) {
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; ++i) {
+ Register reg = {JSCallerSavedCode(i)};
__ Move(reg, Immediate(kDebugZapValue));
}
- bool taken = reg.code() == esi.code();
- if ((object_regs & (1 << r)) != 0) {
- __ pop(reg);
- taken = true;
- }
- if (!taken) {
- unused_reg = reg;
- }
}
- DCHECK(unused_reg.code() != -1);
+ if (mode == SAVE_RESULT_REGISTER) __ pop(eax);
- // Read current padding counter and skip corresponding number of words.
- __ pop(unused_reg);
+ __ pop(ebx);
// We divide stored value by 2 (untagging) and multiply it by word's size.
STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
- __ lea(esp, Operand(esp, unused_reg, times_half_pointer_size, 0));
+ __ lea(esp, Operand(esp, ebx, times_half_pointer_size, 0));
// Get rid of the internal frame.
}
}
-void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
- // Register state just before return from JS function (from codegen-ia32.cc).
- // ----------- S t a t e -------------
- // -- eax: return value
- // -----------------------------------
- Generate_DebugBreakCallHelper(masm, eax.bit());
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm,
- DebugCodegen::SlotLocation location,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction.
- Label check_codesize;
- __ bind(&check_codesize);
- RecordRelocInfo(masm, location, call_argc);
- __ Nop(Assembler::kDebugBreakSlotLength);
- DCHECK_EQ(Assembler::kDebugBreakSlotLength,
- masm->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
- Generate_DebugBreakCallHelper(masm, 0);
-}
-
-
void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
masm->ret(0);
}
__ pop(eax);
EmitProfilingCounterReset();
__ bind(&ok);
-#ifdef DEBUG
- // Add a label for checking the size of the code used for returning.
- Label check_exit_codesize;
- masm_->bind(&check_exit_codesize);
-#endif
+
SetReturnPosition(function());
- __ RecordJSReturn();
- // Do not use the leave instruction here because it is too short to
- // patch with the code required by the debugger.
- __ mov(esp, ebp);
int no_frame_start = masm_->pc_offset();
- __ pop(ebp);
+ __ leave();
int arg_count = info_->scope()->num_parameters() + 1;
int arguments_bytes = arg_count * kPointerSize;
__ Ret(arguments_bytes, ecx);
- // Check that the size of the code used for returning is large enough
- // for the debugger's requirements.
- DCHECK(Assembler::kJSReturnSequenceLength <=
- masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
}
class CodePatcher {
public:
CodePatcher(byte* address, int size);
- virtual ~CodePatcher();
+ ~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }
Address IC::address() const {
// Get the address of the call.
- Address result = Assembler::target_address_from_return_address(pc());
-
- Debug* debug = isolate()->debug();
- // First check if any break points are active if not just return the address
- // of the call.
- if (!debug->has_break_points()) return result;
-
- // At least one break point is active perform additional test to ensure that
- // break point locations are updated correctly.
- if (debug->IsDebugBreak(
- Assembler::target_address_at(result, raw_constant_pool()))) {
- // If the call site is a call to debug break then return the address in
- // the original code instead of the address in the running code. This will
- // cause the original code to be updated and keeps the breakpoint active in
- // the running code.
- Code* code = GetCode();
- Code* original_code = GetOriginalCode();
- intptr_t delta =
- original_code->instruction_start() - code->instruction_start();
- // Return the address in the original code. This is the place where
- // the call which has been overwritten by the DebugBreakXXX resides
- // and the place where the inline cache system should look.
- return result + delta;
- } else {
- // No break point here just return the address of the call.
- return result;
- }
+ return Assembler::target_address_from_return_address(pc());
}
Address IC::constant_pool() const {
- if (!FLAG_enable_embedded_constant_pool) {
- return NULL;
+ if (FLAG_enable_embedded_constant_pool) {
+ return raw_constant_pool();
} else {
- Address constant_pool = raw_constant_pool();
- Debug* debug = isolate()->debug();
- // First check if any break points are active if not just return the
- // original constant pool.
- if (!debug->has_break_points()) return constant_pool;
-
- // At least one break point is active perform additional test to ensure that
- // break point locations are updated correctly.
- Address target = Assembler::target_address_from_return_address(pc());
- if (debug->IsDebugBreak(
- Assembler::target_address_at(target, constant_pool))) {
- // If the call site is a call to debug break then we want to return the
- // constant pool for the original code instead of the breakpointed code.
- return GetOriginalCode()->constant_pool();
- }
- return constant_pool;
+ return NULL;
}
}
}
-Code* IC::GetOriginalCode() const {
- HandleScope scope(isolate());
- Handle<SharedFunctionInfo> shared(GetSharedFunctionInfo(), isolate());
- DCHECK(Debug::HasDebugInfo(shared));
- Code* original_code = Debug::GetDebugInfo(shared)->original_code();
- DCHECK(original_code->IsCode());
- return original_code;
-}
-
-
bool IC::AddressIsOptimizedCode() const {
Code* host =
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(address())->code;
SharedFunctionInfo* GetSharedFunctionInfo() const;
// Get the code object of the caller.
Code* GetCode() const;
- // Get the original (non-breakpointed) code object of the caller.
- Code* GetOriginalCode() const;
bool AddressIsOptimizedCode() const;
inline bool AddressIsDeoptimizedCode() const;
}
}
- if (shared_info->debug_info()->IsDebugInfo()) {
- Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info()));
- Handle<Code> new_original_code =
- isolate->factory()->CopyCode(compile_info_wrapper.GetFunctionCode());
- debug_info->set_original_code(*new_original_code);
- }
-
int start_position = compile_info_wrapper.GetStartPosition();
int end_position = compile_info_wrapper.GetEndPosition();
shared_info->set_start_position(start_position);
// -----------------------------------------------------------------------------
// RelocInfo.
-void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
+void RelocInfo::apply(intptr_t delta) {
if (IsCodeTarget(rmode_)) {
uint32_t scope1 = (uint32_t) target_address() & ~kImm28Mask;
uint32_t scope2 = reinterpret_cast<uint32_t>(pc_) & ~kImm28Mask;
}
-Address Assembler::break_address_from_return_address(Address pc) {
- return pc - Assembler::kPatchDebugBreakSlotReturnOffset;
-}
-
-
void Assembler::set_target_internal_reference_encoded_at(Address pc,
Address target) {
// Encoded internal references are lui/ori load of 32-bit abolute address.
}
-Address RelocInfo::call_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- // The pc_ offset of 0 assumes mips patched return sequence per
- // debug-mips.cc BreakLocation::SetDebugBreakAtReturn(), or
- // debug break slot per BreakLocation::SetDebugBreakAtSlot().
+Address RelocInfo::debug_call_address() {
+ // The pc_ offset of 0 assumes patched debug break slot or return
+ // sequence.
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
return Assembler::target_address_at(pc_, host_);
}
-void RelocInfo::set_call_address(Address target) {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- // The pc_ offset of 0 assumes mips patched return sequence per
- // debug-mips.cc BreakLocation::SetDebugBreakAtReturn(), or
- // debug break slot per BreakLocation::SetDebugBreakAtSlot().
+void RelocInfo::set_debug_call_address(Address target) {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ // The pc_ offset of 0 assumes patched debug break slot or return
+ // sequence.
Assembler::set_target_address_at(pc_, host_, target);
if (host() != NULL) {
Object* target_code = Code::GetCodeFromTargetAddress(target);
}
-Object* RelocInfo::call_object() {
- return *call_object_address();
-}
-
-
-Object** RelocInfo::call_object_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
-}
-
-
-void RelocInfo::set_call_object(Object* target) {
- *call_object_address() = target;
-}
-
-
void RelocInfo::WipeOut() {
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
visitor->VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
- } else if (((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence())) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence() &&
isolate->debug()->has_break_points()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
} else if (heap->isolate()->debug()->has_break_points() &&
- ((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence()))) {
+ RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
StaticVisitor::VisitRuntimeEntry(this);
void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
// We do not try to reuse pool constants.
RelocInfo rinfo(pc_, rmode, data, NULL);
- if (rmode >= RelocInfo::JS_RETURN &&
+ if (rmode >= RelocInfo::COMMENT &&
rmode <= RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL) {
// Adjust code for new modes.
DCHECK(RelocInfo::IsDebugBreakSlot(rmode)
- || RelocInfo::IsJSReturn(rmode)
|| RelocInfo::IsComment(rmode)
|| RelocInfo::IsPosition(rmode));
// These modes do not need an entry in the constant pool.
// of that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
- // Return the code target address of the patch debug break slot
- inline static Address break_address_from_return_address(Address pc);
-
static void JumpToJumpRegister(Address pc);
static void QuietNaN(HeapObject* nan);
// target and the return address.
static const int kCallTargetAddressOffset = 4 * kInstrSize;
- // Distance between start of patched return sequence and the emitted address
- // to jump to.
- static const int kPatchReturnSequenceAddressOffset = 0;
-
// Distance between start of patched debug break slot and the emitted address
// to jump to.
- static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize;
+ static const int kPatchDebugBreakSlotAddressOffset = 4 * kInstrSize;
// Difference between address of current opcode and value read from pc
// register.
static const int kPcLoadDelta = 4;
- static const int kPatchDebugBreakSlotReturnOffset = 4 * kInstrSize;
-
- // Number of instructions used for the JS return sequence. The constant is
- // used by the debugger to patch the JS return sequence.
- static const int kJSReturnSequenceInstructions = 7;
- static const int kJSReturnSequenceLength =
- kJSReturnSequenceInstructions * kInstrSize;
static const int kDebugBreakSlotInstructions = 4;
static const int kDebugBreakSlotLength =
kDebugBreakSlotInstructions * kInstrSize;
// Debugging.
- // Mark address of the ExitJSFrame code.
- void RecordJSReturn();
-
// Mark generator continuation.
void RecordGeneratorContinuation();
// Mark address of a debug break slot.
- void RecordDebugBreakSlot();
- void RecordDebugBreakSlotForCall(int argc);
- void RecordDebugBreakSlotForConstructCall();
+ void RecordDebugBreakSlot(RelocInfo::Mode mode, int argc = 0);
// Record the AST id of the CallIC being compiled, so that it can be placed
// in the relocation information.
namespace v8 {
namespace internal {
-void BreakLocation::SetDebugBreakAtReturn() {
- // Mips return sequence:
- // mov sp, fp
- // lw fp, sp(0)
- // lw ra, sp(4)
- // addiu sp, sp, 8
- // addiu sp, sp, N
- // jr ra
- // nop (in branch delay slot)
-
- // Make sure this constant matches the number if instrucntions we emit.
- DCHECK(Assembler::kJSReturnSequenceInstructions == 7);
- CodePatcher patcher(pc(), Assembler::kJSReturnSequenceInstructions);
- // li and Call pseudo-instructions emit two instructions each.
- patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>(
- debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry())));
- patcher.masm()->Call(v8::internal::t9);
- patcher.masm()->nop();
- patcher.masm()->nop();
- patcher.masm()->nop();
+#define __ ACCESS_MASM(masm)
+
- // TODO(mips): Open issue about using breakpoint instruction instead of nops.
- // patcher.masm()->bkpt(0);
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_size));
}
-void BreakLocation::SetDebugBreakAtSlot() {
- DCHECK(IsDebugBreakSlot());
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the trampoline pool in the debug break slot code.
+ Assembler::BlockTrampolinePoolScope block_pool(masm);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
// Patch the code changing the debug break slot code from:
// nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
// nop(DEBUG_BREAK_NOP)
// to a call to the debug break slot code.
// li t9, address (lui t9 / ori t9 instruction pair)
// call t9 (jalr t9 / nop instruction pair)
- CodePatcher patcher(pc(), Assembler::kDebugBreakSlotInstructions);
- patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>(
- debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry())));
+ patcher.masm()->li(v8::internal::t9,
+ Operand(reinterpret_cast<int32_t>(code->entry())));
patcher.masm()->Call(v8::internal::t9);
}
-#define __ ACCESS_MASM(masm)
-
-
-static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList object_regs) {
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(at);
- // Store the registers containing live values on the expression stack to
- // make sure that these are correctly updated during GC. Non object values
- // are stored as a smi causing it to be untouched by GC.
- DCHECK((object_regs & ~kJSCallerSaved) == 0);
- if (object_regs != 0) {
- __ MultiPush(object_regs);
- }
+ if (mode == SAVE_RESULT_REGISTER) __ push(v0);
-#ifdef DEBUG
- __ RecordComment("// Calling from debug break to runtime - come in - over");
-#endif
__ PrepareCEntryArgs(0); // No arguments.
__ PrepareCEntryFunction(ExternalReference::debug_break(masm->isolate()));
CEntryStub ceb(masm->isolate(), 1);
__ CallStub(&ceb);
- // Restore the register values from the expression stack.
- if (object_regs != 0) {
- __ MultiPop(object_regs);
- }
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- Register reg = {r};
- if (FLAG_debug_code && ((object_regs & (1 << r)) == 0)) {
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = {JSCallerSavedCode(i)};
__ li(reg, kDebugZapValue);
}
}
+ if (mode == SAVE_RESULT_REGISTER) __ pop(v0);
+
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
}
-void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
- // In places other than IC call sites it is expected that v0 is TOS which
- // is an object - this is not generally the case so this should be used with
- // care.
- Generate_DebugBreakCallHelper(masm, v0.bit());
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm,
- DebugCodegen::SlotLocation location,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the trampoline pool in the debug break slot code.
- Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
- Label check_codesize;
- __ bind(&check_codesize);
- RecordRelocInfo(masm, location, call_argc);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(MacroAssembler::DEBUG_BREAK_NOP);
- }
- DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
- masm->InstructionsGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
- // In the places where a debug break slot is inserted no registers can contain
- // object pointers.
- Generate_DebugBreakCallHelper(masm, 0);
-}
-
-
void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
__ Ret();
}
EmitProfilingCounterReset();
__ bind(&ok);
-#ifdef DEBUG
- // Add a label for checking the size of the code used for returning.
- Label check_exit_codesize;
- masm_->bind(&check_exit_codesize);
-#endif
// Make sure that the constant pool is not emitted inside of the return
// sequence.
{ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
int32_t arg_count = info_->scope()->num_parameters() + 1;
int32_t sp_delta = arg_count * kPointerSize;
SetReturnPosition(function());
- __ RecordJSReturn();
masm_->mov(sp, fp);
int no_frame_start = masm_->pc_offset();
masm_->MultiPop(static_cast<RegList>(fp.bit() | ra.bit()));
masm_->Jump(ra);
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
-
-#ifdef DEBUG
- // Check that the size of the code used for returning is large enough
- // for the debugger's requirements.
- DCHECK(Assembler::kJSReturnSequenceInstructions <=
- masm_->InstructionsGeneratedSince(&check_exit_codesize));
-#endif
}
}
CodePatcher(byte* address,
int instructions,
FlushICache flush_cache = FLUSH);
- virtual ~CodePatcher();
+ ~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }
// -----------------------------------------------------------------------------
// RelocInfo.
-void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
+void RelocInfo::apply(intptr_t delta) {
if (IsInternalReference(rmode_) || IsInternalReferenceEncoded(rmode_)) {
// Absolute code pointer inside code object moves with the code object.
byte* p = reinterpret_cast<byte*>(pc_);
}
-Address Assembler::break_address_from_return_address(Address pc) {
- return pc - Assembler::kPatchDebugBreakSlotReturnOffset;
-}
-
-
void Assembler::set_target_internal_reference_encoded_at(Address pc,
Address target) {
// Encoded internal references are j/jal instructions.
}
-Address RelocInfo::call_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- // The pc_ offset of 0 assumes mips patched return sequence per
- // debug-mips.cc BreakLocation::SetDebugBreakAtReturn(), or
- // debug break slot per BreakLocation::SetDebugBreakAtSlot().
+Address RelocInfo::debug_call_address() {
+ // The pc_ offset of 0 assumes patched debug break slot or return
+ // sequence.
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
return Assembler::target_address_at(pc_, host_);
}
-void RelocInfo::set_call_address(Address target) {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- // The pc_ offset of 0 assumes mips patched return sequence per
- // debug-mips.cc BreakLocation::SetDebugBreakAtReturn(), or
- // debug break slot per BreakLocation::SetDebugBreakAtSlot().
+void RelocInfo::set_debug_call_address(Address target) {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ // The pc_ offset of 0 assumes patched debug break slot or return
+ // sequence.
Assembler::set_target_address_at(pc_, host_, target);
if (host() != NULL) {
Object* target_code = Code::GetCodeFromTargetAddress(target);
}
-Object* RelocInfo::call_object() {
- return *call_object_address();
-}
-
-
-Object** RelocInfo::call_object_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return reinterpret_cast<Object**>(pc_ + 6 * Assembler::kInstrSize);
-}
-
-
-void RelocInfo::set_call_object(Object* target) {
- *call_object_address() = target;
-}
-
-
void RelocInfo::WipeOut() {
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
visitor->VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
- } else if (((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence())) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence() &&
isolate->debug()->has_break_points()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
} else if (heap->isolate()->debug()->has_break_points() &&
- ((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence()))) {
+ RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
StaticVisitor::VisitRuntimeEntry(this);
void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
// We do not try to reuse pool constants.
RelocInfo rinfo(pc_, rmode, data, NULL);
- if (rmode >= RelocInfo::JS_RETURN &&
+ if (rmode >= RelocInfo::COMMENT &&
rmode <= RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL) {
// Adjust code for new modes.
DCHECK(RelocInfo::IsDebugBreakSlot(rmode)
- || RelocInfo::IsJSReturn(rmode)
|| RelocInfo::IsComment(rmode)
|| RelocInfo::IsPosition(rmode));
// These modes do not need an entry in the constant pool.
// of that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
- // Return the code target address of the patch debug break slot
- inline static Address break_address_from_return_address(Address pc);
-
static void JumpLabelToJumpRegister(Address pc);
static void QuietNaN(HeapObject* nan);
// target and the return address.
static const int kCallTargetAddressOffset = 6 * kInstrSize;
- // Distance between start of patched return sequence and the emitted address
- // to jump to.
- static const int kPatchReturnSequenceAddressOffset = 0;
-
// Distance between start of patched debug break slot and the emitted address
// to jump to.
- static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize;
+ static const int kPatchDebugBreakSlotAddressOffset = 6 * kInstrSize;
// Difference between address of current opcode and value read from pc
// register.
static const int kPcLoadDelta = 4;
- static const int kPatchDebugBreakSlotReturnOffset = 6 * kInstrSize;
-
- // Number of instructions used for the JS return sequence. The constant is
- // used by the debugger to patch the JS return sequence.
- static const int kJSReturnSequenceInstructions = 7;
- static const int kJSReturnSequenceLength =
- kJSReturnSequenceInstructions * kInstrSize;
static const int kDebugBreakSlotInstructions = 6;
static const int kDebugBreakSlotLength =
kDebugBreakSlotInstructions * kInstrSize;
// Debugging.
- // Mark address of the ExitJSFrame code.
- void RecordJSReturn();
-
// Mark generator continuation.
void RecordGeneratorContinuation();
// Mark address of a debug break slot.
- void RecordDebugBreakSlot();
- void RecordDebugBreakSlotForCall(int argc);
- void RecordDebugBreakSlotForConstructCall();
+ void RecordDebugBreakSlot(RelocInfo::Mode mode, int argc = 0);
// Record the AST id of the CallIC being compiled, so that it can be placed
// in the relocation information.
namespace v8 {
namespace internal {
-void BreakLocation::SetDebugBreakAtReturn() {
- // Mips return sequence:
- // mov sp, fp
- // lw fp, sp(0)
- // lw ra, sp(4)
- // addiu sp, sp, 8
- // addiu sp, sp, N
- // jr ra
- // nop (in branch delay slot)
-
- // Make sure this constant matches the number if instructions we emit.
- DCHECK(Assembler::kJSReturnSequenceInstructions == 7);
- CodePatcher patcher(pc(), Assembler::kJSReturnSequenceInstructions);
- // li and Call pseudo-instructions emit 6 + 2 instructions.
- patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int64_t>(
- debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry())),
- ADDRESS_LOAD);
- patcher.masm()->Call(v8::internal::t9);
- // Place nop to match return sequence size.
- patcher.masm()->nop();
- // TODO(mips): Open issue about using breakpoint instruction instead of nops.
- // patcher.masm()->bkpt(0);
+#define __ ACCESS_MASM(masm)
+
+void EmitDebugBreakSlot(MacroAssembler* masm) {
+ Label check_size;
+ __ bind(&check_size);
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_size));
}
-void BreakLocation::SetDebugBreakAtSlot() {
- DCHECK(IsDebugBreakSlot());
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the trampoline pool in the debug break slot code.
+ Assembler::BlockTrampolinePoolScope block_pool(masm);
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
+}
+
+
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
+ EmitDebugBreakSlot(patcher.masm());
+}
+
+
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotInstructions);
// Patch the code changing the debug break slot code from:
// nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
// nop(DEBUG_BREAK_NOP)
// to a call to the debug break slot code.
// li t9, address (4-instruction sequence on mips64)
// call t9 (jalr t9 / nop instruction pair)
- CodePatcher patcher(pc(), Assembler::kDebugBreakSlotInstructions);
patcher.masm()->li(v8::internal::t9,
- Operand(reinterpret_cast<int64_t>(
- debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry())),
- ADDRESS_LOAD);
+ Operand(reinterpret_cast<int64_t>(code->entry())),
+ ADDRESS_LOAD);
patcher.masm()->Call(v8::internal::t9);
}
-#define __ ACCESS_MASM(masm)
-
-
-static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList object_regs) {
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(at);
+ if (mode == SAVE_RESULT_REGISTER) __ push(v0);
- // TODO(plind): This needs to be revised to store pairs of smi's per
- // the other 64-bit arch's.
-
- // Store the registers containing live values on the expression stack to
- // make sure that these are correctly updated during GC. Non object values
- // are stored as a smi causing it to be untouched by GC.
- DCHECK((object_regs & ~kJSCallerSaved) == 0);
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- Register reg = { r };
- if ((object_regs & (1 << r)) != 0) {
- __ push(reg);
- }
- }
-
-#ifdef DEBUG
- __ RecordComment("// Calling from debug break to runtime - come in - over");
-#endif
__ PrepareCEntryArgs(0); // No arguments.
__ PrepareCEntryFunction(ExternalReference::debug_break(masm->isolate()));
CEntryStub ceb(masm->isolate(), 1);
__ CallStub(&ceb);
- // Restore the register values from the expression stack.
- for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
- int r = JSCallerSavedCode(i);
- Register reg = { r };
- if ((object_regs & (1 << r)) != 0) {
- __ pop(reg);
- }
- if (FLAG_debug_code && ((object_regs & (1 << r)) == 0)) {
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ Register reg = {JSCallerSavedCode(i)};
__ li(reg, kDebugZapValue);
}
}
+ if (mode == SAVE_RESULT_REGISTER) __ pop(v0);
+
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
}
-void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
- // In places other than IC call sites it is expected that v0 is TOS which
- // is an object - this is not generally the case so this should be used with
- // care.
- Generate_DebugBreakCallHelper(masm, v0.bit());
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm,
- DebugCodegen::SlotLocation location,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction. Avoid emitting
- // the trampoline pool in the debug break slot code.
- Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
- Label check_codesize;
- __ bind(&check_codesize);
- RecordRelocInfo(masm, location, call_argc);
- for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
- __ nop(MacroAssembler::DEBUG_BREAK_NOP);
- }
- DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
- masm->InstructionsGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
- // In the places where a debug break slot is inserted no registers can contain
- // object pointers.
- Generate_DebugBreakCallHelper(masm, 0);
-}
-
-
void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
__ Ret();
}
EmitProfilingCounterReset();
__ bind(&ok);
-#ifdef DEBUG
- // Add a label for checking the size of the code used for returning.
- Label check_exit_codesize;
- masm_->bind(&check_exit_codesize);
-#endif
-
// Make sure that the constant pool is not emitted inside of the return
// sequence.
{ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
int32_t arg_count = info_->scope()->num_parameters() + 1;
int32_t sp_delta = arg_count * kPointerSize;
SetReturnPosition(function());
- __ RecordJSReturn();
masm_->mov(sp, fp);
int no_frame_start = masm_->pc_offset();
masm_->MultiPop(static_cast<RegList>(fp.bit() | ra.bit()));
masm_->Jump(ra);
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
-
-#ifdef DEBUG
- // Check that the size of the code used for returning is large enough
- // for the debugger's requirements.
- DCHECK(Assembler::kJSReturnSequenceInstructions <=
- masm_->InstructionsGeneratedSince(&check_exit_codesize));
-#endif
}
}
CodePatcher(byte* address,
int instructions,
FlushICache flush_cache = FLUSH);
- virtual ~CodePatcher();
+ ~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }
void DebugInfo::DebugInfoVerify() {
CHECK(IsDebugInfo());
VerifyPointer(shared());
- VerifyPointer(original_code());
VerifyPointer(code());
VerifyPointer(break_points());
}
ACCESSORS(DebugInfo, shared, SharedFunctionInfo, kSharedFunctionInfoIndex)
-ACCESSORS(DebugInfo, original_code, Code, kOriginalCodeIndex)
-ACCESSORS(DebugInfo, code, Code, kPatchedCodeIndex)
+ACCESSORS(DebugInfo, code, Code, kCodeIndex)
ACCESSORS(DebugInfo, break_points, FixedArray, kBreakPointsStateIndex)
ACCESSORS_TO_SMI(BreakPointInfo, code_position, kCodePositionIndex)
void DebugInfo::DebugInfoPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "DebugInfo");
os << "\n - shared: " << Brief(shared());
- os << "\n - original_code: " << Brief(original_code());
os << "\n - code: " << Brief(code());
os << "\n - break_points: ";
break_points()->Print(os);
void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
- DCHECK((RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
- rinfo->IsPatchedDebugBreakSlotSequence()));
- Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
+ DCHECK(RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence());
+ Object* target = Code::GetCodeFromTargetAddress(rinfo->debug_call_address());
Object* old_target = target;
VisitPointer(&target);
CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
void Code::Relocate(intptr_t delta) {
for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
- it.rinfo()->apply(delta, SKIP_ICACHE_FLUSH);
+ it.rinfo()->apply(delta);
}
CpuFeatures::FlushICache(instruction_start(), instruction_size());
}
Code* code = Code::cast(*p);
it.rinfo()->set_code_age_stub(code, SKIP_ICACHE_FLUSH);
} else {
- it.rinfo()->apply(delta, SKIP_ICACHE_FLUSH);
+ it.rinfo()->apply(delta);
}
}
CpuFeatures::FlushICache(instruction_start(), instruction_size());
public:
// The shared function info for the source being debugged.
DECL_ACCESSORS(shared, SharedFunctionInfo)
- // Code object for the original code.
- DECL_ACCESSORS(original_code, Code)
// Code object for the patched code. This code object is the code object
// currently active for the function.
DECL_ACCESSORS(code, Code)
DECLARE_VERIFIER(DebugInfo)
static const int kSharedFunctionInfoIndex = Struct::kHeaderSize;
- static const int kOriginalCodeIndex = kSharedFunctionInfoIndex + kPointerSize;
- static const int kPatchedCodeIndex = kOriginalCodeIndex + kPointerSize;
- static const int kActiveBreakPointsCountIndex =
- kPatchedCodeIndex + kPointerSize;
- static const int kBreakPointsStateIndex =
- kActiveBreakPointsCountIndex + kPointerSize;
+ static const int kCodeIndex = kSharedFunctionInfoIndex + kPointerSize;
+ static const int kBreakPointsStateIndex = kCodeIndex + kPointerSize;
static const int kSize = kBreakPointsStateIndex + kPointerSize;
static const int kEstimatedNofBreakPointsInFunction = 16;
return;
}
- // Get the debug info (create it if it does not exist).
- if (!isolate->debug()->EnsureDebugInfo(shared_info, function_)) {
- // Return if ensuring debug info failed.
- return;
- }
-
// Currently it takes too much time to find nested scopes due to script
// parsing. Sometimes we want to run the ScopeIterator as fast as possible
// (for example, while collecting async call stacks on every
// addEventListener call), even if we drop some nested scopes.
// Later we may optimize getting the nested scopes (cache the result?)
// and include nested scopes into the "fast" iteration case as well.
- if (!ignore_nested_scopes) {
+
+ if (!ignore_nested_scopes && !shared_info->debug_info()->IsUndefined()) {
+ // The source position at return is always the end of the function,
+ // which is not consistent with the current scope chain. Therefore all
+ // nested with, catch and block contexts are skipped, and we can only
+ // inspect the function scope.
+ // This can only happen if we set a break point inside right before the
+ // return, which requires a debug info to be available.
Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info);
// PC points to the instruction after the current one, possibly a break
BreakLocation location =
BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
- // Within the return sequence at the moment it is not possible to
- // get a source position which is consistent with the current scope chain.
- // Thus all nested with, catch and block contexts are skipped and we only
- // provide the function scope.
- ignore_nested_scopes = location.IsExit();
+ ignore_nested_scopes = location.IsReturn();
}
if (ignore_nested_scopes) {
}
-Address Assembler::break_address_from_return_address(Address pc) {
- return pc - Assembler::kPatchDebugBreakSlotReturnOffset;
-}
-
-
Handle<Object> Assembler::code_target_object_handle_at(Address pc) {
return code_targets_[Memory::int32_at(pc)];
}
// Implementation of RelocInfo
// The modes possibly affected by apply must be in kApplyMask.
-void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
- bool flush_icache = icache_flush_mode != SKIP_ICACHE_FLUSH;
- if (IsInternalReference(rmode_)) {
- // absolute code pointer inside code object moves with the code object.
- Memory::Address_at(pc_) += delta;
- if (flush_icache) CpuFeatures::FlushICache(pc_, sizeof(Address));
- } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) {
+void RelocInfo::apply(intptr_t delta) {
+ if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) {
Memory::int32_at(pc_) -= static_cast<int32_t>(delta);
- if (flush_icache) CpuFeatures::FlushICache(pc_, sizeof(int32_t));
- } else if (rmode_ == CODE_AGE_SEQUENCE) {
+ } else if (IsCodeAgeSequence(rmode_)) {
if (*pc_ == kCallOpcode) {
int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
*p -= static_cast<int32_t>(delta); // Relocate entry.
- if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t));
}
+ } else if (IsInternalReference(rmode_)) {
+ // absolute code pointer inside code object moves with the code object.
+ Memory::Address_at(pc_) += delta;
}
}
}
-Address RelocInfo::call_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return Memory::Address_at(
- pc_ + Assembler::kRealPatchReturnSequenceAddressOffset);
+Address RelocInfo::debug_call_address() {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ return Memory::Address_at(pc_ + Assembler::kPatchDebugBreakSlotAddressOffset);
}
-void RelocInfo::set_call_address(Address target) {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- Memory::Address_at(pc_ + Assembler::kRealPatchReturnSequenceAddressOffset) =
+void RelocInfo::set_debug_call_address(Address target) {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
+ Memory::Address_at(pc_ + Assembler::kPatchDebugBreakSlotAddressOffset) =
target;
- CpuFeatures::FlushICache(
- pc_ + Assembler::kRealPatchReturnSequenceAddressOffset, sizeof(Address));
+ CpuFeatures::FlushICache(pc_ + Assembler::kPatchDebugBreakSlotAddressOffset,
+ sizeof(Address));
if (host() != NULL) {
Object* target_code = Code::GetCodeFromTargetAddress(target);
host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
}
-Object* RelocInfo::call_object() {
- return *call_object_address();
-}
-
-
-void RelocInfo::set_call_object(Object* target) {
- *call_object_address() = target;
-}
-
-
-Object** RelocInfo::call_object_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return reinterpret_cast<Object**>(
- pc_ + Assembler::kPatchReturnSequenceAddressOffset);
-}
-
-
void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
RelocInfo::Mode mode = rmode();
if (mode == RelocInfo::EMBEDDED_OBJECT) {
visitor->VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
- } else if (((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence())) &&
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence() &&
isolate->debug()->has_break_points()) {
visitor->VisitDebugTarget(this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
} else if (heap->isolate()->debug()->has_break_points() &&
- ((RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence()))) {
+ RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (RelocInfo::IsRuntimeEntry(mode)) {
StaticVisitor::VisitRuntimeEntry(this);
// of that call in the instruction stream.
static inline Address target_address_from_return_address(Address pc);
- // Return the code target address of the patch debug break slot
- inline static Address break_address_from_return_address(Address pc);
-
// This sets the branch destination (which is in the instruction on x64).
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
kMoveAddressIntoScratchRegisterInstructionLength +
kCallScratchRegisterInstructionLength;
- // The js return and debug break slot must be able to contain an indirect
- // call sequence, some x64 JS code is padded with int3 to make it large
- // enough to hold an instruction when the debugger patches it.
- static const int kJSReturnSequenceLength = kCallSequenceLength;
+ // The debug break slot must be able to contain an indirect call sequence.
static const int kDebugBreakSlotLength = kCallSequenceLength;
- static const int kPatchDebugBreakSlotReturnOffset = kCallTargetAddressOffset;
- // Distance between the start of the JS return sequence and where the
- // 32-bit displacement of a short call would be. The short call is from
- // SetDebugBreakAtIC from debug-x64.cc.
- static const int kPatchReturnSequenceAddressOffset =
- kJSReturnSequenceLength - kPatchDebugBreakSlotReturnOffset;
- // Distance between the start of the JS return sequence and where the
- // 32-bit displacement of a short call would be. The short call is from
- // SetDebugBreakAtIC from debug-x64.cc.
+ // Distance between start of patched debug break slot and the emitted address
+ // to jump to.
static const int kPatchDebugBreakSlotAddressOffset =
- kDebugBreakSlotLength - kPatchDebugBreakSlotReturnOffset;
- static const int kRealPatchReturnSequenceAddressOffset =
kMoveAddressIntoScratchRegisterInstructionLength - kPointerSize;
// One byte opcode for test eax,0xXXXXXXXX.
return pc_offset() - label->pos();
}
- // Mark address of the ExitJSFrame code.
- void RecordJSReturn();
-
// Mark generator continuation.
void RecordGeneratorContinuation();
// Mark address of a debug break slot.
- void RecordDebugBreakSlot();
- void RecordDebugBreakSlotForCall(int argc);
- void RecordDebugBreakSlotForConstructCall();
+ void RecordDebugBreakSlot(RelocInfo::Mode mode, int argc = 0);
// Record a comment relocation entry that can be used by a disassembler.
// Use --code-comments to enable.
namespace v8 {
namespace internal {
-// Patch the code at the current PC with a call to the target address.
-// Additional guard int3 instructions can be added if required.
-void PatchCodeWithCall(Address pc, Address target, int guard_bytes) {
- int code_size = Assembler::kCallSequenceLength + guard_bytes;
+#define __ ACCESS_MASM(masm)
- // Create a code patcher.
- CodePatcher patcher(pc, code_size);
-// Add a label for checking the size of the code used for returning.
-#ifdef DEBUG
+void EmitDebugBreakSlot(MacroAssembler* masm) {
Label check_codesize;
- patcher.masm()->bind(&check_codesize);
-#endif
-
- // Patch the code.
- patcher.masm()->movp(kScratchRegister, reinterpret_cast<void*>(target),
- Assembler::RelocInfoNone());
- patcher.masm()->call(kScratchRegister);
-
- // Check that the size of the code generated is as expected.
- DCHECK_EQ(Assembler::kCallSequenceLength,
- patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
+ __ bind(&check_codesize);
+ __ Nop(Assembler::kDebugBreakSlotLength);
+ DCHECK_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
- // Add the requested number of int3 instructions after the call.
- for (int i = 0; i < guard_bytes; i++) {
- patcher.masm()->int3();
- }
- CpuFeatures::FlushICache(pc, code_size);
+void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode,
+ int call_argc) {
+ // Generate enough nop's to make space for a call instruction.
+ masm->RecordDebugBreakSlot(mode, call_argc);
+ EmitDebugBreakSlot(masm);
}
-// Patch the JS frame exit code with a debug break call. See
-// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-x64.cc
-// for the precise return instructions sequence.
-void BreakLocation::SetDebugBreakAtReturn() {
- DCHECK(Assembler::kJSReturnSequenceLength >= Assembler::kCallSequenceLength);
- PatchCodeWithCall(
- pc(), debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
- Assembler::kJSReturnSequenceLength - Assembler::kCallSequenceLength);
+void DebugCodegen::ClearDebugBreakSlot(Address pc) {
+ CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
+ EmitDebugBreakSlot(patcher.masm());
}
-void BreakLocation::SetDebugBreakAtSlot() {
- DCHECK(IsDebugBreakSlot());
- PatchCodeWithCall(
- pc(), debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry(),
- Assembler::kDebugBreakSlotLength - Assembler::kCallSequenceLength);
+void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) {
+ DCHECK_EQ(Code::BUILTIN, code->kind());
+ static const int kSize = Assembler::kDebugBreakSlotLength;
+ CodePatcher patcher(pc, kSize);
+ Label check_codesize;
+ patcher.masm()->bind(&check_codesize);
+ patcher.masm()->movp(kScratchRegister, reinterpret_cast<void*>(code->entry()),
+ Assembler::RelocInfoNone());
+ patcher.masm()->call(kScratchRegister);
+ // Check that the size of the code generated is as expected.
+ DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
}
-#define __ ACCESS_MASM(masm)
-
+void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
+ DebugBreakCallHelperMode mode) {
+ __ RecordComment("Debug break");
-static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList object_regs) {
// Enter an internal frame.
{
FrameScope scope(masm, StackFrame::INTERNAL);
}
__ Push(Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
- // Store the registers containing live values on the expression stack to
- // make sure that these are correctly updated during GC. Non object values
- // are stored as as two smis causing it to be untouched by GC.
- DCHECK((object_regs & ~kJSCallerSaved) == 0);
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- Register reg = { r };
- DCHECK(!reg.is(kScratchRegister));
- if ((object_regs & (1 << r)) != 0) {
- __ Push(reg);
- }
- }
+ if (mode == SAVE_RESULT_REGISTER) __ Push(rax);
-#ifdef DEBUG
- __ RecordComment("// Calling from debug break to runtime - come in - over");
-#endif
__ Set(rax, 0); // No arguments (argc == 0).
__ Move(rbx, ExternalReference::debug_break(masm->isolate()));
CEntryStub ceb(masm->isolate(), 1);
__ CallStub(&ceb);
- // Restore the register values from the expression stack.
- for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
- int r = JSCallerSavedCode(i);
- Register reg = { r };
- if (FLAG_debug_code) {
+ if (FLAG_debug_code) {
+ for (int i = 0; i < kNumJSCallerSaved; ++i) {
+ Register reg = {JSCallerSavedCode(i)};
__ Set(reg, kDebugZapValue);
}
- if ((object_regs & (1 << r)) != 0) {
- __ Pop(reg);
- }
}
+ if (mode == SAVE_RESULT_REGISTER) __ Pop(rax);
+
// Read current padding counter and skip corresponding number of words.
__ Pop(kScratchRegister);
__ SmiToInteger32(kScratchRegister, kScratchRegister);
}
-void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
- // Register state just before return from JS function (from codegen-x64.cc).
- // ----------- S t a t e -------------
- // -- rax: return value
- // -----------------------------------
- Generate_DebugBreakCallHelper(masm, rax.bit());
-}
-
-
-void DebugCodegen::GenerateSlot(MacroAssembler* masm,
- DebugCodegen::SlotLocation location,
- int call_argc) {
- // Generate enough nop's to make space for a call instruction.
- Label check_codesize;
- __ bind(&check_codesize);
- RecordRelocInfo(masm, location, call_argc);
- __ Nop(Assembler::kDebugBreakSlotLength);
- DCHECK_EQ(Assembler::kDebugBreakSlotLength,
- masm->SizeOfCodeGeneratedSince(&check_codesize));
-}
-
-
-void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
- // In the places where a debug break slot is inserted no registers can contain
- // object pointers.
- Generate_DebugBreakCallHelper(masm, 0);
-}
-
-
void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
masm->ret(0);
}
__ Pop(rax);
EmitProfilingCounterReset();
__ bind(&ok);
-#ifdef DEBUG
- // Add a label for checking the size of the code used for returning.
- Label check_exit_codesize;
- masm_->bind(&check_exit_codesize);
-#endif
+
SetReturnPosition(function());
- __ RecordJSReturn();
- // Do not use the leave instruction here because it is too short to
- // patch with the code required by the debugger.
- __ movp(rsp, rbp);
- __ popq(rbp);
int no_frame_start = masm_->pc_offset();
+ __ leave();
int arg_count = info_->scope()->num_parameters() + 1;
int arguments_bytes = arg_count * kPointerSize;
__ Ret(arguments_bytes, rcx);
- // Add padding that will be overwritten by a debugger breakpoint. We
- // have just generated at least 7 bytes: "movp rsp, rbp; pop rbp; ret k"
- // (3 + 1 + 3) for x64 and at least 6 (2 + 1 + 3) bytes for x32.
- const int kPadding = Assembler::kJSReturnSequenceLength -
- kPointerSize == kInt64Size ? 7 : 6;
- for (int i = 0; i < kPadding; ++i) {
- masm_->int3();
- }
- // Check that the size of the code used for returning is large enough
- // for the debugger's requirements.
- DCHECK(Assembler::kJSReturnSequenceLength <=
- masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
-
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
}
class CodePatcher {
public:
CodePatcher(byte* address, int size);
- virtual ~CodePatcher();
+ ~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }
if (check_functions) {
if (obj->IsJSFunction()) {
JSFunction* fun = JSFunction::cast(obj);
- for (RelocIterator it(fun->shared()->code()); !it.done(); it.next()) {
- RelocInfo::Mode rmode = it.rinfo()->rmode();
- if (RelocInfo::IsCodeTarget(rmode)) {
- CHECK(!Debug::IsDebugBreak(it.rinfo()->target_address()));
- } else if (RelocInfo::IsJSReturn(rmode)) {
- CHECK(!it.rinfo()->IsPatchedReturnSequence());
- }
+ for (RelocIterator it(fun->shared()->code(),
+ RelocInfo::kDebugBreakSlotMask);
+ !it.done(); it.next()) {
+ CHECK(!it.rinfo()->IsPatchedDebugBreakSlotSequence());
}
}
}
}
} catch (e) {
exception = e
+ print(e + e.stack)
};
};