1 // Copyright 2012 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 
6 
7 #include "src/v8.h"
8 
9 #if V8_TARGET_ARCH_MIPS64
10 
11 #include "src/codegen.h"
12 #include "src/debug.h"
13 
14 namespace v8 {
15 namespace internal {
16 
IsDebugBreakAtReturn()17 bool BreakLocationIterator::IsDebugBreakAtReturn() {
18   return Debug::IsDebugBreakAtReturn(rinfo());
19 }
20 
21 
SetDebugBreakAtReturn()22 void BreakLocationIterator::SetDebugBreakAtReturn() {
23   // Mips return sequence:
24   // mov sp, fp
25   // lw fp, sp(0)
26   // lw ra, sp(4)
27   // addiu sp, sp, 8
28   // addiu sp, sp, N
29   // jr ra
30   // nop (in branch delay slot)
31 
32   // Make sure this constant matches the number if instructions we emit.
33   DCHECK(Assembler::kJSReturnSequenceInstructions == 7);
34   CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
35   // li and Call pseudo-instructions emit 6 + 2 instructions.
36   patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int64_t>(
37       debug_info_->GetIsolate()->builtins()->Return_DebugBreak()->entry())),
38       ADDRESS_LOAD);
39   patcher.masm()->Call(v8::internal::t9);
40   // Place nop to match return sequence size.
41   patcher.masm()->nop();
42   // TODO(mips): Open issue about using breakpoint instruction instead of nops.
43   // patcher.masm()->bkpt(0);
44 }
45 
46 
47 // Restore the JS frame exit code.
ClearDebugBreakAtReturn()48 void BreakLocationIterator::ClearDebugBreakAtReturn() {
49   rinfo()->PatchCode(original_rinfo()->pc(),
50                      Assembler::kJSReturnSequenceInstructions);
51 }
52 
53 
54 // A debug break in the exit code is identified by the JS frame exit code
55 // having been patched with li/call psuedo-instrunction (liu/ori/jalr).
IsDebugBreakAtReturn(RelocInfo * rinfo)56 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
57   DCHECK(RelocInfo::IsJSReturn(rinfo->rmode()));
58   return rinfo->IsPatchedReturnSequence();
59 }
60 
61 
IsDebugBreakAtSlot()62 bool BreakLocationIterator::IsDebugBreakAtSlot() {
63   DCHECK(IsDebugBreakSlot());
64   // Check whether the debug break slot instructions have been patched.
65   return rinfo()->IsPatchedDebugBreakSlotSequence();
66 }
67 
68 
SetDebugBreakAtSlot()69 void BreakLocationIterator::SetDebugBreakAtSlot() {
70   DCHECK(IsDebugBreakSlot());
71   // Patch the code changing the debug break slot code from:
72   //   nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
73   //   nop(DEBUG_BREAK_NOP)
74   //   nop(DEBUG_BREAK_NOP)
75   //   nop(DEBUG_BREAK_NOP)
76   //   nop(DEBUG_BREAK_NOP)
77   //   nop(DEBUG_BREAK_NOP)
78   // to a call to the debug break slot code.
79   //   li t9, address   (4-instruction sequence on mips64)
80   //   call t9          (jalr t9 / nop instruction pair)
81   CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
82   patcher.masm()->li(v8::internal::t9,
83       Operand(reinterpret_cast<int64_t>(
84           debug_info_->GetIsolate()->builtins()->Slot_DebugBreak()->entry())),
85       ADDRESS_LOAD);
86   patcher.masm()->Call(v8::internal::t9);
87 }
88 
89 
ClearDebugBreakAtSlot()90 void BreakLocationIterator::ClearDebugBreakAtSlot() {
91   DCHECK(IsDebugBreakSlot());
92   rinfo()->PatchCode(original_rinfo()->pc(),
93                      Assembler::kDebugBreakSlotInstructions);
94 }
95 
96 
97 #define __ ACCESS_MASM(masm)
98 
99 
100 
Generate_DebugBreakCallHelper(MacroAssembler * masm,RegList object_regs,RegList non_object_regs)101 static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
102                                           RegList object_regs,
103                                           RegList non_object_regs) {
104   {
105     FrameScope scope(masm, StackFrame::INTERNAL);
106 
107     // Load padding words on stack.
108     __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingValue)));
109     __ Dsubu(sp, sp,
110             Operand(kPointerSize * LiveEdit::kFramePaddingInitialSize));
111     for (int i = LiveEdit::kFramePaddingInitialSize - 1; i >= 0; i--) {
112       __ sd(at, MemOperand(sp, kPointerSize * i));
113     }
114     __ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
115     __ push(at);
116 
117 
118     // TODO(plind): This needs to be revised to store pairs of smi's per
119     //    the other 64-bit arch's.
120 
121     // Store the registers containing live values on the expression stack to
122     // make sure that these are correctly updated during GC. Non object values
123     // are stored as a smi causing it to be untouched by GC.
124     DCHECK((object_regs & ~kJSCallerSaved) == 0);
125     DCHECK((non_object_regs & ~kJSCallerSaved) == 0);
126     DCHECK((object_regs & non_object_regs) == 0);
127     for (int i = 0; i < kNumJSCallerSaved; i++) {
128       int r = JSCallerSavedCode(i);
129       Register reg = { r };
130       if ((object_regs & (1 << r)) != 0) {
131         __ push(reg);
132       }
133       if ((non_object_regs & (1 << r)) != 0) {
134         __ PushRegisterAsTwoSmis(reg);
135       }
136     }
137 
138 #ifdef DEBUG
139     __ RecordComment("// Calling from debug break to runtime - come in - over");
140 #endif
141     __ PrepareCEntryArgs(0);  // No arguments.
142     __ PrepareCEntryFunction(ExternalReference::debug_break(masm->isolate()));
143 
144     CEntryStub ceb(masm->isolate(), 1);
145     __ CallStub(&ceb);
146 
147     // Restore the register values from the expression stack.
148     for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
149       int r = JSCallerSavedCode(i);
150       Register reg = { r };
151       if ((non_object_regs & (1 << r)) != 0) {
152         __ PopRegisterAsTwoSmis(reg, at);
153       }
154       if ((object_regs & (1 << r)) != 0) {
155         __ pop(reg);
156       }
157       if (FLAG_debug_code &&
158           (((object_regs |non_object_regs) & (1 << r)) == 0)) {
159         __ li(reg, kDebugZapValue);
160       }
161     }
162 
163     // Don't bother removing padding bytes pushed on the stack
164     // as the frame is going to be restored right away.
165 
166     // Leave the internal frame.
167   }
168 
169   // Now that the break point has been handled, resume normal execution by
170   // jumping to the target address intended by the caller and that was
171   // overwritten by the address of DebugBreakXXX.
172   ExternalReference after_break_target =
173       ExternalReference::debug_after_break_target_address(masm->isolate());
174   __ li(t9, Operand(after_break_target));
175   __ ld(t9, MemOperand(t9));
176   __ Jump(t9);
177 }
178 
179 
GenerateCallICStubDebugBreak(MacroAssembler * masm)180 void DebugCodegen::GenerateCallICStubDebugBreak(MacroAssembler* masm) {
181   // Register state for CallICStub
182   // ----------- S t a t e -------------
183   //  -- a1 : function
184   //  -- a3 : slot in feedback array (smi)
185   // -----------------------------------
186   Generate_DebugBreakCallHelper(masm, a1.bit() | a3.bit(), 0);
187 }
188 
189 
GenerateLoadICDebugBreak(MacroAssembler * masm)190 void DebugCodegen::GenerateLoadICDebugBreak(MacroAssembler* masm) {
191   Register receiver = LoadDescriptor::ReceiverRegister();
192   Register name = LoadDescriptor::NameRegister();
193   Generate_DebugBreakCallHelper(masm, receiver.bit() | name.bit(), 0);
194 }
195 
196 
GenerateStoreICDebugBreak(MacroAssembler * masm)197 void DebugCodegen::GenerateStoreICDebugBreak(MacroAssembler* masm) {
198   Register receiver = StoreDescriptor::ReceiverRegister();
199   Register name = StoreDescriptor::NameRegister();
200   Register value = StoreDescriptor::ValueRegister();
201   Generate_DebugBreakCallHelper(
202       masm, receiver.bit() | name.bit() | value.bit(), 0);
203 }
204 
205 
GenerateKeyedLoadICDebugBreak(MacroAssembler * masm)206 void DebugCodegen::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
207   // Calling convention for keyed IC load (from ic-mips64.cc).
208   GenerateLoadICDebugBreak(masm);
209 }
210 
211 
GenerateKeyedStoreICDebugBreak(MacroAssembler * masm)212 void DebugCodegen::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
213   // Calling convention for IC keyed store call (from ic-mips64.cc).
214   Register receiver = StoreDescriptor::ReceiverRegister();
215   Register name = StoreDescriptor::NameRegister();
216   Register value = StoreDescriptor::ValueRegister();
217   Generate_DebugBreakCallHelper(
218       masm, receiver.bit() | name.bit() | value.bit(), 0);
219 }
220 
221 
GenerateCompareNilICDebugBreak(MacroAssembler * masm)222 void DebugCodegen::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
223   // Register state for CompareNil IC
224   // ----------- S t a t e -------------
225   //  -- a0    : value
226   // -----------------------------------
227   Generate_DebugBreakCallHelper(masm, a0.bit(), 0);
228 }
229 
230 
GenerateReturnDebugBreak(MacroAssembler * masm)231 void DebugCodegen::GenerateReturnDebugBreak(MacroAssembler* masm) {
232   // In places other than IC call sites it is expected that v0 is TOS which
233   // is an object - this is not generally the case so this should be used with
234   // care.
235   Generate_DebugBreakCallHelper(masm, v0.bit(), 0);
236 }
237 
238 
GenerateCallFunctionStubDebugBreak(MacroAssembler * masm)239 void DebugCodegen::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
240   // Register state for CallFunctionStub (from code-stubs-mips.cc).
241   // ----------- S t a t e -------------
242   //  -- a1 : function
243   // -----------------------------------
244   Generate_DebugBreakCallHelper(masm, a1.bit(), 0);
245 }
246 
247 
GenerateCallConstructStubDebugBreak(MacroAssembler * masm)248 void DebugCodegen::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
249   // Calling convention for CallConstructStub (from code-stubs-mips.cc).
250   // ----------- S t a t e -------------
251   //  -- a0     : number of arguments (not smi)
252   //  -- a1     : constructor function
253   // -----------------------------------
254   Generate_DebugBreakCallHelper(masm, a1.bit() , a0.bit());
255 }
256 
257 
258 
GenerateCallConstructStubRecordDebugBreak(MacroAssembler * masm)259 void DebugCodegen::GenerateCallConstructStubRecordDebugBreak(
260     MacroAssembler* masm) {
261   // Calling convention for CallConstructStub (from code-stubs-mips.cc).
262   // ----------- S t a t e -------------
263   //  -- a0     : number of arguments (not smi)
264   //  -- a1     : constructor function
265   //  -- a2     : feedback array
266   //  -- a3     : feedback slot (smi)
267   // -----------------------------------
268   Generate_DebugBreakCallHelper(masm, a1.bit() | a2.bit() | a3.bit(), a0.bit());
269 }
270 
271 
GenerateSlot(MacroAssembler * masm)272 void DebugCodegen::GenerateSlot(MacroAssembler* masm) {
273   // Generate enough nop's to make space for a call instruction. Avoid emitting
274   // the trampoline pool in the debug break slot code.
275   Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
276   Label check_codesize;
277   __ bind(&check_codesize);
278   __ RecordDebugBreakSlot();
279   for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
280     __ nop(MacroAssembler::DEBUG_BREAK_NOP);
281   }
282   DCHECK_EQ(Assembler::kDebugBreakSlotInstructions,
283             masm->InstructionsGeneratedSince(&check_codesize));
284 }
285 
286 
GenerateSlotDebugBreak(MacroAssembler * masm)287 void DebugCodegen::GenerateSlotDebugBreak(MacroAssembler* masm) {
288   // In the places where a debug break slot is inserted no registers can contain
289   // object pointers.
290   Generate_DebugBreakCallHelper(masm, 0, 0);
291 }
292 
293 
GeneratePlainReturnLiveEdit(MacroAssembler * masm)294 void DebugCodegen::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
295   __ Ret();
296 }
297 
298 
GenerateFrameDropperLiveEdit(MacroAssembler * masm)299 void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
300   ExternalReference restarter_frame_function_slot =
301       ExternalReference::debug_restarter_frame_function_pointer_address(
302           masm->isolate());
303   __ li(at, Operand(restarter_frame_function_slot));
304   __ sw(zero_reg, MemOperand(at, 0));
305 
306   // We do not know our frame height, but set sp based on fp.
307   __ Dsubu(sp, fp, Operand(kPointerSize));
308 
309   __ Pop(ra, fp, a1);  // Return address, Frame, Function.
310 
311   // Load context from the function.
312   __ ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
313 
314   // Get function code.
315   __ ld(at, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
316   __ ld(at, FieldMemOperand(at, SharedFunctionInfo::kCodeOffset));
317   __ Daddu(t9, at, Operand(Code::kHeaderSize - kHeapObjectTag));
318 
319   // Re-run JSFunction, a1 is function, cp is context.
320   __ Jump(t9);
321 }
322 
323 
324 const bool LiveEdit::kFrameDropperSupported = true;
325 
326 #undef __
327 
328 } }  // namespace v8::internal
329 
330 #endif  // V8_TARGET_ARCH_MIPS64
331