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((ip0.Bit() & (object_regs | non_object_regs)) == 0); |
| 179 ASSERT((ip1.Bit() & (object_regs | non_object_regs)) == 0); |
| 180 STATIC_ASSERT(kSmiValueSize == 32); |
| 181 |
| 182 CPURegList non_object_list = |
| 183 CPURegList(CPURegister::kRegister, kXRegSize, non_object_regs); |
| 184 while (!non_object_list.IsEmpty()) { |
| 185 // Store each non-object register as two SMIs. |
| 186 Register reg = Register(non_object_list.PopLowestIndex()); |
| 187 __ Push(reg); |
| 188 __ Poke(wzr, 0); |
| 189 __ Push(reg.W(), wzr); |
| 190 // Stack: |
| 191 // jssp[12]: reg[63:32] |
| 192 // jssp[8]: 0x00000000 (SMI tag & padding) |
| 193 // jssp[4]: reg[31:0] |
| 194 // jssp[0]: 0x00000000 (SMI tag & padding) |
| 195 STATIC_ASSERT((kSmiTag == 0) && (kSmiShift == 32)); |
| 196 } |
| 197 |
| 198 if (object_regs != 0) { |
| 199 __ PushXRegList(object_regs); |
| 200 } |
| 201 |
| 202 #ifdef DEBUG |
| 203 __ RecordComment("// Calling from debug break to runtime - come in - over"); |
| 204 #endif |
| 205 __ Mov(x0, 0); // No arguments. |
| 206 __ Mov(x1, Operand(ExternalReference::debug_break(masm->isolate()))); |
| 207 |
| 208 CEntryStub stub(1); |
| 209 __ CallStub(&stub); |
| 210 |
| 211 // Restore the register values from the expression stack. |
| 212 if (object_regs != 0) { |
| 213 __ PopXRegList(object_regs); |
| 214 } |
| 215 |
| 216 non_object_list = |
| 217 CPURegList(CPURegister::kRegister, kXRegSize, non_object_regs); |
| 218 while (!non_object_list.IsEmpty()) { |
| 219 // Load each non-object register from two SMIs. |
| 220 // Stack: |
| 221 // jssp[12]: reg[63:32] |
| 222 // jssp[8]: 0x00000000 (SMI tag & padding) |
| 223 // jssp[4]: reg[31:0] |
| 224 // jssp[0]: 0x00000000 (SMI tag & padding) |
| 225 Register reg = Register(non_object_list.PopHighestIndex()); |
| 226 __ Pop(scratch, reg); |
| 227 __ Bfxil(reg, scratch, 32, 32); |
| 228 } |
| 229 |
| 230 // Leave the internal frame. |
| 231 } |
| 232 |
| 233 // Now that the break point has been handled, resume normal execution by |
| 234 // jumping to the target address intended by the caller and that was |
| 235 // overwritten by the address of DebugBreakXXX. |
| 236 ExternalReference after_break_target(Debug_Address::AfterBreakTarget(), |
| 237 masm->isolate()); |
| 238 __ Mov(scratch, Operand(after_break_target)); |
| 239 __ Ldr(scratch, MemOperand(scratch)); |
| 240 __ Br(scratch); |
| 241 } |
| 242 |
| 243 |
| 244 void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { |
| 245 // Calling convention for IC load (from ic-arm.cc). |
| 246 // ----------- S t a t e ------------- |
| 247 // -- x2 : name |
| 248 // -- lr : return address |
| 249 // -- x0 : receiver |
| 250 // -- [sp] : receiver |
| 251 // ----------------------------------- |
| 252 // Registers x0 and x2 contain objects that need to be pushed on the |
| 253 // expression stack of the fake JS frame. |
| 254 Generate_DebugBreakCallHelper(masm, x0.Bit() | x2.Bit(), 0, x10); |
| 255 } |
| 256 |
| 257 |
| 258 void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) { |
| 259 // Calling convention for IC store (from ic-arm.cc). |
| 260 // ----------- S t a t e ------------- |
| 261 // -- x0 : value |
| 262 // -- x1 : receiver |
| 263 // -- x2 : name |
| 264 // -- lr : return address |
| 265 // ----------------------------------- |
| 266 // Registers x0, x1, and x2 contain objects that need to be pushed on the |
| 267 // expression stack of the fake JS frame. |
| 268 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10); |
| 269 } |
| 270 |
| 271 |
| 272 void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { |
| 273 // ---------- S t a t e -------------- |
| 274 // -- lr : return address |
| 275 // -- x0 : key |
| 276 // -- x1 : receiver |
| 277 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit(), 0, x10); |
| 278 } |
| 279 |
| 280 |
| 281 void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { |
| 282 // ---------- S t a t e -------------- |
| 283 // -- x0 : value |
| 284 // -- x1 : key |
| 285 // -- x2 : receiver |
| 286 // -- lr : return address |
| 287 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10); |
| 288 } |
| 289 |
| 290 |
| 291 void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { |
| 292 // Register state for CompareNil IC |
| 293 // ----------- S t a t e ------------- |
| 294 // -- r0 : value |
| 295 // ----------------------------------- |
| 296 Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10); |
| 297 } |
| 298 |
| 299 |
| 300 void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { |
| 301 // Calling convention for IC call (from ic-arm.cc) |
| 302 // ----------- S t a t e ------------- |
| 303 // -- x2 : name |
| 304 // ----------------------------------- |
| 305 Generate_DebugBreakCallHelper(masm, x2.Bit(), 0, x10); |
| 306 } |
| 307 |
| 308 |
| 309 void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { |
| 310 // In places other than IC call sites it is expected that r0 is TOS which |
| 311 // is an object - this is not generally the case so this should be used with |
| 312 // care. |
| 313 Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10); |
| 314 } |
| 315 |
| 316 |
| 317 void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { |
| 318 // Register state for CallFunctionStub (from code-stubs-a64.cc). |
| 319 // ----------- S t a t e ------------- |
| 320 // -- x1 : function |
| 321 // ----------------------------------- |
| 322 Generate_DebugBreakCallHelper(masm, x1.Bit(), 0, x10); |
| 323 } |
| 324 |
| 325 |
| 326 void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { |
| 327 // Register state for CallFunctionStub (from code-stubs-a64.cc). |
| 328 // ----------- S t a t e ------------- |
| 329 // -- x1 : function |
| 330 // -- x2 : feedback array |
| 331 // -- x3 : slot in feedback array |
| 332 // ----------------------------------- |
| 333 Generate_DebugBreakCallHelper(masm, x1.Bit() | x2.Bit() | x3.Bit(), 0, x10); |
| 334 } |
| 335 |
| 336 |
| 337 void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { |
| 338 // Calling convention for CallConstructStub (from code-stubs-a64.cc). |
| 339 // ----------- S t a t e ------------- |
| 340 // -- x0 : number of arguments (not smi) |
| 341 // -- x1 : constructor function |
| 342 // ----------------------------------- |
| 343 Generate_DebugBreakCallHelper(masm, x1.Bit(), x0.Bit(), x10); |
| 344 } |
| 345 |
| 346 |
| 347 void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { |
| 348 // Calling convention for CallConstructStub (from code-stubs-a64.cc). |
| 349 // ----------- S t a t e ------------- |
| 350 // -- x0 : number of arguments (not smi) |
| 351 // -- x1 : constructor function |
| 352 // -- x2 : feedback array |
| 353 // -- x3 : feedback slot (smi) |
| 354 // ----------------------------------- |
| 355 Generate_DebugBreakCallHelper( |
| 356 masm, x1.Bit() | x2.Bit() | x3.Bit(), x0.Bit(), x10); |
| 357 } |
| 358 |
| 359 |
| 360 void Debug::GenerateSlot(MacroAssembler* masm) { |
| 361 // Generate enough nop's to make space for a call instruction. Avoid emitting |
| 362 // the constant pool in the debug break slot code. |
| 363 InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions); |
| 364 |
| 365 __ RecordDebugBreakSlot(); |
| 366 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { |
| 367 __ nop(Assembler::DEBUG_BREAK_NOP); |
| 368 } |
| 369 } |
| 370 |
| 371 |
| 372 void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) { |
| 373 // In the places where a debug break slot is inserted no registers can contain |
| 374 // object pointers. |
| 375 Generate_DebugBreakCallHelper(masm, 0, 0, x10); |
| 376 } |
| 377 |
| 378 |
| 379 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { |
| 380 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnA64); |
| 381 } |
| 382 |
| 383 |
| 384 void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { |
| 385 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnA64); |
| 386 } |
| 387 |
| 388 const bool Debug::kFrameDropperSupported = false; |
| 389 |
| 390 #endif // ENABLE_DEBUGGER_SUPPORT |
| 391 |
| 392 } } // namespace v8::internal |
| 393 |
| 394 #endif // V8_TARGET_ARCH_A64 |
OLD | NEW |