Debugger: use debug break slots to break at function exit.

By not having to patch the return sequence (we patch the debug
break slot right before it), we don't overwrite it and therefore
don't have to keep the original copy of the code around.

R=ulan@chromium.org
BUG=v8:4269
LOG=N

Review URL: https://codereview.chromium.org/1234833003

Cr-Commit-Position: refs/heads/master@{#29672}
This commit is contained in:
yangguo 2015-07-15 02:22:33 -07:00 committed by Commit bot
parent ae11f20e26
commit fc9c5275c3
55 changed files with 534 additions and 1460 deletions

View File

@ -97,7 +97,7 @@ DwVfpRegister DwVfpRegister::FromAllocationIndex(int index) {
}
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_);
@ -272,19 +272,18 @@ void RelocInfo::set_code_age_stub(Code* stub,
}
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(
@ -293,23 +292,6 @@ void RelocInfo::set_call_address(Address target) {
}
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_) ||
@ -353,10 +335,8 @@ void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
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)) {
@ -381,10 +361,8 @@ void RelocInfo::Visit(Heap* heap) {
} 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);
@ -504,11 +482,6 @@ Address Assembler::target_address_from_return_address(Address pc) {
}
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))) {

View File

@ -737,9 +737,6 @@ class Assembler : public AssemblerBase {
// 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(
@ -758,30 +755,18 @@ class Assembler : public AssemblerBase {
// 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;
@ -1354,16 +1339,11 @@ class Assembler : public AssemblerBase {
// 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.

View File

@ -12,49 +12,62 @@
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);
@ -66,36 +79,23 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
__ 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.
@ -113,38 +113,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
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();
}

View File

@ -498,11 +498,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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_);
@ -511,7 +506,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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));
@ -519,13 +513,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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
}
}

View File

@ -1506,7 +1506,7 @@ class CodePatcher {
CodePatcher(byte* address,
int instructions,
FlushICache flush_cache = FLUSH);
virtual ~CodePatcher();
~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }

View File

@ -17,7 +17,7 @@ namespace internal {
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_));
@ -611,11 +611,6 @@ Address Assembler::target_address_from_return_address(Address pc) {
}
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:
@ -825,18 +820,18 @@ void RelocInfo::set_code_age_stub(Code* stub,
}
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);
@ -862,7 +857,7 @@ bool RelocInfo::IsPatchedReturnSequence() {
// 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) &&
@ -888,10 +883,8 @@ void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
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)) {
@ -914,10 +907,8 @@ void RelocInfo::Visit(Heap* heap) {
} 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);

View File

@ -2901,16 +2901,15 @@ void Assembler::GrowBuffer() {
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));

View File

@ -893,9 +893,6 @@ class Assembler : public AssemblerBase {
// 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(
@ -955,25 +952,13 @@ class Assembler : public AssemblerBase {
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.
@ -1022,16 +1007,11 @@ class Assembler : public AssemblerBase {
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.
//

View File

@ -12,90 +12,73 @@
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 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.
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 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);
@ -106,42 +89,24 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
__ 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 (mode == SAVE_RESULT_REGISTER) __ Push(x0);
if (object_regs != 0) {
__ PushXRegList(object_regs);
}
#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.
@ -159,34 +124,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
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();
}

View File

@ -492,34 +492,23 @@ void FullCodeGenerator::EmitReturnSequence() {
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());
}
}

View File

