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 | |
35 | namespace JSC { |
36 | |
37 | DataFormat 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 | |
72 | void 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 | |
80 | void 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 | |
157 | bool 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 | |
181 | void 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 | |