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