1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#if V8_TARGET_ARCH_X64
31
32#include "assembler.h"
33#include "codegen.h"
34#include "debug.h"
35
36
37namespace v8 {
38namespace internal {
39
40#ifdef ENABLE_DEBUGGER_SUPPORT
41
42bool BreakLocationIterator::IsDebugBreakAtReturn()  {
43  return Debug::IsDebugBreakAtReturn(rinfo());
44}
45
46
47// Patch the JS frame exit code with a debug break call. See
48// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-x64.cc
49// for the precise return instructions sequence.
50void BreakLocationIterator::SetDebugBreakAtReturn()  {
51  ASSERT(Assembler::kJSReturnSequenceLength >= Assembler::kCallSequenceLength);
52  rinfo()->PatchCodeWithCall(
53      debug_info_->GetIsolate()->debug()->debug_break_return()->entry(),
54      Assembler::kJSReturnSequenceLength - Assembler::kCallSequenceLength);
55}
56
57
58// Restore the JS frame exit code.
59void BreakLocationIterator::ClearDebugBreakAtReturn() {
60  rinfo()->PatchCode(original_rinfo()->pc(),
61                     Assembler::kJSReturnSequenceLength);
62}
63
64
65// A debug break in the frame exit code is identified by the JS frame exit code
66// having been patched with a call instruction.
67bool Debug::IsDebugBreakAtReturn(v8::internal::RelocInfo* rinfo) {
68  ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
69  return rinfo->IsPatchedReturnSequence();
70}
71
72
73bool BreakLocationIterator::IsDebugBreakAtSlot() {
74  ASSERT(IsDebugBreakSlot());
75  // Check whether the debug break slot instructions have been patched.
76  return !Assembler::IsNop(rinfo()->pc());
77}
78
79
80void BreakLocationIterator::SetDebugBreakAtSlot() {
81  ASSERT(IsDebugBreakSlot());
82  rinfo()->PatchCodeWithCall(
83      debug_info_->GetIsolate()->debug()->debug_break_slot()->entry(),
84      Assembler::kDebugBreakSlotLength - Assembler::kCallSequenceLength);
85}
86
87
88void BreakLocationIterator::ClearDebugBreakAtSlot() {
89  ASSERT(IsDebugBreakSlot());
90  rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
91}
92
93const bool Debug::FramePaddingLayout::kIsSupported = true;
94
95
96#define __ ACCESS_MASM(masm)
97
98
99static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
100                                          RegList object_regs,
101                                          RegList non_object_regs,
102                                          bool convert_call_to_jmp) {
103  // Enter an internal frame.
104  {
105    FrameScope scope(masm, StackFrame::INTERNAL);
106
107    // Load padding words on stack.
108    for (int i = 0; i < Debug::FramePaddingLayout::kInitialSize; i++) {
109      __ Push(Smi::FromInt(Debug::FramePaddingLayout::kPaddingValue));
110    }
111    __ Push(Smi::FromInt(Debug::FramePaddingLayout::kInitialSize));
112
113    // Store the registers containing live values on the expression stack to
114    // make sure that these are correctly updated during GC. Non object values
115    // are stored as as two smis causing it to be untouched by GC.
116    ASSERT((object_regs & ~kJSCallerSaved) == 0);
117    ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
118    ASSERT((object_regs & non_object_regs) == 0);
119    for (int i = 0; i < kNumJSCallerSaved; i++) {
120      int r = JSCallerSavedCode(i);
121      Register reg = { r };
122      ASSERT(!reg.is(kScratchRegister));
123      if ((object_regs & (1 << r)) != 0) {
124        __ push(reg);
125      }
126      if ((non_object_regs & (1 << r)) != 0) {
127        __ PushInt64AsTwoSmis(reg);
128      }
129    }
130
131#ifdef DEBUG
132    __ RecordComment("// Calling from debug break to runtime - come in - over");
133#endif
134    __ Set(rax, 0);  // No arguments (argc == 0).
135    __ Move(rbx, ExternalReference::debug_break(masm->isolate()));
136
137    CEntryStub ceb(1);
138    __ CallStub(&ceb);
139
140    // Restore the register values from the expression stack.
141    for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
142      int r = JSCallerSavedCode(i);
143      Register reg = { r };
144      if (FLAG_debug_code) {
145        __ Set(reg, kDebugZapValue);
146      }
147      if ((object_regs & (1 << r)) != 0) {
148        __ pop(reg);
149      }
150      // Reconstruct the 64-bit value from two smis.
151      if ((non_object_regs & (1 << r)) != 0) {
152        __ PopInt64AsTwoSmis(reg);
153      }
154    }
155
156    // Read current padding counter and skip corresponding number of words.
157    __ pop(kScratchRegister);
158    __ SmiToInteger32(kScratchRegister, kScratchRegister);
159    __ lea(rsp, Operand(rsp, kScratchRegister, times_pointer_size, 0));
160
161    // Get rid of the internal frame.
162  }
163
164  // If this call did not replace a call but patched other code then there will
165  // be an unwanted return address left on the stack. Here we get rid of that.
166  if (convert_call_to_jmp) {
167    __ addq(rsp, Immediate(kPointerSize));
168  }
169
170  // Now that the break point has been handled, resume normal execution by
171  // jumping to the target address intended by the caller and that was
172  // overwritten by the address of DebugBreakXXX.
173  ExternalReference after_break_target =
174      ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate());
175  __ Move(kScratchRegister, after_break_target);
176  __ jmp(Operand(kScratchRegister, 0));
177}
178
179
180void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
181  // Register state for IC load call (from ic-x64.cc).
182  // ----------- S t a t e -------------
183  //  -- rax    : receiver
184  //  -- rcx    : name
185  // -----------------------------------
186  Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit(), 0, false);
187}
188
189
190void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
191  // Register state for IC store call (from ic-x64.cc).
192  // ----------- S t a t e -------------
193  //  -- rax    : value
194  //  -- rcx    : name
195  //  -- rdx    : receiver
196  // -----------------------------------
197  Generate_DebugBreakCallHelper(
198      masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
199}
200
201
202void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
203  // Register state for keyed IC load call (from ic-x64.cc).
204  // ----------- S t a t e -------------
205  //  -- rax     : key
206  //  -- rdx     : receiver
207  // -----------------------------------
208  Generate_DebugBreakCallHelper(masm, rax.bit() | rdx.bit(), 0, false);
209}
210
211
212void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
213  // Register state for keyed IC load call (from ic-x64.cc).
214  // ----------- S t a t e -------------
215  //  -- rax    : value
216  //  -- rcx    : key
217  //  -- rdx    : receiver
218  // -----------------------------------
219  Generate_DebugBreakCallHelper(
220      masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
221}
222
223
224void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
225  // Register state for CompareNil IC
226  // ----------- S t a t e -------------
227  //  -- rax    : value
228  // -----------------------------------
229  Generate_DebugBreakCallHelper(masm, rax.bit(), 0, false);
230}
231
232
233void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
234  // Register state for IC call call (from ic-x64.cc)
235  // ----------- S t a t e -------------
236  //  -- rcx: function name
237  // -----------------------------------
238  Generate_DebugBreakCallHelper(masm, rcx.bit(), 0, false);
239}
240
241
242void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
243  // Register state just before return from JS function (from codegen-x64.cc).
244  // ----------- S t a t e -------------
245  //  -- rax: return value
246  // -----------------------------------
247  Generate_DebugBreakCallHelper(masm, rax.bit(), 0, true);
248}
249
250
251void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
252  // Register state for CallFunctionStub (from code-stubs-x64.cc).
253  // ----------- S t a t e -------------
254  //  -- rdi : function
255  // -----------------------------------
256  Generate_DebugBreakCallHelper(masm, rdi.bit(), 0, false);
257}
258
259
260void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) {
261  // Register state for CallFunctionStub (from code-stubs-x64.cc).
262  // ----------- S t a t e -------------
263  //  -- rdi : function
264  //  -- rbx: cache cell for call target
265  // -----------------------------------
266  Generate_DebugBreakCallHelper(masm, rbx.bit() | rdi.bit(), 0, false);
267}
268
269
270void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
271  // Register state for CallConstructStub (from code-stubs-x64.cc).
272  // rax is the actual number of arguments not encoded as a smi, see comment
273  // above IC call.
274  // ----------- S t a t e -------------
275  //  -- rax: number of arguments
276  // -----------------------------------
277  // The number of arguments in rax is not smi encoded.
278  Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false);
279}
280
281
282void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) {
283  // Register state for CallConstructStub (from code-stubs-x64.cc).
284  // rax is the actual number of arguments not encoded as a smi, see comment
285  // above IC call.
286  // ----------- S t a t e -------------
287  //  -- rax: number of arguments
288  //  -- rbx: cache cell for call target
289  // -----------------------------------
290  // The number of arguments in rax is not smi encoded.
291  Generate_DebugBreakCallHelper(masm, rbx.bit() | rdi.bit(), rax.bit(), false);
292}
293
294
295void Debug::GenerateSlot(MacroAssembler* masm) {
296  // Generate enough nop's to make space for a call instruction.
297  Label check_codesize;
298  __ bind(&check_codesize);
299  __ RecordDebugBreakSlot();
300  __ Nop(Assembler::kDebugBreakSlotLength);
301  ASSERT_EQ(Assembler::kDebugBreakSlotLength,
302            masm->SizeOfCodeGeneratedSince(&check_codesize));
303}
304
305
306void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
307  // In the places where a debug break slot is inserted no registers can contain
308  // object pointers.
309  Generate_DebugBreakCallHelper(masm, 0, 0, true);
310}
311
312
313void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
314  masm->ret(0);
315}
316
317
318void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
319  ExternalReference restarter_frame_function_slot =
320      ExternalReference(Debug_Address::RestarterFrameFunctionPointer(),
321                        masm->isolate());
322  __ Move(rax, restarter_frame_function_slot);
323  __ movq(Operand(rax, 0), Immediate(0));
324
325  // We do not know our frame height, but set rsp based on rbp.
326  __ lea(rsp, Operand(rbp, -1 * kPointerSize));
327
328  __ pop(rdi);  // Function.
329  __ pop(rbp);
330
331  // Load context from the function.
332  __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
333
334  // Get function code.
335  __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
336  __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
337  __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
338
339  // Re-run JSFunction, rdi is function, rsi is context.
340  __ jmp(rdx);
341}
342
343const bool Debug::kFrameDropperSupported = true;
344
345#undef __
346
347#endif  // ENABLE_DEBUGGER_SUPPORT
348
349} }  // namespace v8::internal
350
351#endif  // V8_TARGET_ARCH_X64
352