@ -747,8 +747,6 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
return "property cell";
case RUNTIME_ENTRY:
return "runtime entry";
case JS_RETURN:
return "js return";
case COMMENT:
return "comment";
case POSITION:
@ -769,6 +767,8 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
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:
@ -860,7 +860,6 @@ void RelocInfo::Verify(Isolate* isolate) {
break;
}
case RUNTIME_ENTRY:
case JS_RETURN:
case COMMENT:
case POSITION:
case STATEMENT_POSITION:
@ -869,6 +868,7 @@ void RelocInfo::Verify(Isolate* isolate) {
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:
@ -1806,35 +1806,17 @@ void Assembler::RecordComment(const char* msg) {
}
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() {
void Assembler::RecordDebugBreakSlot(RelocInfo::Mode mode, int call_argc) {
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() {
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);
}

View File

@ -370,13 +370,13 @@ class RelocInfo {
// 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,
@ -442,9 +442,6 @@ class RelocInfo {
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;
}
@ -473,12 +470,16 @@ class RelocInfo {
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;
}
@ -507,10 +508,11 @@ class RelocInfo {
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
@ -596,11 +598,8 @@ class RelocInfo {
// 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.
@ -640,9 +639,9 @@ class RelocInfo {
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

View File

@ -1370,12 +1370,14 @@ static void Generate_KeyedStoreIC_PreMonomorphic_Strict(MacroAssembler* masm) {
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);
}

View File

@ -59,16 +59,11 @@ static v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) {
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) {}
@ -77,11 +72,10 @@ BreakLocation::Iterator::Iterator(Handle<DebugInfo> debug_info,
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();
}
@ -89,7 +83,7 @@ int BreakLocation::Iterator::GetModeMask(BreakLocatorType type) {
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) {
@ -102,15 +96,15 @@ int BreakLocation::Iterator::GetModeMask(BreakLocatorType type) {
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.
@ -128,7 +122,10 @@ void BreakLocation::Iterator::Next() {
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() -
@ -137,11 +134,8 @@ void BreakLocation::Iterator::Next() {
position_ = 0;
}
statement_position_ = position_;
break;
}
DCHECK(RelocInfo::IsDebugBreakSlot(rmode()) ||
RelocInfo::IsDebuggerStatement(rmode()));
break;
}
break_index_++;
@ -152,6 +146,7 @@ void BreakLocation::Iterator::Next() {
// 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();
@ -163,6 +158,7 @@ BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info,
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);
@ -195,6 +191,7 @@ int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info,
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;
@ -286,13 +283,11 @@ void BreakLocation::SetDebugBreak() {
// 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());
}
@ -301,32 +296,19 @@ void BreakLocation::ClearDebugBreak() {
// 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;
@ -338,22 +320,6 @@ Handle<Object> BreakLocation::BreakPointObjects() const {
}
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;
@ -1150,14 +1116,14 @@ void Debug::PrepareStep(StepAction step_action,
}
// 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.
@ -1263,7 +1229,7 @@ bool Debug::StepNextContinue(BreakLocation* break_location,
// 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 =
@ -2015,67 +1981,8 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
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();
}
@ -2085,9 +1992,7 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
// 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();
@ -2106,17 +2011,11 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
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;
}
@ -3177,23 +3076,5 @@ void LockingCommandMessageQueue::Clear() {
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

View File

@ -78,7 +78,10 @@ class BreakLocation {
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_);
}
@ -112,17 +115,13 @@ class BreakLocation {
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 {
@ -130,11 +129,11 @@ class BreakLocation {
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) {
@ -143,10 +142,6 @@ class BreakLocation {
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_; }
@ -155,12 +150,8 @@ class BreakLocation {
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_;
@ -175,12 +166,8 @@ class BreakLocation {
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_);
@ -191,11 +178,8 @@ class BreakLocation {
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_;
};
@ -792,16 +776,14 @@ class SuppressDebug BASE_EMBEDDED {
// 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
@ -810,9 +792,12 @@ class DebugCodegen : public AllStatic {
// 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);
};

View File

@ -2269,13 +2269,6 @@ Handle<String> Factory::NumberToString(Handle<Object> number,
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.
@ -2288,8 +2281,7 @@ Handle<DebugInfo> Factory::NewDebugInfo(Handle<SharedFunctionInfo> shared) {
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.

View File

@ -424,6 +424,10 @@ void FullCodeGenerator::SetFunctionPosition(FunctionLiteral* fun) {
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);
}
}
@ -433,7 +437,7 @@ void FullCodeGenerator::SetStatementPosition(
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);
}
}
@ -443,7 +447,7 @@ void FullCodeGenerator::SetExpressionPosition(
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);
}
}
@ -452,7 +456,7 @@ void FullCodeGenerator::SetExpressionAsStatementPosition(Expression* expr) {
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);
}
}
@ -462,7 +466,8 @@ void FullCodeGenerator::SetCallPosition(Expression* expr, int argc) {
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);
}
}
@ -473,7 +478,7 @@ void FullCodeGenerator::SetConstructCallPosition(Expression* expr) {
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);
}
}

