1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/v8.h"
6
7#if V8_TARGET_ARCH_X87
8
9#include "src/codegen.h"
10#include "src/debug.h"
11
12
13namespace v8 {
14namespace internal {
15
16bool BreakLocationIterator::IsDebugBreakAtReturn() {
17  return Debug::IsDebugBreakAtReturn(rinfo());
18}
19
20
21// Patch the JS frame exit code with a debug break call. See
22// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-x87.cc
23// for the precise return instructions sequence.
24void BreakLocationIterator::SetDebugBreakAtReturn() {
25  DCHECK(Assembler::kJSReturnSequenceLength >=
26         Assembler::kCallInstructionLength);
27  rinfo()->PatchCodeWithCall(
28      debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(),
29      Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
30}
31
32
33// Restore the JS frame exit code.
34void BreakLocationIterator::ClearDebugBreakAtReturn() {
35  rinfo()->PatchCode(original_rinfo()->pc(),
36                     Assembler::kJSReturnSequenceLength);
37}
38
39
40// A debug break in the frame exit code is identified by the JS frame exit code
41// having been patched with a call instruction.
42bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
43  DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
44  return rinfo->IsPatchedReturnSequence();
45}
46
47
48bool BreakLocationIterator::IsDebugBreakAtSlot() {
49  DCHECK(IsDebugBreakSlot());
50  // Check whether the debug break slot instructions have been patched.
51  return rinfo()->IsPatchedDebugBreakSlotSequence();
52}
53
54
55void BreakLocationIterator::SetDebugBreakAtSlot() {
56  DCHECK(IsDebugBreakSlot());
57  Isolate* isolate = debug_info_->GetIsolate();
58  rinfo()->PatchCodeWithCall(
59      isolate->builtins()->Slot_DebugBreak()->entry(),
60      Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
61}
62
63
64void BreakLocationIterator::ClearDebugBreakAtSlot() {
65  DCHECK(IsDebugBreakSlot());
66  rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
67}
68
69
70#define __ ACCESS_MASM(masm)
71
72static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
73                                          RegList object_regs,
74                                          RegList non_object_regs,
75                                          bool convert_call_to_jmp) {
76  // Enter an internal frame.
77  {
78    FrameScope scope(masm, StackFrame::INTERNAL);
79
80    // Load padding words on stack.
81    for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
82      __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue)));
83    }
84    __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
85
86    // Store the registers containing live values on the expression stack to
87    // make sure that these are correctly updated during GC. Non object values
88    // are stored as a smi causing it to be untouched by GC.
89    DCHECK((object_regs & ~kJSCallerSaved) == 0);
90    DCHECK((non_object_regs & ~kJSCallerSaved) == 0);
91    DCHECK((object_regs & non_object_regs) == 0);
92    for (int i = 0; i < kNumJSCallerSaved; i++) {
93      int r = JSCallerSavedCode(i);
94      Register reg = { r };
95      if ((object_regs & (1 << r)) != 0) {
96        __ push(reg);
97      }
98      if ((non_object_regs & (1 << r)) != 0) {
99        if (FLAG_debug_code) {
100          __ test(reg, Immediate(0xc0000000));
101          __ Assert(zero, kUnableToEncodeValueAsSmi);
102        }
103        __ SmiTag(reg);
104        __ push(reg);
105      }
106    }
107
108#ifdef DEBUG
109    __ RecordComment("// Calling from debug break to runtime - come in - over");
110#endif
111    __ Move(eax, Immediate(0));  // No arguments.
112    __ mov(ebx, Immediate(ExternalReference::debug_break(masm->isolate())));
113
114    CEntryStub ceb(masm->isolate(), 1);
115    __ CallStub(&ceb);
116
117    // Automatically find register that could be used after register restore.
118    // We need one register for padding skip instructions.
119    Register unused_reg = { -1 };
120
121    // Restore the register values containing object pointers from the
122    // expression stack.
123    for (int i = kNumJSCallerSaved; --i >= 0;) {
124      int r = JSCallerSavedCode(i);
125      Register reg = { r };
126      if (FLAG_debug_code) {
127        __ Move(reg, Immediate(kDebugZapValue));
128      }
129      bool taken = reg.code() == esi.code();
130      if ((object_regs & (1 << r)) != 0) {
131        __ pop(reg);
132        taken = true;
133      }
134      if ((non_object_regs & (1 << r)) != 0) {
135        __ pop(reg);
136        __ SmiUntag(reg);
137        taken = true;
138      }
139      if (!taken) {
140        unused_reg = reg;
141      }
142    }
143
144    DCHECK(unused_reg.code() != -1);
145
146    // Read current padding counter and skip corresponding number of words.
147    __ pop(unused_reg);
148    // We divide stored value by 2 (untagging) and multiply it by word's size.
149    STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
150    __ lea(esp, Operand(esp, unused_reg, times_half_pointer_size, 0));
151
152    // Get rid of the internal frame.
153  }
154
155  // If this call did not replace a call but patched other code then there will
156  // be an unwanted return address left on the stack. Here we get rid of that.
157  if (convert_call_to_jmp) {
158    __ add(esp, Immediate(kPointerSize));
159  }
160
161  // Now that the break point has been handled, resume normal execution by
162  // jumping to the target address intended by the caller and that was
163  // overwritten by the address of DebugBreakXXX.
164  ExternalReference after_break_target =
165      ExternalReference::debug_after_break_target_address(masm->isolate());
166  __ jmp(Operand::StaticVariable(after_break_target));
167}
168
169
170void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
171  // Register state for CallICStub
172  // ----------- S t a t e -------------
173  //  -- edx    : type feedback slot (smi)
174  //  -- edi    : function
175  // -----------------------------------
176  Generate_DebugBreakCallHelper(masm, edx.bit() | edi.bit(),
177                                0, false);
178}
179
180
181void DebugCodegen::GenerateLoadICDebugBreak(MacroAssembler* masm) {
182  // Register state for IC load call (from ic-x87.cc).
183  Register receiver = LoadDescriptor::ReceiverRegister();
184  Register name = LoadDescriptor::NameRegister();
185  Generate_DebugBreakCallHelper(masm, receiver.bit() | name.bit(), 0, false);
186}
187
188
189void DebugCodegen::GenerateStoreICDebugBreak(MacroAssembler* masm) {
190  // Register state for IC store call (from ic-x87.cc).
191  Register receiver = StoreDescriptor::ReceiverRegister();
192  Register name = StoreDescriptor::NameRegister();
193  Register value = StoreDescriptor::ValueRegister();
194  Generate_DebugBreakCallHelper(
195      masm, receiver.bit() | name.bit() | value.bit(), 0, false);
196}
197
198
199void DebugCodegen::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
200  // Register state for keyed IC load call (from ic-x87.cc).
201  GenerateLoadICDebugBreak(masm);
202}
203
204
205void DebugCodegen::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
206  // Register state for keyed IC store call (from ic-x87.cc).
207  Register receiver = StoreDescriptor::ReceiverRegister();
208  Register name = StoreDescriptor::NameRegister();
209  Register value = StoreDescriptor::ValueRegister();
210  Generate_DebugBreakCallHelper(
211      masm, receiver.bit() | name.bit() | value.bit(), 0, false);
212}
213
214
215void DebugCodegen::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
216  // Register state for CompareNil IC
217  // ----------- S t a t e -------------
218  //  -- eax    : value
219  // -----------------------------------
220  Generate_DebugBreakCallHelper(masm, eax.bit(), 0, false);
221}
222
223
224void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
225  // Register state just before return from JS function (from codegen-x87.cc).
226  // ----------- S t a t e -------------
227  //  -- eax: return value
228  // -----------------------------------
229  Generate_DebugBreakCallHelper(masm, eax.bit(), 0, true);
230}
231
232
233void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
234  // Register state for CallFunctionStub (from code-stubs-x87.cc).
235  // ----------- S t a t e -------------
236  //  -- edi: function
237  // -----------------------------------
238  Generate_DebugBreakCallHelper(masm, edi.bit(), 0, false);
239}
240
241
242void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
243  // Register state for CallConstructStub (from code-stubs-x87.cc).
244  // eax is the actual number of arguments not encoded as a smi see comment
245  // above IC call.
246  // ----------- S t a t e -------------
247  //  -- eax: number of arguments (not smi)
248  //  -- edi: constructor function
249  // -----------------------------------
250  // The number of arguments in eax is not smi encoded.
251  Generate_DebugBreakCallHelper(masm, edi.bit(), eax.bit(), false);
252}
253
254
255void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
256    MacroAssembler* masm) {
257  // Register state for CallConstructStub (from code-stubs-x87.cc).
258  // eax is the actual number of arguments not encoded as a smi see comment
259  // above IC call.
260  // ----------- S t a t e -------------
261  //  -- eax: number of arguments (not smi)
262  //  -- ebx: feedback array
263  //  -- edx: feedback slot (smi)
264  //  -- edi: constructor function
265  // -----------------------------------
266  // The number of arguments in eax is not smi encoded.
267  Generate_DebugBreakCallHelper(masm, ebx.bit() | edx.bit() | edi.bit(),
268                                eax.bit(), false);
269}
270
271
272void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
273  // Generate enough nop's to make space for a call instruction.
274  Label check_codesize;
275  __ bind(&check_codesize);
276  __ RecordDebugBreakSlot();
277  __ Nop(Assembler::kDebugBreakSlotLength);
278  DCHECK_EQ(Assembler::kDebugBreakSlotLength,
279            masm->SizeOfCodeGeneratedSince(&check_codesize));
280}
281
282
283void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
284  // In the places where a debug break slot is inserted no registers can contain
285  // object pointers.
286  Generate_DebugBreakCallHelper(masm, 0, 0, true);
287}
288
289
290void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
291  masm->ret(0);
292}
293
294
295void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
296  ExternalReference restarter_frame_function_slot =
297      ExternalReference::debug_restarter_frame_function_pointer_address(
298          masm->isolate());
299  __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0));
300
301  // We do not know our frame height, but set esp based on ebp.
302  __ lea(esp, Operand(ebp, -1 * kPointerSize));
303
304  __ pop(edi);  // Function.
305  __ pop(ebp);
306
307  // Load context from the function.
308  __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
309
310  // Get function code.
311  __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
312  __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
313  __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
314
315  // Re-run JSFunction, edi is function, esi is context.
316  __ jmp(edx);
317}
318
319
320const bool LiveEdit::kFrameDropperSupported = true;
321
322#undef __
323
324} }  // namespace v8::internal
325
326#endif  // V8_TARGET_ARCH_X87
327