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 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode())); 71 return rinfo->IsPatchedReturnSequence(); 72 } 73 74 75 bool BreakLocationIterator::IsDebugBreakAtSlot() { 76 ASSERT(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 ASSERT(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 // Any live values (object_regs and non_object_regs) in caller-saved 135 // registers (or lr) need to be stored on the stack so that their values are 136 // safely preserved for a call into C code. 137 // 138 // Also: 139 // * object_regs may be modified during the C code by the garbage 140 // collector. Every object register must be a valid tagged pointer or 141 // SMI. 142 // 143 // * non_object_regs will be converted to SMIs so that the garbage 144 // collector doesn't try to interpret them as pointers. 145 // 146 // TODO(jbramley): Why can't this handle callee-saved registers? 147 ASSERT((~kCallerSaved.list() & object_regs) == 0); 148 ASSERT((~kCallerSaved.list() & non_object_regs) == 0); 149 ASSERT((object_regs & non_object_regs) == 0); 150 ASSERT((scratch.Bit() & object_regs) == 0); 151 ASSERT((scratch.Bit() & non_object_regs) == 0); 152 ASSERT((masm->TmpList()->list() & (object_regs | non_object_regs)) == 0); 153 STATIC_ASSERT(kSmiValueSize == 32); 154 155 CPURegList non_object_list = 156 CPURegList(CPURegister::kRegister, kXRegSizeInBits, non_object_regs); 157 while (!non_object_list.IsEmpty()) { 158 // Store each non-object register as two SMIs. 159 Register reg = Register(non_object_list.PopLowestIndex()); 160 __ Lsr(scratch, reg, 32); 161 __ SmiTagAndPush(scratch, reg); 162 163 // Stack: 164 // jssp[12]: reg[63:32] 165 // jssp[8]: 0x00000000 (SMI tag & padding) 166 // jssp[4]: reg[31:0] 167 // jssp[0]: 0x00000000 (SMI tag & padding) 168 STATIC_ASSERT((kSmiTag == 0) && (kSmiShift == 32)); 169 } 170 171 if (object_regs != 0) { 172 __ PushXRegList(object_regs); 173 } 174 175 #ifdef DEBUG 176 __ RecordComment("// Calling from debug break to runtime - come in - over"); 177 #endif 178 __ Mov(x0, 0); // No arguments. 179 __ Mov(x1, ExternalReference::debug_break(masm->isolate())); 180 181 CEntryStub stub(masm->isolate(), 1); 182 __ CallStub(&stub); 183 184 // Restore the register values from the expression stack. 185 if (object_regs != 0) { 186 __ PopXRegList(object_regs); 187 } 188 189 non_object_list = 190 CPURegList(CPURegister::kRegister, kXRegSizeInBits, non_object_regs); 191 while (!non_object_list.IsEmpty()) { 192 // Load each non-object register from two SMIs. 193 // Stack: 194 // jssp[12]: reg[63:32] 195 // jssp[8]: 0x00000000 (SMI tag & padding) 196 // jssp[4]: reg[31:0] 197 // jssp[0]: 0x00000000 (SMI tag & padding) 198 Register reg = Register(non_object_list.PopHighestIndex()); 199 __ Pop(scratch, reg); 200 __ Bfxil(reg, scratch, 32, 32); 201 } 202 203 // Leave the internal frame. 204 } 205 206 // Now that the break point has been handled, resume normal execution by 207 // jumping to the target address intended by the caller and that was 208 // overwritten by the address of DebugBreakXXX. 209 ExternalReference after_break_target = 210 ExternalReference::debug_after_break_target_address(masm->isolate()); 211 __ Mov(scratch, after_break_target); 212 __ Ldr(scratch, MemOperand(scratch)); 213 __ Br(scratch); 214 } 215 216 217 void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) { 218 // Register state for CallICStub 219 // ----------- S t a t e ------------- 220 // -- x1 : function 221 // -- x3 : slot in feedback array 222 // ----------------------------------- 223 Generate_DebugBreakCallHelper(masm, x1.Bit() | x3.Bit(), 0, x10); 224 } 225 226 227 void DebugCodegen::GenerateLoadICDebugBreak(MacroAssembler* masm) { 228 // Calling convention for IC load (from ic-arm.cc). 229 // ----------- S t a t e ------------- 230 // -- x2 : name 231 // -- lr : return address 232 // -- x0 : receiver 233 // -- [sp] : receiver 234 // ----------------------------------- 235 // Registers x0 and x2 contain objects that need to be pushed on the 236 // expression stack of the fake JS frame. 237 Generate_DebugBreakCallHelper(masm, x0.Bit() | x2.Bit(), 0, x10); 238 } 239 240 241 void DebugCodegen::GenerateStoreICDebugBreak(MacroAssembler* masm) { 242 // Calling convention for IC store (from ic-arm.cc). 243 // ----------- S t a t e ------------- 244 // -- x0 : value 245 // -- x1 : receiver 246 // -- x2 : name 247 // -- lr : return address 248 // ----------------------------------- 249 // Registers x0, x1, and x2 contain objects that need to be pushed on the 250 // expression stack of the fake JS frame. 251 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10); 252 } 253 254 255 void DebugCodegen::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { 256 // ---------- S t a t e -------------- 257 // -- lr : return address 258 // -- x0 : key 259 // -- x1 : receiver 260 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit(), 0, x10); 261 } 262 263 264 void DebugCodegen::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { 265 // ---------- S t a t e -------------- 266 // -- x0 : value 267 // -- x1 : key 268 // -- x2 : receiver 269 // -- lr : return address 270 Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10); 271 } 272 273 274 void DebugCodegen::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { 275 // Register state for CompareNil IC 276 // ----------- S t a t e ------------- 277 // -- r0 : value 278 // ----------------------------------- 279 Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10); 280 } 281 282 283 void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) { 284 // In places other than IC call sites it is expected that r0 is TOS which 285 // is an object - this is not generally the case so this should be used with 286 // care. 287 Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10); 288 } 289 290 291 void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { 292 // Register state for CallFunctionStub (from code-stubs-arm64.cc). 293 // ----------- S t a t e ------------- 294 // -- x1 : function 295 // ----------------------------------- 296 Generate_DebugBreakCallHelper(masm, x1.Bit(), 0, x10); 297 } 298 299 300 void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { 301 // Calling convention for CallConstructStub (from code-stubs-arm64.cc). 302 // ----------- S t a t e ------------- 303 // -- x0 : number of arguments (not smi) 304 // -- x1 : constructor function 305 // ----------------------------------- 306 Generate_DebugBreakCallHelper(masm, x1.Bit(), x0.Bit(), x10); 307 } 308 309 310 void DebugCodegen::GenerateCallConstructStubRecordDebugBreak( 311 MacroAssembler* masm) { 312 // Calling convention for CallConstructStub (from code-stubs-arm64.cc). 313 // ----------- S t a t e ------------- 314 // -- x0 : number of arguments (not smi) 315 // -- x1 : constructor function 316 // -- x2 : feedback array 317 // -- x3 : feedback slot (smi) 318 // ----------------------------------- 319 Generate_DebugBreakCallHelper( 320 masm, x1.Bit() | x2.Bit() | x3.Bit(), x0.Bit(), x10); 321 } 322 323 324 void DebugCodegen::GenerateSlot(MacroAssembler* masm) { 325 // Generate enough nop's to make space for a call instruction. Avoid emitting 326 // the constant pool in the debug break slot code. 327 InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions); 328 329 __ RecordDebugBreakSlot(); 330 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { 331 __ nop(Assembler::DEBUG_BREAK_NOP); 332 } 333 } 334 335 336 void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) { 337 // In the places where a debug break slot is inserted no registers can contain 338 // object pointers. 339 Generate_DebugBreakCallHelper(masm, 0, 0, x10); 340 } 341 342 343 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { 344 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnARM64); 345 } 346 347 348 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 349 masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnARM64); 350 } 351 352 353 const bool LiveEdit::kFrameDropperSupported = false; 354 355 } } // namespace v8::internal 356 357 #endif // V8_TARGET_ARCH_ARM64 358