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