View File

@ -2869,13 +2869,12 @@ class PointersUpdatingVisitor : public ObjectVisitor {
}
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) {
@ -3435,11 +3434,6 @@ static inline void UpdateSlot(Isolate* isolate, ObjectVisitor* v,
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);
@ -4529,8 +4523,6 @@ static inline SlotsBuffer::SlotType SlotTypeForRMode(RelocInfo::Mode rmode) {
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;

View File

@ -335,7 +335,6 @@ class SlotsBuffer {
CODE_TARGET_SLOT,
CODE_ENTRY_SLOT,
DEBUG_TARGET_SLOT,
JS_RETURN_SLOT,
NUMBER_OF_SLOT_TYPES
};
@ -355,8 +354,6 @@ class SlotsBuffer {
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";
}

View File

@ -233,11 +233,9 @@ void StaticMarkingVisitor<StaticVisitor>::VisitCell(Heap* heap,
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);
}
@ -788,7 +786,6 @@ void Code::CodeIterateBody(ObjectVisitor* v) {
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;
@ -816,7 +813,6 @@ void Code::CodeIterateBody(Heap* heap) {
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;

View File

@ -53,35 +53,25 @@ static const int kNoCodeAgeSequenceLength = 5;
// 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));
}
}
@ -244,17 +234,17 @@ void RelocInfo::set_code_age_stub(Code* stub,
}
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(
@ -263,23 +253,6 @@ void RelocInfo::set_call_address(Address target) {
}
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_)) {
@ -318,10 +291,8 @@ void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
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)) {
@ -347,10 +318,8 @@ void RelocInfo::Visit(Heap* heap) {
} 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);
@ -502,11 +471,6 @@ Address Assembler::target_address_from_return_address(Address pc) {
}
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()));
}

View File

@ -169,8 +169,8 @@ void Displacement::init(Label* L, Type type) {
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() {

View File

@ -533,9 +533,6 @@ class Assembler : public AssemblerBase {
// 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(
@ -553,21 +550,16 @@ class Assembler : public AssemblerBase {
// 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.
static const int kCallInstructionLength = 5;
// 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.
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;
// One byte opcode for test al, 0xXX.
static const byte kTestAlByte = 0xA8;
// One byte opcode for nop.
@ -1433,16 +1425,11 @@ class Assembler : public AssemblerBase {
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.

View File

@ -13,65 +13,50 @@
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) {
// 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
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));
// 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);
}
// 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 BreakLocation::SetDebugBreakAtSlot() {
DCHECK(IsDebugBreakSlot());
Isolate* isolate = debug_info_->GetIsolate();
PatchCodeWithCall(
pc(), isolate->builtins()->Slot_DebugBreak()->entry(),
Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
}
#define __ ACCESS_MASM(masm)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
RegList object_regs) {
void EmitDebugBreakSlot(MacroAssembler* masm) {
Label check_codesize;
__ bind(&check_codesize);
__ Nop(Assembler::kDebugBreakSlotLength);
DCHECK_EQ(Assembler::kDebugBreakSlotLength,
masm->SizeOfCodeGeneratedSince(&check_codesize));
}
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);
}
void DebugCodegen::ClearDebugBreakSlot(Address pc) {
CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
EmitDebugBreakSlot(patcher.masm());
}
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));
}
void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
DebugBreakCallHelperMode mode) {
__ RecordComment("Debug break");
// Enter an internal frame.
{
FrameScope scope(masm, StackFrame::INTERNAL);
@ -82,56 +67,27 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
__ 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.
}
@ -149,33 +105,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
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);
}

View File

@ -464,26 +464,14 @@ void FullCodeGenerator::EmitReturnSequence() {
__ 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());
}
}

View File

