• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
12 namespace v8 {
13 namespace internal {
14 
IsDebugBreakAtReturn()15 bool BreakLocationIterator::IsDebugBreakAtReturn() {
16   return Debug::IsDebugBreakAtReturn(rinfo());
17 }
18 
19 
SetDebugBreakAtReturn()20 void 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.
ClearDebugBreakAtReturn()41 void 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.
IsDebugBreakAtReturn(RelocInfo * rinfo)49 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
50   DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
51   return rinfo->IsPatchedReturnSequence();
52 }
53 
54 
IsDebugBreakAtSlot()55 bool BreakLocationIterator::IsDebugBreakAtSlot() {
56   DCHECK(IsDebugBreakSlot());
57   // Check whether the debug break slot instructions have been patched.
58   return rinfo()->IsPatchedDebugBreakSlotSequence();
59 }
60 
61 
SetDebugBreakAtSlot()62 void 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 
ClearDebugBreakAtSlot()80 void BreakLocationIterator::ClearDebugBreakAtSlot() {
81   DCHECK(IsDebugBreakSlot());
82   rinfo()->PatchCode(original_rinfo()->pc(),
83                      Assembler::kDebugBreakSlotInstructions);
84 }
85 
86 
87 #define __ ACCESS_MASM(masm)
88 
89 
Generate_DebugBreakCallHelper(MacroAssembler * masm,RegList object_regs,RegList non_object_regs)90 static 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 
GenerateCallICStubDebugBreak(MacroAssembler * masm)167 void 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 
GenerateLoadICDebugBreak(MacroAssembler * masm)177 void 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 
GenerateStoreICDebugBreak(MacroAssembler * masm)185 void 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 
GenerateKeyedLoadICDebugBreak(MacroAssembler * masm)195 void DebugCodegen::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
196   // Calling convention for keyed IC load (from ic-arm.cc).
197   GenerateLoadICDebugBreak(masm);
198 }
199 
200 
GenerateKeyedStoreICDebugBreak(MacroAssembler * masm)201 void 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 
GenerateCompareNilICDebugBreak(MacroAssembler * masm)211 void 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 
GenerateReturnDebugBreak(MacroAssembler * masm)220 void 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 
GenerateCallFunctionStubDebugBreak(MacroAssembler * masm)228 void 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 
GenerateCallConstructStubDebugBreak(MacroAssembler * masm)237 void 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 
GenerateCallConstructStubRecordDebugBreak(MacroAssembler * masm)247 void 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 
GenerateSlot(MacroAssembler * masm)260 void 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 
GenerateSlotDebugBreak(MacroAssembler * masm)275 void 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 
GeneratePlainReturnLiveEdit(MacroAssembler * masm)282 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
283   __ Ret();
284 }
285 
286 
GenerateFrameDropperLiveEdit(MacroAssembler * masm)287 void 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 
318 const bool LiveEdit::kFrameDropperSupported = true;
319 
320 #undef __
321 
322 } }  // namespace v8::internal
323 
324 #endif  // V8_TARGET_ARCH_ARM
325