| 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 | 
|---|