Commit be5763fc authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Cleanup the way the debugger stores live registers when entering at a break

The live registers are now only stored to the expression stack with the non pointer values being stored as smis (on the 32-bit platforms these values are assumed to be 31-bit max).

This makes the CEntryStub entry/exit code much simpler, and there is no longer any need for a mode (debug or normal) on it.

Fix a missing live register when breaking at ARM keyed load.
Review URL: http://codereview.chromium.org/3141047

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5358 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ee1dc277
......@@ -2702,7 +2702,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
// r0:r1: result
// sp: stack pointer
// fp: frame pointer
__ LeaveExitFrame(mode_);
__ LeaveExitFrame();
// check if we should retry or throw exception
Label retry;
......@@ -2751,7 +2751,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// builtin once.
// Enter the exit frame that transitions from JavaScript to C++.
__ EnterExitFrame(mode_);
__ EnterExitFrame();
// r4: number of arguments (C callee-saved)
// r5: pointer to builtin function (C callee-saved)
......
......@@ -130,21 +130,30 @@ void BreakLocationIterator::ClearDebugBreakAtSlot() {
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
RegList pointer_regs) {
// Save the content of all general purpose registers in memory. This copy in
// memory is later pushed onto the JS expression stack for the fake JS frame
// generated and also to the C frame generated on top of that. In the JS
// frame ONLY the registers containing pointers will be pushed on the
// expression stack. This causes the GC to update these pointers so that
// they will have the correct value when returning from the debugger.
__ SaveRegistersToMemory(kJSCallerSaved);
RegList object_regs,
RegList non_object_regs) {
__ EnterInternalFrame();
// Store the registers containing object pointers on the expression stack to
// make sure that these are correctly updated during GC.
// Use sp as base to push.
__ CopyRegistersFromMemoryToStack(sp, pointer_regs);
// 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.
ASSERT((object_regs & ~kJSCallerSaved) == 0);
ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
ASSERT((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, "Unable to encode value as smi");
}
__ mov(reg, Operand(reg, LSL, kSmiTagSize));
}
}
__ stm(db_w, sp, object_regs | non_object_regs);
}
#ifdef DEBUG
__ RecordComment("// Calling from debug break to runtime - come in - over");
......@@ -152,19 +161,27 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
__ mov(r0, Operand(0)); // no arguments
__ mov(r1, Operand(ExternalReference::debug_break()));
CEntryStub ceb(1, ExitFrame::MODE_DEBUG);
CEntryStub ceb(1);
__ CallStub(&ceb);
// Restore the register values containing object pointers from the expression
// stack in the reverse order as they where pushed.
// Use sp as base to pop.
__ CopyRegistersFromStackToMemory(sp, r3, pointer_regs);
// 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) {
__ mov(reg, Operand(reg, LSR, kSmiTagSize));
}
if (FLAG_debug_code &&
(((object_regs |non_object_regs) & (1 << r)) == 0)) {
__ mov(reg, Operand(kDebugZapValue));
}
}
}
__ LeaveInternalFrame();
// Finally restore all registers.
__ RestoreRegistersFromMemory(kJSCallerSaved);
// Now that the break point has been handled, resume normal execution by
// jumping to the target address intended by the caller and that was
// overwritten by the address of DebugBreakXXX.
......@@ -184,7 +201,7 @@ void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
// -----------------------------------
// Registers r0 and r2 contain objects that need to be pushed on the
// expression stack of the fake JS frame.
Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit());
Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit(), 0);
}
......@@ -198,7 +215,7 @@ void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
// -----------------------------------
// Registers r0, r1, and r2 contain objects that need to be pushed on the
// expression stack of the fake JS frame.
Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit());
Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit(), 0);
}
......@@ -206,9 +223,8 @@ void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
// ---------- S t a t e --------------
// -- lr : return address
// -- r0 : key
// -- sp[0] : key
// -- sp[4] : receiver
Generate_DebugBreakCallHelper(masm, r0.bit());
// -- r1 : receiver
Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit(), 0);
}
......@@ -218,24 +234,24 @@ void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
// -- r1 : key
// -- r2 : receiver
// -- lr : return address
Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit());
Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit(), 0);
}
void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
// Calling convention for IC call (from ic-arm.cc)
// ----------- S t a t e -------------
// -- r2: name
// -- r2 : name
// -----------------------------------
Generate_DebugBreakCallHelper(masm, r2.bit());
Generate_DebugBreakCallHelper(masm, r2.bit(), 0);
}
void Debug::GenerateConstructCallDebugBreak(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());
// Calling convention for construct call (from builtins-arm.cc)
// -- r0 : number of arguments (not smi)
// -- r1 : constructor function
Generate_DebugBreakCallHelper(masm, r1.bit(), r0.bit());
}
......@@ -243,7 +259,7 @@ void Debug::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());
Generate_DebugBreakCallHelper(masm, r0.bit(), 0);
}
......@@ -251,7 +267,7 @@ void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
// ----------- S t a t e -------------
// No registers used on entry.
// -----------------------------------
Generate_DebugBreakCallHelper(masm, 0);
Generate_DebugBreakCallHelper(masm, 0, 0);
}
......@@ -273,7 +289,7 @@ void Debug::GenerateSlot(MacroAssembler* masm) {
void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
// In the places where a debug break slot is inserted no registers can contain
// object pointers.
Generate_DebugBreakCallHelper(masm, 0);
Generate_DebugBreakCallHelper(masm, 0, 0);
}
......
......@@ -55,17 +55,13 @@ StackFrame::Type StackFrame::ComputeType(State* state) {
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
if (fp == 0) return NONE;
// Compute frame type and stack pointer.
Address sp = fp + ExitFrameConstants::kSPDisplacement;
const int offset = ExitFrameConstants::kCodeOffset;
Object* code = Memory::Object_at(fp + offset);
bool is_debug_exit = code->IsSmi();
if (is_debug_exit) {
sp -= kNumJSCallerSaved * kPointerSize;
}
Address sp = fp + ExitFrameConstants::kSPOffset;
// Fill in the state.
state->sp = sp;
state->fp = fp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
ASSERT(*state->pc_address != NULL);
return EXIT;
}
......
......@@ -96,11 +96,8 @@ class EntryFrameConstants : public AllStatic {
class ExitFrameConstants : public AllStatic {
public:
// Exit frames have a debug marker on the stack.
static const int kSPDisplacement = -1 * kPointerSize;
// The debug marker is just above the frame pointer.
static const int kCodeOffset = -1 * kPointerSize;
static const int kSPOffset = -1 * kPointerSize;
static const int kSavedRegistersOffset = 0 * kPointerSize;
......
......@@ -513,7 +513,7 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) {
}
void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) {
void MacroAssembler::EnterExitFrame() {
// Compute the argv pointer and keep it in a callee-saved register.
// r0 is argc.
add(r6, sp, Operand(r0, LSL, kPointerSizeLog2));
......@@ -556,16 +556,6 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) {
// Setup argc and the builtin function in callee-saved registers.
mov(r4, Operand(r0));
mov(r5, Operand(r1));
#ifdef ENABLE_DEBUGGER_SUPPORT
// Save the state of all registers to the stack from the memory
// location. This is needed to allow nested break points.
if (mode == ExitFrame::MODE_DEBUG) {
// Use sp as base to push.
CopyRegistersFromMemoryToStack(sp, kJSCallerSaved);
}
#endif
}
......@@ -600,19 +590,7 @@ int MacroAssembler::ActivationFrameAlignment() {
}
void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode) {
#ifdef ENABLE_DEBUGGER_SUPPORT
// Restore the memory copy of the registers by digging them out from
// the stack. This is needed to allow nested break points.
if (mode == ExitFrame::MODE_DEBUG) {
// This code intentionally clobbers r2 and r3.
const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
const int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize;
add(r3, fp, Operand(kOffset));
CopyRegistersFromStackToMemory(r3, r2, kJSCallerSaved);
}
#endif
void MacroAssembler::LeaveExitFrame() {
// Clear top frame.
mov(r3, Operand(0));
mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
......@@ -779,66 +757,8 @@ void MacroAssembler::InvokeFunction(JSFunction* function,
InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag);
}
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of registers to memory location.
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
Register reg = { r };
mov(ip, Operand(ExternalReference(Debug_Address::Register(i))));
str(reg, MemOperand(ip));
}
}
}
void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of memory location to registers.
for (int i = kNumJSCallerSaved; --i >= 0;) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
Register reg = { r };
mov(ip, Operand(ExternalReference(Debug_Address::Register(i))));
ldr(reg, MemOperand(ip));
}
}
}
void MacroAssembler::CopyRegistersFromMemoryToStack(Register base,
RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of the memory location to the stack and adjust base.
for (int i = kNumJSCallerSaved; --i >= 0;) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
mov(ip, Operand(ExternalReference(Debug_Address::Register(i))));
ldr(ip, MemOperand(ip));
str(ip, MemOperand(base, 4, NegPreIndex));
}
}
}
void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of the stack to the memory location and adjust base.
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
mov(ip, Operand(ExternalReference(Debug_Address::Register(i))));
ldr(scratch, MemOperand(base, 4, PostIndex));
str(scratch, MemOperand(ip));
}
}
}
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::DebugBreak() {
ASSERT(allow_stub_calls());
mov(r0, Operand(0));
......
......@@ -250,14 +250,14 @@ class MacroAssembler: public Assembler {
void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); }
void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); }
// Enter specific kind of exit frame; either normal or debug mode.
// Enter exit frame.
// Expects the number of arguments in register r0 and
// the builtin function to call in register r1. Exits with argc in
// r4, argv in r6, and and the builtin function to call in r5.
void EnterExitFrame(ExitFrame::Mode mode);
void EnterExitFrame();
// Leave the current exit frame. Expects the return value in r0.
void LeaveExitFrame(ExitFrame::Mode mode);
void LeaveExitFrame();
// Get the actual activation frame alignment for target environment.
static int ActivationFrameAlignment();
......@@ -294,12 +294,6 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// Debugger Support
void SaveRegistersToMemory(RegList regs);
void RestoreRegistersFromMemory(RegList regs);
void CopyRegistersFromMemoryToStack(Register base, RegList regs);
void CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs);
void DebugBreak();
#endif
......
......@@ -501,12 +501,11 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
int CEntryStub::MinorKey() {
ASSERT(result_size_ <= 2);
ASSERT(result_size_ == 1 || result_size_ == 2);
#ifdef _WIN64
return ExitFrameModeBits::encode(mode_)
| IndirectResultBits::encode(result_size_ > 1);
return result_size_ == 1 ? 0 : 1;
#else
return ExitFrameModeBits::encode(mode_);
return 0;
#endif
}
......
......@@ -560,9 +560,7 @@ class CompareStub: public CodeStub {
class CEntryStub : public CodeStub {
public:
explicit CEntryStub(int result_size,
ExitFrame::Mode mode = ExitFrame::MODE_NORMAL)
: result_size_(result_size), mode_(mode) { }
explicit CEntryStub(int result_size) : result_size_(result_size) { }
void Generate(MacroAssembler* masm);
......@@ -580,10 +578,8 @@ class CEntryStub : public CodeStub {
// Number of pointers/values returned.
const int result_size_;
const ExitFrame::Mode mode_;
// Minor key encoding
class ExitFrameModeBits: public BitField<ExitFrame::Mode, 0, 1> {};
class IndirectResultBits: public BitField<bool, 1, 1> {};
Major MajorKey() { return CEntry; }
......
......@@ -332,8 +332,7 @@ class Debug {
k_after_break_target_address,
k_debug_break_return_address,
k_debug_break_slot_address,
k_restarter_frame_function_pointer,
k_register_address
k_restarter_frame_function_pointer
};
// Support for setting the address to jump to when returning from break point.
......@@ -953,10 +952,7 @@ class DisableBreak BASE_EMBEDDED {
// code.
class Debug_Address {
public:
Debug_Address(Debug::AddressId id, int reg = 0)
: id_(id), reg_(reg) {
ASSERT(reg == 0 || id == Debug::k_register_address);
}
Debug_Address(Debug::AddressId id) : id_(id) { }
static Debug_Address AfterBreakTarget() {
return Debug_Address(Debug::k_after_break_target_address);
......@@ -970,10 +966,6 @@ class Debug_Address {
return Debug_Address(Debug::k_restarter_frame_function_pointer);
}
static Debug_Address Register(int reg) {
return Debug_Address(Debug::k_register_address, reg);
}
Address address() const {
switch (id_) {
case Debug::k_after_break_target_address:
......@@ -985,8 +977,6 @@ class Debug_Address {
case Debug::k_restarter_frame_function_pointer:
return reinterpret_cast<Address>(
Debug::restarter_frame_function_pointer_address());
case Debug::k_register_address:
return reinterpret_cast<Address>(Debug::register_address(reg_));
default:
UNREACHABLE();
return NULL;
......@@ -994,7 +984,6 @@ class Debug_Address {
}
private:
Debug::AddressId id_;
int reg_;
};
// The optional thread that Debug Agent may use to temporary call V8 to process
......
......@@ -280,7 +280,6 @@ class EntryConstructFrame: public EntryFrame {
// Exit frames are used to exit JavaScript execution and go to C.
class ExitFrame: public StackFrame {
public:
enum Mode { MODE_NORMAL, MODE_DEBUG };
virtual Type type() const { return EXIT; }
virtual Code* unchecked_code() const;
......
......@@ -3054,7 +3054,7 @@ void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
Label empty_handle;
Label prologue;
Label promote_scheduled_exception;
__ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, kArgc);
__ EnterApiExitFrame(kStackSpace, kArgc);
STATIC_ASSERT(kArgc == 4);
if (kPassHandlesDirectly) {
// When handles as passed directly we don't have to allocate extra
......@@ -3100,7 +3100,7 @@ void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
// It was non-zero. Dereference to get the result value.
__ mov(eax, Operand(eax, 0));
__ bind(&prologue);
__ LeaveExitFrame(ExitFrame::MODE_NORMAL);
__ LeaveExitFrame();
__ ret(0);
__ bind(&promote_scheduled_exception);
__ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
......@@ -3177,7 +3177,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ j(zero, &failure_returned, not_taken);
// Exit the JavaScript to C++ exit frame.
__ LeaveExitFrame(mode_);
__ LeaveExitFrame();
__ ret(0);
// Handling of failure.
......@@ -3277,7 +3277,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// a garbage collection and retrying the builtin (twice).
// Enter the exit frame that transitions from JavaScript to C++.
__ EnterExitFrame(mode_);
__ EnterExitFrame();
// eax: result parameter for PerformGC, if any (setup below)
// ebx: pointer to builtin function (C callee-saved)
......
......@@ -94,22 +94,33 @@ void BreakLocationIterator::ClearDebugBreakAtSlot() {
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
RegList pointer_regs,
RegList object_regs,
RegList non_object_regs,
bool convert_call_to_jmp) {
// Save the content of all general purpose registers in memory. This copy in
// memory is later pushed onto the JS expression stack for the fake JS frame
// generated and also to the C frame generated on top of that. In the JS
// frame ONLY the registers containing pointers will be pushed on the
// expression stack. This causes the GC to update these pointers so that
// they will have the correct value when returning from the debugger.
__ SaveRegistersToMemory(kJSCallerSaved);
// Enter an internal frame.
__ EnterInternalFrame();
// Store the registers containing object pointers on the expression stack to
// make sure that these are correctly updated during GC.
__ PushRegistersFromMemory(pointer_regs);
// 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.
ASSERT((object_regs & ~kJSCallerSaved) == 0);
ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
ASSERT((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, "Unable to encode value as smi");
}
__ SmiTag(reg);
__ push(reg);
}
}
#ifdef DEBUG
__ RecordComment("// Calling from debug break to runtime - come in - over");
......@@ -117,12 +128,25 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
__ Set(eax, Immediate(0)); // no arguments
__ mov(ebx, Immediate(ExternalReference::debug_break()));
CEntryStub ceb(1, ExitFrame::MODE_DEBUG);
CEntryStub ceb(1);
__ CallStub(&ceb);
// Restore the register values containing object pointers from the expression
// stack in the reverse order as they where pushed.
__ PopRegistersToMemory(pointer_regs);
// stack.
for (int i = kNumJSCallerSaved; --i >= 0;) {
int r = JSCallerSavedCode(i);
Register reg = { r };
if (FLAG_debug_code) {
__ Set(reg, Immediate(kDebugZapValue));
}
if ((object_regs & (1 << r)) != 0) {
__ pop(reg);
}
if ((non_object_regs & (1 << r)) != 0) {
__ pop(reg);
__ SmiUntag(reg);
}
}
// Get rid of the internal frame.
__ LeaveInternalFrame();
......@@ -130,12 +154,9 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
// 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) {
__ pop(eax);
__ add(Operand(esp), Immediate(kPointerSize));
}
// Finally restore all registers.
__ RestoreRegistersFromMemory(kJSCallerSaved);
// Now that the break point has been handled, resume normal execution by
// jumping to the target address intended by the caller and that was
// overwritten by the address of DebugBreakXXX.
......@@ -151,7 +172,7 @@ void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
// -- eax : receiver
// -- ecx : name
// -----------------------------------
Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit(), false);
Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit(), 0, false);
}
......@@ -162,7 +183,8 @@ void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
// -- ecx : name
// -- edx : receiver
// -----------------------------------
Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit() | edx.bit(), false);
Generate_DebugBreakCallHelper(
masm, eax.bit() | ecx.bit() | edx.bit(), 0, false);
}
......@@ -172,7 +194,7 @@ void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
// -- edx : receiver
// -- eax : key
// -----------------------------------
Generate_DebugBreakCallHelper(masm, eax.bit() | edx.bit(), false);
Generate_DebugBreakCallHelper(masm, eax.bit() | edx.bit(), 0, false);
}
......@@ -183,7 +205,8 @@ void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
// -- ecx : key
// -- edx : receiver
// -----------------------------------
Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit() | edx.bit(), false);
Generate_DebugBreakCallHelper(
masm, eax.bit() | ecx.bit() | edx.bit(), 0, false);
}
......@@ -192,7 +215,7 @@ void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- ecx: name
// -----------------------------------
Generate_DebugBreakCallHelper(masm, ecx.bit(), false);
Generate_DebugBreakCallHelper(masm, ecx.bit(), 0, false);
}
......@@ -201,10 +224,11 @@ void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) {
// 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
// -- eax: number of arguments (not smi)
// -- edi: constructor function
// -----------------------------------
// The number of arguments in eax is not smi encoded.
Generate_DebugBreakCallHelper(masm, 0, false);
Generate_DebugBreakCallHelper(masm, edi.bit(), eax.bit(), false);
}
......@@ -213,7 +237,7 @@ void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax: return value
// -----------------------------------
Generate_DebugBreakCallHelper(masm, eax.bit(), true);
Generate_DebugBreakCallHelper(masm, eax.bit(), 0, true);
}
......@@ -222,7 +246,7 @@ void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
// ----------- S t a t e -------------
// No registers used on entry.
// -----------------------------------
Generate_DebugBreakCallHelper(masm, 0, false);
Generate_DebugBreakCallHelper(masm, 0, 0, false);
}
......@@ -242,7 +266,7 @@ void Debug::GenerateSlot(MacroAssembler* masm) {
void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
// In the places where a debug break slot is inserted no registers can contain
// object pointers.
Generate_DebugBreakCallHelper(masm, 0, true);
Generate_DebugBreakCallHelper(masm, 0, 0, true);
}
......
......@@ -58,6 +58,7 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
state->fp = fp;
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
ASSERT(*state->pc_address != NULL);
return EXIT;
}
......
......@@ -191,81 +191,6 @@ void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of registers to memory location.
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
Register reg = { r };
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
mov(Operand::StaticVariable(reg_addr), reg);
}
}
}
void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of memory location to registers.
for (int i = kNumJSCallerSaved; --i >= 0;) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
Register reg = { r };
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
mov(reg, Operand::StaticVariable(reg_addr));
}
}
}
void MacroAssembler::PushRegistersFromMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Push the content of the memory location to the stack.
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
push(Operand::StaticVariable(reg_addr));
}
}
}
void MacroAssembler::PopRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Pop the content from the stack to the memory location.
for (int i = kNumJSCallerSaved; --i >= 0;) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
pop(Operand::StaticVariable(reg_addr));
}
}
}
void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of the stack to the memory location and adjust base.
for (int i = kNumJSCallerSaved; --i >= 0;) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
mov(scratch, Operand(base, 0));
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
mov(Operand::StaticVariable(reg_addr), scratch);
lea(base, Operand(base, kPointerSize));
}
}
}
void MacroAssembler::DebugBreak() {
Set(eax, Immediate(0));
mov(ebx, Immediate(ExternalReference(Runtime::kDebugBreak)));
......@@ -274,6 +199,7 @@ void MacroAssembler::DebugBreak() {
}
#endif
void MacroAssembler::Set(Register dst, const Immediate& x) {
if (x.is_zero()) {
xor_(dst, Operand(dst)); // shorter than mov
......@@ -405,7 +331,8 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) {
leave();
}
void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode) {
void MacroAssembler::EnterExitFramePrologue() {
// Setup the frame structure on the stack.
ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
......@@ -413,7 +340,7 @@ void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode) {
push(ebp);
mov(ebp, Operand(esp));
// Reserve room for entry stack pointer and push the debug marker.
// Reserve room for entry stack pointer and push the code object.
ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
push(Immediate(0)); // Saved entry sp, patched before call.
push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot.
......@@ -425,21 +352,8 @@ void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode) {
mov(Operand::StaticVariable(context_address), esi);
}
void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc) {
#ifdef ENABLE_DEBUGGER_SUPPORT
// Save the state of all registers to the stack from the memory
// location. This is needed to allow nested break points.
if (mode == ExitFrame::MODE_DEBUG) {
// TODO(1243899): This should be symmetric to
// CopyRegistersFromStackToMemory() but it isn't! esp is assumed
// correct here, but computed for the other call. Very error
// prone! FIX THIS. Actually there are deeper problems with
// register saving than this asymmetry (see the bug report
// associated with this issue).
PushRegistersFromMemory(kJSCallerSaved);
}
#endif
void MacroAssembler::EnterExitFrameEpilogue(int argc) {
// Reserve space for arguments.
sub(Operand(esp), Immediate(argc * kPointerSize));
......@@ -455,44 +369,30 @@ void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc) {
}
void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) {
EnterExitFramePrologue(mode);
void MacroAssembler::EnterExitFrame() {
EnterExitFramePrologue();
// Setup argc and argv in callee-saved registers.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
mov(edi, Operand(eax));
lea(esi, Operand(ebp, eax, times_4, offset));
EnterExitFrameEpilogue(mode, 2);
EnterExitFrameEpilogue(2);
}
void MacroAssembler::EnterApiExitFrame(ExitFrame::Mode mode,
int stack_space,
void MacroAssembler::EnterApiExitFrame(int stack_space,
int argc) {
EnterExitFramePrologue(mode);
EnterExitFramePrologue();
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
lea(esi, Operand(ebp, (stack_space * kPointerSize) + offset));
EnterExitFrameEpilogue(mode, argc);
EnterExitFrameEpilogue(argc);
}
void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode) {
#ifdef ENABLE_DEBUGGER_SUPPORT
// Restore the memory copy of the registers by digging them out from
// the stack. This is needed to allow nested break points.
if (mode == ExitFrame::MODE_DEBUG) {
// It's okay to clobber register ebx below because we don't need
// the function pointer after this.
const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize;
lea(ebx, Operand(ebp, kOffset));
CopyRegistersFromStackToMemory(ebx, ecx, kJSCallerSaved);
}
#endif
void MacroAssembler::LeaveExitFrame() {
// Get the return address from the stack and restore the frame pointer.
mov(ecx, Operand(ebp, 1 * kPointerSize));
mov(ebp, Operand(ebp, 0 * kPointerSize));
......
......@@ -99,13 +99,6 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// Debugger Support
void SaveRegistersToMemory(RegList regs);
void RestoreRegistersFromMemory(RegList regs);
void PushRegistersFromMemory(RegList regs);
void PopRegistersToMemory(RegList regs);
void CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs);
void DebugBreak();
#endif
......@@ -128,14 +121,14 @@ class MacroAssembler: public Assembler {
// Expects the number of arguments in register eax and
// sets up the number of arguments in register edi and the pointer
// to the first argument in register esi.
void EnterExitFrame(ExitFrame::Mode mode);
void EnterExitFrame();
void EnterApiExitFrame(ExitFrame::Mode mode, int stack_space, int argc);
void EnterApiExitFrame(int stack_space, int argc);
// Leave the current exit frame. Expects the return value in
// register eax:edx (untouched) and the pointer to the first
// argument in register esi.
void LeaveExitFrame(ExitFrame::Mode mode);
void LeaveExitFrame();
// Find the function context up the context chain.
void LoadContext(Register dst, int context_chain_length);
......@@ -571,8 +564,8 @@ class MacroAssembler: public Assembler {
void EnterFrame(StackFrame::Type type);
void LeaveFrame(StackFrame::Type type);
void EnterExitFramePrologue(ExitFrame::Mode mode);
void EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc);
void EnterExitFramePrologue();
void EnterExitFrameEpilogue(int argc);
// Allocation support helpers.
void LoadAllocationTopHelper(Register result,
......
......@@ -241,16 +241,6 @@ void ExternalReferenceTable::PopulateTable() {
DEBUG_ADDRESS,
Debug::k_restarter_frame_function_pointer << kDebugIdShift,
"Debug::restarter_frame_function_pointer_address()");
const char* debug_register_format = "Debug::register_address(%i)";
int dr_format_length = StrLength(debug_register_format);
for (int i = 0; i < kNumJSCallerSaved; ++i) {
Vector<char> name = Vector<char>::New(dr_format_length + 1);
OS::SNPrintF(name, debug_register_format, i);
Add(Debug_Address(Debug::k_register_address, i).address(),
DEBUG_ADDRESS,
Debug::k_register_address << kDebugIdShift | i,
name.start());
}
#endif
// Stat counters
......
......@@ -2476,7 +2476,7 @@ void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
Label empty_result;
Label prologue;
Label promote_scheduled_exception;
__ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, 0);
__ EnterApiExitFrame(kStackSpace, 0);
ASSERT_EQ(kArgc, 4);
#ifdef _WIN64
// All the parameters should be set up by a caller.
......@@ -2507,7 +2507,7 @@ void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
// It was non-zero. Dereference to get the result value.
__ movq(rax, Operand(rax, 0));
__ bind(&prologue);
__ LeaveExitFrame(ExitFrame::MODE_NORMAL);
__ LeaveExitFrame();
__ ret(0);
__ bind(&promote_scheduled_exception);
__ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
......@@ -2617,7 +2617,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ j(zero, &failure_returned);
// Exit the JavaScript to C++ exit frame.
__ LeaveExitFrame(mode_, result_size_);
__ LeaveExitFrame(result_size_);
__ ret(0);
// Handling of failure.
......@@ -2721,7 +2721,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// builtin once.
// Enter the exit frame that transitions from JavaScript to C++.
__ EnterExitFrame(mode_, result_size_);
__ EnterExitFrame(result_size_);
// rax: Holds the context at this point, but should not be used.
// On entry to code generated by GenerateCore, it must hold
......
......@@ -47,22 +47,35 @@ bool Debug::IsDebugBreakAtReturn(v8::internal::RelocInfo* rinfo) {
#define __ ACCESS_MASM(masm)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
RegList pointer_regs,
RegList object_regs,
RegList non_object_regs,
bool convert_call_to_jmp) {
// Save the content of all general purpose registers in memory. This copy in
// memory is later pushed onto the JS expression stack for the fake JS frame
// generated and also to the C frame generated on top of that. In the JS
// frame ONLY the registers containing pointers will be pushed on the
// expression stack. This causes the GC to update these pointers so that
// they will have the correct value when returning from the debugger.
__ SaveRegistersToMemory(kJSCallerSaved);
// Enter an internal frame.
__ EnterInternalFrame();
// Store the registers containing object pointers on the expression stack to
// make sure that these are correctly updated during GC.
__ PushRegistersFromMemory(pointer_regs);
// 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 smi causing it to be untouched by GC.
ASSERT((object_regs & ~kJSCallerSaved) == 0);
ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
ASSERT((object_regs & non_object_regs) == 0);
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
Register reg = { r };
ASSERT(!reg.is(kScratchRegister));
if ((object_regs & (1 << r)) != 0) {
__ push(reg);
}
// Store the 64-bit value as two smis.
if ((non_object_regs & (1 << r)) != 0) {
__ movq(kScratchRegister, reg);
__ Integer32ToSmi(reg, reg);
__ push(reg);
__ sar(kScratchRegister, Immediate(32));
__ Integer32ToSmi(kScratchRegister, kScratchRegister);
__ push(kScratchRegister);
}
}
#ifdef DEBUG
__ RecordComment("// Calling from debug break to runtime - come in - over");
......@@ -70,12 +83,29 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
__ xor_(rax, rax); // No arguments (argc == 0).
__ movq(rbx, ExternalReference::debug_break());
CEntryStub ceb(1, ExitFrame::MODE_DEBUG);
CEntryStub ceb(1);
__ CallStub(&ceb);
// Restore the register values containing object pointers from the expression
// stack in the reverse order as they where pushed.
__ PopRegistersToMemory(pointer_regs);
// 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) {
__ Set(reg, kDebugZapValue);
}
if ((object_regs & (1 << r)) != 0) {
__ pop(reg);
}
// Reconstruct the 64-bit value from two smis.
if ((non_object_regs & (1 << r)) != 0) {
__ pop(kScratchRegister);
__ SmiToInteger32(kScratchRegister, kScratchRegister);
__ shl(kScratchRegister, Immediate(32));
__ pop(reg);
__ SmiToInteger32(reg, reg);
__ or_(reg, kScratchRegister);
}
}
// Get rid of the internal frame.
__ LeaveInternalFrame();
......@@ -83,12 +113,9 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
// 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) {
__ pop(rax);
__ addq(rsp, Immediate(kPointerSize));
}
// Finally restore all registers.
__ RestoreRegistersFromMemory(kJSCallerSaved);
// Now that the break point has been handled, resume normal execution by
// jumping to the target address intended by the caller and that was
// overwritten by the address of DebugBreakXXX.
......@@ -104,7 +131,7 @@ void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rcx: function name
// -----------------------------------
Generate_DebugBreakCallHelper(masm, rcx.bit(), false);
Generate_DebugBreakCallHelper(masm, rcx.bit(), 0, false);
}
......@@ -116,7 +143,7 @@ void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) {
// -- rax: number of arguments
// -----------------------------------
// The number of arguments in rax is not smi encoded.
Generate_DebugBreakCallHelper(masm, 0, false);
Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false);
}
......@@ -126,7 +153,7 @@ void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
// -- rax : key
// -- rdx : receiver
// -----------------------------------
Generate_DebugBreakCallHelper(masm, rax.bit() | rdx.bit(), false);
Generate_DebugBreakCallHelper(masm, rax.bit() | rdx.bit(), 0, false);
}
......@@ -137,7 +164,8 @@ void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
// -- rcx : key
// -- rdx : receiver
// -----------------------------------
Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit() | rdx.bit(), false);
Generate_DebugBreakCallHelper(
masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
}
......@@ -147,7 +175,7 @@ void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
// -- rax : receiver
// -- rcx : name
// -----------------------------------
Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit(), false);
Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit(), 0, false);
}
......@@ -156,7 +184,7 @@ void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax: return value
// -----------------------------------
Generate_DebugBreakCallHelper(masm, rax.bit(), true);
Generate_DebugBreakCallHelper(masm, rax.bit(), 0, true);
}
......@@ -167,7 +195,8 @@ void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
// -- rcx : name
// -- rdx : receiver
// -----------------------------------
Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit() | rdx.bit(), false);
Generate_DebugBreakCallHelper(
masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
}
......@@ -176,7 +205,7 @@ void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
// ----------- S t a t e -------------
// No registers used on entry.
// -----------------------------------
Generate_DebugBreakCallHelper(masm, 0, false);
Generate_DebugBreakCallHelper(masm, 0, 0, false);
}
......@@ -196,7 +225,7 @@ void Debug::GenerateSlot(MacroAssembler* masm) {
void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
// In the places where a debug break slot is inserted no registers can contain
// object pointers.
Generate_DebugBreakCallHelper(masm, 0, true);
Generate_DebugBreakCallHelper(masm, 0, 0, true);
}
......
......@@ -58,7 +58,7 @@ StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
state->fp = fp;
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
// Determine frame type.
ASSERT(*state->pc_address != NULL);
return EXIT;
}
......
......@@ -2102,91 +2102,8 @@ void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
}
}
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::PushRegistersFromMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Push the content of the memory location to the stack.
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
push(Operand(kScratchRegister, 0));
}
}
}
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of registers to memory location.
for (int i = 0; i < kNumJSCallerSaved; i++) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
Register reg = { r };
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
movq(Operand(kScratchRegister, 0), reg);
}
}
}
void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of memory location to registers.
for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
Register reg = { r };
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
movq(reg, Operand(kScratchRegister, 0));
}
}
}
void MacroAssembler::PopRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Pop the content from the stack to the memory location.
for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
pop(Operand(kScratchRegister, 0));
}
}
}
void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs) {
ASSERT(!scratch.is(kScratchRegister));
ASSERT(!base.is(kScratchRegister));
ASSERT(!base.is(scratch));
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of the stack to the memory location and adjust base.
for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
int r = JSCallerSavedCode(i);
if ((regs & (1 << r)) != 0) {
movq(scratch, Operand(base, 0));
ExternalReference reg_addr =
ExternalReference(Debug_Address::Register(i));
movq(kScratchRegister, reg_addr);
movq(Operand(kScratchRegister, 0), scratch);
lea(base, Operand(base, kPointerSize));
}
}
}
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::DebugBreak() {
ASSERT(allow_stub_calls());
xor_(rax, rax); // no arguments
......@@ -2356,8 +2273,7 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) {
}
void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode,
bool save_rax) {
void MacroAssembler::EnterExitFramePrologue(bool save_rax) {
// Setup the frame structure on the stack.
// All constants are relative to the frame pointer of the exit frame.
ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
......@@ -2366,7 +2282,7 @@ void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode,
push(rbp);
movq(rbp, rsp);
// Reserve room for entry stack pointer and push the debug marker.
// Reserve room for entry stack pointer and push the code object.
ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
push(Immediate(0)); // Saved entry sp, patched before call.
movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
......@@ -2385,23 +2301,8 @@ void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode,
store_rax(context_address);
}
void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode,
int result_size,
void MacroAssembler::EnterExitFrameEpilogue(int result_size,
int argc) {
#ifdef ENABLE_DEBUGGER_SUPPORT
// Save the state of all registers to the stack from the memory
// location. This is needed to allow nested break points.
if (mode == ExitFrame::MODE_DEBUG) {
// TODO(1243899): This should be symmetric to
// CopyRegistersFromStackToMemory() but it isn't! esp is assumed
// correct here, but computed for the other call. Very error
// prone! FIX THIS. Actually there are deeper problems with
// register saving than this asymmetry (see the bug report
// associated with this issue).
PushRegistersFromMemory(kJSCallerSaved);
}
#endif
#ifdef _WIN64
// Reserve space on stack for result and argument structures, if necessary.
int result_stack_space = (result_size < 2) ? 0 : result_size * kPointerSize;
......@@ -2430,48 +2331,35 @@ void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode,
}
void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) {
EnterExitFramePrologue(mode, true);
void MacroAssembler::EnterExitFrame(int result_size) {
EnterExitFramePrologue(true);
// Setup argv in callee-saved register r12. It is reused in LeaveExitFrame,
// so it must be retained across the C-call.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
lea(r12, Operand(rbp, r14, times_pointer_size, offset));
EnterExitFrameEpilogue(mode, result_size, 2);
EnterExitFrameEpilogue(result_size, 2);
}
void MacroAssembler::EnterApiExitFrame(ExitFrame::Mode mode,
int stack_space,
void MacroAssembler::EnterApiExitFrame(int stack_space,
int argc,
int result_size) {
EnterExitFramePrologue(mode, false);
EnterExitFramePrologue(false);
// Setup argv in callee-saved register r12. It is reused in LeaveExitFrame,
// so it must be retained across the C-call.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
lea(r12, Operand(rbp, (stack_space * kPointerSize) + offset));
EnterExitFrameEpilogue(mode, result_size, argc);
EnterExitFrameEpilogue(result_size, argc);
}
void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode, int result_size) {
void MacroAssembler::LeaveExitFrame(int result_size) {
// Registers:
// r12 : argv
#ifdef ENABLE_DEBUGGER_SUPPORT
// Restore the memory copy of the registers by digging them out from
// the stack. This is needed to allow nested break points.
if (mode == ExitFrame::MODE_DEBUG) {
// It's okay to clobber register rbx below because we don't need
// the function pointer after this.
const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize;
lea(rbx, Operand(rbp, kOffset));
CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved);
}
#endif
// Get the return address from the stack and restore the frame pointer.
movq(rcx, Operand(rbp, 1 * kPointerSize));
......
......@@ -132,13 +132,6 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// Debugger Support
void SaveRegistersToMemory(RegList regs);
void RestoreRegistersFromMemory(RegList regs);
void PushRegistersFromMemory(RegList regs);
void PopRegistersToMemory(RegList regs);
void CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs);
void DebugBreak();
#endif
......@@ -161,17 +154,16 @@ class MacroAssembler: public Assembler {
// debug mode. Expects the number of arguments in register rax and
// sets up the number of arguments in register rdi and the pointer
// to the first argument in register rsi.
void EnterExitFrame(ExitFrame::Mode mode, int result_size = 1);
void EnterExitFrame(int result_size = 1);
void EnterApiExitFrame(ExitFrame::Mode mode,
int stack_space,
void EnterApiExitFrame(int stack_space,
int argc,
int result_size = 1);
// Leave the current exit frame. Expects/provides the return value in
// register rax:rdx (untouched) and the pointer to the first
// argument in register rsi.
void LeaveExitFrame(ExitFrame::Mode mode, int result_size = 1);
void LeaveExitFrame(int result_size = 1);
// ---------------------------------------------------------------------------
......@@ -878,8 +870,8 @@ class MacroAssembler: public Assembler {
void EnterFrame(StackFrame::Type type);
void LeaveFrame(StackFrame::Type type);
void EnterExitFramePrologue(ExitFrame::Mode mode, bool save_rax);
void EnterExitFrameEpilogue(ExitFrame::Mode mode, int result_size, int argc);
void EnterExitFramePrologue(bool save_rax);
void EnterExitFrameEpilogue(int result_size, int argc);
// Allocation support helpers.
// Loads the top of new-space into the result register.
......
......@@ -56,10 +56,6 @@ test-api/OutOfMemoryNested: SKIP
# BUG(355): Test crashes on ARM.
test-log/ProfLazyMode: SKIP
# BUG(845)
test-debug/GCDuringBreakPointProcessing: SKIP
test-debug/BreakPointICCallWithGC: SKIP
[ $arch == mips ]
test-accessors: SKIP
test-alloc: SKIP
......
......@@ -869,7 +869,7 @@ static void DebugEventBreakPointCollectGarbage(
// Scavenge.
Heap::CollectGarbage(0, v8::internal::NEW_SPACE);
} else {
// Mark sweep (and perhaps compact).
// Mark sweep compact.
Heap::CollectAllGarbage(true);
}
}
......@@ -1177,6 +1177,40 @@ TEST(BreakPointICCallWithGC) {
}
// Test that a break point can be set at an IC call location and survive a GC.
TEST(BreakPointConstructCallWithGC) {
break_point_hit_count = 0;
v8::HandleScope scope;
DebugLocalContext env;
v8::Debug::SetDebugEventListener(DebugEventBreakPointCollectGarbage,
v8::Undefined());
v8::Script::Compile(v8::String::New("function bar(){ this.x = 1;}"))->Run();
v8::Script::Compile(v8::String::New(
"function foo(){return new bar(1).x;}"))->Run();
v8::Local<v8::Function> foo =
v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo")));
// Run without breakpoints.
CHECK_EQ(1, foo->Call(env->Global(), 0, NULL)->Int32Value());
CHECK_EQ(0, break_point_hit_count);
// Run with breakpoint.
int bp = SetBreakPoint(foo, 0);
CHECK_EQ(1, foo->Call(env->Global(), 0, NULL)->Int32Value());
CHECK_EQ(1, break_point_hit_count);
CHECK_EQ(1, foo->Call(env->Global(), 0, NULL)->Int32Value());
CHECK_EQ(2, break_point_hit_count);
// Run without breakpoints.
ClearBreakPoint(bp);
foo->Call(env->Global(), 0, NULL);
CHECK_EQ(2, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
}
// Test that a break point can be set at a return store location.
TEST(BreakPointReturn) {
break_point_hit_count = 0;
......
......@@ -98,13 +98,6 @@ static int make_code(TypeCode type, int id) {
}
#ifdef ENABLE_DEBUGGER_SUPPORT
static int register_code(int reg) {
return Debug::k_register_address << kDebugIdShift | reg;
}
#endif // ENABLE_DEBUGGER_SUPPORT
TEST(ExternalReferenceEncoder) {
StatsTable::SetCounterFunction(counter_function);
Heap::Setup(false);
......@@ -115,10 +108,6 @@ TEST(ExternalReferenceEncoder) {
Encode(encoder, Runtime::kAbort));
CHECK_EQ(make_code(IC_UTILITY, IC::kLoadCallbackProperty),
Encode(encoder, IC_Utility(IC::kLoadCallbackProperty)));
#ifdef ENABLE_DEBUGGER_SUPPORT
CHECK_EQ(make_code(DEBUG_ADDRESS, register_code(3)),
Encode(encoder, Debug_Address(Debug::k_register_address, 3)));
#endif // ENABLE_DEBUGGER_SUPPORT
ExternalReference keyed_load_function_prototype =
ExternalReference(&Counters::keyed_load_function_prototype);
CHECK_EQ(make_code(STATS_COUNTER, Counters::k_keyed_load_function_prototype),
......@@ -156,10 +145,6 @@ TEST(ExternalReferenceDecoder) {
decoder.Decode(make_code(RUNTIME_FUNCTION, Runtime::kAbort)));
CHECK_EQ(AddressOf(IC_Utility(IC::kLoadCallbackProperty)),
decoder.Decode(make_code(IC_UTILITY, IC::kLoadCallbackProperty)));
#ifdef ENABLE_DEBUGGER_SUPPORT
CHECK_EQ(AddressOf(Debug_Address(Debug::k_register_address, 3)),
decoder.Decode(make_code(DEBUG_ADDRESS, register_code(3))));
#endif // ENABLE_DEBUGGER_SUPPORT
ExternalReference keyed_load_function =
ExternalReference(&Counters::keyed_load_function_prototype);
CHECK_EQ(keyed_load_function.address(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment