1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef jit_mips_shared_SharedICHelpers_mips_shared_h
8 #define jit_mips_shared_SharedICHelpers_mips_shared_h
9 
10 #include "jit/BaselineFrame.h"
11 #include "jit/BaselineIC.h"
12 #include "jit/MacroAssembler.h"
13 #include "jit/SharedICRegisters.h"
14 
15 namespace js {
16 namespace jit {
17 
18 // Distance from sp to the top Value inside an IC stub (no return address on
19 // the stack on MIPS).
20 static const size_t ICStackValueOffset = 0;
21 
22 inline void
EmitRestoreTailCallReg(MacroAssembler & masm)23 EmitRestoreTailCallReg(MacroAssembler& masm)
24 {
25     // No-op on MIPS because ra register is always holding the return address.
26 }
27 
28 inline void
EmitRepushTailCallReg(MacroAssembler & masm)29 EmitRepushTailCallReg(MacroAssembler& masm)
30 {
31     // No-op on MIPS because ra register is always holding the return address.
32 }
33 
34 inline void
EmitCallIC(CodeOffset * patchOffset,MacroAssembler & masm)35 EmitCallIC(CodeOffset* patchOffset, MacroAssembler& masm)
36 {
37     // Move ICEntry offset into ICStubReg.
38     CodeOffset offset = masm.movWithPatch(ImmWord(-1), ICStubReg);
39     *patchOffset = offset;
40 
41     // Load stub pointer into ICStubReg.
42     masm.loadPtr(Address(ICStubReg, ICEntry::offsetOfFirstStub()), ICStubReg);
43 
44     // Load stubcode pointer from BaselineStubEntry.
45     // R2 won't be active when we call ICs, so we can use it as scratch.
46     masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
47 
48     // Call the stubcode via a direct jump-and-link
49     masm.call(R2.scratchReg());
50 }
51 
52 inline void
53 EmitEnterTypeMonitorIC(MacroAssembler& masm,
54                        size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
55 {
56     // This is expected to be called from within an IC, when ICStubReg
57     // is properly initialized to point to the stub.
58     masm.loadPtr(Address(ICStubReg, (uint32_t) monitorStubOffset), ICStubReg);
59 
60     // Load stubcode pointer from BaselineStubEntry.
61     // R2 won't be active when we call ICs, so we can use it.
62     masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
63 
64     // Jump to the stubcode.
65     masm.branch(R2.scratchReg());
66 }
67 
68 inline void
EmitReturnFromIC(MacroAssembler & masm)69 EmitReturnFromIC(MacroAssembler& masm)
70 {
71     masm.branch(ra);
72 }
73 
74 inline void
EmitChangeICReturnAddress(MacroAssembler & masm,Register reg)75 EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
76 {
77     masm.movePtr(reg, ra);
78 }
79 
80 inline void
EmitBaselineTailCallVM(JitCode * target,MacroAssembler & masm,uint32_t argSize)81 EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
82 {
83     Register scratch = R2.scratchReg();
84 
85     // Compute frame size.
86     masm.movePtr(BaselineFrameReg, scratch);
87     masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), scratch);
88     masm.subPtr(BaselineStackReg, scratch);
89 
90     // Store frame size without VMFunction arguments for GC marking.
91     masm.subPtr(Imm32(argSize), scratch);
92     masm.store32(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
93     masm.addPtr(Imm32(argSize), scratch);
94 
95     // Push frame descriptor and perform the tail call.
96     // ICTailCallReg (ra) already contains the return address (as we
97     // keep it there through the stub calls), but the VMWrapper code being
98     // called expects the return address to also be pushed on the stack.
99     MOZ_ASSERT(ICTailCallReg == ra);
100     masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS, ExitFrameLayout::Size());
101     masm.subPtr(Imm32(sizeof(CommonFrameLayout)), StackPointer);
102     masm.storePtr(scratch, Address(StackPointer, CommonFrameLayout::offsetOfDescriptor()));
103     masm.storePtr(ra, Address(StackPointer, CommonFrameLayout::offsetOfReturnAddress()));
104 
105     masm.branch(target);
106 }
107 
108 inline void
EmitIonTailCallVM(JitCode * target,MacroAssembler & masm,uint32_t stackSize)109 EmitIonTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t stackSize)
110 {
111     Register scratch = R2.scratchReg();
112 
113     masm.loadPtr(Address(sp, stackSize), scratch);
114     masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch);
115     masm.addPtr(Imm32(stackSize + JitStubFrameLayout::Size() - sizeof(intptr_t)), scratch);
116 
117     // Push frame descriptor and perform the tail call.
118     MOZ_ASSERT(ICTailCallReg == ra);
119     masm.makeFrameDescriptor(scratch, JitFrame_IonJS, ExitFrameLayout::Size());
120     masm.push(scratch);
121     masm.push(ICTailCallReg);
122     masm.branch(target);
123 }
124 
125 inline void
EmitBaselineCreateStubFrameDescriptor(MacroAssembler & masm,Register reg,uint32_t headerSize)126 EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg, uint32_t headerSize)
127 {
128     // Compute stub frame size. We have to add two pointers: the stub reg and
129     // previous frame pointer pushed by EmitEnterStubFrame.
130     masm.movePtr(BaselineFrameReg, reg);
131     masm.addPtr(Imm32(sizeof(intptr_t) * 2), reg);
132     masm.subPtr(BaselineStackReg, reg);
133 
134     masm.makeFrameDescriptor(reg, JitFrame_BaselineStub, headerSize);
135 }
136 
137 inline void
EmitBaselineCallVM(JitCode * target,MacroAssembler & masm)138 EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
139 {
140     Register scratch = R2.scratchReg();
141     EmitBaselineCreateStubFrameDescriptor(masm, scratch, ExitFrameLayout::Size());
142     masm.push(scratch);
143     masm.call(target);
144 }
145 
146 inline void
EmitIonCallVM(JitCode * target,size_t stackSlots,MacroAssembler & masm)147 EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm)
148 {
149     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonStub,
150                                               ExitFrameLayout::Size());
151     masm.Push(Imm32(descriptor));
152     masm.callJit(target);
153 
154     // Remove rest of the frame left on the stack. We remove the return address
155     // which is implicitly popped when returning.
156     size_t framePop = sizeof(ExitFrameLayout) - sizeof(void*);
157 
158     // Pop arguments from framePushed.
159     masm.implicitPop(stackSlots * sizeof(void*) + framePop);
160 }
161 
162 struct BaselineStubFrame {
163     uintptr_t savedFrame;
164     uintptr_t savedStub;
165     uintptr_t returnAddress;
166     uintptr_t descriptor;
167 };
168 
169 static const uint32_t STUB_FRAME_SIZE = sizeof(BaselineStubFrame);
170 static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = offsetof(BaselineStubFrame, savedStub);
171 
172 inline void
EmitBaselineEnterStubFrame(MacroAssembler & masm,Register scratch)173 EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch)
174 {
175     MOZ_ASSERT(scratch != ICTailCallReg);
176 
177     // Compute frame size.
178     masm.movePtr(BaselineFrameReg, scratch);
179     masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), scratch);
180     masm.subPtr(BaselineStackReg, scratch);
181 
182     masm.store32(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
183 
184     // Note: when making changes here, don't forget to update
185     // BaselineStubFrame if needed.
186 
187     // Push frame descriptor and return address.
188     masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS, BaselineStubFrameLayout::Size());
189     masm.subPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
190     masm.storePtr(scratch, Address(StackPointer, offsetof(BaselineStubFrame, descriptor)));
191     masm.storePtr(ICTailCallReg, Address(StackPointer,
192                                                offsetof(BaselineStubFrame, returnAddress)));
193 
194     // Save old frame pointer, stack pointer and stub reg.
195     masm.storePtr(ICStubReg, Address(StackPointer,
196                                            offsetof(BaselineStubFrame, savedStub)));
197     masm.storePtr(BaselineFrameReg, Address(StackPointer,
198                                             offsetof(BaselineStubFrame, savedFrame)));
199     masm.movePtr(BaselineStackReg, BaselineFrameReg);
200 
201     // Stack should remain aligned.
202     masm.assertStackAlignment(sizeof(Value), 0);
203 }
204 
205 inline void
EmitIonEnterStubFrame(MacroAssembler & masm,Register scratch)206 EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
207 {
208     MOZ_ASSERT(ICTailCallReg == ra);
209 
210     // In MIPS the ra register contains the return address,
211     // but in jit frames we expect it to be on the stack. As a result
212     // push the link register (which is actually part of the previous frame.
213     // Therefore using push instead of Push).
214     masm.push(ICTailCallReg);
215 
216     masm.Push(ICStubReg);
217 }
218 
219 inline void
220 EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
221 {
222     // Ion frames do not save and restore the frame pointer. If we called
223     // into Ion, we have to restore the stack pointer from the frame descriptor.
224     // If we performed a VM call, the descriptor has been popped already so
225     // in that case we use the frame pointer.
226     if (calledIntoIon) {
227         masm.pop(ScratchRegister);
228         masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), ScratchRegister);
229         masm.addPtr(ScratchRegister, BaselineStackReg);
230     } else {
231         masm.movePtr(BaselineFrameReg, BaselineStackReg);
232     }
233 
234     masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedFrame)),
235                  BaselineFrameReg);
236     masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedStub)),
237                  ICStubReg);
238 
239     // Load the return address.
240     masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, returnAddress)),
241                  ICTailCallReg);
242 
243     // Discard the frame descriptor.
244     masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, descriptor)), ScratchRegister);
245     masm.addPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
246 }
247 
248 inline void
EmitIonLeaveStubFrame(MacroAssembler & masm)249 EmitIonLeaveStubFrame(MacroAssembler& masm)
250 {
251     masm.Pop(ICStubReg);
252     masm.pop(ICTailCallReg); // See EmitIonEnterStubFrame for explanation on pop/Pop.
253 }
254 
255 inline void
EmitStowICValues(MacroAssembler & masm,int values)256 EmitStowICValues(MacroAssembler& masm, int values)
257 {
258     MOZ_ASSERT(values >= 0 && values <= 2);
259     switch(values) {
260       case 1:
261         // Stow R0
262         masm.Push(R0);
263         break;
264       case 2:
265         // Stow R0 and R1
266         masm.Push(R0);
267         masm.Push(R1);
268     }
269 }
270 
271 inline void
272 EmitUnstowICValues(MacroAssembler& masm, int values, bool discard = false)
273 {
274     MOZ_ASSERT(values >= 0 && values <= 2);
275     switch(values) {
276       case 1:
277         // Unstow R0.
278         if (discard)
279             masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
280         else
281             masm.popValue(R0);
282         break;
283       case 2:
284         // Unstow R0 and R1.
285         if (discard) {
286             masm.addPtr(Imm32(sizeof(Value) * 2), BaselineStackReg);
287         } else {
288             masm.popValue(R1);
289             masm.popValue(R0);
290         }
291         break;
292     }
293     masm.adjustFrame(-values * sizeof(Value));
294 }
295 
296 inline void
EmitCallTypeUpdateIC(MacroAssembler & masm,JitCode * code,uint32_t objectOffset)297 EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
298 {
299     // R0 contains the value that needs to be typechecked.
300     // The object we're updating is a boxed Value on the stack, at offset
301     // objectOffset from $sp, excluding the return address.
302 
303     // Save the current ICStubReg to stack, as well as the TailCallReg,
304     // since on mips, the $ra is live.
305     masm.subPtr(Imm32(2 * sizeof(intptr_t)), StackPointer);
306     masm.storePtr(ICStubReg, Address(StackPointer, sizeof(intptr_t)));
307     masm.storePtr(ICTailCallReg, Address(StackPointer, 0));
308 
309     // This is expected to be called from within an IC, when ICStubReg
310     // is properly initialized to point to the stub.
311     masm.loadPtr(Address(ICStubReg, ICUpdatedStub::offsetOfFirstUpdateStub()),
312                  ICStubReg);
313 
314     // Load stubcode pointer from ICStubReg into ICTailCallReg.
315     masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
316 
317     // Call the stubcode.
318     masm.call(R2.scratchReg());
319 
320     // Restore the old stub reg and tailcall reg.
321     masm.loadPtr(Address(StackPointer, 0), ICTailCallReg);
322     masm.loadPtr(Address(StackPointer, sizeof(intptr_t)), ICStubReg);
323     masm.addPtr(Imm32(2 * sizeof(intptr_t)), StackPointer);
324 
325     // The update IC will store 0 or 1 in R1.scratchReg() reflecting if the
326     // value in R0 type-checked properly or not.
327     Label success;
328     masm.ma_b(R1.scratchReg(), Imm32(1), &success, Assembler::Equal, ShortJump);
329 
330     // If the IC failed, then call the update fallback function.
331     EmitBaselineEnterStubFrame(masm, R1.scratchReg());
332 
333     masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1);
334 
335     masm.Push(R0);
336     masm.Push(R1);
337     masm.Push(ICStubReg);
338 
339     // Load previous frame pointer, push BaselineFrame*.
340     masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg());
341     masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
342 
343     EmitBaselineCallVM(code, masm);
344     EmitBaselineLeaveStubFrame(masm);
345 
346     // Success at end.
347     masm.bind(&success);
348 }
349 
350 template <typename AddrType>
351 inline void
EmitPreBarrier(MacroAssembler & masm,const AddrType & addr,MIRType type)352 EmitPreBarrier(MacroAssembler& masm, const AddrType& addr, MIRType type)
353 {
354     // On MIPS, $ra is clobbered by patchableCallPreBarrier. Save it first.
355     masm.push(ra);
356     masm.patchableCallPreBarrier(addr, type);
357     masm.pop(ra);
358 }
359 
360 inline void
EmitStubGuardFailure(MacroAssembler & masm)361 EmitStubGuardFailure(MacroAssembler& masm)
362 {
363     // NOTE: This routine assumes that the stub guard code left the stack in
364     // the same state it was in when it was entered.
365 
366     // BaselineStubEntry points to the current stub.
367 
368     // Load next stub into ICStubReg
369     masm.loadPtr(Address(ICStubReg, ICStub::offsetOfNext()), ICStubReg);
370 
371     // Load stubcode pointer from BaselineStubEntry into scratch register.
372     masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
373 
374     // Return address is already loaded, just jump to the next stubcode.
375     MOZ_ASSERT(ICTailCallReg == ra);
376     masm.branch(R2.scratchReg());
377 }
378 
379 } // namespace jit
380 } // namespace js
381 
382 #endif /* jit_mips_shared_SharedICHelpers_mips_shared_h */
383