@ -1025,7 +1025,7 @@ class MacroAssembler: public Assembler {
class CodePatcher {
public:
CodePatcher(byte* address, int size);
virtual ~CodePatcher();
~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }

View File

@ -18,56 +18,15 @@ namespace internal {
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;
}
}

View File

@ -213,16 +213,6 @@ Code* IC::GetCode() const {
}
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;

View File

@ -131,8 +131,6 @@ class IC {
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;

View File

@ -1188,13 +1188,6 @@ void LiveEdit::ReplaceFunctionCode(
}
}
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);

View File

@ -117,7 +117,7 @@ int FPURegister::ToAllocationIndex(FPURegister reg) {
// -----------------------------------------------------------------------------
// 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;
@ -195,11 +195,6 @@ Address Assembler::target_address_from_return_address(Address 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 lui/ori load of 32-bit abolute address.
@ -359,22 +354,18 @@ void RelocInfo::set_code_age_stub(Code* stub,
}
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);
@ -384,23 +375,6 @@ void RelocInfo::set_call_address(Address 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_) ||
@ -449,10 +423,8 @@ void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
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)) {
@ -478,10 +450,8 @@ void RelocInfo::Visit(Heap* heap) {
} 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);

View File

@ -2826,11 +2826,10 @@ void Assembler::emit_code_stub_address(Code* stub) {
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.

View File

@ -525,9 +525,6 @@ class Assembler : public AssemblerBase {
// 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);
@ -575,25 +572,14 @@ class Assembler : public AssemblerBase {
// 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;
@ -1058,16 +1044,11 @@ class Assembler : public AssemblerBase {
// 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.

View File

@ -14,34 +14,39 @@
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)
#define __ ACCESS_MASM(masm)
// 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();
// 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)
@ -50,18 +55,15 @@ void BreakLocation::SetDebugBreakAtSlot() {
// 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);
@ -75,35 +77,23 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
__ 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.
@ -121,38 +111,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
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();
}

View File

@ -494,11 +494,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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_);
@ -507,7 +502,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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()));
@ -515,13 +509,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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
}
}

View File

@ -1703,7 +1703,7 @@ class CodePatcher {
CodePatcher(byte* address,
int instructions,
FlushICache flush_cache = FLUSH);
virtual ~CodePatcher();
~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }

View File

@ -117,7 +117,7 @@ int FPURegister::ToAllocationIndex(FPURegister reg) {
// -----------------------------------------------------------------------------
// 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_);
@ -189,11 +189,6 @@ Address Assembler::target_address_from_return_address(Address 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.
@ -349,22 +344,18 @@ void RelocInfo::set_code_age_stub(Code* stub,
}
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);
@ -374,23 +365,6 @@ void RelocInfo::set_call_address(Address 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_) ||
@ -442,10 +416,8 @@ void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
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)) {
@ -471,10 +443,8 @@ void RelocInfo::Visit(Heap* heap) {
} 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);

View File

@ -3066,11 +3066,10 @@ void Assembler::emit_code_stub_address(Code* stub) {
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.

View File

@ -518,9 +518,6 @@ class Assembler : public AssemblerBase {
// 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);
@ -566,25 +563,14 @@ class Assembler : public AssemblerBase {
// 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;
@ -1100,16 +1086,11 @@ class Assembler : public AssemblerBase {
// 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.

View File

@ -14,33 +14,38 @@
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)
#define __ ACCESS_MASM(masm)
// 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);
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)
@ -51,20 +56,16 @@ void BreakLocation::SetDebugBreakAtSlot() {
// 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);
@ -78,43 +79,23 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
__ 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.
@ -132,38 +113,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
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();
}

View File

@ -490,12 +490,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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_);
@ -504,7 +498,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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()));
@ -512,13 +505,6 @@ void FullCodeGenerator::EmitReturnSequence() {
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
}
}

View File

@ -1789,7 +1789,7 @@ class CodePatcher {
CodePatcher(byte* address,
int instructions,
FlushICache flush_cache = FLUSH);
virtual ~CodePatcher();
~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }

View File

