OLD | NEW |
| (Empty) |
1 // Copyright 2013 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_A64 | |
31 | |
32 #include "codegen.h" | |
33 #include "debug.h" | |
34 | |
35 namespace v8 { | |
36 namespace internal { | |
37 | |
38 | |
39 #define __ ACCESS_MASM(masm) | |
40 | |
41 | |
42 #ifdef ENABLE_DEBUGGER_SUPPORT | |
43 bool BreakLocationIterator::IsDebugBreakAtReturn() { | |
44 return Debug::IsDebugBreakAtReturn(rinfo()); | |
45 } | |
46 | |
47 | |
48 void BreakLocationIterator::SetDebugBreakAtReturn() { | |
49 // Patch the code emitted by FullCodeGenerator::EmitReturnSequence, changing | |
50 // the return from JS function sequence from | |
51 // mov sp, fp | |
52 // ldp fp, lr, [sp] #16 | |
53 // lrd ip0, [pc, #(3 * kInstructionSize)] | |
54 // add sp, sp, ip0 | |
55 // ret | |
56 // <number of paramters ... | |
57 // ... plus one (64 bits)> | |
58 // to a call to the debug break return code. | |
59 // ldr ip0, [pc, #(3 * kInstructionSize)] | |
60 // blr ip0 | |
61 // hlt kHltBadCode @ code should not return, catch if it does. | |
62 // <debug break return code ... | |
63 // ... entry point address (64 bits)> | |
64 | |
65 // The patching code must not overflow the space occupied by the return | |
66 // sequence. | |
67 STATIC_ASSERT(Assembler::kJSRetSequenceInstructions >= 5); | |
68 PatchingAssembler patcher(reinterpret_cast<Instruction*>(rinfo()->pc()), 5); | |
69 byte* entry = | |
70 debug_info_->GetIsolate()->debug()->debug_break_return()->entry(); | |
71 | |
72 // The first instruction of a patched return sequence must be a load literal | |
73 // loading the address of the debug break return code. | |
74 patcher.LoadLiteral(ip0, 3 * kInstructionSize); | |
75 // TODO(all): check the following is correct. | |
76 // The debug break return code will push a frame and call statically compiled | |
77 // code. By using blr, even though control will not return after the branch, | |
78 // this call site will be registered in the frame (lr being saved as the pc | |
79 // of the next instruction to execute for this frame). The debugger can now | |
80 // iterate on the frames to find call to debug break return code. | |
81 patcher.blr(ip0); | |
82 patcher.hlt(kHltBadCode); | |
83 patcher.dc64(reinterpret_cast<int64_t>(entry)); | |
84 } | |
85 | |
86 | |
87 void BreakLocationIterator::ClearDebugBreakAtReturn() { | |
88 // Reset the code emitted by EmitReturnSequence to its original state. | |
89 rinfo()->PatchCode(original_rinfo()->pc(), | |
90 Assembler::kJSRetSequenceInstructions); | |
91 } | |
92 | |
93 | |
94 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) { | |
95 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode())); | |
96 return rinfo->IsPatchedReturnSequence(); | |
97 } | |
98 | |
99 | |
100 bool BreakLocationIterator::IsDebugBreakAtSlot() { | |
101 ASSERT(IsDebugBreakSlot()); | |
102 // Check whether the debug break slot instructions have been patched. | |
103 return rinfo()->IsPatchedDebugBreakSlotSequence(); | |
104 } | |
105 | |
106 | |
107 void BreakLocationIterator::SetDebugBreakAtSlot() { | |
108 // Patch the code emitted by Debug::GenerateSlots, changing the debug break | |
109 // slot code from | |
110 // mov x0, x0 @ nop DEBUG_BREAK_NOP | |
111 // mov x0, x0 @ nop DEBUG_BREAK_NOP | |
112 // mov x0, x0 @ nop DEBUG_BREAK_NOP | |
113 // mov x0, x0 @ nop DEBUG_BREAK_NOP | |
114 // to a call to the debug slot code. | |
115 // ldr ip0, [pc, #(2 * kInstructionSize)] | |
116 // blr ip0 | |
117 // <debug break slot code ... | |
118 // ... entry point address (64 bits)> | |
119 | |
120 // TODO(all): consider adding a hlt instruction after the blr as we don't | |
121 // expect control to return here. This implies increasing | |
122 // kDebugBreakSlotInstructions to 5 instructions. | |
123 | |
124 // The patching code must not overflow the space occupied by the return | |
125 // sequence. | |
126 STATIC_ASSERT(Assembler::kDebugBreakSlotInstructions >= 4); | |
127 PatchingAssembler patcher(reinterpret_cast<Instruction*>(rinfo()->pc()), 4); | |
128 byte* entry = | |
129 debug_info_->GetIsolate()->debug()->debug_break_slot()->entry(); | |
130 | |
131 // The first instruction of a patched debug break slot must be a load literal | |
132 // loading the address of the debug break slot code. | |
133 patcher.LoadLiteral(ip0, 2 * kInstructionSize); | |
134 // TODO(all): check the following is correct. | |
135 // The debug break slot code will push a frame and call statically compiled | |
136 // code. By using blr, event hough control will not return after the branch, | |
137 // this call site will be registered in the frame (lr being saved as the pc | |
138 // of the next instruction to execute for this frame). The debugger can now | |
139 // iterate on the frames to find call to debug break slot code. | |
140 patcher.blr(ip0); | |
141 patcher.dc64(reinterpret_cast<int64_t>(entry)); | |
142 } | |
143 | |
144 | |
145 void BreakLocationIterator::ClearDebugBreakAtSlot() { | |
146 ASSERT(IsDebugBreakSlot()); | |
147 rinfo()->PatchCode(original_rinfo()->pc(), | |
148 Assembler::kDebugBreakSlotInstructions); | |
149 } | |
150 | |
151 const bool Debug::FramePaddingLayout::kIsSupported = false; | |
152 | |
153 static void Generate_DebugBreakCallHelper(MacroAssembler* masm, | |
154 RegList object_regs, | |
155 RegList non_object_regs, | |
156 Register scratch) { | |
157 { | |
158 FrameScope scope(masm, StackFrame::INTERNAL); | |
159 | |
160 // Any live values (object_regs and non_object_regs) in caller-saved | |
161 // registers (or lr) need to be stored on the stack so that their values are | |
162 // safely preserved for a call into C code. | |
163 // | |
164 // Also: | |
165 // * object_regs may be modified during the C code by the garbage | |
166 // collector. Every object register must be a valid tagged pointer or | |
167 // SMI. | |
168 // | |
169 // * non_object_regs will be converted to SMIs so that the garbage | |
170 // collector doesn't try to interpret them as pointers. | |
171 // | |
172 // TODO(jbramley): Why can't this handle callee-saved registers? | |
173 ASSERT((~kCallerSaved.list() & object_regs) == 0); | |
174 ASSERT((~kCallerSaved.list() & non_object_regs) == 0); | |
175 ASSERT((object_regs & non_object_regs) == 0); | |
176 ASSERT((scratch.Bit() & object_regs) == 0); | |
177 ASSERT((scratch.Bit() & non_object_regs) == 0); | |
178 ASSERT((masm->TmpList()->list() & (object_regs | non_object_regs)) == 0); | |
179 STATIC_ASSERT(kSmiValueSize == 32); | |
180 | |
181 CPURegList non_object_list = | |
182 CPURegList(CPURegister::kRegister, kXRegSizeInBits, non_object_regs); | |
183 while (!non_object_list.IsEmpty()) { | |
184 // Store each non-object register as two SMIs. | |
185 Register reg = Register(non_object_list.PopLowestIndex()); | |
186 __ Push(reg); | |
187 __ Poke(wzr, 0); | |
188 __ Push(reg.W(), wzr); | |
189 // Stack: | |
190 // jssp[12]: reg[63:32] | |
191 // jssp[8]: 0x00000000 (SMI tag & padding) | |
192 // jssp[4]: reg[31:0] | |
193 // jssp[0]: 0x00000000 (SMI tag & padding) | |
194 STATIC_ASSERT((kSmiTag == 0) && (kSmiShift == 32)); | |
195 } | |
196 | |
197 if (object_regs != 0) { | |
198 __ PushXRegList(object_regs); | |
199 } | |
200 | |
201 #ifdef DEBUG | |
202 __ RecordComment("// Calling from debug break to runtime - come in - over"); | |
203 #endif | |
204 __ Mov(x0, 0); // No arguments. | |
205 __ Mov(x1, ExternalReference::debug_break(masm->isolate())); | |
206 | |
207 CEntryStub stub(1); | |
208 __ CallStub(&stub); | |
209 | |
210 // Restore the register values from the expression stack. | |
211 if (object_regs != 0) { | |
212 __ PopXRegList(object_regs); | |
213 } | |
214 | |
215 non_object_list = | |
216 CPURegList(CPURegister::kRegister, kXRegSizeInBits, non_object_regs); | |
217 while (!non_object_list.IsEmpty()) { | |
218 // Load each non-object register from two SMIs. | |
219 // Stack: | |
220 // jssp[12]: reg[63:32] | |
221 // jssp[8]: 0x00000000 (SMI tag & padding) | |
222 // jssp[4]: reg[31:0] | |
223 // jssp[0]: 0x00000000 (SMI tag & padding) | |
224 Register reg = Register(non_object_list.PopHighestIndex()); | |
225 __ Pop(scratch, reg); | |
226 __ Bfxil(reg, scratch, 32, 32); | |
227 } | |
228 | |
229 // Leave the internal frame. | |
230 } | |
231 | |
232 // Now that the break point has been handled, resume normal execution by | |
233 // jumping to the target address intended by the caller and that was | |
234 // overwritten by the address of DebugBreakXXX. | |
235 ExternalReference after_break_target(Debug_Address::AfterBreakTarget(), | |
236 masm->isolate()); | |
237 __ Mov(scratch, after_break_target); | |
238 __ Ldr(scratch, MemOperand(scratch)); | |
239 __ Br(scratch); | |
240 } | |
241 | |
242 | |
243 void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { | |
244 // Calling convention for IC load (from ic-arm.cc). | |
245 // ----------- S t a t e ------------- | |
246 // -- x2 : name | |
247 // -- lr : return address | |
248 // -- x0 : receiver | |
249 // -- [sp] : receiver | |
250 // ----------------------------------- | |
251 // Registers x0 and x2 contain objects that need to be pushed on the | |
252 // expression stack of the fake JS frame. | |
253 Generate_DebugBreakCallHelper(masm, x0.Bit() | x2.Bit(), 0, x10); | |
254 } | |
255 | |
256 | |
257 void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) { | |
258 // Calling convention for IC store (from ic-arm.cc). | |
259 // ----------- S t a t e ------------- | |
260 // -- x0 : value | |
261 // -- x1 : receiver | |
262 // -- x2 : name | |
263 // -- lr : return address | |
264 // ----------------------------------- | |
265 // Registers x0, x1, and x2 contain objects that need to be pushed on the | |
266 // expression stack of the fake JS frame. | |
267 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10); | |
268 } | |
269 | |
270 | |
271 void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { | |
272 // ---------- S t a t e -------------- | |
273 // -- lr : return address | |
274 // -- x0 : key | |
275 // -- x1 : receiver | |
276 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit(), 0, x10); | |
277 } | |
278 | |
279 | |
280 void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { | |
281 // ---------- S t a t e -------------- | |
282 // -- x0 : value | |
283 // -- x1 : key | |
284 // -- x2 : receiver | |
285 // -- lr : return address | |
286 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10); | |
287 } | |
288 | |
289 | |
290 void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { | |
291 // Register state for CompareNil IC | |
292 // ----------- S t a t e ------------- | |
293 // -- r0 : value | |
294 // ----------------------------------- | |
295 Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10); | |
296 } | |
297 | |
298 | |
299 void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { | |
300 // Calling convention for IC call (from ic-arm.cc) | |
301 // ----------- S t a t e ------------- | |
302 // -- x2 : name | |
303 // ----------------------------------- | |
304 Generate_DebugBreakCallHelper(masm, x2.Bit(), 0, x10); | |
305 } | |
306 | |
307 | |
308 void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { | |
309 // In places other than IC call sites it is expected that r0 is TOS which | |
310 // is an object - this is not generally the case so this should be used with | |
311 // care. | |
312 Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10); | |
313 } | |
314 | |
315 | |
316 void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { | |
317 // Register state for CallFunctionStub (from code-stubs-a64.cc). | |
318 // ----------- S t a t e ------------- | |
319 // -- x1 : function | |
320 // ----------------------------------- | |
321 Generate_DebugBreakCallHelper(masm, x1.Bit(), 0, x10); | |
322 } | |
323 | |
324 | |
325 void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { | |
326 // Register state for CallFunctionStub (from code-stubs-a64.cc). | |
327 // ----------- S t a t e ------------- | |
328 // -- x1 : function | |
329 // -- x2 : feedback array | |
330 // -- x3 : slot in feedback array | |
331 // ----------------------------------- | |
332 Generate_DebugBreakCallHelper(masm, x1.Bit() | x2.Bit() | x3.Bit(), 0, x10); | |
333 } | |
334 | |
335 | |
336 void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { | |
337 // Calling convention for CallConstructStub (from code-stubs-a64.cc). | |
338 // ----------- S t a t e ------------- | |
339 // -- x0 : number of arguments (not smi) | |
340 // -- x1 : constructor function | |
341 // ----------------------------------- | |
342 Generate_DebugBreakCallHelper(masm, x1.Bit(), x0.Bit(), x10); | |
343 } | |
344 | |
345 | |
346 void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { | |
347 // Calling convention for CallConstructStub (from code-stubs-a64.cc). | |
348 // ----------- S t a t e ------------- | |
349 // -- x0 : number of arguments (not smi) | |
350 // -- x1 : constructor function | |
351 // -- x2 : feedback array | |
352 // -- x3 : feedback slot (smi) | |
353 // ----------------------------------- | |
354 Generate_DebugBreakCallHelper( | |
355 masm, x1.Bit() | x2.Bit() | x3.Bit(), x0.Bit(), x10); | |
356 } | |
357 | |
358 | |
359 void Debug::GenerateSlot(MacroAssembler* masm) { | |
360 // Generate enough nop's to make space for a call instruction. Avoid emitting | |
361 // the constant pool in the debug break slot code. | |
362 InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions); | |
363 | |
364 __ RecordDebugBreakSlot(); | |
365 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { | |
366 __ nop(Assembler::DEBUG_BREAK_NOP); | |
367 } | |
368 } | |
369 | |
370 | |
371 void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) { | |
372 // In the places where a debug break slot is inserted no registers can contain | |
373 // object pointers. | |
374 Generate_DebugBreakCallHelper(masm, 0, 0, x10); | |
375 } | |
376 | |
377 | |
378 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { | |
379 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnA64); | |
380 } | |
381 | |
382 | |
383 void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { | |
384 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnA64); | |
385 } | |
386 | |
387 const bool Debug::kFrameDropperSupported = false; | |
388 | |
389 #endif // ENABLE_DEBUGGER_SUPPORT | |
390 | |
391 } } // namespace v8::internal | |
392 | |
393 #endif // V8_TARGET_ARCH_A64 | |
OLD | NEW |