Debugger: use debug break slot to break on call.
Break point at calls are currently set via IC. To change this, we need to set debug break slots instead. We also need to distinguish those debug break slots as calls to support step-in. To implement this, we add a data field to debug break reloc info to indicate non-call debug breaks or in case of call debug breaks, the number of arguments. We can later use this to find the callee on the evaluation stack in Debug::PrepareStep. BUG=v8:4269 R=ulan@chromium.org LOG=N Review URL: https://codereview.chromium.org/1222093007 Cr-Commit-Position: refs/heads/master@{#29561}
This commit is contained in:
parent
6d12699ab4
commit
8965b683ce
@ -1359,6 +1359,8 @@ class Assembler : public AssemblerBase {
|
||||
|
||||
// Mark address of a debug break slot.
|
||||
void RecordDebugBreakSlot();
|
||||
void RecordDebugBreakSlotForCall(int argc);
|
||||
void RecordDebugBreakSlotForConstructCall();
|
||||
|
||||
// Record the AST id of the CallIC being compiled, so that it can be placed
|
||||
// in the relocation information.
|
||||
|
@ -54,8 +54,7 @@ void BreakLocation::SetDebugBreakAtSlot() {
|
||||
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList object_regs,
|
||||
RegList non_object_regs) {
|
||||
RegList object_regs) {
|
||||
{
|
||||
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
@ -71,21 +70,8 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
// 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);
|
||||
DCHECK((non_object_regs & ~kJSCallerSaved) == 0);
|
||||
DCHECK((object_regs & non_object_regs) == 0);
|
||||
if ((object_regs | non_object_regs) != 0) {
|
||||
for (int i = 0; i < kNumJSCallerSaved; i++) {
|
||||
int r = JSCallerSavedCode(i);
|
||||
Register reg = { r };
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
if (FLAG_debug_code) {
|
||||
__ tst(reg, Operand(0xc0000000));
|
||||
__ Assert(eq, kUnableToEncodeValueAsSmi);
|
||||
}
|
||||
__ SmiTag(reg);
|
||||
}
|
||||
}
|
||||
__ stm(db_w, sp, object_regs | non_object_regs);
|
||||
if (object_regs != 0) {
|
||||
__ stm(db_w, sp, object_regs);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -98,18 +84,15 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
__ CallStub(&ceb);
|
||||
|
||||
// Restore the register values from the expression stack.
|
||||
if ((object_regs | non_object_regs) != 0) {
|
||||
__ ldm(ia_w, sp, object_regs | non_object_regs);
|
||||
for (int i = 0; i < kNumJSCallerSaved; i++) {
|
||||
int r = JSCallerSavedCode(i);
|
||||
Register reg = { r };
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
__ SmiUntag(reg);
|
||||
}
|
||||
if (FLAG_debug_code &&
|
||||
(((object_regs |non_object_regs) & (1 << r)) == 0)) {
|
||||
__ mov(reg, Operand(kDebugZapValue));
|
||||
}
|
||||
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)) {
|
||||
__ mov(reg, Operand(kDebugZapValue));
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,53 +113,11 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallICStub
|
||||
// ----------- S t a t e -------------
|
||||
// -- r1 : function
|
||||
// -- r3 : slot in feedback array (smi)
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, r1.bit() | r3.bit(), 0);
|
||||
}
|
||||
|
||||
|
||||
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(), 0);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallFunctionStub (from code-stubs-arm.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- r1 : function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, r1.bit(), 0);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
|
||||
// Calling convention for CallConstructStub (from code-stubs-arm.cc)
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : number of arguments (not smi)
|
||||
// -- r1 : constructor function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, r1.bit(), r0.bit());
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
|
||||
MacroAssembler* masm) {
|
||||
// Calling convention for CallConstructStub (from code-stubs-arm.cc)
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : number of arguments (not smi)
|
||||
// -- r1 : constructor function
|
||||
// -- r2 : feedback array
|
||||
// -- r3 : feedback slot (smi)
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit() | r3.bit(), r0.bit());
|
||||
Generate_DebugBreakCallHelper(masm, r0.bit());
|
||||
}
|
||||
|
||||
|
||||
@ -186,7 +127,6 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
Assembler::BlockConstPoolScope block_const_pool(masm);
|
||||
Label check_codesize;
|
||||
__ bind(&check_codesize);
|
||||
__ RecordDebugBreakSlot();
|
||||
for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
|
||||
__ nop(MacroAssembler::DEBUG_BREAK_NOP);
|
||||
}
|
||||
@ -198,7 +138,7 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
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, 0);
|
||||
Generate_DebugBreakCallHelper(masm, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2269,6 +2269,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
CallIC(ic, TypeFeedbackId::None());
|
||||
__ mov(r1, r0);
|
||||
__ str(r1, MemOperand(sp, 2 * kPointerSize));
|
||||
SetCallPosition(expr, 1);
|
||||
CallFunctionStub stub(isolate(), 1, CALL_AS_METHOD);
|
||||
__ CallStub(&stub);
|
||||
|
||||
@ -3103,7 +3104,7 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
Handle<Code> ic = CodeFactory::CallIC(isolate(), arg_count, call_type).code();
|
||||
__ mov(r3, Operand(SmiFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
@ -3238,7 +3239,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
|
||||
// Record source position for debugger.
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
@ -3310,7 +3311,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into r1 and r0.
|
||||
__ mov(r0, Operand(arg_count));
|
||||
@ -3353,7 +3354,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into r1 and r0.
|
||||
__ mov(r0, Operand(arg_count));
|
||||
@ -4732,7 +4733,7 @@ void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
|
@ -1027,6 +1027,8 @@ class Assembler : public AssemblerBase {
|
||||
|
||||
// Mark address of a debug break slot.
|
||||
void RecordDebugBreakSlot();
|
||||
void RecordDebugBreakSlotForCall(int argc);
|
||||
void RecordDebugBreakSlotForConstructCall();
|
||||
|
||||
// Record the emission of a constant pool.
|
||||
//
|
||||
|
@ -94,9 +94,8 @@ void BreakLocation::SetDebugBreakAtSlot() {
|
||||
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList object_regs,
|
||||
RegList non_object_regs,
|
||||
Register scratch) {
|
||||
RegList object_regs) {
|
||||
Register scratch = x10;
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
@ -120,30 +119,10 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
//
|
||||
// TODO(jbramley): Why can't this handle callee-saved registers?
|
||||
DCHECK((~kCallerSaved.list() & object_regs) == 0);
|
||||
DCHECK((~kCallerSaved.list() & non_object_regs) == 0);
|
||||
DCHECK((object_regs & non_object_regs) == 0);
|
||||
DCHECK((scratch.Bit() & object_regs) == 0);
|
||||
DCHECK((scratch.Bit() & non_object_regs) == 0);
|
||||
DCHECK((masm->TmpList()->list() & (object_regs | non_object_regs)) == 0);
|
||||
DCHECK((masm->TmpList()->list() & object_regs) == 0);
|
||||
STATIC_ASSERT(kSmiValueSize == 32);
|
||||
|
||||
CPURegList non_object_list =
|
||||
CPURegList(CPURegister::kRegister, kXRegSizeInBits, non_object_regs);
|
||||
while (!non_object_list.IsEmpty()) {
|
||||
// Store each non-object register as two SMIs.
|
||||
Register reg = Register(non_object_list.PopLowestIndex());
|
||||
__ Lsr(scratch, reg, 32);
|
||||
__ SmiTagAndPush(scratch, reg);
|
||||
|
||||
// Stack:
|
||||
// jssp[12]: reg[63:32]
|
||||
// jssp[8]: 0x00000000 (SMI tag & padding)
|
||||
// jssp[4]: reg[31:0]
|
||||
// jssp[0]: 0x00000000 (SMI tag & padding)
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
STATIC_ASSERT(static_cast<unsigned>(kSmiShift) == kWRegSizeInBits);
|
||||
}
|
||||
|
||||
if (object_regs != 0) {
|
||||
__ PushXRegList(object_regs);
|
||||
}
|
||||
@ -162,20 +141,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
__ PopXRegList(object_regs);
|
||||
}
|
||||
|
||||
non_object_list =
|
||||
CPURegList(CPURegister::kRegister, kXRegSizeInBits, non_object_regs);
|
||||
while (!non_object_list.IsEmpty()) {
|
||||
// Load each non-object register from two SMIs.
|
||||
// Stack:
|
||||
// jssp[12]: reg[63:32]
|
||||
// jssp[8]: 0x00000000 (SMI tag & padding)
|
||||
// jssp[4]: reg[31:0]
|
||||
// jssp[0]: 0x00000000 (SMI tag & padding)
|
||||
Register reg = Register(non_object_list.PopHighestIndex());
|
||||
__ Pop(scratch, reg);
|
||||
__ Bfxil(reg, scratch, 32, 32);
|
||||
}
|
||||
|
||||
// Don't bother removing padding bytes pushed on the stack
|
||||
// as the frame is going to be restored right away.
|
||||
|
||||
@ -193,54 +158,11 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallICStub
|
||||
// ----------- S t a t e -------------
|
||||
// -- x1 : function
|
||||
// -- x3 : slot in feedback array
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, x1.Bit() | x3.Bit(), 0, x10);
|
||||
}
|
||||
|
||||
|
||||
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(), 0, x10);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallFunctionStub (from code-stubs-arm64.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- x1 : function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, x1.Bit(), 0, x10);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
|
||||
// Calling convention for CallConstructStub (from code-stubs-arm64.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- x0 : number of arguments (not smi)
|
||||
// -- x1 : constructor function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, x1.Bit(), x0.Bit(), x10);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
|
||||
MacroAssembler* masm) {
|
||||
// Calling convention for CallConstructStub (from code-stubs-arm64.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- x0 : number of arguments (not smi)
|
||||
// -- x1 : constructor function
|
||||
// -- x2 : feedback array
|
||||
// -- x3 : feedback slot (smi)
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(
|
||||
masm, x1.Bit() | x2.Bit() | x3.Bit(), x0.Bit(), x10);
|
||||
Generate_DebugBreakCallHelper(masm, x0.Bit());
|
||||
}
|
||||
|
||||
|
||||
@ -249,7 +171,6 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
// the constant pool in the debug break slot code.
|
||||
InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions);
|
||||
|
||||
__ RecordDebugBreakSlot();
|
||||
for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
|
||||
__ nop(Assembler::DEBUG_BREAK_NOP);
|
||||
}
|
||||
@ -259,7 +180,7 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
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, 0, x10);
|
||||
Generate_DebugBreakCallHelper(masm, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2788,10 +2788,11 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
|
||||
// Load the arguments.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
SetExpressionPosition(expr);
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
SetCallPosition(expr, arg_count);
|
||||
|
||||
Handle<Code> ic = CodeFactory::CallIC(isolate(), arg_count, call_type).code();
|
||||
__ Mov(x3, SmiFromSlot(expr->CallFeedbackICSlot()));
|
||||
@ -2907,26 +2908,26 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
|
||||
PushCalleeAndWithBaseObject(expr);
|
||||
PushCalleeAndWithBaseObject(expr);
|
||||
|
||||
// Push the arguments.
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
// Push the arguments.
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
// Push a copy of the function (found below the arguments) and
|
||||
// resolve eval.
|
||||
__ Peek(x10, (arg_count + 1) * kPointerSize);
|
||||
__ Push(x10);
|
||||
EmitResolvePossiblyDirectEval(arg_count);
|
||||
// Push a copy of the function (found below the arguments) and
|
||||
// resolve eval.
|
||||
__ Peek(x10, (arg_count + 1) * kPointerSize);
|
||||
__ Push(x10);
|
||||
EmitResolvePossiblyDirectEval(arg_count);
|
||||
|
||||
// Touch up the stack with the resolved function.
|
||||
__ Poke(x0, (arg_count + 1) * kPointerSize);
|
||||
// Touch up the stack with the resolved function.
|
||||
__ Poke(x0, (arg_count + 1) * kPointerSize);
|
||||
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
|
||||
// Record source position for debugger.
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
|
||||
// Call the evaluated function.
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
@ -3001,7 +3002,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into x1 and x0.
|
||||
__ Mov(x0, arg_count);
|
||||
@ -3044,7 +3045,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into x1 and x0.
|
||||
__ Mov(x0, arg_count);
|
||||
@ -4424,7 +4425,7 @@ void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ Peek(x1, (arg_count + 1) * kPointerSize);
|
||||
__ CallStub(&stub);
|
||||
@ -5186,6 +5187,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
CallIC(ic, TypeFeedbackId::None());
|
||||
__ Mov(x1, x0);
|
||||
__ Poke(x1, 2 * kPointerSize);
|
||||
SetCallPosition(expr, 1);
|
||||
CallFunctionStub stub(isolate(), 1, CALL_AS_METHOD);
|
||||
__ CallStub(&stub);
|
||||
|
||||
|
@ -380,23 +380,27 @@ void RelocInfoWriter::WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag) {
|
||||
}
|
||||
|
||||
|
||||
void RelocInfoWriter::WriteInt(int number) {
|
||||
for (int i = 0; i < kIntSize; i++) {
|
||||
*--pos_ = static_cast<byte>(number);
|
||||
// Signed right shift is arithmetic shift. Tested in test-utils.cc.
|
||||
number = number >> kBitsPerByte;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RelocInfoWriter::WriteDebugBreakSlotData(int data) { WriteInt(data); }
|
||||
|
||||
|
||||
void RelocInfoWriter::WriteExtraTaggedIntData(int data_delta, int top_tag) {
|
||||
WriteExtraTag(kDataJumpExtraTag, top_tag);
|
||||
for (int i = 0; i < kIntSize; i++) {
|
||||
*--pos_ = static_cast<byte>(data_delta);
|
||||
// Signed right shift is arithmetic shift. Tested in test-utils.cc.
|
||||
data_delta = data_delta >> kBitsPerByte;
|
||||
}
|
||||
WriteInt(data_delta);
|
||||
}
|
||||
|
||||
|
||||
void RelocInfoWriter::WriteExtraTaggedPoolData(int data, int pool_type) {
|
||||
WriteExtraTag(kPoolExtraTag, pool_type);
|
||||
for (int i = 0; i < kIntSize; i++) {
|
||||
*--pos_ = static_cast<byte>(data);
|
||||
// Signed right shift is arithmetic shift. Tested in test-utils.cc.
|
||||
data = data >> kBitsPerByte;
|
||||
}
|
||||
WriteInt(data);
|
||||
}
|
||||
|
||||
|
||||
@ -498,10 +502,10 @@ void RelocInfoWriter::Write(const RelocInfo* rinfo) {
|
||||
WriteExtraTaggedData(rinfo->data(), kCommentTag);
|
||||
DCHECK(begin_pos - pos_ >= RelocInfo::kMinRelocCommentSize);
|
||||
} else if (RelocInfo::IsConstPool(rmode) || RelocInfo::IsVeneerPool(rmode)) {
|
||||
WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
|
||||
WriteExtraTaggedPoolData(static_cast<int>(rinfo->data()),
|
||||
RelocInfo::IsConstPool(rmode) ? kConstPoolTag
|
||||
: kVeneerPoolTag);
|
||||
WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
|
||||
WriteExtraTaggedPoolData(
|
||||
static_cast<int>(rinfo->data()),
|
||||
RelocInfo::IsConstPool(rmode) ? kConstPoolTag : kVeneerPoolTag);
|
||||
} else {
|
||||
DCHECK(rmode > RelocInfo::LAST_COMPACT_ENUM);
|
||||
DCHECK(rmode <= RelocInfo::LAST_STANDARD_NONCOMPACT_ENUM);
|
||||
@ -513,6 +517,9 @@ void RelocInfoWriter::Write(const RelocInfo* rinfo) {
|
||||
// None of these modes need a data component.
|
||||
DCHECK(0 <= saved_mode && saved_mode < kPoolExtraTag);
|
||||
WriteExtraTaggedPC(pc_delta, saved_mode);
|
||||
if (RelocInfo::IsDebugBreakSlot(rmode)) {
|
||||
WriteDebugBreakSlotData(static_cast<int>(rinfo->data()));
|
||||
}
|
||||
}
|
||||
last_pc_ = rinfo->pc();
|
||||
last_mode_ = rmode;
|
||||
@ -557,7 +564,7 @@ void RelocIterator::AdvanceReadId() {
|
||||
}
|
||||
|
||||
|
||||
void RelocIterator::AdvanceReadPoolData() {
|
||||
void RelocIterator::AdvanceReadInt() {
|
||||
int x = 0;
|
||||
for (int i = 0; i < kIntSize; i++) {
|
||||
x |= static_cast<int>(*--pos_) << i * kBitsPerByte;
|
||||
@ -566,6 +573,12 @@ void RelocIterator::AdvanceReadPoolData() {
|
||||
}
|
||||
|
||||
|
||||
void RelocIterator::AdvanceReadPoolData() { AdvanceReadInt(); }
|
||||
|
||||
|
||||
void RelocIterator::AdvanceReadDebugBreakSlotData() { AdvanceReadInt(); }
|
||||
|
||||
|
||||
void RelocIterator::AdvanceReadPosition() {
|
||||
int x = 0;
|
||||
for (int i = 0; i < kIntSize; i++) {
|
||||
@ -718,8 +731,17 @@ void RelocIterator::next() {
|
||||
Advance(kIntSize);
|
||||
} else {
|
||||
AdvanceReadPC();
|
||||
int rmode = extra_tag + RelocInfo::LAST_COMPACT_ENUM + 1;
|
||||
if (SetMode(static_cast<RelocInfo::Mode>(rmode))) return;
|
||||
RelocInfo::Mode rmode = static_cast<RelocInfo::Mode>(
|
||||
extra_tag + RelocInfo::LAST_COMPACT_ENUM + 1);
|
||||
if (RelocInfo::IsDebugBreakSlot(rmode)) {
|
||||
if (SetMode(rmode)) {
|
||||
AdvanceReadDebugBreakSlotData();
|
||||
return;
|
||||
}
|
||||
Advance(kIntSize);
|
||||
} else if (SetMode(rmode)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -883,6 +905,14 @@ void RelocInfo::Print(Isolate* isolate, std::ostream& os) { // NOLINT
|
||||
}
|
||||
} else if (IsConstPool(rmode_)) {
|
||||
os << " (size " << static_cast<int>(data_) << ")";
|
||||
} else if (IsDebugBreakSlot(rmode_)) {
|
||||
if (DebugBreakIsCall(data_)) {
|
||||
os << " (call with " << DebugBreakCallArgumentsCount(data_) << " args)";
|
||||
} else if (DebugBreakIsConstructCall(data_)) {
|
||||
os << " (construct call)";
|
||||
} else {
|
||||
os << " (slot)";
|
||||
}
|
||||
}
|
||||
|
||||
os << "\n";
|
||||
@ -946,6 +976,20 @@ void RelocInfo::Verify(Isolate* isolate) {
|
||||
#endif // VERIFY_HEAP
|
||||
|
||||
|
||||
bool RelocInfo::DebugBreakIsConstructCall(intptr_t data) {
|
||||
return data == static_cast<intptr_t>(kDebugBreakConstructCallSentinel);
|
||||
}
|
||||
|
||||
|
||||
bool RelocInfo::DebugBreakIsCall(intptr_t data) { return data >= 0; }
|
||||
|
||||
|
||||
int RelocInfo::DebugBreakCallArgumentsCount(intptr_t data) {
|
||||
DCHECK(DebugBreakIsCall(data));
|
||||
return static_cast<int>(data);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation of ExternalReference
|
||||
|
||||
@ -1869,7 +1913,23 @@ void Assembler::RecordJSReturn() {
|
||||
|
||||
void Assembler::RecordDebugBreakSlot() {
|
||||
EnsureSpace ensure_space(this);
|
||||
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
|
||||
intptr_t data = static_cast<intptr_t>(RelocInfo::kDebugBreakNonCallSentinel);
|
||||
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT, data);
|
||||
}
|
||||
|
||||
|
||||
void Assembler::RecordDebugBreakSlotForCall(int argc) {
|
||||
EnsureSpace ensure_space(this);
|
||||
intptr_t data = static_cast<intptr_t>(argc);
|
||||
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT, data);
|
||||
}
|
||||
|
||||
|
||||
void Assembler::RecordDebugBreakSlotForConstructCall() {
|
||||
EnsureSpace ensure_space(this);
|
||||
intptr_t data =
|
||||
static_cast<intptr_t>(RelocInfo::kDebugBreakConstructCallSentinel);
|
||||
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT, data);
|
||||
}
|
||||
|
||||
|
||||
|
@ -506,6 +506,10 @@ class RelocInfo {
|
||||
// constant pool, otherwise the pointer is embedded in the instruction stream.
|
||||
bool IsInConstantPool();
|
||||
|
||||
static bool DebugBreakIsConstructCall(intptr_t data);
|
||||
static bool DebugBreakIsCall(intptr_t data);
|
||||
static int DebugBreakCallArgumentsCount(intptr_t data);
|
||||
|
||||
// Read/modify the code target in the branch/call instruction
|
||||
// this relocation applies to;
|
||||
// can only be called if IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
|
||||
@ -623,6 +627,8 @@ class RelocInfo {
|
||||
static const int kDataMask =
|
||||
(1 << CODE_TARGET_WITH_ID) | kPositionMask | (1 << COMMENT);
|
||||
static const int kApplyMask; // Modes affected by apply. Depends on arch.
|
||||
static const int kDebugBreakNonCallSentinel = -2;
|
||||
static const int kDebugBreakConstructCallSentinel = -1;
|
||||
|
||||
private:
|
||||
// On ARM, note that pc_ is the address of the constant pool entry
|
||||
@ -690,6 +696,8 @@ class RelocInfoWriter BASE_EMBEDDED {
|
||||
inline uint32_t WriteVariableLengthPCJump(uint32_t pc_delta);
|
||||
inline void WriteTaggedPC(uint32_t pc_delta, int tag);
|
||||
inline void WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag);
|
||||
inline void WriteInt(int number);
|
||||
inline void WriteDebugBreakSlotData(int data);
|
||||
inline void WriteExtraTaggedIntData(int data_delta, int top_tag);
|
||||
inline void WriteExtraTaggedPoolData(int data, int pool_type);
|
||||
inline void WriteExtraTaggedData(intptr_t data_delta, int top_tag);
|
||||
@ -750,7 +758,9 @@ class RelocIterator: public Malloced {
|
||||
void ReadTaggedPC();
|
||||
void AdvanceReadPC();
|
||||
void AdvanceReadId();
|
||||
void AdvanceReadInt();
|
||||
void AdvanceReadPoolData();
|
||||
void AdvanceReadDebugBreakSlotData();
|
||||
void AdvanceReadPosition();
|
||||
void AdvanceReadData();
|
||||
void AdvanceReadVariableLengthPCJump();
|
||||
|
@ -1369,32 +1369,11 @@ static void Generate_KeyedStoreIC_PreMonomorphic_Strict(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
static void Generate_CallICStub_DebugBreak(MacroAssembler* masm) {
|
||||
DebugCodegen::GenerateCallICStubDebugBreak(masm);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_Return_DebugBreak(MacroAssembler* masm) {
|
||||
DebugCodegen::GenerateReturnDebugBreak(masm);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_CallFunctionStub_DebugBreak(MacroAssembler* masm) {
|
||||
DebugCodegen::GenerateCallFunctionStubDebugBreak(masm);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_CallConstructStub_DebugBreak(MacroAssembler* masm) {
|
||||
DebugCodegen::GenerateCallConstructStubDebugBreak(masm);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_CallConstructStub_Recording_DebugBreak(
|
||||
MacroAssembler* masm) {
|
||||
DebugCodegen::GenerateCallConstructStubRecordDebugBreak(masm);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_Slot_DebugBreak(MacroAssembler* masm) {
|
||||
DebugCodegen::GenerateSlotDebugBreak(masm);
|
||||
}
|
||||
|
@ -143,14 +143,6 @@ enum BuiltinExtraArguments {
|
||||
#define BUILTIN_LIST_DEBUG_A(V) \
|
||||
V(Return_DebugBreak, BUILTIN, DEBUG_STUB, \
|
||||
DEBUG_BREAK) \
|
||||
V(CallFunctionStub_DebugBreak, BUILTIN, DEBUG_STUB, \
|
||||
DEBUG_BREAK) \
|
||||
V(CallConstructStub_DebugBreak, BUILTIN, DEBUG_STUB, \
|
||||
DEBUG_BREAK) \
|
||||
V(CallConstructStub_Recording_DebugBreak, BUILTIN, DEBUG_STUB, \
|
||||
DEBUG_BREAK) \
|
||||
V(CallICStub_DebugBreak, CALL_IC, DEBUG_STUB, \
|
||||
DEBUG_BREAK) \
|
||||
V(Slot_DebugBreak, BUILTIN, DEBUG_STUB, \
|
||||
DEBUG_BREAK) \
|
||||
V(PlainReturn_LiveEdit, BUILTIN, DEBUG_STUB, \
|
||||
|
@ -1013,11 +1013,6 @@ class CallICStub: public PlatformCodeStub {
|
||||
minor_key_ = state.GetExtraICState();
|
||||
}
|
||||
|
||||
static int ExtractArgcFromMinorKey(int minor_key) {
|
||||
CallICState state(static_cast<ExtraICState>(minor_key));
|
||||
return state.arg_count();
|
||||
}
|
||||
|
||||
Code::Kind GetCodeKind() const override { return Code::CALL_IC; }
|
||||
|
||||
InlineCacheState GetICState() const override { return DEFAULT; }
|
||||
@ -1913,10 +1908,6 @@ class CallFunctionStub: public PlatformCodeStub {
|
||||
minor_key_ = ArgcBits::encode(argc) | FlagBits::encode(flags);
|
||||
}
|
||||
|
||||
static int ExtractArgcFromMinorKey(int minor_key) {
|
||||
return ArgcBits::decode(minor_key);
|
||||
}
|
||||
|
||||
private:
|
||||
int argc() const { return ArgcBits::decode(minor_key_); }
|
||||
int flags() const { return FlagBits::decode(minor_key_); }
|
||||
|
198
src/debug.cc
198
src/debug.cc
@ -117,7 +117,6 @@ void BreakLocation::Iterator::Next() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for break at return.
|
||||
if (RelocInfo::IsJSReturn(rmode())) {
|
||||
// Set the positions to the end of the function.
|
||||
if (debug_info_->shared()->HasSourceCode()) {
|
||||
@ -127,43 +126,21 @@ void BreakLocation::Iterator::Next() {
|
||||
position_ = 0;
|
||||
}
|
||||
statement_position_ = position_;
|
||||
break_index_++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (RelocInfo::IsCodeTarget(rmode())) {
|
||||
// Check for breakable code target. Look in the original code as setting
|
||||
// break points can cause the code targets in the running (debugged) code
|
||||
// to be of a different kind than in the original code.
|
||||
Address target = original_rinfo()->target_address();
|
||||
Code* code = Code::GetCodeFromTargetAddress(target);
|
||||
|
||||
if (RelocInfo::IsConstructCall(rmode()) || code->is_call_stub()) {
|
||||
break_index_++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (code->kind() == Code::STUB &&
|
||||
CodeStub::GetMajorKey(code) == CodeStub::CallFunction) {
|
||||
break_index_++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip below if we only want locations for calls and returns.
|
||||
if (type_ == CALLS_AND_RETURNS) continue;
|
||||
|
||||
if (RelocInfo::IsDebuggerStatement(rmode())) {
|
||||
break_index_++;
|
||||
if (RelocInfo::IsDebugBreakSlot(rmode()) &&
|
||||
(type_ == ALL_BREAK_LOCATIONS ||
|
||||
RelocInfo::DebugBreakIsCall(rinfo()->data()))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (RelocInfo::IsDebugBreakSlot(rmode()) && type_ != CALLS_AND_RETURNS) {
|
||||
// There is always a possible break point at a debug break slot.
|
||||
break_index_++;
|
||||
if (RelocInfo::IsDebuggerStatement(rmode()) &&
|
||||
type_ == ALL_BREAK_LOCATIONS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break_index_++;
|
||||
}
|
||||
|
||||
|
||||
@ -311,9 +288,6 @@ void BreakLocation::SetDebugBreak() {
|
||||
} else if (IsDebugBreakSlot()) {
|
||||
// Patch the code in the break slot.
|
||||
SetDebugBreakAtSlot();
|
||||
} else {
|
||||
// Patch the IC call.
|
||||
SetDebugBreakAtIC();
|
||||
}
|
||||
DCHECK(IsDebugBreak());
|
||||
}
|
||||
@ -329,13 +303,6 @@ void BreakLocation::ClearDebugBreak() {
|
||||
} else if (IsDebugBreakSlot()) {
|
||||
// Restore the code in the break slot.
|
||||
RestoreFromOriginal(Assembler::kDebugBreakSlotLength);
|
||||
} else {
|
||||
// Restore the IC call.
|
||||
rinfo().set_target_address(original_rinfo().target_address());
|
||||
// Some ICs store data in the feedback vector. Clear this to ensure we
|
||||
// won't miss future stepping requirements.
|
||||
SharedFunctionInfo* shared = debug_info_->shared();
|
||||
shared->feedback_vector()->ClearICSlots(shared);
|
||||
}
|
||||
DCHECK(!IsDebugBreak());
|
||||
}
|
||||
@ -348,13 +315,7 @@ void BreakLocation::RestoreFromOriginal(int length_in_bytes) {
|
||||
|
||||
|
||||
bool BreakLocation::IsStepInLocation() const {
|
||||
if (IsConstructCall()) return true;
|
||||
if (RelocInfo::IsCodeTarget(rmode())) {
|
||||
HandleScope scope(debug_info_->GetIsolate());
|
||||
Handle<Code> target_code = CodeTarget();
|
||||
return target_code->is_call_stub();
|
||||
}
|
||||
return false;
|
||||
return IsConstructCall() || IsCall();
|
||||
}
|
||||
|
||||
|
||||
@ -363,52 +324,8 @@ bool BreakLocation::IsDebugBreak() const {
|
||||
return rinfo().IsPatchedReturnSequence();
|
||||
} else if (IsDebugBreakSlot()) {
|
||||
return rinfo().IsPatchedDebugBreakSlotSequence();
|
||||
} else {
|
||||
return Debug::IsDebugBreak(rinfo().target_address());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Find the builtin to use for invoking the debug break
|
||||
static Handle<Code> DebugBreakForIC(Handle<Code> code, RelocInfo::Mode mode) {
|
||||
Isolate* isolate = code->GetIsolate();
|
||||
|
||||
// Find the builtin debug break function matching the calling convention
|
||||
// used by the call site.
|
||||
if (code->is_inline_cache_stub()) {
|
||||
DCHECK(code->kind() == Code::CALL_IC);
|
||||
return isolate->builtins()->CallICStub_DebugBreak();
|
||||
}
|
||||
if (RelocInfo::IsConstructCall(mode)) {
|
||||
if (code->has_function_cache()) {
|
||||
return isolate->builtins()->CallConstructStub_Recording_DebugBreak();
|
||||
} else {
|
||||
return isolate->builtins()->CallConstructStub_DebugBreak();
|
||||
}
|
||||
}
|
||||
if (code->kind() == Code::STUB) {
|
||||
DCHECK(CodeStub::GetMajorKey(*code) == CodeStub::CallFunction);
|
||||
return isolate->builtins()->CallFunctionStub_DebugBreak();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocation::SetDebugBreakAtIC() {
|
||||
// Patch the original code with the current address as the current address
|
||||
// might have changed by the inline caching since the code was copied.
|
||||
original_rinfo().set_target_address(rinfo().target_address());
|
||||
|
||||
if (RelocInfo::IsCodeTarget(rmode_)) {
|
||||
Handle<Code> target_code = CodeTarget();
|
||||
|
||||
// Patch the code to invoke the builtin debug break function matching the
|
||||
// calling convention used by the call site.
|
||||
Handle<Code> debug_break_code = DebugBreakForIC(target_code, rmode_);
|
||||
rinfo().set_target_address(debug_break_code->entry());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -417,20 +334,6 @@ Handle<Object> BreakLocation::BreakPointObjects() const {
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> BreakLocation::CodeTarget() const {
|
||||
DCHECK(IsCodeTarget());
|
||||
Address target = rinfo().target_address();
|
||||
return Handle<Code>(Code::GetCodeFromTargetAddress(target));
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> BreakLocation::OriginalCodeTarget() const {
|
||||
DCHECK(IsCodeTarget());
|
||||
Address target = original_rinfo().target_address();
|
||||
return Handle<Code>(Code::GetCodeFromTargetAddress(target));
|
||||
}
|
||||
|
||||
|
||||
bool BreakLocation::Iterator::RinfoDone() const {
|
||||
DCHECK(reloc_iterator_.done() == reloc_iterator_original_.done());
|
||||
return reloc_iterator_.done();
|
||||
@ -442,7 +345,7 @@ void BreakLocation::Iterator::RinfoNext() {
|
||||
reloc_iterator_original_.next();
|
||||
#ifdef DEBUG
|
||||
DCHECK(reloc_iterator_.done() == reloc_iterator_original_.done());
|
||||
DCHECK(reloc_iterator_.done() || rmode() == original_rmode());
|
||||
DCHECK(reloc_iterator_.done() || rmode() == original_rinfo()->rmode());
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -941,7 +844,7 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function,
|
||||
|
||||
// Find the break point and change it.
|
||||
BreakLocation location = BreakLocation::FromPosition(
|
||||
debug_info, SOURCE_BREAK_LOCATIONS, *source_position, STATEMENT_ALIGNED);
|
||||
debug_info, ALL_BREAK_LOCATIONS, *source_position, STATEMENT_ALIGNED);
|
||||
*source_position = location.statement_position();
|
||||
location.SetBreakPoint(break_point_object);
|
||||
|
||||
@ -985,7 +888,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
|
||||
|
||||
// Find the break point and change it.
|
||||
BreakLocation location = BreakLocation::FromPosition(
|
||||
debug_info, SOURCE_BREAK_LOCATIONS, position, alignment);
|
||||
debug_info, ALL_BREAK_LOCATIONS, position, alignment);
|
||||
location.SetBreakPoint(break_point_object);
|
||||
|
||||
position = (alignment == STATEMENT_ALIGNED) ? location.statement_position()
|
||||
@ -1017,7 +920,7 @@ void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
|
||||
break_point_info->code_position()->value();
|
||||
|
||||
BreakLocation location =
|
||||
BreakLocation::FromAddress(debug_info, SOURCE_BREAK_LOCATIONS, pc);
|
||||
BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, pc);
|
||||
location.ClearBreakPoint(break_point_object);
|
||||
|
||||
// If there are no more break points left remove the debug info for this
|
||||
@ -1238,27 +1141,7 @@ void Debug::PrepareStep(StepAction step_action,
|
||||
BreakLocation location =
|
||||
BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc);
|
||||
|
||||
if (thread_local_.restarter_frame_function_pointer_ == NULL) {
|
||||
if (location.IsCodeTarget()) {
|
||||
Handle<Code> target_code = location.CodeTarget();
|
||||
|
||||
// Check if target code is CallFunction stub.
|
||||
Handle<Code> maybe_call_function_stub = target_code;
|
||||
// If there is a breakpoint at this line look at the original code to
|
||||
// check if it is a CallFunction stub.
|
||||
if (location.IsDebugBreak()) {
|
||||
maybe_call_function_stub = location.OriginalCodeTarget();
|
||||
}
|
||||
if ((maybe_call_function_stub->kind() == Code::STUB &&
|
||||
CodeStub::GetMajorKey(*maybe_call_function_stub) ==
|
||||
CodeStub::CallFunction) ||
|
||||
maybe_call_function_stub->is_call_stub()) {
|
||||
// Save reference to the code as we may need it to find out arguments
|
||||
// count for 'step in' later.
|
||||
call_function_stub = maybe_call_function_stub;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (thread_local_.restarter_frame_function_pointer_ != NULL) {
|
||||
is_at_restarted_function = true;
|
||||
}
|
||||
|
||||
@ -1297,24 +1180,7 @@ void Debug::PrepareStep(StepAction step_action,
|
||||
Handle<JSFunction> restarted_function(
|
||||
JSFunction::cast(*thread_local_.restarter_frame_function_pointer_));
|
||||
FloodWithOneShot(restarted_function);
|
||||
} else if (!call_function_stub.is_null()) {
|
||||
// If it's CallFunction stub ensure target function is compiled and flood
|
||||
// it with one shot breakpoints.
|
||||
bool is_call_ic = call_function_stub->kind() == Code::CALL_IC;
|
||||
|
||||
// Find out number of arguments from the stub minor key.
|
||||
uint32_t key = call_function_stub->stub_key();
|
||||
// Argc in the stub is the number of arguments passed - not the
|
||||
// expected arguments of the called function.
|
||||
int call_function_arg_count = is_call_ic
|
||||
? CallICStub::ExtractArgcFromMinorKey(CodeStub::MinorKeyFromKey(key))
|
||||
: CallFunctionStub::ExtractArgcFromMinorKey(
|
||||
CodeStub::MinorKeyFromKey(key));
|
||||
|
||||
DCHECK(is_call_ic ||
|
||||
CodeStub::GetMajorKey(*call_function_stub) ==
|
||||
CodeStub::MajorKeyFromKey(key));
|
||||
|
||||
} else if (location.IsCall()) {
|
||||
// Find target function on the expression stack.
|
||||
// Expression stack looks like this (top to bottom):
|
||||
// argN
|
||||
@ -1322,10 +1188,10 @@ void Debug::PrepareStep(StepAction step_action,
|
||||
// arg0
|
||||
// Receiver
|
||||
// Function to call
|
||||
int expressions_count = frame->ComputeExpressionsCount();
|
||||
DCHECK(expressions_count - 2 - call_function_arg_count >= 0);
|
||||
Object* fun = frame->GetExpression(
|
||||
expressions_count - 2 - call_function_arg_count);
|
||||
int num_expressions_without_args =
|
||||
frame->ComputeExpressionsCount() - location.CallArgumentsCount();
|
||||
DCHECK(num_expressions_without_args >= 2);
|
||||
Object* fun = frame->GetExpression(num_expressions_without_args - 2);
|
||||
|
||||
// Flood the actual target of call/apply.
|
||||
if (fun->IsJSFunction()) {
|
||||
@ -1338,10 +1204,9 @@ void Debug::PrepareStep(StepAction step_action,
|
||||
while (fun->IsJSFunction()) {
|
||||
Code* code = JSFunction::cast(fun)->shared()->code();
|
||||
if (code != apply && code != call) break;
|
||||
DCHECK(expressions_count - i - call_function_arg_count >= 0);
|
||||
fun = frame->GetExpression(expressions_count - i -
|
||||
call_function_arg_count);
|
||||
i -= 1;
|
||||
DCHECK(num_expressions_without_args >= i);
|
||||
fun = frame->GetExpression(num_expressions_without_args - i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2209,27 +2074,6 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
|
||||
|
||||
// Continue just after the slot.
|
||||
after_break_target_ = addr + Assembler::kDebugBreakSlotLength;
|
||||
} else {
|
||||
addr = Assembler::target_address_from_return_address(frame->pc());
|
||||
if (IsDebugBreak(Assembler::target_address_at(addr, *code))) {
|
||||
// We now know that there is still a debug break call at the target
|
||||
// address, so the break point is still there and the original code will
|
||||
// hold the address to jump to in order to complete the call which is
|
||||
// replaced by a call to DebugBreakXXX.
|
||||
|
||||
// Find the corresponding address in the original code.
|
||||
addr += original_code->instruction_start() - code->instruction_start();
|
||||
|
||||
// Install jump to the call address in the original code. This will be the
|
||||
// call which was overwritten by the call to DebugBreakXXX.
|
||||
after_break_target_ = Assembler::target_address_at(addr, *original_code);
|
||||
} else {
|
||||
// There is no longer a break point present. Don't try to look in the
|
||||
// original code as the running code will have the right address. This
|
||||
// takes care of the case where the last break point is removed from the
|
||||
// function and therefore no "original code" is available.
|
||||
after_break_target_ = Assembler::target_address_at(addr, *code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
48
src/debug.h
48
src/debug.h
@ -51,11 +51,7 @@ enum ExceptionBreakType {
|
||||
|
||||
|
||||
// Type of exception break.
|
||||
enum BreakLocatorType {
|
||||
ALL_BREAK_LOCATIONS = 0,
|
||||
SOURCE_BREAK_LOCATIONS = 1,
|
||||
CALLS_AND_RETURNS = 2
|
||||
};
|
||||
enum BreakLocatorType { ALL_BREAK_LOCATIONS, CALLS_AND_RETURNS };
|
||||
|
||||
|
||||
// The different types of breakpoint position alignments.
|
||||
@ -83,13 +79,16 @@ class BreakLocation {
|
||||
|
||||
bool IsDebugBreak() const;
|
||||
inline bool IsExit() const { return RelocInfo::IsJSReturn(rmode_); }
|
||||
inline bool IsConstructCall() const {
|
||||
return RelocInfo::IsConstructCall(rmode_);
|
||||
inline bool IsCall() const {
|
||||
return IsDebugBreakSlot() && RelocInfo::DebugBreakIsCall(data_);
|
||||
}
|
||||
inline bool IsConstructCall() const {
|
||||
return IsDebugBreakSlot() && RelocInfo::DebugBreakIsConstructCall(data_);
|
||||
}
|
||||
inline int CallArgumentsCount() const {
|
||||
DCHECK(IsCall());
|
||||
return RelocInfo::DebugBreakCallArgumentsCount(data_);
|
||||
}
|
||||
inline bool IsCodeTarget() const { return RelocInfo::IsCodeTarget(rmode_); }
|
||||
|
||||
Handle<Code> CodeTarget() const;
|
||||
Handle<Code> OriginalCodeTarget() const;
|
||||
|
||||
bool IsStepInLocation() const;
|
||||
inline bool HasBreakPoint() const {
|
||||
@ -104,28 +103,22 @@ class BreakLocation {
|
||||
void SetOneShot();
|
||||
void ClearOneShot();
|
||||
|
||||
|
||||
inline RelocInfo rinfo() const {
|
||||
return RelocInfo(pc(), rmode(), data_, code());
|
||||
}
|
||||
|
||||
inline RelocInfo original_rinfo() const {
|
||||
return RelocInfo(original_pc(), original_rmode(), original_data_,
|
||||
original_code());
|
||||
}
|
||||
|
||||
inline int position() const { return position_; }
|
||||
inline int statement_position() const { return statement_position_; }
|
||||
|
||||
inline Address pc() const { return code()->entry() + pc_offset_; }
|
||||
inline Address original_pc() const {
|
||||
return original_code()->entry() + original_pc_offset_;
|
||||
return debug_info_->original_code()->entry() + original_pc_offset_;
|
||||
}
|
||||
|
||||
inline RelocInfo::Mode rmode() const { return rmode_; }
|
||||
inline RelocInfo::Mode original_rmode() const { return original_rmode_; }
|
||||
|
||||
inline Code* code() const { return debug_info_->code(); }
|
||||
inline Code* original_code() const { return debug_info_->original_code(); }
|
||||
|
||||
private:
|
||||
BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo,
|
||||
@ -149,20 +142,13 @@ class BreakLocation {
|
||||
}
|
||||
|
||||
inline RelocInfo::Mode rmode() { return reloc_iterator_.rinfo()->rmode(); }
|
||||
inline RelocInfo::Mode original_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(); }
|
||||
inline Address original_pc() { return original_rinfo()->pc(); }
|
||||
|
||||
int break_index() const { return break_index_; }
|
||||
|
||||
inline int position() const { return position_; }
|
||||
inline int statement_position() const { return statement_position_; }
|
||||
|
||||
@ -194,7 +180,6 @@ class BreakLocation {
|
||||
void SetDebugBreak();
|
||||
void SetDebugBreakAtReturn();
|
||||
void SetDebugBreakAtSlot();
|
||||
void SetDebugBreakAtIC();
|
||||
|
||||
inline bool IsDebuggerStatement() const {
|
||||
return RelocInfo::IsDebuggerStatement(rmode_);
|
||||
@ -807,16 +792,7 @@ class SuppressDebug BASE_EMBEDDED {
|
||||
class DebugCodegen : public AllStatic {
|
||||
public:
|
||||
static void GenerateSlot(MacroAssembler* masm);
|
||||
static void GenerateCallICStubDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateLoadICDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateStoreICDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateKeyedLoadICDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateKeyedStoreICDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateCompareNilICDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateReturnDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateCallFunctionStubDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateCallConstructStubDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm);
|
||||
static void GenerateSlotDebugBreak(MacroAssembler* masm);
|
||||
static void GeneratePlainReturnLiveEdit(MacroAssembler* masm);
|
||||
|
||||
|
@ -216,15 +216,7 @@ static int DecodeIt(Isolate* isolate, std::ostream* os,
|
||||
DCHECK(major_key == CodeStub::MajorKeyFromKey(key));
|
||||
out.AddFormatted(" %s, %s, ", Code::Kind2String(kind),
|
||||
CodeStub::MajorName(major_key, false));
|
||||
switch (major_key) {
|
||||
case CodeStub::CallFunction: {
|
||||
int argc = CallFunctionStub::ExtractArgcFromMinorKey(minor_key);
|
||||
out.AddFormatted("argc = %d", argc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
out.AddFormatted("minor: %d", minor_key);
|
||||
}
|
||||
out.AddFormatted("minor: %d", minor_key);
|
||||
} else {
|
||||
out.AddFormatted(" %s", Code::Kind2String(kind));
|
||||
}
|
||||
|
@ -433,6 +433,7 @@ void FullCodeGenerator::SetStatementPosition(
|
||||
bool recorded = RecordStatementPosition(masm_, stmt->position());
|
||||
if (recorded && insert_break == INSERT_BREAK && info_->is_debug() &&
|
||||
!stmt->IsDebuggerStatement()) {
|
||||
masm_->RecordDebugBreakSlot();
|
||||
DebugCodegen::GenerateSlot(masm_);
|
||||
}
|
||||
}
|
||||
@ -443,6 +444,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()) {
|
||||
masm_->RecordDebugBreakSlot();
|
||||
DebugCodegen::GenerateSlot(masm_);
|
||||
}
|
||||
}
|
||||
@ -451,7 +453,32 @@ void FullCodeGenerator::SetExpressionPosition(
|
||||
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_);
|
||||
if (recorded && info_->is_debug()) {
|
||||
masm_->RecordDebugBreakSlot();
|
||||
DebugCodegen::GenerateSlot(masm_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::SetCallPosition(Expression* expr, int argc) {
|
||||
if (expr->position() == RelocInfo::kNoPosition) return;
|
||||
RecordPosition(masm_, expr->position());
|
||||
if (info_->is_debug()) {
|
||||
// Always emit a debug break slot before a call.
|
||||
masm_->RecordDebugBreakSlotForCall(argc);
|
||||
DebugCodegen::GenerateSlot(masm_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::SetConstructCallPosition(Expression* expr) {
|
||||
if (expr->position() == RelocInfo::kNoPosition) return;
|
||||
RecordPosition(masm_, expr->position());
|
||||
if (info_->is_debug()) {
|
||||
// Always emit a debug break slot before a construct call.
|
||||
masm_->RecordDebugBreakSlotForConstructCall();
|
||||
DebugCodegen::GenerateSlot(masm_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -676,6 +676,10 @@ class FullCodeGenerator: public AstVisitor {
|
||||
// This is used in loop headers where we want to break for each iteration.
|
||||
void SetExpressionAsStatementPosition(Expression* expr);
|
||||
|
||||
void SetCallPosition(Expression* expr, int argc);
|
||||
|
||||
void SetConstructCallPosition(Expression* expr);
|
||||
|
||||
// Non-local control flow support.
|
||||
void EnterTryBlock(int handler_index, Label* handler);
|
||||
void ExitTryBlock(int handler_index);
|
||||
|
@ -1438,6 +1438,8 @@ class Assembler : public AssemblerBase {
|
||||
|
||||
// Mark address of a debug break slot.
|
||||
void RecordDebugBreakSlot();
|
||||
void RecordDebugBreakSlotForCall(int argc);
|
||||
void RecordDebugBreakSlotForConstructCall();
|
||||
|
||||
// Record a comment relocation entry that can be used by a disassembler.
|
||||
// Use --code-comments to enable.
|
||||
|
@ -71,9 +71,7 @@ void BreakLocation::SetDebugBreakAtSlot() {
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList object_regs,
|
||||
RegList non_object_regs,
|
||||
bool convert_call_to_jmp) {
|
||||
RegList object_regs) {
|
||||
// Enter an internal frame.
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
@ -88,22 +86,12 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
// 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);
|
||||
DCHECK((non_object_regs & ~kJSCallerSaved) == 0);
|
||||
DCHECK((object_regs & non_object_regs) == 0);
|
||||
for (int i = 0; i < kNumJSCallerSaved; i++) {
|
||||
int r = JSCallerSavedCode(i);
|
||||
Register reg = { r };
|
||||
if ((object_regs & (1 << r)) != 0) {
|
||||
__ push(reg);
|
||||
}
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
if (FLAG_debug_code) {
|
||||
__ test(reg, Immediate(0xc0000000));
|
||||
__ Assert(zero, kUnableToEncodeValueAsSmi);
|
||||
}
|
||||
__ SmiTag(reg);
|
||||
__ push(reg);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -132,11 +120,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
__ pop(reg);
|
||||
taken = true;
|
||||
}
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
__ pop(reg);
|
||||
__ SmiUntag(reg);
|
||||
taken = true;
|
||||
}
|
||||
if (!taken) {
|
||||
unused_reg = reg;
|
||||
}
|
||||
@ -153,11 +136,9 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
// Get rid of the internal frame.
|
||||
}
|
||||
|
||||
// If this call did not replace a call but patched other code then there will
|
||||
// be an unwanted return address left on the stack. Here we get rid of that.
|
||||
if (convert_call_to_jmp) {
|
||||
__ add(esp, Immediate(kPointerSize));
|
||||
}
|
||||
// This call did not replace a call , so there will be an unwanted
|
||||
// return address left on the stack. Here we get rid of that.
|
||||
__ add(esp, Immediate(kPointerSize));
|
||||
|
||||
// Now that the break point has been handled, resume normal execution by
|
||||
// jumping to the target address intended by the caller and that was
|
||||
@ -168,62 +149,12 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallICStub
|
||||
// ----------- S t a t e -------------
|
||||
// -- edx : type feedback slot (smi)
|
||||
// -- edi : function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, edx.bit() | edi.bit(),
|
||||
0, false);
|
||||
}
|
||||
|
||||
|
||||
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(), 0, true);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallFunctionStub (from code-stubs-ia32.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- edi: function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, edi.bit(), 0, false);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallConstructStub (from code-stubs-ia32.cc).
|
||||
// eax is the actual number of arguments not encoded as a smi see comment
|
||||
// above IC call.
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax: number of arguments (not smi)
|
||||
// -- edi: constructor function
|
||||
// -----------------------------------
|
||||
// The number of arguments in eax is not smi encoded.
|
||||
Generate_DebugBreakCallHelper(masm, edi.bit(), eax.bit(), false);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
|
||||
MacroAssembler* masm) {
|
||||
// Register state for CallConstructStub (from code-stubs-ia32.cc).
|
||||
// eax is the actual number of arguments not encoded as a smi see comment
|
||||
// above IC call.
|
||||
// ----------- S t a t e -------------
|
||||
// -- eax: number of arguments (not smi)
|
||||
// -- ebx: feedback array
|
||||
// -- edx: feedback slot (smi)
|
||||
// -- edi: constructor function
|
||||
// -----------------------------------
|
||||
// The number of arguments in eax is not smi encoded.
|
||||
Generate_DebugBreakCallHelper(masm, ebx.bit() | edx.bit() | edi.bit(),
|
||||
eax.bit(), false);
|
||||
Generate_DebugBreakCallHelper(masm, eax.bit());
|
||||
}
|
||||
|
||||
|
||||
@ -231,7 +162,6 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
// Generate enough nop's to make space for a call instruction.
|
||||
Label check_codesize;
|
||||
__ bind(&check_codesize);
|
||||
__ RecordDebugBreakSlot();
|
||||
__ Nop(Assembler::kDebugBreakSlotLength);
|
||||
DCHECK_EQ(Assembler::kDebugBreakSlotLength,
|
||||
masm->SizeOfCodeGeneratedSince(&check_codesize));
|
||||
@ -239,7 +169,7 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
|
||||
|
||||
void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
|
||||
Generate_DebugBreakCallHelper(masm, 0, 0, true);
|
||||
Generate_DebugBreakCallHelper(masm, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2194,6 +2194,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
CallIC(ic, TypeFeedbackId::None());
|
||||
__ mov(edi, eax);
|
||||
__ mov(Operand(esp, 2 * kPointerSize), edi);
|
||||
SetCallPosition(expr, 1);
|
||||
CallFunctionStub stub(isolate(), 1, CALL_AS_METHOD);
|
||||
__ CallStub(&stub);
|
||||
|
||||
@ -2995,7 +2996,7 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
Handle<Code> ic = CodeFactory::CallIC(isolate(), arg_count, call_type).code();
|
||||
__ Move(edx, Immediate(SmiFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
|
||||
@ -3126,7 +3127,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
@ -3197,7 +3198,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into edi and eax.
|
||||
__ Move(eax, Immediate(arg_count));
|
||||
@ -3240,7 +3241,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into edi and eax.
|
||||
__ Move(eax, Immediate(arg_count));
|
||||
@ -4663,7 +4664,7 @@ void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
|
@ -1063,6 +1063,8 @@ class Assembler : public AssemblerBase {
|
||||
|
||||
// Mark address of a debug break slot.
|
||||
void RecordDebugBreakSlot();
|
||||
void RecordDebugBreakSlotForCall(int argc);
|
||||
void RecordDebugBreakSlotForConstructCall();
|
||||
|
||||
// Record the AST id of the CallIC being compiled, so that it can be placed
|
||||
// in the relocation information.
|
||||
|
@ -60,10 +60,8 @@ void BreakLocation::SetDebugBreakAtSlot() {
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList object_regs,
|
||||
RegList non_object_regs) {
|
||||
RegList object_regs) {
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
@ -81,21 +79,8 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
// 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);
|
||||
DCHECK((non_object_regs & ~kJSCallerSaved) == 0);
|
||||
DCHECK((object_regs & non_object_regs) == 0);
|
||||
if ((object_regs | non_object_regs) != 0) {
|
||||
for (int i = 0; i < kNumJSCallerSaved; i++) {
|
||||
int r = JSCallerSavedCode(i);
|
||||
Register reg = { r };
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
if (FLAG_debug_code) {
|
||||
__ And(at, reg, 0xc0000000);
|
||||
__ Assert(eq, kUnableToEncodeValueAsSmi, at, Operand(zero_reg));
|
||||
}
|
||||
__ sll(reg, reg, kSmiTagSize);
|
||||
}
|
||||
}
|
||||
__ MultiPush(object_regs | non_object_regs);
|
||||
if (object_regs != 0) {
|
||||
__ MultiPush(object_regs);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -108,18 +93,14 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
__ CallStub(&ceb);
|
||||
|
||||
// Restore the register values from the expression stack.
|
||||
if ((object_regs | non_object_regs) != 0) {
|
||||
__ MultiPop(object_regs | non_object_regs);
|
||||
for (int i = 0; i < kNumJSCallerSaved; i++) {
|
||||
int r = JSCallerSavedCode(i);
|
||||
Register reg = { r };
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
__ srl(reg, reg, kSmiTagSize);
|
||||
}
|
||||
if (FLAG_debug_code &&
|
||||
(((object_regs |non_object_regs) & (1 << r)) == 0)) {
|
||||
__ li(reg, kDebugZapValue);
|
||||
}
|
||||
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)) {
|
||||
__ li(reg, kDebugZapValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,53 +121,11 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallICStub
|
||||
// ----------- S t a t e -------------
|
||||
// -- a1 : function
|
||||
// -- a3 : slot in feedback array (smi)
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, a1.bit() | a3.bit(), 0);
|
||||
}
|
||||
|
||||
|
||||
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(), 0);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallFunctionStub (from code-stubs-mips.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- a1 : function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, a1.bit(), 0);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
|
||||
// Calling convention for CallConstructStub (from code-stubs-mips.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : number of arguments (not smi)
|
||||
// -- a1 : constructor function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, a1.bit() , a0.bit());
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
|
||||
MacroAssembler* masm) {
|
||||
// Calling convention for CallConstructStub (from code-stubs-mips.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : number of arguments (not smi)
|
||||
// -- a1 : constructor function
|
||||
// -- a2 : feedback array
|
||||
// -- a3 : feedback slot (smi)
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit() | a3.bit(), a0.bit());
|
||||
Generate_DebugBreakCallHelper(masm, v0.bit());
|
||||
}
|
||||
|
||||
|
||||
@ -196,7 +135,6 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
|
||||
Label check_codesize;
|
||||
__ bind(&check_codesize);
|
||||
__ RecordDebugBreakSlot();
|
||||
for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
|
||||
__ nop(MacroAssembler::DEBUG_BREAK_NOP);
|
||||
}
|
||||
@ -208,7 +146,7 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
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, 0);
|
||||
Generate_DebugBreakCallHelper(masm, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2266,6 +2266,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
__ mov(a0, v0);
|
||||
__ mov(a1, a0);
|
||||
__ sw(a1, MemOperand(sp, 2 * kPointerSize));
|
||||
SetCallPosition(expr, 1);
|
||||
CallFunctionStub stub(isolate(), 1, CALL_AS_METHOD);
|
||||
__ CallStub(&stub);
|
||||
|
||||
@ -3089,7 +3090,7 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
|
||||
}
|
||||
|
||||
// Record source position of the IC call.
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
Handle<Code> ic = CodeFactory::CallIC(isolate(), arg_count, call_type).code();
|
||||
__ li(a3, Operand(SmiFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
@ -3205,25 +3206,25 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
// function using the given arguments.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
PushCalleeAndWithBaseObject(expr);
|
||||
PushCalleeAndWithBaseObject(expr);
|
||||
|
||||
// Push the arguments.
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
// Push the arguments.
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
// Push a copy of the function (found below the arguments) and
|
||||
// resolve eval.
|
||||
__ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ push(a1);
|
||||
EmitResolvePossiblyDirectEval(arg_count);
|
||||
// Push a copy of the function (found below the arguments) and
|
||||
// resolve eval.
|
||||
__ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ push(a1);
|
||||
EmitResolvePossiblyDirectEval(arg_count);
|
||||
|
||||
// Touch up the stack with the resolved function.
|
||||
__ sw(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
// Touch up the stack with the resolved function.
|
||||
__ sw(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
// Record source position for debugger.
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
@ -3294,7 +3295,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into a1 and a0.
|
||||
__ li(a0, Operand(arg_count));
|
||||
@ -3337,7 +3338,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into a1 and a0.
|
||||
__ li(a0, Operand(arg_count));
|
||||
@ -4751,7 +4752,7 @@ void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
|
@ -1105,6 +1105,8 @@ class Assembler : public AssemblerBase {
|
||||
|
||||
// Mark address of a debug break slot.
|
||||
void RecordDebugBreakSlot();
|
||||
void RecordDebugBreakSlotForCall(int argc);
|
||||
void RecordDebugBreakSlotForConstructCall();
|
||||
|
||||
// Record the AST id of the CallIC being compiled, so that it can be placed
|
||||
// in the relocation information.
|
||||
|
@ -63,10 +63,8 @@ void BreakLocation::SetDebugBreakAtSlot() {
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList object_regs,
|
||||
RegList non_object_regs) {
|
||||
RegList object_regs) {
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
@ -88,17 +86,12 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
// 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);
|
||||
DCHECK((non_object_regs & ~kJSCallerSaved) == 0);
|
||||
DCHECK((object_regs & non_object_regs) == 0);
|
||||
for (int i = 0; i < kNumJSCallerSaved; i++) {
|
||||
int r = JSCallerSavedCode(i);
|
||||
Register reg = { r };
|
||||
if ((object_regs & (1 << r)) != 0) {
|
||||
__ push(reg);
|
||||
}
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
__ PushRegisterAsTwoSmis(reg);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -114,14 +107,10 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
|
||||
int r = JSCallerSavedCode(i);
|
||||
Register reg = { r };
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
__ PopRegisterAsTwoSmis(reg, at);
|
||||
}
|
||||
if ((object_regs & (1 << r)) != 0) {
|
||||
__ pop(reg);
|
||||
}
|
||||
if (FLAG_debug_code &&
|
||||
(((object_regs |non_object_regs) & (1 << r)) == 0)) {
|
||||
if (FLAG_debug_code && ((object_regs & (1 << r)) == 0)) {
|
||||
__ li(reg, kDebugZapValue);
|
||||
}
|
||||
}
|
||||
@ -143,54 +132,11 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallICStub
|
||||
// ----------- S t a t e -------------
|
||||
// -- a1 : function
|
||||
// -- a3 : slot in feedback array (smi)
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, a1.bit() | a3.bit(), 0);
|
||||
}
|
||||
|
||||
|
||||
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(), 0);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallFunctionStub (from code-stubs-mips.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- a1 : function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, a1.bit(), 0);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
|
||||
// Calling convention for CallConstructStub (from code-stubs-mips.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : number of arguments (not smi)
|
||||
// -- a1 : constructor function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, a1.bit() , a0.bit());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
|
||||
MacroAssembler* masm) {
|
||||
// Calling convention for CallConstructStub (from code-stubs-mips.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- a0 : number of arguments (not smi)
|
||||
// -- a1 : constructor function
|
||||
// -- a2 : feedback array
|
||||
// -- a3 : feedback slot (smi)
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit() | a3.bit(), a0.bit());
|
||||
Generate_DebugBreakCallHelper(masm, v0.bit());
|
||||
}
|
||||
|
||||
|
||||
@ -200,7 +146,6 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
|
||||
Label check_codesize;
|
||||
__ bind(&check_codesize);
|
||||
__ RecordDebugBreakSlot();
|
||||
for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
|
||||
__ nop(MacroAssembler::DEBUG_BREAK_NOP);
|
||||
}
|
||||
@ -212,7 +157,7 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
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, 0);
|
||||
Generate_DebugBreakCallHelper(masm, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2261,6 +2261,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
__ mov(a0, v0);
|
||||
__ mov(a1, a0);
|
||||
__ sd(a1, MemOperand(sp, 2 * kPointerSize));
|
||||
SetCallPosition(expr, 1);
|
||||
CallFunctionStub stub(isolate(), 1, CALL_AS_METHOD);
|
||||
__ CallStub(&stub);
|
||||
|
||||
@ -3091,7 +3092,7 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
|
||||
}
|
||||
|
||||
// Record source position of the IC call.
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
Handle<Code> ic = CodeFactory::CallIC(isolate(), arg_count, call_type).code();
|
||||
__ li(a3, Operand(SmiFromSlot(expr->CallFeedbackICSlot())));
|
||||
__ ld(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
@ -3206,25 +3207,25 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
// function using the given arguments.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
PushCalleeAndWithBaseObject(expr);
|
||||
PushCalleeAndWithBaseObject(expr);
|
||||
|
||||
// Push the arguments.
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
// Push the arguments.
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
// Push a copy of the function (found below the arguments) and
|
||||
// resolve eval.
|
||||
__ ld(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ push(a1);
|
||||
EmitResolvePossiblyDirectEval(arg_count);
|
||||
// Push a copy of the function (found below the arguments) and
|
||||
// resolve eval.
|
||||
__ ld(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ push(a1);
|
||||
EmitResolvePossiblyDirectEval(arg_count);
|
||||
|
||||
// Touch up the stack with the resolved function.
|
||||
__ sd(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
// Touch up the stack with the resolved function.
|
||||
__ sd(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
// Record source position for debugger.
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ ld(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
@ -3295,7 +3296,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into a1 and a0.
|
||||
__ li(a0, Operand(arg_count));
|
||||
@ -3338,7 +3339,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into a1 and a0.
|
||||
__ li(a0, Operand(arg_count));
|
||||
@ -4754,7 +4755,7 @@ void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ ld(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
|
@ -1619,6 +1619,8 @@ class Assembler : public AssemblerBase {
|
||||
|
||||
// Mark address of a debug break slot.
|
||||
void RecordDebugBreakSlot();
|
||||
void RecordDebugBreakSlotForCall(int argc);
|
||||
void RecordDebugBreakSlotForConstructCall();
|
||||
|
||||
// Record a comment relocation entry that can be used by a disassembler.
|
||||
// Use --code-comments to enable.
|
||||
|
@ -69,9 +69,7 @@ void BreakLocation::SetDebugBreakAtSlot() {
|
||||
|
||||
|
||||
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
RegList object_regs,
|
||||
RegList non_object_regs,
|
||||
bool convert_call_to_jmp) {
|
||||
RegList object_regs) {
|
||||
// Enter an internal frame.
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
@ -86,8 +84,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
// 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);
|
||||
DCHECK((non_object_regs & ~kJSCallerSaved) == 0);
|
||||
DCHECK((object_regs & non_object_regs) == 0);
|
||||
for (int i = 0; i < kNumJSCallerSaved; i++) {
|
||||
int r = JSCallerSavedCode(i);
|
||||
Register reg = { r };
|
||||
@ -95,9 +91,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
if ((object_regs & (1 << r)) != 0) {
|
||||
__ Push(reg);
|
||||
}
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
__ PushRegisterAsTwoSmis(reg);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -119,10 +112,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
if ((object_regs & (1 << r)) != 0) {
|
||||
__ Pop(reg);
|
||||
}
|
||||
// Reconstruct the 64-bit value from two smis.
|
||||
if ((non_object_regs & (1 << r)) != 0) {
|
||||
__ PopRegisterAsTwoSmis(reg);
|
||||
}
|
||||
}
|
||||
|
||||
// Read current padding counter and skip corresponding number of words.
|
||||
@ -133,11 +122,9 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
// Get rid of the internal frame.
|
||||
}
|
||||
|
||||
// If this call did not replace a call but patched other code then there will
|
||||
// be an unwanted return address left on the stack. Here we get rid of that.
|
||||
if (convert_call_to_jmp) {
|
||||
__ addp(rsp, Immediate(kPCOnStackSize));
|
||||
}
|
||||
// This call did not replace a call , so there will be an unwanted
|
||||
// return address left on the stack. Here we get rid of that.
|
||||
__ addp(rsp, Immediate(kPCOnStackSize));
|
||||
|
||||
// Now that the break point has been handled, resume normal execution by
|
||||
// jumping to the target address intended by the caller and that was
|
||||
@ -149,59 +136,12 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallICStub
|
||||
// ----------- S t a t e -------------
|
||||
// -- rdx : type feedback slot (smi)
|
||||
// -- rdi : function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, rdx.bit() | rdi.bit(), 0, false);
|
||||
}
|
||||
|
||||
|
||||
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(), 0, true);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallFunctionStub (from code-stubs-x64.cc).
|
||||
// ----------- S t a t e -------------
|
||||
// -- rdi : function
|
||||
// -----------------------------------
|
||||
Generate_DebugBreakCallHelper(masm, rdi.bit(), 0, false);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
|
||||
// Register state for CallConstructStub (from code-stubs-x64.cc).
|
||||
// rax is the actual number of arguments not encoded as a smi, see comment
|
||||
// above IC call.
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax: number of arguments
|
||||
// -----------------------------------
|
||||
// The number of arguments in rax is not smi encoded.
|
||||
Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false);
|
||||
}
|
||||
|
||||
|
||||
void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
|
||||
MacroAssembler* masm) {
|
||||
// Register state for CallConstructStub (from code-stubs-x64.cc).
|
||||
// rax is the actual number of arguments not encoded as a smi, see comment
|
||||
// above IC call.
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax: number of arguments
|
||||
// -- rbx: feedback array
|
||||
// -- rdx: feedback slot (smi)
|
||||
// -----------------------------------
|
||||
// The number of arguments in rax is not smi encoded.
|
||||
Generate_DebugBreakCallHelper(masm, rbx.bit() | rdx.bit() | rdi.bit(),
|
||||
rax.bit(), false);
|
||||
Generate_DebugBreakCallHelper(masm, rax.bit());
|
||||
}
|
||||
|
||||
|
||||
@ -209,7 +149,6 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
// Generate enough nop's to make space for a call instruction.
|
||||
Label check_codesize;
|
||||
__ bind(&check_codesize);
|
||||
__ RecordDebugBreakSlot();
|
||||
__ Nop(Assembler::kDebugBreakSlotLength);
|
||||
DCHECK_EQ(Assembler::kDebugBreakSlotLength,
|
||||
masm->SizeOfCodeGeneratedSince(&check_codesize));
|
||||
@ -219,7 +158,7 @@ void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
|
||||
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, 0, true);
|
||||
Generate_DebugBreakCallHelper(masm, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2228,6 +2228,8 @@ void FullCodeGenerator::VisitYield(Yield* expr) {
|
||||
CallIC(ic, TypeFeedbackId::None());
|
||||
__ movp(rdi, rax);
|
||||
__ movp(Operand(rsp, 2 * kPointerSize), rdi);
|
||||
|
||||
SetCallPosition(expr, 1);
|
||||
CallFunctionStub stub(isolate(), 1, CALL_AS_METHOD);
|
||||
__ CallStub(&stub);
|
||||
|
||||
@ -2996,7 +2998,7 @@ void FullCodeGenerator::EmitCall(Call* expr, CallICState::CallType call_type) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
Handle<Code> ic = CodeFactory::CallIC(isolate(), arg_count, call_type).code();
|
||||
__ Move(rdx, SmiFromSlot(expr->CallFeedbackICSlot()));
|
||||
__ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
|
||||
@ -3108,24 +3110,24 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
// function using the given arguments.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
PushCalleeAndWithBaseObject(expr);
|
||||
PushCalleeAndWithBaseObject(expr);
|
||||
|
||||
// Push the arguments.
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
// Push the arguments.
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForStackValue(args->at(i));
|
||||
}
|
||||
|
||||
// Push a copy of the function (found below the arguments) and resolve
|
||||
// eval.
|
||||
__ Push(Operand(rsp, (arg_count + 1) * kPointerSize));
|
||||
EmitResolvePossiblyDirectEval(arg_count);
|
||||
// Push a copy of the function (found below the arguments) and resolve
|
||||
// eval.
|
||||
__ Push(Operand(rsp, (arg_count + 1) * kPointerSize));
|
||||
EmitResolvePossiblyDirectEval(arg_count);
|
||||
|
||||
// Touch up the callee.
|
||||
__ movp(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
|
||||
// Touch up the callee.
|
||||
__ movp(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
|
||||
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
// Record source position for debugger.
|
||||
SetExpressionPosition(expr);
|
||||
PrepareForBailoutForId(expr->EvalId(), NO_REGISTERS);
|
||||
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
@ -3196,7 +3198,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into rdi and rax.
|
||||
__ Set(rax, arg_count);
|
||||
@ -3239,7 +3241,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
|
||||
|
||||
// Call the construct call builtin that handles allocation and
|
||||
// constructor invocation.
|
||||
SetExpressionPosition(expr);
|
||||
SetConstructCallPosition(expr);
|
||||
|
||||
// Load function and argument count into edi and eax.
|
||||
__ Set(rax, arg_count);
|
||||
@ -4688,7 +4690,7 @@ void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
|
||||
SetExpressionPosition(expr);
|
||||
SetCallPosition(expr, arg_count);
|
||||
CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
|
||||
__ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
|
||||
__ CallStub(&stub);
|
||||
|
@ -439,61 +439,6 @@ static void CheckDebuggerUnloaded(bool check_functions = false) {
|
||||
}
|
||||
|
||||
|
||||
// Compile a function, set a break point and check that the call at the break
|
||||
// location in the code is the expected debug_break function.
|
||||
void CheckDebugBreakFunction(DebugLocalContext* env,
|
||||
const char* source, const char* name,
|
||||
int position, v8::internal::RelocInfo::Mode mode,
|
||||
Code* debug_break) {
|
||||
EnableDebugger();
|
||||
i::Debug* debug = CcTest::i_isolate()->debug();
|
||||
|
||||
// Create function and set the break point.
|
||||
Handle<i::JSFunction> fun =
|
||||
v8::Utils::OpenHandle(*CompileFunction(env, source, name));
|
||||
int bp = SetBreakPoint(fun, position);
|
||||
|
||||
// Check that the debug break function is as expected.
|
||||
Handle<i::SharedFunctionInfo> shared(fun->shared());
|
||||
CHECK(Debug::HasDebugInfo(shared));
|
||||
i::BreakLocation location = i::BreakLocation::FromPosition(
|
||||
Debug::GetDebugInfo(shared), i::SOURCE_BREAK_LOCATIONS, position,
|
||||
i::STATEMENT_ALIGNED);
|
||||
i::RelocInfo::Mode actual_mode = location.rmode();
|
||||
if (actual_mode == i::RelocInfo::CODE_TARGET_WITH_ID) {
|
||||
actual_mode = i::RelocInfo::CODE_TARGET;
|
||||
}
|
||||
CHECK_EQ(mode, actual_mode);
|
||||
if (mode != i::RelocInfo::JS_RETURN) {
|
||||
CHECK_EQ(debug_break, *location.CodeTarget());
|
||||
} else {
|
||||
i::RelocInfo rinfo = location.rinfo();
|
||||
CHECK(i::RelocInfo::IsJSReturn(rinfo.rmode()));
|
||||
CHECK(rinfo.IsPatchedReturnSequence());
|
||||
}
|
||||
|
||||
// Clear the break point and check that the debug break function is no longer
|
||||
// there
|
||||
ClearBreakPoint(bp);
|
||||
CHECK(!debug->HasDebugInfo(shared));
|
||||
CHECK(debug->EnsureDebugInfo(shared, fun));
|
||||
location = i::BreakLocation::FromPosition(Debug::GetDebugInfo(shared),
|
||||
i::SOURCE_BREAK_LOCATIONS, position,
|
||||
i::STATEMENT_ALIGNED);
|
||||
actual_mode = location.rmode();
|
||||
if (actual_mode == i::RelocInfo::CODE_TARGET_WITH_ID) {
|
||||
actual_mode = i::RelocInfo::CODE_TARGET;
|
||||
}
|
||||
CHECK_EQ(mode, actual_mode);
|
||||
if (mode == i::RelocInfo::JS_RETURN) {
|
||||
i::RelocInfo rinfo = location.rinfo();
|
||||
CHECK(!rinfo.IsPatchedReturnSequence());
|
||||
}
|
||||
|
||||
DisableDebugger();
|
||||
}
|
||||
|
||||
|
||||
// --- D e b u g E v e n t H a n d l e r s
|
||||
// ---
|
||||
// --- The different tests uses a number of debug event handlers.
|
||||
|
42
test/mjsunit/debug-stepin-construct-call.js
Normal file
42
test/mjsunit/debug-stepin-construct-call.js
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --expose-debug-as debug
|
||||
|
||||
var break_count = 0;
|
||||
var exception = null;
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event != Debug.DebugEvent.Break) return;
|
||||
try {
|
||||
var source_line = exec_state.frame(0).sourceLineText();
|
||||
print(source_line);
|
||||
exec_state.prepareStep(Debug.StepAction.StepIn, 1);
|
||||
break_count++;
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
var Debug = debug.Debug;
|
||||
Debug.setListener(listener);
|
||||
|
||||
|
||||
function f() {
|
||||
this.x = 1;
|
||||
}
|
||||
|
||||
function g() {
|
||||
new f();
|
||||
}
|
||||
|
||||
Debug.setBreakPoint(g, 6, Debug.BreakPositionAlignment.BreakPosition);
|
||||
print(Debug.showBreakPoints(g, undefined,
|
||||
Debug.BreakPositionAlignment.BreakPosition));
|
||||
|
||||
g();
|
||||
Debug.setListener(null);
|
||||
|
||||
assertEquals(6, break_count);
|
||||
assertNull(exception);
|
Loading…
Reference in New Issue
Block a user