@ -1029,7 +1029,6 @@ void NormalizedMapCache::NormalizedMapCacheVerify() {
void DebugInfo::DebugInfoVerify() {
CHECK(IsDebugInfo());
VerifyPointer(shared());
VerifyPointer(original_code());
VerifyPointer(code());
VerifyPointer(break_points());
}

View File

@ -5158,8 +5158,7 @@ void Script::set_origin_options(ScriptOriginOptions origin_options) {
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)

View File

@ -988,7 +988,6 @@ void Script::ScriptPrint(std::ostream& os) { // NOLINT
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);

View File

@ -10563,11 +10563,9 @@ void ObjectVisitor::VisitCell(RelocInfo* rinfo) {
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.
@ -10611,7 +10609,7 @@ void Code::InvalidateEmbeddedObjects() {
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());
}
@ -10664,7 +10662,7 @@ void Code::CopyFrom(const CodeDesc& desc) {
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());

View File

@ -10558,8 +10558,6 @@ class DebugInfo: public Struct {
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)
@ -10593,12 +10591,8 @@ class DebugInfo: public Struct {
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;

View File

@ -1390,19 +1390,20 @@ class ScopeIterator {
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
@ -1413,11 +1414,7 @@ class ScopeIterator {
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) {

View File

@ -293,11 +293,6 @@ Address Assembler::target_address_from_return_address(Address pc) {
}
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)];
}
@ -311,21 +306,17 @@ Address Assembler::runtime_entry_at(Address 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;
}
}
@ -526,21 +517,18 @@ void RelocInfo::set_code_age_stub(Code* stub,
}
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(
@ -549,24 +537,6 @@ void RelocInfo::set_call_address(Address target) {
}
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) {
@ -582,10 +552,8 @@ void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
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)) {
@ -611,10 +579,8 @@ void RelocInfo::Visit(Heap* heap) {
} 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);

View File

@ -556,9 +556,6 @@ class Assembler : public AssemblerBase {
// 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(
@ -599,23 +596,11 @@ class Assembler : public AssemblerBase {
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.
@ -1614,16 +1599,11 @@ class Assembler : public AssemblerBase {
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.

View File

@ -14,62 +14,50 @@
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;
// Create a code patcher.
CodePatcher patcher(pc, code_size);
// Add a label for checking the size of the code used for returning.
#ifdef DEBUG
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));
// 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);
}
// 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 BreakLocation::SetDebugBreakAtSlot() {
DCHECK(IsDebugBreakSlot());
PatchCodeWithCall(
pc(), debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry(),
Assembler::kDebugBreakSlotLength - Assembler::kCallSequenceLength);
}
#define __ ACCESS_MASM(masm)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
RegList object_regs) {
void EmitDebugBreakSlot(MacroAssembler* masm) {
Label check_codesize;
__ bind(&check_codesize);
__ Nop(Assembler::kDebugBreakSlotLength);
DCHECK_EQ(Assembler::kDebugBreakSlotLength,
masm->SizeOfCodeGeneratedSince(&check_codesize));
}
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);
}
void DebugCodegen::ClearDebugBreakSlot(Address pc) {
CodePatcher patcher(pc, Assembler::kDebugBreakSlotLength);
EmitDebugBreakSlot(patcher.masm());
}
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));
}
void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
DebugBreakCallHelperMode mode) {
__ RecordComment("Debug break");
// Enter an internal frame.
{
FrameScope scope(masm, StackFrame::INTERNAL);
@ -80,40 +68,23 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
__ 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);
@ -136,35 +107,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
}
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);
}

View File

@ -467,36 +467,15 @@ void FullCodeGenerator::EmitReturnSequence() {
__ 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());
}
}

View File

@ -1533,7 +1533,7 @@ class MacroAssembler: public Assembler {
class CodePatcher {
public:
CodePatcher(byte* address, int size);
virtual ~CodePatcher();
~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }

View File

@ -412,13 +412,10 @@ void CheckDebuggerUnloaded(bool check_functions) {
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());
}
}
}

View File

@ -124,6 +124,7 @@ function listener(event, exec_state, event_data, data) {
}
} catch (e) {
exception = e
print(e + e.stack)
};
};