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