1 // Copyright 2011 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29
30 #include "v8.h"
31
32 #if defined(V8_TARGET_ARCH_MIPS)
33
34 #include "codegen.h"
35 #include "debug.h"
36
37 namespace v8 {
38 namespace internal {
39
40 #ifdef ENABLE_DEBUGGER_SUPPORT
41
IsDebugBreakAtReturn()42 bool BreakLocationIterator::IsDebugBreakAtReturn() {
43 return Debug::IsDebugBreakAtReturn(rinfo());
44 }
45
46
SetDebugBreakAtReturn()47 void BreakLocationIterator::SetDebugBreakAtReturn() {
48 // Mips return sequence:
49 // mov sp, fp
50 // lw fp, sp(0)
51 // lw ra, sp(4)
52 // addiu sp, sp, 8
53 // addiu sp, sp, N
54 // jr ra
55 // nop (in branch delay slot)
56
57 // Make sure this constant matches the number if instrucntions we emit.
58 ASSERT(Assembler::kJSReturnSequenceInstructions == 7);
59 CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
60 // li and Call pseudo-instructions emit two instructions each.
61 patcher.masm()->li(v8::internal::t9,
62 Operand(reinterpret_cast<int32_t>(
63 Isolate::Current()->debug()->debug_break_return()->entry())));
64 patcher.masm()->Call(v8::internal::t9);
65 patcher.masm()->nop();
66 patcher.masm()->nop();
67 patcher.masm()->nop();
68
69 // TODO(mips): Open issue about using breakpoint instruction instead of nops.
70 // patcher.masm()->bkpt(0);
71 }
72
73
74 // Restore the JS frame exit code.
ClearDebugBreakAtReturn()75 void BreakLocationIterator::ClearDebugBreakAtReturn() {
76 rinfo()->PatchCode(original_rinfo()->pc(),
77 Assembler::kJSReturnSequenceInstructions);
78 }
79
80
81 // A debug break in the exit code is identified by the JS frame exit code
82 // having been patched with li/call psuedo-instrunction (liu/ori/jalr).
IsDebugBreakAtReturn(RelocInfo * rinfo)83 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
84 ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
85 return rinfo->IsPatchedReturnSequence();
86 }
87
88
IsDebugBreakAtSlot()89 bool BreakLocationIterator::IsDebugBreakAtSlot() {
90 ASSERT(IsDebugBreakSlot());
91 // Check whether the debug break slot instructions have been patched.
92 return rinfo()->IsPatchedDebugBreakSlotSequence();
93 }
94
95
SetDebugBreakAtSlot()96 void BreakLocationIterator::SetDebugBreakAtSlot() {
97 ASSERT(IsDebugBreakSlot());
98 // Patch the code changing the debug break slot code from:
99 // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
100 // nop(DEBUG_BREAK_NOP)
101 // nop(DEBUG_BREAK_NOP)
102 // nop(DEBUG_BREAK_NOP)
103 // to a call to the debug break slot code.
104 // li t9, address (lui t9 / ori t9 instruction pair)
105 // call t9 (jalr t9 / nop instruction pair)
106 CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
107 patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>(
108 Isolate::Current()->debug()->debug_break_slot()->entry())));
109 patcher.masm()->Call(v8::internal::t9);
110 }
111
112
ClearDebugBreakAtSlot()113 void BreakLocationIterator::ClearDebugBreakAtSlot() {
114 ASSERT(IsDebugBreakSlot());
115 rinfo()->PatchCode(original_rinfo()->pc(),
116 Assembler::kDebugBreakSlotInstructions);
117 }
118
119
120 #define __ ACCESS_MASM(masm)
121
122
123
Generate_DebugBreakCallHelper(MacroAssembler * masm,RegList object_regs,RegList non_object_regs)124 static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
125 RegList object_regs,
126 RegList non_object_regs) {
127 {
128 FrameScope scope(masm, StackFrame::INTERNAL);
129
130 // Store the registers containing live values on the expression stack to
131 // make sure that these are correctly updated during GC. Non object values
132 // are stored as a smi causing it to be untouched by GC.
133 ASSERT((object_regs & ~kJSCallerSaved) == 0);
134 ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
135 ASSERT((object_regs & non_object_regs) == 0);
136 if ((object_regs | non_object_regs) != 0) {
137 for (int i = 0; i < kNumJSCallerSaved; i++) {
138 int r = JSCallerSavedCode(i);
139 Register reg = { r };
140 if ((non_object_regs & (1 << r)) != 0) {
141 if (FLAG_debug_code) {
142 __ And(at, reg, 0xc0000000);
143 __ Assert(
144 eq, "Unable to encode value as smi", at, Operand(zero_reg));
145 }
146 __ sll(reg, reg, kSmiTagSize);
147 }
148 }
149 __ MultiPush(object_regs | non_object_regs);
150 }
151
152 #ifdef DEBUG
153 __ RecordComment("// Calling from debug break to runtime - come in - over");
154 #endif
155 __ PrepareCEntryArgs(0); // No arguments.
156 __ PrepareCEntryFunction(ExternalReference::debug_break(masm->isolate()));
157
158 CEntryStub ceb(1);
159 __ CallStub(&ceb);
160
161 // Restore the register values from the expression stack.
162 if ((object_regs | non_object_regs) != 0) {
163 __ MultiPop(object_regs | non_object_regs);
164 for (int i = 0; i < kNumJSCallerSaved; i++) {
165 int r = JSCallerSavedCode(i);
166 Register reg = { r };
167 if ((non_object_regs & (1 << r)) != 0) {
168 __ srl(reg, reg, kSmiTagSize);
169 }
170 if (FLAG_debug_code &&
171 (((object_regs |non_object_regs) & (1 << r)) == 0)) {
172 __ li(reg, kDebugZapValue);
173 }
174 }
175 }
176
177 // Leave the internal frame.
178 }
179
180 // Now that the break point has been handled, resume normal execution by
181 // jumping to the target address intended by the caller and that was
182 // overwritten by the address of DebugBreakXXX.
183 __ li(t9, Operand(
184 ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate())));
185 __ lw(t9, MemOperand(t9));
186 __ Jump(t9);
187 }
188
189
GenerateLoadICDebugBreak(MacroAssembler * masm)190 void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
191 // Calling convention for IC load (from ic-mips.cc).
192 // ----------- S t a t e -------------
193 // -- a2 : name
194 // -- ra : return address
195 // -- a0 : receiver
196 // -- [sp] : receiver
197 // -----------------------------------
198 // Registers a0 and a2 contain objects that need to be pushed on the
199 // expression stack of the fake JS frame.
200 Generate_DebugBreakCallHelper(masm, a0.bit() | a2.bit(), 0);
201 }
202
203
GenerateStoreICDebugBreak(MacroAssembler * masm)204 void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
205 // Calling convention for IC store (from ic-mips.cc).
206 // ----------- S t a t e -------------
207 // -- a0 : value
208 // -- a1 : receiver
209 // -- a2 : name
210 // -- ra : return address
211 // -----------------------------------
212 // Registers a0, a1, and a2 contain objects that need to be pushed on the
213 // expression stack of the fake JS frame.
214 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0);
215 }
216
217
GenerateKeyedLoadICDebugBreak(MacroAssembler * masm)218 void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
219 // ---------- S t a t e --------------
220 // -- ra : return address
221 // -- a0 : key
222 // -- a1 : receiver
223 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit(), 0);
224 }
225
226
GenerateKeyedStoreICDebugBreak(MacroAssembler * masm)227 void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
228 // ---------- S t a t e --------------
229 // -- a0 : value
230 // -- a1 : key
231 // -- a2 : receiver
232 // -- ra : return address
233 Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0);
234 }
235
236
GenerateCallICDebugBreak(MacroAssembler * masm)237 void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
238 // Calling convention for IC call (from ic-mips.cc).
239 // ----------- S t a t e -------------
240 // -- a2: name
241 // -----------------------------------
242 Generate_DebugBreakCallHelper(masm, a2.bit(), 0);
243 }
244
245
GenerateReturnDebugBreak(MacroAssembler * masm)246 void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
247 // In places other than IC call sites it is expected that v0 is TOS which
248 // is an object - this is not generally the case so this should be used with
249 // care.
250 Generate_DebugBreakCallHelper(masm, v0.bit(), 0);
251 }
252
253
GenerateCallFunctionStubDebugBreak(MacroAssembler * masm)254 void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
255 // Register state for CallFunctionStub (from code-stubs-mips.cc).
256 // ----------- S t a t e -------------
257 // -- a1 : function
258 // -----------------------------------
259 Generate_DebugBreakCallHelper(masm, a1.bit(), 0);
260 }
261
262
GenerateCallFunctionStubRecordDebugBreak(MacroAssembler * masm)263 void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) {
264 // Register state for CallFunctionStub (from code-stubs-mips.cc).
265 // ----------- S t a t e -------------
266 // -- a1 : function
267 // -- a2 : cache cell for call target
268 // -----------------------------------
269 Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit(), 0);
270 }
271
272
GenerateCallConstructStubDebugBreak(MacroAssembler * masm)273 void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
274 // Calling convention for CallConstructStub (from code-stubs-mips.cc).
275 // ----------- S t a t e -------------
276 // -- a0 : number of arguments (not smi)
277 // -- a1 : constructor function
278 // -----------------------------------
279 Generate_DebugBreakCallHelper(masm, a1.bit() , a0.bit());
280 }
281
282
GenerateCallConstructStubRecordDebugBreak(MacroAssembler * masm)283 void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) {
284 // Calling convention for CallConstructStub (from code-stubs-mips.cc).
285 // ----------- S t a t e -------------
286 // -- a0 : number of arguments (not smi)
287 // -- a1 : constructor function
288 // -- a2 : cache cell for call target
289 // -----------------------------------
290 Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit(), a0.bit());
291 }
292
293
GenerateSlot(MacroAssembler * masm)294 void Debug::GenerateSlot(MacroAssembler* masm) {
295 // Generate enough nop's to make space for a call instruction. Avoid emitting
296 // the trampoline pool in the debug break slot code.
297 Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
298 Label check_codesize;
299 __ bind(&check_codesize);
300 __ RecordDebugBreakSlot();
301 for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
302 __ nop(MacroAssembler::DEBUG_BREAK_NOP);
303 }
304 ASSERT_EQ(Assembler::kDebugBreakSlotInstructions,
305 masm->InstructionsGeneratedSince(&check_codesize));
306 }
307
308
GenerateSlotDebugBreak(MacroAssembler * masm)309 void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
310 // In the places where a debug break slot is inserted no registers can contain
311 // object pointers.
312 Generate_DebugBreakCallHelper(masm, 0, 0);
313 }
314
315
GeneratePlainReturnLiveEdit(MacroAssembler * masm)316 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
317 masm->Abort("LiveEdit frame dropping is not supported on mips");
318 }
319
320
GenerateFrameDropperLiveEdit(MacroAssembler * masm)321 void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
322 masm->Abort("LiveEdit frame dropping is not supported on mips");
323 }
324
325
326 const bool Debug::kFrameDropperSupported = false;
327
328 #undef __
329
330
331 #endif // ENABLE_DEBUGGER_SUPPORT
332
333 } } // namespace v8::internal
334
335 #endif // V8_TARGET_ARCH_MIPS
336