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 |