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_ARM
8
9#include "src/codegen.h"
10#include "src/debug.h"
11
12namespace v8 {
13namespace internal {
14
15bool BreakLocationIterator::IsDebugBreakAtReturn() {
16  return Debug::IsDebugBreakAtReturn(rinfo());
17}
18
19
20void BreakLocationIterator::SetDebugBreakAtReturn() {
21  // Patch the code changing the return from JS function sequence from
22  //   mov sp, fp
23  //   ldmia sp!, {fp, lr}
24  //   add sp, sp, #4
25  //   bx lr
26  // to a call to the debug break return code.
27  //   ldr ip, [pc, #0]
28  //   blx ip
29  //   <debug break return code entry point address>
30  //   bkpt 0
31  CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
32  patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
33  patcher.masm()->blx(v8::internal::ip);
34  patcher.Emit(
35      debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry());
36  patcher.masm()->bkpt(0);
37}
38
39
40// Restore the JS frame exit code.
41void BreakLocationIterator::ClearDebugBreakAtReturn() {
42  rinfo()->PatchCode(original_rinfo()->pc(),
43                     Assembler::kJSReturnSequenceInstructions);
44}
45
46
47// A debug break in the frame exit code is identified by the JS frame exit code
48// having been patched with a call instruction.
49bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
50  DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
51  return rinfo->IsPatchedReturnSequence();
52}
53
54
55bool BreakLocationIterator::IsDebugBreakAtSlot() {
56  DCHECK(IsDebugBreakSlot());
57  // Check whether the debug break slot instructions have been patched.
58  return rinfo()->IsPatchedDebugBreakSlotSequence();
59}
60
61
62void BreakLocationIterator::SetDebugBreakAtSlot() {
63  DCHECK(IsDebugBreakSlot());
64  // Patch the code changing the debug break slot code from
65  //   mov r2, r2
66  //   mov r2, r2
67  //   mov r2, r2
68  // to a call to the debug break slot code.
69  //   ldr ip, [pc, #0]
70  //   blx ip
71  //   <debug break slot code entry point address>
72  CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
73  patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
74  patcher.masm()->blx(v8::internal::ip);
75  patcher.Emit(
76      debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry());
77}
78
79
80void BreakLocationIterator::ClearDebugBreakAtSlot() {
81  DCHECK(IsDebugBreakSlot());
82  rinfo()->PatchCode(original_rinfo()->pc(),
83                     Assembler::kDebugBreakSlotInstructions);
84}
85
86
87#define __ ACCESS_MASM(masm)
88
89
90static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
91                                          RegList object_regs,
92                                          RegList non_object_regs) {
93  {
94    FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
95
96    // Load padding words on stack.
97    __ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue)));
98    for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) {
99      __ push(ip);
100    }
101    __ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
102    __ push(ip);
103
104    // Store the registers containing live values on the expression stack to
105    // make sure that these are correctly updated during GC. Non object values
106    // are stored as a smi causing it to be untouched by GC.
107    DCHECK((object_regs & ~kJSCallerSaved) == 0);
108    DCHECK((non_object_regs & ~kJSCallerSaved) == 0);
109    DCHECK((object_regs & non_object_regs) == 0);
110    if ((object_regs | non_object_regs) != 0) {
111      for (int i = 0; i < kNumJSCallerSaved; i++) {
112        int r = JSCallerSavedCode(i);
113        Register reg = { r };
114        if ((non_object_regs & (1 << r)) != 0) {
115          if (FLAG_debug_code) {
116            __ tst(reg, Operand(0xc0000000));
117            __ Assert(eq, kUnableToEncodeValueAsSmi);
118          }
119          __ SmiTag(reg);
120        }
121      }
122      __ stm(db_w, sp, object_regs | non_object_regs);
123    }
124
125#ifdef DEBUG
126    __ RecordComment("// Calling from debug break to runtime - come in - over");
127#endif
128    __ mov(r0, Operand::Zero());  // no arguments
129    __ mov(r1, Operand(ExternalReference::debug_break(masm->isolate())));
130
131    CEntryStub ceb(masm->isolate(), 1);
132    __ CallStub(&ceb);
133
134    // Restore the register values from the expression stack.
135    if ((object_regs | non_object_regs) != 0) {
136      __ ldm(ia_w, sp, object_regs | non_object_regs);
137      for (int i = 0; i < kNumJSCallerSaved; i++) {
138        int r = JSCallerSavedCode(i);
139        Register reg = { r };
140        if ((non_object_regs & (1 << r)) != 0) {
141          __ SmiUntag(reg);
142        }
143        if (FLAG_debug_code &&
144            (((object_regs |non_object_regs) & (1 << r)) == 0)) {
145          __ mov(reg, Operand(kDebugZapValue));
146        }
147      }
148    }
149
150    // Don't bother removing padding bytes pushed on the stack
151    // as the frame is going to be restored right away.
152
153    // Leave the internal frame.
154  }
155
156  // Now that the break point has been handled, resume normal execution by
157  // jumping to the target address intended by the caller and that was
158  // overwritten by the address of DebugBreakXXX.
159  ExternalReference after_break_target =
160      ExternalReference::debug_after_break_target_address(masm->isolate());
161  __ mov(ip, Operand(after_break_target));
162  __ ldr(ip, MemOperand(ip));
163  __ Jump(ip);
164}
165
166
167void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
168  // Register state for CallICStub
169  // ----------- S t a t e -------------
170  //  -- r1 : function
171  //  -- r3 : slot in feedback array (smi)
172  // -----------------------------------
173  Generate_DebugBreakCallHelper(masm, r1.bit() | r3.bit(), 0);
174}
175
176
177void DebugCodegen::GenerateLoadICDebugBreak(MacroAssembler* masm) {
178  // Calling convention for IC load (from ic-arm.cc).
179  Register receiver = LoadDescriptor::ReceiverRegister();
180  Register name = LoadDescriptor::NameRegister();
181  Generate_DebugBreakCallHelper(masm, receiver.bit() | name.bit(), 0);
182}
183
184
185void DebugCodegen::GenerateStoreICDebugBreak(MacroAssembler* masm) {
186  // Calling convention for IC store (from ic-arm.cc).
187  Register receiver = StoreDescriptor::ReceiverRegister();
188  Register name = StoreDescriptor::NameRegister();
189  Register value = StoreDescriptor::ValueRegister();
190  Generate_DebugBreakCallHelper(
191      masm, receiver.bit() | name.bit() | value.bit(), 0);
192}
193
194
195void DebugCodegen::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
196  // Calling convention for keyed IC load (from ic-arm.cc).
197  GenerateLoadICDebugBreak(masm);
198}
199
200
201void DebugCodegen::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
202  // Calling convention for IC keyed store call (from ic-arm.cc).
203  Register receiver = StoreDescriptor::ReceiverRegister();
204  Register name = StoreDescriptor::NameRegister();
205  Register value = StoreDescriptor::ValueRegister();
206  Generate_DebugBreakCallHelper(
207      masm, receiver.bit() | name.bit() | value.bit(), 0);
208}
209
210
211void DebugCodegen::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
212  // Register state for CompareNil IC
213  // ----------- S t a t e -------------
214  //  -- r0    : value
215  // -----------------------------------
216  Generate_DebugBreakCallHelper(masm, r0.bit(), 0);
217}
218
219
220void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
221  // In places other than IC call sites it is expected that r0 is TOS which
222  // is an object - this is not generally the case so this should be used with
223  // care.
224  Generate_DebugBreakCallHelper(masm, r0.bit(), 0);
225}
226
227
228void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
229  // Register state for CallFunctionStub (from code-stubs-arm.cc).
230  // ----------- S t a t e -------------
231  //  -- r1 : function
232  // -----------------------------------
233  Generate_DebugBreakCallHelper(masm, r1.bit(), 0);
234}
235
236
237void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
238  // Calling convention for CallConstructStub (from code-stubs-arm.cc)
239  // ----------- S t a t e -------------
240  //  -- r0     : number of arguments (not smi)
241  //  -- r1     : constructor function
242  // -----------------------------------
243  Generate_DebugBreakCallHelper(masm, r1.bit(), r0.bit());
244}
245
246
247void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
248    MacroAssembler* masm) {
249  // Calling convention for CallConstructStub (from code-stubs-arm.cc)
250  // ----------- S t a t e -------------
251  //  -- r0     : number of arguments (not smi)
252  //  -- r1     : constructor function
253  //  -- r2     : feedback array
254  //  -- r3     : feedback slot (smi)
255  // -----------------------------------
256  Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit() | r3.bit(), r0.bit());
257}
258
259
260void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
261  // Generate enough nop's to make space for a call instruction. Avoid emitting
262  // the constant pool in the debug break slot code.
263  Assembler::BlockConstPoolScope block_const_pool(masm);
264  Label check_codesize;
265  __ bind(&check_codesize);
266  __ RecordDebugBreakSlot();
267  for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
268    __ nop(MacroAssembler::DEBUG_BREAK_NOP);
269  }
270  DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
271            masm->InstructionsGeneratedSince(&check_codesize));
272}
273
274
275void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
276  // In the places where a debug break slot is inserted no registers can contain
277  // object pointers.
278  Generate_DebugBreakCallHelper(masm, 0, 0);
279}
280
281
282void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
283  __ Ret();
284}
285
286
287void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
288  ExternalReference restarter_frame_function_slot =
289      ExternalReference::debug_restarter_frame_function_pointer_address(
290          masm->isolate());
291  __ mov(ip, Operand(restarter_frame_function_slot));
292  __ mov(r1, Operand::Zero());
293  __ str(r1, MemOperand(ip, 0));
294
295  // Load the function pointer off of our current stack frame.
296  __ ldr(r1, MemOperand(fp,
297         StandardFrameConstants::kConstantPoolOffset - kPointerSize));
298
299  // Pop return address, frame and constant pool pointer (if
300  // FLAG_enable_ool_constant_pool).
301  __ LeaveFrame(StackFrame::INTERNAL);
302
303  { ConstantPoolUnavailableScope constant_pool_unavailable(masm);
304    // Load context from the function.
305    __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
306
307    // Get function code.
308    __ ldr(ip, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
309    __ ldr(ip, FieldMemOperand(ip, SharedFunctionInfo::kCodeOffset));
310    __ add(ip, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
311
312    // Re-run JSFunction, r1 is function, cp is context.
313    __ Jump(ip);
314  }
315}
316
317
318const bool LiveEdit::kFrameDropperSupported = true;
319
320#undef __
321
322} }  // namespace v8::internal
323
324#endif  // V8_TARGET_ARCH_ARM
325