1/*
2 * Copyright (C) 2015 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CallFrameShuffler.h"
28
29#if ENABLE(JIT) && USE(JSVALUE32_64)
30
31#include "CCallHelpers.h"
32#include "DataFormat.h"
33#include "JSCInlines.h"
34
35namespace JSC {
36
37DataFormat CallFrameShuffler::emitStore(CachedRecovery& location, MacroAssembler::Address address)
38{
39 ASSERT(!location.recovery().isInJSStack());
40
41 switch (location.recovery().technique()) {
42 case UnboxedInt32InGPR:
43 m_jit.store32(MacroAssembler::TrustedImm32(JSValue::Int32Tag),
44 address.withOffset(TagOffset));
45 m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset));
46 return DataFormatInt32;
47 case UnboxedCellInGPR:
48 m_jit.store32(MacroAssembler::TrustedImm32(JSValue::CellTag),
49 address.withOffset(TagOffset));
50 m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset));
51 return DataFormatCell;
52 case Constant:
53 m_jit.storeTrustedValue(location.recovery().constant(), address);
54 return DataFormatJS;
55 case InPair:
56 m_jit.storeValue(location.recovery().jsValueRegs(), address);
57 return DataFormatJS;
58 case UnboxedBooleanInGPR:
59 m_jit.store32(MacroAssembler::TrustedImm32(JSValue::BooleanTag),
60 address.withOffset(TagOffset));
61 m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset));
62 return DataFormatBoolean;
63 case InFPR:
64 case UnboxedDoubleInFPR:
65 m_jit.storeDouble(location.recovery().fpr(), address);
66 return DataFormatJS;
67 default:
68 RELEASE_ASSERT_NOT_REACHED();
69 }
70}
71
72void CallFrameShuffler::emitBox(CachedRecovery& location)
73{
74 // Nothing to do, we're good! JSValues and doubles can be stored
75 // immediately, and other formats don't need any transformation -
76 // just storing a constant tag separately.
77 ASSERT_UNUSED(location, canBox(location));
78}
79
80void CallFrameShuffler::emitLoad(CachedRecovery& location)
81{
82 if (!location.recovery().isInJSStack())
83 return;
84
85 if (verbose)
86 dataLog(" * Loading ", location.recovery(), " into ");
87 VirtualRegister reg { location.recovery().virtualRegister() };
88 MacroAssembler::Address address { addressForOld(reg) };
89
90 bool tryFPR { true };
91 JSValueRegs wantedJSValueRegs { location.wantedJSValueRegs() };
92 if (wantedJSValueRegs) {
93 if (wantedJSValueRegs.payloadGPR() != InvalidGPRReg
94 && !m_registers[wantedJSValueRegs.payloadGPR()]
95 && !m_lockedRegisters.get(wantedJSValueRegs.payloadGPR()))
96 tryFPR = false;
97 if (wantedJSValueRegs.tagGPR() != InvalidGPRReg
98 && !m_registers[wantedJSValueRegs.tagGPR()]
99 && !m_lockedRegisters.get(wantedJSValueRegs.tagGPR()))
100 tryFPR = false;
101 }
102
103 if (tryFPR && location.loadsIntoFPR()) {
104 FPRReg resultFPR = location.wantedFPR();
105 if (resultFPR == InvalidFPRReg || m_registers[resultFPR] || m_lockedRegisters.get(resultFPR))
106 resultFPR = getFreeFPR();
107 if (resultFPR != InvalidFPRReg) {
108 m_jit.loadDouble(address, resultFPR);
109 DataFormat dataFormat = DataFormatJS;
110 if (location.recovery().dataFormat() == DataFormatDouble)
111 dataFormat = DataFormatDouble;
112 updateRecovery(location,
113 ValueRecovery::inFPR(resultFPR, dataFormat));
114 if (verbose)
115 dataLog(location.recovery(), "\n");
116 if (reg == newAsOld(dangerFrontier()))
117 updateDangerFrontier();
118 return;
119 }
120 }
121
122 if (location.loadsIntoGPR()) {
123 GPRReg resultGPR { wantedJSValueRegs.payloadGPR() };
124 if (resultGPR == InvalidGPRReg || m_registers[resultGPR] || m_lockedRegisters.get(resultGPR))
125 resultGPR = getFreeGPR();
126 ASSERT(resultGPR != InvalidGPRReg);
127 m_jit.loadPtr(address.withOffset(PayloadOffset), resultGPR);
128 updateRecovery(location,
129 ValueRecovery::inGPR(resultGPR, location.recovery().dataFormat()));
130 if (verbose)
131 dataLog(location.recovery(), "\n");
132 if (reg == newAsOld(dangerFrontier()))
133 updateDangerFrontier();
134 return;
135 }
136
137 ASSERT(location.recovery().technique() == DisplacedInJSStack);
138 GPRReg payloadGPR { wantedJSValueRegs.payloadGPR() };
139 GPRReg tagGPR { wantedJSValueRegs.tagGPR() };
140 if (payloadGPR == InvalidGPRReg || m_registers[payloadGPR] || m_lockedRegisters.get(payloadGPR))
141 payloadGPR = getFreeGPR();
142 m_lockedRegisters.set(payloadGPR);
143 if (tagGPR == InvalidGPRReg || m_registers[tagGPR] || m_lockedRegisters.get(tagGPR))
144 tagGPR = getFreeGPR();
145 m_lockedRegisters.clear(payloadGPR);
146 ASSERT(payloadGPR != InvalidGPRReg && tagGPR != InvalidGPRReg && tagGPR != payloadGPR);
147 m_jit.loadPtr(address.withOffset(PayloadOffset), payloadGPR);
148 m_jit.loadPtr(address.withOffset(TagOffset), tagGPR);
149 updateRecovery(location,
150 ValueRecovery::inPair(tagGPR, payloadGPR));
151 if (verbose)
152 dataLog(location.recovery(), "\n");
153 if (reg == newAsOld(dangerFrontier()))
154 updateDangerFrontier();
155}
156
157bool CallFrameShuffler::canLoad(CachedRecovery& location)
158{
159 if (!location.recovery().isInJSStack())
160 return true;
161
162 if (location.loadsIntoFPR() && getFreeFPR() != InvalidFPRReg)
163 return true;
164
165 if (location.loadsIntoGPR() && getFreeGPR() != InvalidGPRReg)
166 return true;
167
168 if (location.recovery().technique() == DisplacedInJSStack) {
169 GPRReg payloadGPR { getFreeGPR() };
170 if (payloadGPR == InvalidGPRReg)
171 return false;
172 m_lockedRegisters.set(payloadGPR);
173 GPRReg tagGPR { getFreeGPR() };
174 m_lockedRegisters.clear(payloadGPR);
175 return tagGPR != InvalidGPRReg;
176 }
177
178 return false;
179}
180
181void CallFrameShuffler::emitDisplace(CachedRecovery& location)
182{
183 ASSERT(location.recovery().isInRegisters());
184 JSValueRegs wantedJSValueRegs { location.wantedJSValueRegs() };
185 ASSERT(wantedJSValueRegs); // We don't support wanted FPRs on 32bit platforms
186
187 GPRReg wantedTagGPR { wantedJSValueRegs.tagGPR() };
188 GPRReg wantedPayloadGPR { wantedJSValueRegs.payloadGPR() };
189
190 if (wantedTagGPR != InvalidGPRReg) {
191 ASSERT(!m_lockedRegisters.get(wantedTagGPR));
192 if (CachedRecovery* currentTag { m_registers[wantedTagGPR] }) {
193 if (currentTag == &location) {
194 if (verbose)
195 dataLog(" + ", wantedTagGPR, " is OK\n");
196 } else {
197 // This can never happen on 32bit platforms since we
198 // have at most one wanted JSValueRegs, for the
199 // callee, and no callee-save registers.
200 RELEASE_ASSERT_NOT_REACHED();
201 }
202 }
203 }
204
205 if (wantedPayloadGPR != InvalidGPRReg) {
206 ASSERT(!m_lockedRegisters.get(wantedPayloadGPR));
207 if (CachedRecovery* currentPayload { m_registers[wantedPayloadGPR] }) {
208 if (currentPayload == &location) {
209 if (verbose)
210 dataLog(" + ", wantedPayloadGPR, " is OK\n");
211 } else {
212 // See above
213 RELEASE_ASSERT_NOT_REACHED();
214 }
215 }
216 }
217
218 if (location.recovery().technique() == InPair
219 || location.recovery().isInGPR()) {
220 GPRReg payloadGPR;
221 if (location.recovery().technique() == InPair)
222 payloadGPR = location.recovery().payloadGPR();
223 else
224 payloadGPR = location.recovery().gpr();
225
226 if (wantedPayloadGPR == InvalidGPRReg)
227 wantedPayloadGPR = payloadGPR;
228
229 if (payloadGPR != wantedPayloadGPR) {
230 if (location.recovery().technique() == InPair
231 && wantedPayloadGPR == location.recovery().tagGPR()) {
232 if (verbose)
233 dataLog(" * Swapping ", payloadGPR, " and ", wantedPayloadGPR, "\n");
234 m_jit.swap(payloadGPR, wantedPayloadGPR);
235 updateRecovery(location,
236 ValueRecovery::inPair(payloadGPR, wantedPayloadGPR));
237 } else {
238 if (verbose)
239 dataLog(" * Moving ", payloadGPR, " into ", wantedPayloadGPR, "\n");
240 m_jit.move(payloadGPR, wantedPayloadGPR);
241 if (location.recovery().technique() == InPair) {
242 updateRecovery(location,
243 ValueRecovery::inPair(location.recovery().tagGPR(),
244 wantedPayloadGPR));
245 } else {
246 updateRecovery(location,
247 ValueRecovery::inGPR(wantedPayloadGPR, location.recovery().dataFormat()));
248 }
249 }
250 }
251
252 if (wantedTagGPR == InvalidGPRReg)
253 wantedTagGPR = getFreeGPR();
254 switch (location.recovery().dataFormat()) {
255 case DataFormatInt32:
256 if (verbose)
257 dataLog(" * Moving int32 tag into ", wantedTagGPR, "\n");
258 m_jit.move(MacroAssembler::TrustedImm32(JSValue::Int32Tag),
259 wantedTagGPR);
260 break;
261 case DataFormatCell:
262 if (verbose)
263 dataLog(" * Moving cell tag into ", wantedTagGPR, "\n");
264 m_jit.move(MacroAssembler::TrustedImm32(JSValue::CellTag),
265 wantedTagGPR);
266 break;
267 case DataFormatBoolean:
268 if (verbose)
269 dataLog(" * Moving boolean tag into ", wantedTagGPR, "\n");
270 m_jit.move(MacroAssembler::TrustedImm32(JSValue::BooleanTag),
271 wantedTagGPR);
272 break;
273 case DataFormatJS:
274 ASSERT(wantedTagGPR != location.recovery().payloadGPR());
275 if (wantedTagGPR != location.recovery().tagGPR()) {
276 if (verbose)
277 dataLog(" * Moving ", location.recovery().tagGPR(), " into ", wantedTagGPR, "\n");
278 m_jit.move(location.recovery().tagGPR(), wantedTagGPR);
279 }
280 break;
281
282 default:
283 RELEASE_ASSERT_NOT_REACHED();
284 }
285 } else {
286 ASSERT(location.recovery().isInFPR());
287 if (wantedTagGPR == InvalidGPRReg) {
288 ASSERT(wantedPayloadGPR != InvalidGPRReg);
289 m_lockedRegisters.set(wantedPayloadGPR);
290 wantedTagGPR = getFreeGPR();
291 m_lockedRegisters.clear(wantedPayloadGPR);
292 }
293 if (wantedPayloadGPR == InvalidGPRReg) {
294 m_lockedRegisters.set(wantedTagGPR);
295 wantedPayloadGPR = getFreeGPR();
296 m_lockedRegisters.clear(wantedTagGPR);
297 }
298 m_jit.boxDouble(location.recovery().fpr(), wantedTagGPR, wantedPayloadGPR);
299 }
300 updateRecovery(location, ValueRecovery::inPair(wantedTagGPR, wantedPayloadGPR));
301}
302
303} // namespace JSC
304
305#endif // ENABLE(JIT) && USE(JSVALUE32_64)
306