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 #if V8_TARGET_ARCH_ARM64 6 7 #include "src/debug/debug.h" 8 9 #include "src/arm64/frames-arm64.h" 10 #include "src/codegen.h" 11 #include "src/debug/liveedit.h" 12 13 namespace v8 { 14 namespace internal { 15 16 #define __ ACCESS_MASM(masm) 17 18 19 void EmitDebugBreakSlot(Assembler* masm) { 20 Label check_size; 21 __ bind(&check_size); 22 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { 23 __ nop(Assembler::DEBUG_BREAK_NOP); 24 } 25 DCHECK_EQ(Assembler::kDebugBreakSlotInstructions, 26 static_cast<int>(masm->InstructionsGeneratedSince(&check_size))); 27 } 28 29 30 void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) { 31 // Generate enough nop's to make space for a call instruction. Avoid emitting 32 // the constant pool in the debug break slot code. 33 InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions); 34 masm->RecordDebugBreakSlot(mode); 35 EmitDebugBreakSlot(masm); 36 } 37 38 39 void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) { 40 PatchingAssembler patcher(isolate, reinterpret_cast<Instruction*>(pc), 41 Assembler::kDebugBreakSlotInstructions); 42 EmitDebugBreakSlot(&patcher); 43 } 44 45 46 void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc, 47 Handle<Code> code) { 48 DCHECK(code->is_debug_stub()); 49 PatchingAssembler patcher(isolate, reinterpret_cast<Instruction*>(pc), 50 Assembler::kDebugBreakSlotInstructions); 51 // Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug 52 // break slot code from 53 // mov x0, x0 @ nop DEBUG_BREAK_NOP 54 // mov x0, x0 @ nop DEBUG_BREAK_NOP 55 // mov x0, x0 @ nop DEBUG_BREAK_NOP 56 // mov x0, x0 @ nop DEBUG_BREAK_NOP 57 // mov x0, x0 @ nop DEBUG_BREAK_NOP 58 // to a call to the debug slot code. 59 // ldr ip0, [pc, #(2 * kInstructionSize)] 60 // blr ip0 61 // b skip 62 // <debug break slot code entry point address (64 bits)> 63 // skip: 64 65 Label skip_constant; 66 // The first instruction of a patched debug break slot must be a load literal 67 // loading the address of the debug break slot code. 68 patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2); 69 patcher.b(&skip_constant); 70 patcher.dc64(reinterpret_cast<int64_t>(code->entry())); 71 patcher.bind(&skip_constant); 72 // TODO(all): check the following is correct. 73 // The debug break slot code will push a frame and call statically compiled 74 // code. By using blr, this call site will be registered in the frame. 75 // The debugger can now iterate on the frames to find this call. 76 patcher.blr(ip0); 77 } 78 79 bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) { 80 Instruction* current_instr = reinterpret_cast<Instruction*>(pc); 81 return !current_instr->IsNop(Assembler::DEBUG_BREAK_NOP); 82 } 83 84 void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm, 85 DebugBreakCallHelperMode mode) { 86 __ RecordComment("Debug break"); 87 Register scratch = x10; 88 { 89 FrameScope scope(masm, StackFrame::INTERNAL); 90 91 // Load padding words on stack. 92 __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingValue)); 93 __ PushMultipleTimes(scratch, LiveEdit::kFramePaddingInitialSize); 94 __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingInitialSize)); 95 __ Push(scratch); 96 97 // Push arguments for DebugBreak call. 98 if (mode == SAVE_RESULT_REGISTER) { 99 // Break on return. 100 __ Push(x0); 101 } else { 102 // Non-return breaks. 103 __ Push(masm->isolate()->factory()->the_hole_value()); 104 } 105 __ Mov(x0, 1); 106 __ Mov(x1, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak), 107 masm->isolate())); 108 109 CEntryStub stub(masm->isolate(), 1); 110 __ CallStub(&stub); 111 112 if (FLAG_debug_code) { 113 for (int i = 0; i < kNumJSCallerSaved; i++) { 114 Register reg = Register::XRegFromCode(JSCallerSavedCode(i)); 115 // Do not clobber x0 if mode is SAVE_RESULT_REGISTER. It will 116 // contain return value of the function. 117 if (!(reg.is(x0) && (mode == SAVE_RESULT_REGISTER))) { 118 __ Mov(reg, Operand(kDebugZapValue)); 119 } 120 } 121 } 122 123 // Don't bother removing padding bytes pushed on the stack 124 // as the frame is going to be restored right away. 125 126 // Leave the internal frame. 127 } 128 129 // Now that the break point has been handled, resume normal execution by 130 // jumping to the target address intended by the caller and that was 131 // overwritten by the address of DebugBreakXXX. 132 ExternalReference after_break_target = 133 ExternalReference::debug_after_break_target_address(masm->isolate()); 134 __ Mov(scratch, after_break_target); 135 __ Ldr(scratch, MemOperand(scratch)); 136 __ Br(scratch); 137 } 138 139 140 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { 141 // We do not know our frame height, but set sp based on fp. 142 __ Add(masm->StackPointer(), fp, FrameDropperFrameConstants::kFunctionOffset); 143 __ AssertStackConsistency(); 144 145 __ Pop(x1); // Function 146 __ Mov(masm->StackPointer(), Operand(fp)); 147 __ Pop(fp, lr); // Frame, Return address. 148 149 ParameterCount dummy(0); 150 __ FloodFunctionIfStepping(x1, no_reg, dummy, dummy); 151 152 UseScratchRegisterScope temps(masm); 153 Register scratch = temps.AcquireX(); 154 155 // Load context from the function. 156 __ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset)); 157 158 // Clear new.target as a safety measure. 159 __ LoadRoot(x3, Heap::kUndefinedValueRootIndex); 160 161 // Get function code. 162 __ Ldr(scratch, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); 163 __ Ldr(scratch, FieldMemOperand(scratch, SharedFunctionInfo::kCodeOffset)); 164 __ Add(scratch, scratch, Code::kHeaderSize - kHeapObjectTag); 165 166 // Re-run JSFunction, x1 is function, cp is context. 167 __ Br(scratch); 168 } 169 170 171 const bool LiveEdit::kFrameDropperSupported = true; 172 173 } // namespace internal 174 } // namespace v8 175 176 #endif // V8_TARGET_ARCH_ARM64 177