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