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 13namespace v8 { 14namespace internal { 15 16#define __ ACCESS_MASM(masm) 17 18 19void 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 30void 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 39void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) { 40 PatchingAssembler patcher(isolate, reinterpret_cast<Instruction*>(pc), 41 Assembler::kDebugBreakSlotInstructions); 42 EmitDebugBreakSlot(&patcher); 43} 44 45 46void 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 79bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) { 80 Instruction* current_instr = reinterpret_cast<Instruction*>(pc); 81 return !current_instr->IsNop(Assembler::DEBUG_BREAK_NOP); 82} 83 84void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm, 85 DebugBreakCallHelperMode mode) { 86 __ RecordComment("Debug break"); 87 Register scratch = x10; 88 { 89 FrameScope scope(masm, StackFrame::INTERNAL); 90 91 // Push arguments for DebugBreak call. 92 if (mode == SAVE_RESULT_REGISTER) { 93 // Break on return. 94 __ Push(x0); 95 } else { 96 // Non-return breaks. 97 __ Push(masm->isolate()->factory()->the_hole_value()); 98 } 99 __ Mov(x0, 1); 100 __ Mov(x1, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak), 101 masm->isolate())); 102 103 CEntryStub stub(masm->isolate(), 1); 104 __ CallStub(&stub); 105 106 if (FLAG_debug_code) { 107 for (int i = 0; i < kNumJSCallerSaved; i++) { 108 Register reg = Register::XRegFromCode(JSCallerSavedCode(i)); 109 // Do not clobber x0 if mode is SAVE_RESULT_REGISTER. It will 110 // contain return value of the function. 111 if (!(reg.is(x0) && (mode == SAVE_RESULT_REGISTER))) { 112 __ Mov(reg, Operand(kDebugZapValue)); 113 } 114 } 115 } 116 // Leave the internal frame. 117 } 118 119 __ MaybeDropFrames(); 120 121 // Return to caller. 122 __ Ret(); 123} 124 125void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) { 126 { 127 FrameScope scope(masm, StackFrame::INTERNAL); 128 __ CallRuntime(Runtime::kHandleDebuggerStatement, 0); 129 } 130 __ MaybeDropFrames(); 131 132 // Return to caller. 133 __ Ret(); 134} 135 136void DebugCodegen::GenerateFrameDropperTrampoline(MacroAssembler* masm) { 137 // Frame is being dropped: 138 // - Drop to the target frame specified by x1. 139 // - Look up current function on the frame. 140 // - Leave the frame. 141 // - Restart the frame by calling the function. 142 __ Mov(fp, x1); 143 __ AssertStackConsistency(); 144 __ Ldr(x1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); 145 146 __ Mov(masm->StackPointer(), Operand(fp)); 147 __ Pop(fp, lr); // Frame, Return address. 148 149 __ Ldr(x0, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); 150 __ Ldr(x0, 151 FieldMemOperand(x0, SharedFunctionInfo::kFormalParameterCountOffset)); 152 __ mov(x2, x0); 153 154 ParameterCount dummy1(x2); 155 ParameterCount dummy2(x0); 156 __ InvokeFunction(x1, dummy1, dummy2, JUMP_FUNCTION, 157 CheckDebugStepCallWrapper()); 158} 159 160 161const bool LiveEdit::kFrameDropperSupported = true; 162 163} // namespace internal 164} // namespace v8 165 166#endif // V8_TARGET_ARCH_ARM64 167