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