| OLD | NEW |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/v8.h" | 5 #include "src/v8.h" |
| 6 | 6 |
| 7 #if V8_TARGET_ARCH_ARM64 | 7 #if V8_TARGET_ARCH_ARM64 |
| 8 | 8 |
| 9 #include "src/codegen.h" | 9 #include "src/codegen.h" |
| 10 #include "src/debug.h" | 10 #include "src/debug.h" |
| 11 | 11 |
| 12 namespace v8 { | 12 namespace v8 { |
| 13 namespace internal { | 13 namespace internal { |
| 14 | 14 |
| 15 | |
| 16 #define __ ACCESS_MASM(masm) | 15 #define __ ACCESS_MASM(masm) |
| 17 | 16 |
| 18 | 17 |
| 19 void BreakLocation::SetDebugBreakAtReturn() { | 18 void EmitDebugBreakSlot(Assembler* masm) { |
| 20 // Patch the code emitted by FullCodeGenerator::EmitReturnSequence, changing | 19 Label check_size; |
| 21 // the return from JS function sequence from | 20 __ bind(&check_size); |
| 22 // mov sp, fp | 21 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { |
| 23 // ldp fp, lr, [sp] #16 | 22 __ nop(Assembler::DEBUG_BREAK_NOP); |
| 24 // lrd ip0, [pc, #(3 * kInstructionSize)] | 23 } |
| 25 // add sp, sp, ip0 | 24 DCHECK_EQ(Assembler::kDebugBreakSlotInstructions, |
| 26 // ret | 25 static_cast<int>(masm->InstructionsGeneratedSince(&check_size))); |
| 27 // <number of paramters ... | |
| 28 // ... plus one (64 bits)> | |
| 29 // to a call to the debug break return code. | |
| 30 // ldr ip0, [pc, #(3 * kInstructionSize)] | |
| 31 // blr ip0 | |
| 32 // hlt kHltBadCode @ code should not return, catch if it does. | |
| 33 // <debug break return code ... | |
| 34 // ... entry point address (64 bits)> | |
| 35 | |
| 36 // The patching code must not overflow the space occupied by the return | |
| 37 // sequence. | |
| 38 STATIC_ASSERT(Assembler::kJSReturnSequenceInstructions >= 5); | |
| 39 PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc()), 5); | |
| 40 byte* entry = | |
| 41 debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry(); | |
| 42 | |
| 43 // The first instruction of a patched return sequence must be a load literal | |
| 44 // loading the address of the debug break return code. | |
| 45 patcher.ldr_pcrel(ip0, (3 * kInstructionSize) >> kLoadLiteralScaleLog2); | |
| 46 // TODO(all): check the following is correct. | |
| 47 // The debug break return code will push a frame and call statically compiled | |
| 48 // code. By using blr, even though control will not return after the branch, | |
| 49 // this call site will be registered in the frame (lr being saved as the pc | |
| 50 // of the next instruction to execute for this frame). The debugger can now | |
| 51 // iterate on the frames to find call to debug break return code. | |
| 52 patcher.blr(ip0); | |
| 53 patcher.hlt(kHltBadCode); | |
| 54 patcher.dc64(reinterpret_cast<int64_t>(entry)); | |
| 55 } | 26 } |
| 56 | 27 |
| 57 | 28 |
| 58 void BreakLocation::SetDebugBreakAtSlot() { | 29 void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode, |
| 59 DCHECK(IsDebugBreakSlot()); | 30 int call_argc) { |
| 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, call_argc); |
| 35 EmitDebugBreakSlot(masm); |
| 36 } |
| 37 |
| 38 |
| 39 void DebugCodegen::ClearDebugBreakSlot(Address pc) { |
| 40 PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc), |
| 41 Assembler::kDebugBreakSlotInstructions); |
| 42 EmitDebugBreakSlot(&patcher); |
| 43 } |
| 44 |
| 45 |
| 46 void DebugCodegen::PatchDebugBreakSlot(Address pc, Handle<Code> code) { |
| 47 DCHECK_EQ(Code::BUILTIN, code->kind()); |
| 48 PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc), |
| 49 Assembler::kDebugBreakSlotInstructions); |
| 60 // Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug | 50 // Patch the code emitted by DebugCodegen::GenerateSlots, changing the debug |
| 61 // break slot code from | 51 // break slot code from |
| 62 // mov x0, x0 @ nop DEBUG_BREAK_NOP | 52 // mov x0, x0 @ nop DEBUG_BREAK_NOP |
| 63 // mov x0, x0 @ nop DEBUG_BREAK_NOP | 53 // mov x0, x0 @ nop DEBUG_BREAK_NOP |
| 64 // mov x0, x0 @ nop DEBUG_BREAK_NOP | 54 // mov x0, x0 @ nop DEBUG_BREAK_NOP |
| 65 // mov x0, x0 @ nop DEBUG_BREAK_NOP | 55 // mov x0, x0 @ nop DEBUG_BREAK_NOP |
| 56 // mov x0, x0 @ nop DEBUG_BREAK_NOP |
| 66 // to a call to the debug slot code. | 57 // to a call to the debug slot code. |
| 67 // ldr ip0, [pc, #(2 * kInstructionSize)] | 58 // ldr ip0, [pc, #(2 * kInstructionSize)] |
| 68 // blr ip0 | 59 // blr ip0 |
| 69 // <debug break slot code ... | 60 // b skip |
| 70 // ... entry point address (64 bits)> | 61 // <debug break slot code entry point address (64 bits)> |
| 62 // skip: |
| 71 | 63 |
| 72 // TODO(all): consider adding a hlt instruction after the blr as we don't | 64 Label skip_constant; |
| 73 // expect control to return here. This implies increasing | |
| 74 // kDebugBreakSlotInstructions to 5 instructions. | |
| 75 | |
| 76 // The patching code must not overflow the space occupied by the return | |
| 77 // sequence. | |
| 78 STATIC_ASSERT(Assembler::kDebugBreakSlotInstructions >= 4); | |
| 79 PatchingAssembler patcher(reinterpret_cast<Instruction*>(pc()), 4); | |
| 80 byte* entry = | |
| 81 debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry(); | |
| 82 | |
| 83 // The first instruction of a patched debug break slot must be a load literal | 65 // The first instruction of a patched debug break slot must be a load literal |
| 84 // loading the address of the debug break slot code. | 66 // loading the address of the debug break slot code. |
| 85 patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2); | 67 patcher.ldr_pcrel(ip0, (2 * kInstructionSize) >> kLoadLiteralScaleLog2); |
| 68 patcher.b(&skip_constant); |
| 69 patcher.dc64(reinterpret_cast<int64_t>(code->entry())); |
| 70 patcher.bind(&skip_constant); |
| 86 // TODO(all): check the following is correct. | 71 // TODO(all): check the following is correct. |
| 87 // The debug break slot code will push a frame and call statically compiled | 72 // The debug break slot code will push a frame and call statically compiled |
| 88 // code. By using blr, event hough control will not return after the branch, | 73 // code. By using blr, this call site will be registered in the frame. |
| 89 // this call site will be registered in the frame (lr being saved as the pc | 74 // The debugger can now iterate on the frames to find this call. |
| 90 // of the next instruction to execute for this frame). The debugger can now | |
| 91 // iterate on the frames to find call to debug break slot code. | |
| 92 patcher.blr(ip0); | 75 patcher.blr(ip0); |
| 93 patcher.dc64(reinterpret_cast<int64_t>(entry)); | |
| 94 } | 76 } |
| 95 | 77 |
| 96 | 78 |
| 97 static void Generate_DebugBreakCallHelper(MacroAssembler* masm, | 79 void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm, |
| 98 RegList object_regs) { | 80 DebugBreakCallHelperMode mode) { |
| 81 __ RecordComment("Debug break"); |
| 99 Register scratch = x10; | 82 Register scratch = x10; |
| 100 { | 83 { |
| 101 FrameScope scope(masm, StackFrame::INTERNAL); | 84 FrameScope scope(masm, StackFrame::INTERNAL); |
| 102 | 85 |
| 103 // Load padding words on stack. | 86 // Load padding words on stack. |
| 104 __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingValue)); | 87 __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingValue)); |
| 105 __ PushMultipleTimes(scratch, LiveEdit::kFramePaddingInitialSize); | 88 __ PushMultipleTimes(scratch, LiveEdit::kFramePaddingInitialSize); |
| 106 __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingInitialSize)); | 89 __ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingInitialSize)); |
| 107 __ Push(scratch); | 90 __ Push(scratch); |
| 108 | 91 |
| 109 // Any live values (object_regs and non_object_regs) in caller-saved | 92 if (mode == SAVE_RESULT_REGISTER) __ Push(x0); |
| 110 // registers (or lr) need to be stored on the stack so that their values are | |
| 111 // safely preserved for a call into C code. | |
| 112 // | |
| 113 // Also: | |
| 114 // * object_regs may be modified during the C code by the garbage | |
| 115 // collector. Every object register must be a valid tagged pointer or | |
| 116 // SMI. | |
| 117 // | |
| 118 // * non_object_regs will be converted to SMIs so that the garbage | |
| 119 // collector doesn't try to interpret them as pointers. | |
| 120 // | |
| 121 // TODO(jbramley): Why can't this handle callee-saved registers? | |
| 122 DCHECK((~kCallerSaved.list() & object_regs) == 0); | |
| 123 DCHECK((scratch.Bit() & object_regs) == 0); | |
| 124 DCHECK((masm->TmpList()->list() & object_regs) == 0); | |
| 125 STATIC_ASSERT(kSmiValueSize == 32); | |
| 126 | 93 |
| 127 if (object_regs != 0) { | |
| 128 __ PushXRegList(object_regs); | |
| 129 } | |
| 130 | |
| 131 #ifdef DEBUG | |
| 132 __ RecordComment("// Calling from debug break to runtime - come in - over"); | |
| 133 #endif | |
| 134 __ Mov(x0, 0); // No arguments. | 94 __ Mov(x0, 0); // No arguments. |
| 135 __ Mov(x1, ExternalReference::debug_break(masm->isolate())); | 95 __ Mov(x1, ExternalReference::debug_break(masm->isolate())); |
| 136 | 96 |
| 137 CEntryStub stub(masm->isolate(), 1); | 97 CEntryStub stub(masm->isolate(), 1); |
| 138 __ CallStub(&stub); | 98 __ CallStub(&stub); |
| 139 | 99 |
| 100 if (FLAG_debug_code) { |
| 101 for (int i = 0; i < kNumJSCallerSaved; i++) { |
| 102 Register reg = Register::XRegFromCode(JSCallerSavedCode(i)); |
| 103 __ Mov(reg, Operand(kDebugZapValue)); |
| 104 } |
| 105 } |
| 106 |
| 140 // Restore the register values from the expression stack. | 107 // Restore the register values from the expression stack. |
| 141 if (object_regs != 0) { | 108 if (mode == SAVE_RESULT_REGISTER) __ Pop(x0); |
| 142 __ PopXRegList(object_regs); | |
| 143 } | |
| 144 | 109 |
| 145 // Don't bother removing padding bytes pushed on the stack | 110 // Don't bother removing padding bytes pushed on the stack |
| 146 // as the frame is going to be restored right away. | 111 // as the frame is going to be restored right away. |
| 147 | 112 |
| 148 // Leave the internal frame. | 113 // Leave the internal frame. |
| 149 } | 114 } |
| 150 | 115 |
| 151 // Now that the break point has been handled, resume normal execution by | 116 // Now that the break point has been handled, resume normal execution by |
| 152 // jumping to the target address intended by the caller and that was | 117 // jumping to the target address intended by the caller and that was |
| 153 // overwritten by the address of DebugBreakXXX. | 118 // overwritten by the address of DebugBreakXXX. |
| 154 ExternalReference after_break_target = | 119 ExternalReference after_break_target = |
| 155 ExternalReference::debug_after_break_target_address(masm->isolate()); | 120 ExternalReference::debug_after_break_target_address(masm->isolate()); |
| 156 __ Mov(scratch, after_break_target); | 121 __ Mov(scratch, after_break_target); |
| 157 __ Ldr(scratch, MemOperand(scratch)); | 122 __ Ldr(scratch, MemOperand(scratch)); |
| 158 __ Br(scratch); | 123 __ Br(scratch); |
| 159 } | 124 } |
| 160 | 125 |
| 161 | 126 |
| 162 void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) { | |
| 163 // In places other than IC call sites it is expected that r0 is TOS which | |
| 164 // is an object - this is not generally the case so this should be used with | |
| 165 // care. | |
| 166 Generate_DebugBreakCallHelper(masm, x0.Bit()); | |
| 167 } | |
| 168 | |
| 169 | |
| 170 void DebugCodegen::GenerateSlot(MacroAssembler* masm, | |
| 171 DebugCodegen::SlotLocation location, | |
| 172 int call_argc) { | |
| 173 // Generate enough nop's to make space for a call instruction. Avoid emitting | |
| 174 // the constant pool in the debug break slot code. | |
| 175 InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions); | |
| 176 RecordRelocInfo(masm, location, call_argc); | |
| 177 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { | |
| 178 __ nop(Assembler::DEBUG_BREAK_NOP); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 | |
| 183 void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) { | |
| 184 // In the places where a debug break slot is inserted no registers can contain | |
| 185 // object pointers. | |
| 186 Generate_DebugBreakCallHelper(masm, 0); | |
| 187 } | |
| 188 | |
| 189 | |
| 190 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { | 127 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { |
| 191 __ Ret(); | 128 __ Ret(); |
| 192 } | 129 } |
| 193 | 130 |
| 194 | 131 |
| 195 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { | 132 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { |
| 196 ExternalReference restarter_frame_function_slot = | 133 ExternalReference restarter_frame_function_slot = |
| 197 ExternalReference::debug_restarter_frame_function_pointer_address( | 134 ExternalReference::debug_restarter_frame_function_pointer_address( |
| 198 masm->isolate()); | 135 masm->isolate()); |
| 199 UseScratchRegisterScope temps(masm); | 136 UseScratchRegisterScope temps(masm); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 220 __ Br(scratch); | 157 __ Br(scratch); |
| 221 } | 158 } |
| 222 | 159 |
| 223 | 160 |
| 224 const bool LiveEdit::kFrameDropperSupported = true; | 161 const bool LiveEdit::kFrameDropperSupported = true; |
| 225 | 162 |
| 226 } // namespace internal | 163 } // namespace internal |
| 227 } // namespace v8 | 164 } // namespace v8 |
| 228 | 165 |
| 229 #endif // V8_TARGET_ARCH_ARM64 | 166 #endif // V8_TARGET_ARCH_ARM64 |
| OLD | NEW |