Line data Source code
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_x64_SharedICHelpers_x64_h
8 : #define jit_x64_SharedICHelpers_x64_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 Stack top to the top Value inside an IC stub (this is the return address).
19 : static const size_t ICStackValueOffset = sizeof(void*);
20 :
21 : inline void
22 848 : EmitRestoreTailCallReg(MacroAssembler& masm)
23 : {
24 848 : masm.Pop(ICTailCallReg);
25 848 : }
26 :
27 : inline void
28 : EmitRepushTailCallReg(MacroAssembler& masm)
29 : {
30 : masm.Push(ICTailCallReg);
31 : }
32 :
33 : inline void
34 19345 : EmitCallIC(CodeOffset* patchOffset, MacroAssembler& masm)
35 : {
36 : // Move ICEntry offset into ICStubReg
37 19345 : CodeOffset offset = masm.movWithPatch(ImmWord(-1), ICStubReg);
38 19345 : *patchOffset = offset;
39 :
40 : // Load stub pointer into ICStubReg
41 38690 : masm.loadPtr(Address(ICStubReg, (int32_t) ICEntry::offsetOfFirstStub()),
42 19345 : ICStubReg);
43 :
44 : // Call the stubcode.
45 19345 : masm.call(Address(ICStubReg, ICStub::offsetOfStubCode()));
46 19345 : }
47 :
48 : inline void
49 409 : EmitEnterTypeMonitorIC(MacroAssembler& masm,
50 : size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
51 : {
52 : // This is expected to be called from within an IC, when ICStubReg
53 : // is properly initialized to point to the stub.
54 409 : masm.loadPtr(Address(ICStubReg, (int32_t) monitorStubOffset), ICStubReg);
55 :
56 : // Jump to the stubcode.
57 409 : masm.jmp(Operand(ICStubReg, (int32_t) ICStub::offsetOfStubCode()));
58 409 : }
59 :
60 : inline void
61 2114 : EmitReturnFromIC(MacroAssembler& masm)
62 : {
63 2114 : masm.ret();
64 2114 : }
65 :
66 : inline void
67 18 : EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
68 : {
69 18 : masm.storePtr(reg, Address(StackPointer, 0));
70 18 : }
71 :
72 : inline void
73 848 : EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
74 : {
75 1696 : ScratchRegisterScope scratch(masm);
76 :
77 : // We an assume during this that R0 and R1 have been pushed.
78 848 : masm.movq(BaselineFrameReg, scratch);
79 848 : masm.addq(Imm32(BaselineFrame::FramePointerOffset), scratch);
80 848 : masm.subq(BaselineStackReg, scratch);
81 :
82 : // Store frame size without VMFunction arguments for GC marking.
83 848 : masm.movq(scratch, rdx);
84 848 : masm.subq(Imm32(argSize), rdx);
85 848 : masm.store32(rdx, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
86 :
87 : // Push frame descriptor and perform the tail call.
88 848 : masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS, ExitFrameLayout::Size());
89 848 : masm.push(scratch);
90 848 : masm.push(ICTailCallReg);
91 848 : masm.jmp(target);
92 848 : }
93 :
94 : inline void
95 0 : EmitIonTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t stackSize)
96 : {
97 : // For tail calls, find the already pushed JitFrame_IonJS signifying the
98 : // end of the Ion frame. Retrieve the length of the frame and repush
99 : // JitFrame_IonJS with the extra stacksize, rendering the original
100 : // JitFrame_IonJS obsolete.
101 :
102 0 : ScratchRegisterScope scratch(masm);
103 :
104 0 : masm.loadPtr(Address(esp, stackSize), scratch);
105 0 : masm.shrq(Imm32(FRAMESIZE_SHIFT), scratch);
106 0 : masm.addq(Imm32(stackSize + JitStubFrameLayout::Size() - sizeof(intptr_t)), scratch);
107 :
108 : // Push frame descriptor and perform the tail call.
109 0 : masm.makeFrameDescriptor(scratch, JitFrame_IonJS, ExitFrameLayout::Size());
110 0 : masm.push(scratch);
111 0 : masm.push(ICTailCallReg);
112 0 : masm.jmp(target);
113 0 : }
114 :
115 : inline void
116 387 : EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg, uint32_t headerSize)
117 : {
118 : // Compute stub frame size. We have to add two pointers: the stub reg and previous
119 : // frame pointer pushed by EmitEnterStubFrame.
120 387 : masm.movq(BaselineFrameReg, reg);
121 387 : masm.addq(Imm32(sizeof(void*) * 2), reg);
122 387 : masm.subq(BaselineStackReg, reg);
123 :
124 387 : masm.makeFrameDescriptor(reg, JitFrame_BaselineStub, headerSize);
125 387 : }
126 :
127 : inline void
128 246 : EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
129 : {
130 492 : ScratchRegisterScope scratch(masm);
131 246 : EmitBaselineCreateStubFrameDescriptor(masm, scratch, ExitFrameLayout::Size());
132 246 : masm.push(scratch);
133 246 : masm.call(target);
134 246 : }
135 :
136 : // Size of vales pushed by EmitEnterStubFrame.
137 : static const uint32_t STUB_FRAME_SIZE = 4 * sizeof(void*);
138 : static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = sizeof(void*);
139 :
140 : inline void
141 385 : EmitBaselineEnterStubFrame(MacroAssembler& masm, Register)
142 : {
143 770 : ScratchRegisterScope scratch(masm);
144 :
145 : // Compute frame size. Because the return address is still on the stack,
146 : // this is:
147 : //
148 : // BaselineFrameReg
149 : // + BaselineFrame::FramePointerOffset
150 : // - BaselineStackReg
151 : // - sizeof(return address)
152 : //
153 : // The two constants cancel each other out, so we can just calculate
154 : // BaselineFrameReg - BaselineStackReg.
155 :
156 : static_assert(BaselineFrame::FramePointerOffset == sizeof(void*),
157 : "FramePointerOffset must be the same as the return address size");
158 :
159 385 : masm.movq(BaselineFrameReg, scratch);
160 385 : masm.subq(BaselineStackReg, scratch);
161 :
162 385 : masm.store32(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
163 :
164 : // Note: when making changes here, don't forget to update STUB_FRAME_SIZE
165 : // if needed.
166 :
167 : // Push the return address that's currently on top of the stack.
168 385 : masm.Push(Operand(BaselineStackReg, 0));
169 :
170 : // Replace the original return address with the frame descriptor.
171 385 : masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS, BaselineStubFrameLayout::Size());
172 385 : masm.storePtr(scratch, Address(BaselineStackReg, sizeof(uintptr_t)));
173 :
174 : // Save old frame pointer, stack pointer and stub reg.
175 385 : masm.Push(ICStubReg);
176 385 : masm.Push(BaselineFrameReg);
177 385 : masm.mov(BaselineStackReg, BaselineFrameReg);
178 385 : }
179 :
180 : inline void
181 665 : EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
182 : {
183 : // Ion frames do not save and restore the frame pointer. If we called
184 : // into Ion, we have to restore the stack pointer from the frame descriptor.
185 : // If we performed a VM call, the descriptor has been popped already so
186 : // in that case we use the frame pointer.
187 665 : if (calledIntoIon) {
188 582 : ScratchRegisterScope scratch(masm);
189 291 : masm.Pop(scratch);
190 291 : masm.shrq(Imm32(FRAMESIZE_SHIFT), scratch);
191 291 : masm.addq(scratch, BaselineStackReg);
192 : } else {
193 374 : masm.mov(BaselineFrameReg, BaselineStackReg);
194 : }
195 :
196 665 : masm.Pop(BaselineFrameReg);
197 665 : masm.Pop(ICStubReg);
198 :
199 : // The return address is on top of the stack, followed by the frame
200 : // descriptor. Use a pop instruction to overwrite the frame descriptor
201 : // with the return address. Note that pop increments the stack pointer
202 : // before computing the address.
203 665 : masm.Pop(Operand(BaselineStackReg, 0));
204 665 : }
205 :
206 : inline void
207 : EmitStowICValues(MacroAssembler& masm, int values)
208 : {
209 : MOZ_ASSERT(values >= 0 && values <= 2);
210 : switch(values) {
211 : case 1:
212 : // Stow R0
213 : masm.pop(ICTailCallReg);
214 : masm.Push(R0);
215 : masm.push(ICTailCallReg);
216 : break;
217 : case 2:
218 : // Stow R0 and R1
219 : masm.pop(ICTailCallReg);
220 : masm.Push(R0);
221 : masm.Push(R1);
222 : masm.push(ICTailCallReg);
223 : break;
224 : }
225 : }
226 :
227 : inline void
228 : EmitUnstowICValues(MacroAssembler& masm, int values, bool discard = false)
229 : {
230 : MOZ_ASSERT(values >= 0 && values <= 2);
231 : switch(values) {
232 : case 1:
233 : // Unstow R0
234 : masm.pop(ICTailCallReg);
235 : if (discard)
236 : masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
237 : else
238 : masm.popValue(R0);
239 : masm.push(ICTailCallReg);
240 : break;
241 : case 2:
242 : // Unstow R0 and R1
243 : masm.pop(ICTailCallReg);
244 : if (discard) {
245 : masm.addPtr(Imm32(sizeof(Value) * 2), BaselineStackReg);
246 : } else {
247 : masm.popValue(R1);
248 : masm.popValue(R0);
249 : }
250 : masm.push(ICTailCallReg);
251 : break;
252 : }
253 : masm.adjustFrame(-values * sizeof(Value));
254 : }
255 :
256 : template <typename AddrType>
257 : inline void
258 56 : EmitPreBarrier(MacroAssembler& masm, const AddrType& addr, MIRType type)
259 : {
260 56 : masm.guardedCallPreBarrier(addr, type);
261 56 : }
262 :
263 : inline void
264 1900 : EmitStubGuardFailure(MacroAssembler& masm)
265 : {
266 : // Load next stub into ICStubReg
267 1900 : masm.loadPtr(Address(ICStubReg, ICStub::offsetOfNext()), ICStubReg);
268 :
269 : // Return address is already loaded, just jump to the next stubcode.
270 1900 : masm.jmp(Operand(ICStubReg, ICStub::offsetOfStubCode()));
271 1900 : }
272 :
273 : } // namespace jit
274 : } // namespace js
275 :
276 : #endif /* jit_x64_SharedICHelpers_x64_h */
|