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
35namespace 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.
46class GenerationInfo {
47public:
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
402private:
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