1 | /* |
2 | * Copyright (C) 2011, 2013 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 | #pragma once |
27 | |
28 | #if ENABLE(DFG_JIT) |
29 | |
30 | #include "DFGMinifiedIDInlines.h" |
31 | #include "DFGVariableEvent.h" |
32 | #include "DFGVariableEventStream.h" |
33 | #include "DataFormat.h" |
34 | |
35 | namespace JSC { namespace DFG { |
36 | |
37 | // === GenerationInfo === |
38 | // |
39 | // This class is used to track the current status of live values during code generation. |
40 | // Can provide information as to whether a value is in machine registers, and if so which, |
41 | // whether a value has been spilled to the RegisterFile, and if so may be able to provide |
42 | // details of the format in memory (all values are spilled in a boxed form, but we may be |
43 | // able to track the type of box), and tracks how many outstanding uses of a value remain, |
44 | // so that we know when the value is dead and the machine registers associated with it |
45 | // may be released. |
46 | class GenerationInfo { |
47 | public: |
48 | GenerationInfo() |
49 | : m_node(0) |
50 | , m_useCount(0) |
51 | , m_registerFormat(DataFormatNone) |
52 | , m_spillFormat(DataFormatNone) |
53 | , m_canFill(false) |
54 | , m_bornForOSR(false) |
55 | , m_isConstant(false) |
56 | { |
57 | } |
58 | |
59 | void initConstant(Node* node, uint32_t useCount) |
60 | { |
61 | m_node = node; |
62 | m_useCount = useCount; |
63 | m_registerFormat = DataFormatNone; |
64 | m_spillFormat = DataFormatNone; |
65 | m_canFill = true; |
66 | m_bornForOSR = false; |
67 | m_isConstant = true; |
68 | ASSERT(m_useCount); |
69 | } |
70 | void initGPR(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format) |
71 | { |
72 | ASSERT(gpr != InvalidGPRReg); |
73 | m_node = node; |
74 | m_useCount = useCount; |
75 | m_registerFormat = format; |
76 | m_spillFormat = DataFormatNone; |
77 | m_canFill = false; |
78 | u.gpr = gpr; |
79 | m_bornForOSR = false; |
80 | m_isConstant = false; |
81 | ASSERT(m_useCount); |
82 | } |
83 | void initInt32(Node* node, uint32_t useCount, GPRReg gpr) |
84 | { |
85 | initGPR(node, useCount, gpr, DataFormatInt32); |
86 | } |
87 | void initInt52(Node* node, uint32_t useCount, GPRReg reg, DataFormat format) |
88 | { |
89 | ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52); |
90 | initGPR(node, useCount, reg, format); |
91 | } |
92 | void initInt52(Node* node, uint32_t useCount, GPRReg reg) |
93 | { |
94 | initGPR(node, useCount, reg, DataFormatInt52); |
95 | } |
96 | void initStrictInt52(Node* node, uint32_t useCount, GPRReg reg) |
97 | { |
98 | initGPR(node, useCount, reg, DataFormatStrictInt52); |
99 | } |
100 | #if USE(JSVALUE64) |
101 | void initJSValue(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format = DataFormatJS) |
102 | { |
103 | ASSERT(format & DataFormatJS); |
104 | initGPR(node, useCount, gpr, format); |
105 | } |
106 | #elif USE(JSVALUE32_64) |
107 | void initJSValue(Node* node, uint32_t useCount, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS) |
108 | { |
109 | ASSERT(format & DataFormatJS); |
110 | |
111 | m_node = node; |
112 | m_useCount = useCount; |
113 | m_registerFormat = format; |
114 | m_spillFormat = DataFormatNone; |
115 | m_canFill = false; |
116 | u.v.tagGPR = tagGPR; |
117 | u.v.payloadGPR = payloadGPR; |
118 | m_bornForOSR = false; |
119 | m_isConstant = false; |
120 | ASSERT(m_useCount); |
121 | } |
122 | #endif |
123 | void initCell(Node* node, uint32_t useCount, GPRReg gpr) |
124 | { |
125 | initGPR(node, useCount, gpr, DataFormatCell); |
126 | } |
127 | void initBoolean(Node* node, uint32_t useCount, GPRReg gpr) |
128 | { |
129 | initGPR(node, useCount, gpr, DataFormatBoolean); |
130 | } |
131 | void initDouble(Node* node, uint32_t useCount, FPRReg fpr) |
132 | { |
133 | ASSERT(fpr != InvalidFPRReg); |
134 | m_node = node; |
135 | m_useCount = useCount; |
136 | m_registerFormat = DataFormatDouble; |
137 | m_spillFormat = DataFormatNone; |
138 | m_canFill = false; |
139 | u.fpr = fpr; |
140 | m_bornForOSR = false; |
141 | m_isConstant = false; |
142 | ASSERT(m_useCount); |
143 | } |
144 | void initStorage(Node* node, uint32_t useCount, GPRReg gpr) |
145 | { |
146 | initGPR(node, useCount, gpr, DataFormatStorage); |
147 | } |
148 | |
149 | // Get the node that produced this value. |
150 | Node* node() { return m_node; } |
151 | |
152 | void noticeOSRBirth(VariableEventStream& stream, Node* node, VirtualRegister virtualRegister) |
153 | { |
154 | if (m_node != node) |
155 | return; |
156 | if (!alive()) |
157 | return; |
158 | if (m_bornForOSR) |
159 | return; |
160 | |
161 | m_bornForOSR = true; |
162 | |
163 | if (m_isConstant) |
164 | appendBirth(stream); |
165 | else if (m_registerFormat != DataFormatNone) |
166 | appendFill(BirthToFill, stream); |
167 | else if (m_spillFormat != DataFormatNone) |
168 | appendSpill(BirthToSpill, stream, virtualRegister); |
169 | } |
170 | |
171 | // Mark the value as having been used (decrement the useCount). |
172 | // Returns true if this was the last use of the value, and any |
173 | // associated machine registers may be freed. |
174 | bool use(VariableEventStream& stream) |
175 | { |
176 | ASSERT(m_useCount); |
177 | bool result = !--m_useCount; |
178 | |
179 | if (result && m_bornForOSR) { |
180 | ASSERT(m_node); |
181 | stream.appendAndLog(VariableEvent::death(MinifiedID(m_node))); |
182 | } |
183 | |
184 | return result; |
185 | } |
186 | |
187 | // Used to check the operands of operations to see if they are on |
188 | // their last use; in some cases it may be safe to reuse the same |
189 | // machine register for the result of the operation. |
190 | uint32_t useCount() |
191 | { |
192 | ASSERT(m_useCount); |
193 | return m_useCount; |
194 | } |
195 | |
196 | // Get the format of the value in machine registers (or 'none'). |
197 | DataFormat registerFormat() { return m_registerFormat; } |
198 | // Get the format of the value as it is spilled in the JSStack (or 'none'). |
199 | DataFormat spillFormat() { return m_spillFormat; } |
200 | |
201 | bool isFormat(DataFormat expectedFormat) |
202 | { |
203 | return registerFormat() == expectedFormat || spillFormat() == expectedFormat; |
204 | } |
205 | |
206 | bool isJSFormat(DataFormat expectedFormat) |
207 | { |
208 | return JSC::isJSFormat(registerFormat(), expectedFormat) || JSC::isJSFormat(spillFormat(), expectedFormat); |
209 | } |
210 | |
211 | bool isJSInt32() |
212 | { |
213 | return isJSFormat(DataFormatJSInt32); |
214 | } |
215 | |
216 | bool isInt52() |
217 | { |
218 | return isFormat(DataFormatInt52); |
219 | } |
220 | |
221 | bool isStrictInt52() |
222 | { |
223 | return isFormat(DataFormatStrictInt52); |
224 | } |
225 | |
226 | bool isJSDouble() |
227 | { |
228 | return isJSFormat(DataFormatJSDouble); |
229 | } |
230 | |
231 | bool isJSCell() |
232 | { |
233 | return isJSFormat(DataFormatJSCell); |
234 | } |
235 | |
236 | bool isJSBoolean() |
237 | { |
238 | return isJSFormat(DataFormatJSBoolean); |
239 | } |
240 | |
241 | bool isUnknownJS() |
242 | { |
243 | return spillFormat() == DataFormatNone |
244 | ? registerFormat() == DataFormatJS || registerFormat() == DataFormatNone |
245 | : spillFormat() == DataFormatJS; |
246 | } |
247 | |
248 | // Get the machine resister currently holding the value. |
249 | #if USE(JSVALUE64) |
250 | GPRReg gpr() { ASSERT(m_registerFormat && m_registerFormat != DataFormatDouble); return u.gpr; } |
251 | FPRReg fpr() { ASSERT(m_registerFormat == DataFormatDouble); return u.fpr; } |
252 | JSValueRegs jsValueRegs() { ASSERT(m_registerFormat & DataFormatJS); return JSValueRegs(u.gpr); } |
253 | #elif USE(JSVALUE32_64) |
254 | GPRReg gpr() { ASSERT(!(m_registerFormat & DataFormatJS) && m_registerFormat != DataFormatDouble); return u.gpr; } |
255 | GPRReg tagGPR() { ASSERT(m_registerFormat & DataFormatJS); return u.v.tagGPR; } |
256 | GPRReg payloadGPR() { ASSERT(m_registerFormat & DataFormatJS); return u.v.payloadGPR; } |
257 | FPRReg fpr() { ASSERT(m_registerFormat == DataFormatDouble || m_registerFormat == DataFormatJSDouble); return u.fpr; } |
258 | JSValueRegs jsValueRegs() { ASSERT(m_registerFormat & DataFormatJS); return JSValueRegs(u.v.tagGPR, u.v.payloadGPR); } |
259 | #endif |
260 | |
261 | // Check whether a value needs spilling in order to free up any associated machine registers. |
262 | bool needsSpill() |
263 | { |
264 | // This should only be called on values that are currently in a register. |
265 | ASSERT(m_registerFormat != DataFormatNone); |
266 | // Constants do not need spilling, nor do values that have already been |
267 | // spilled to the JSStack. |
268 | return !m_canFill; |
269 | } |
270 | |
271 | // Called when a VirtualRegister is being spilled to the JSStack for the first time. |
272 | void spill(VariableEventStream& stream, VirtualRegister virtualRegister, DataFormat spillFormat) |
273 | { |
274 | // We shouldn't be spill values that don't need spilling. |
275 | ASSERT(!m_canFill); |
276 | ASSERT(m_spillFormat == DataFormatNone); |
277 | // We should only be spilling values that are currently in machine registers. |
278 | ASSERT(m_registerFormat != DataFormatNone); |
279 | |
280 | m_registerFormat = DataFormatNone; |
281 | m_spillFormat = spillFormat; |
282 | m_canFill = true; |
283 | |
284 | if (m_bornForOSR) |
285 | appendSpill(Spill, stream, virtualRegister); |
286 | } |
287 | |
288 | // Called on values that don't need spilling (constants and values that have |
289 | // already been spilled), to mark them as no longer being in machine registers. |
290 | void setSpilled(VariableEventStream& stream, VirtualRegister virtualRegister) |
291 | { |
292 | // Should only be called on values that don't need spilling, and are currently in registers. |
293 | ASSERT(m_canFill && m_registerFormat != DataFormatNone); |
294 | m_registerFormat = DataFormatNone; |
295 | |
296 | if (m_bornForOSR) |
297 | appendSpill(Spill, stream, virtualRegister); |
298 | } |
299 | |
300 | void killSpilled() |
301 | { |
302 | m_spillFormat = DataFormatNone; |
303 | m_canFill = false; |
304 | } |
305 | |
306 | void fillGPR(VariableEventStream& stream, GPRReg gpr, DataFormat format) |
307 | { |
308 | ASSERT(gpr != InvalidGPRReg); |
309 | m_registerFormat = format; |
310 | u.gpr = gpr; |
311 | if (m_bornForOSR) |
312 | appendFill(Fill, stream); |
313 | } |
314 | |
315 | // Record that this value is filled into machine registers, |
316 | // tracking which registers, and what format the value has. |
317 | #if USE(JSVALUE64) |
318 | void fillJSValue(VariableEventStream& stream, GPRReg gpr, DataFormat format = DataFormatJS) |
319 | { |
320 | ASSERT(format & DataFormatJS); |
321 | fillGPR(stream, gpr, format); |
322 | } |
323 | #elif USE(JSVALUE32_64) |
324 | void fillJSValue(VariableEventStream& stream, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS) |
325 | { |
326 | ASSERT(format & DataFormatJS); |
327 | m_registerFormat = format; |
328 | u.v.tagGPR = tagGPR; // FIXME: for JSValues with known type (boolean, integer, cell etc.) no tagGPR is needed? |
329 | u.v.payloadGPR = payloadGPR; |
330 | |
331 | if (m_bornForOSR) |
332 | appendFill(Fill, stream); |
333 | } |
334 | void fillCell(VariableEventStream& stream, GPRReg gpr) |
335 | { |
336 | fillGPR(stream, gpr, DataFormatCell); |
337 | } |
338 | #endif |
339 | void fillInt32(VariableEventStream& stream, GPRReg gpr) |
340 | { |
341 | fillGPR(stream, gpr, DataFormatInt32); |
342 | } |
343 | void fillInt52(VariableEventStream& stream, GPRReg gpr, DataFormat format) |
344 | { |
345 | ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52); |
346 | fillGPR(stream, gpr, format); |
347 | } |
348 | void fillInt52(VariableEventStream& stream, GPRReg gpr) |
349 | { |
350 | fillGPR(stream, gpr, DataFormatInt52); |
351 | } |
352 | void fillStrictInt52(VariableEventStream& stream, GPRReg gpr) |
353 | { |
354 | fillGPR(stream, gpr, DataFormatStrictInt52); |
355 | } |
356 | void fillBoolean(VariableEventStream& stream, GPRReg gpr) |
357 | { |
358 | fillGPR(stream, gpr, DataFormatBoolean); |
359 | } |
360 | void fillDouble(VariableEventStream& stream, FPRReg fpr) |
361 | { |
362 | ASSERT(fpr != InvalidFPRReg); |
363 | m_registerFormat = DataFormatDouble; |
364 | u.fpr = fpr; |
365 | |
366 | if (m_bornForOSR) |
367 | appendFill(Fill, stream); |
368 | } |
369 | void fillStorage(VariableEventStream& stream, GPRReg gpr) |
370 | { |
371 | fillGPR(stream, gpr, DataFormatStorage); |
372 | } |
373 | |
374 | bool alive() |
375 | { |
376 | return m_useCount; |
377 | } |
378 | |
379 | ValueRecovery recovery(VirtualRegister spillSlot) const |
380 | { |
381 | if (m_isConstant) |
382 | return ValueRecovery::constant(m_node->constant()->value()); |
383 | |
384 | if (m_registerFormat == DataFormatDouble) |
385 | return ValueRecovery::inFPR(u.fpr, DataFormatDouble); |
386 | |
387 | #if USE(JSVALUE32_64) |
388 | if (m_registerFormat & DataFormatJS) { |
389 | if (m_registerFormat == DataFormatJS) |
390 | return ValueRecovery::inPair(u.v.tagGPR, u.v.payloadGPR); |
391 | return ValueRecovery::inGPR(u.v.payloadGPR, static_cast<DataFormat>(m_registerFormat & ~DataFormatJS)); |
392 | } |
393 | #endif |
394 | if (m_registerFormat) |
395 | return ValueRecovery::inGPR(u.gpr, m_registerFormat); |
396 | |
397 | ASSERT(m_spillFormat); |
398 | |
399 | return ValueRecovery::displacedInJSStack(spillSlot, m_spillFormat); |
400 | } |
401 | |
402 | private: |
403 | void appendBirth(VariableEventStream& stream) |
404 | { |
405 | stream.appendAndLog(VariableEvent::birth(MinifiedID(m_node))); |
406 | } |
407 | |
408 | void appendFill(VariableEventKind kind, VariableEventStream& stream) |
409 | { |
410 | ASSERT(m_bornForOSR); |
411 | |
412 | if (m_registerFormat == DataFormatDouble) { |
413 | stream.appendAndLog(VariableEvent::fillFPR(kind, MinifiedID(m_node), u.fpr)); |
414 | return; |
415 | } |
416 | #if USE(JSVALUE32_64) |
417 | if (m_registerFormat & DataFormatJS) { |
418 | stream.appendAndLog(VariableEvent::fillPair(kind, MinifiedID(m_node), u.v.tagGPR, u.v.payloadGPR)); |
419 | return; |
420 | } |
421 | #endif |
422 | stream.appendAndLog(VariableEvent::fillGPR(kind, MinifiedID(m_node), u.gpr, m_registerFormat)); |
423 | } |
424 | |
425 | void appendSpill(VariableEventKind kind, VariableEventStream& stream, VirtualRegister virtualRegister) |
426 | { |
427 | stream.appendAndLog(VariableEvent::spill(kind, MinifiedID(m_node), virtualRegister, m_spillFormat)); |
428 | } |
429 | |
430 | // The node whose result is stored in this virtual register. |
431 | Node* m_node; |
432 | uint32_t m_useCount; |
433 | DataFormat m_registerFormat; |
434 | DataFormat m_spillFormat; |
435 | bool m_canFill; |
436 | bool m_bornForOSR; |
437 | bool m_isConstant; |
438 | union { |
439 | GPRReg gpr; |
440 | FPRReg fpr; |
441 | #if USE(JSVALUE32_64) |
442 | struct { |
443 | GPRReg tagGPR; |
444 | GPRReg payloadGPR; |
445 | } v; |
446 | #endif |
447 | } u; |
448 | }; |
449 | |
450 | } } // namespace JSC::DFG |
451 | |
452 | #endif |
453 | |