• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 DFGSpeculativeJIT_h
27 #define DFGSpeculativeJIT_h
28 
29 #if ENABLE(DFG_JIT)
30 
31 #include <dfg/DFGJITCodeGenerator.h>
32 
33 namespace JSC { namespace DFG {
34 
35 class SpeculativeJIT;
36 
37 // This enum describes the types of additional recovery that
38 // may need be performed should a speculation check fail.
39 enum SpeculationRecoveryType {
40     SpeculativeAdd
41 };
42 
43 // === SpeculationRecovery ===
44 //
45 // This class provides additional information that may be associated with a
46 // speculation check - for example
47 class SpeculationRecovery {
48 public:
SpeculationRecovery(SpeculationRecoveryType type,GPRReg dest,GPRReg src)49     SpeculationRecovery(SpeculationRecoveryType type, GPRReg dest, GPRReg src)
50         : m_type(type)
51         , m_dest(dest)
52         , m_src(src)
53     {
54     }
55 
type()56     SpeculationRecoveryType type() { return m_type; }
dest()57     GPRReg dest() { return m_dest; }
src()58     GPRReg src() { return m_src; }
59 
60 private:
61     // Indicates the type of additional recovery to be performed.
62     SpeculationRecoveryType m_type;
63     // different recovery types may required different additional information here.
64     GPRReg m_dest;
65     GPRReg m_src;
66 };
67 
68 // === SpeculationCheck ===
69 //
70 // This structure records a bail-out from the speculative path,
71 // which will need to be linked in to the non-speculative one.
72 struct SpeculationCheck {
73     SpeculationCheck(MacroAssembler::Jump, SpeculativeJIT*, unsigned recoveryIndex = 0);
74 
75     // The location of the jump out from the speculative path,
76     // and the node we were generating code for.
77     MacroAssembler::Jump m_check;
78     NodeIndex m_nodeIndex;
79     // Used to record any additional recovery to be performed; this
80     // value is an index into the SpeculativeJIT's m_speculationRecoveryList
81     // array, offset by 1. (m_recoveryIndex == 0) means no recovery.
82     unsigned m_recoveryIndex;
83 
84     struct RegisterInfo {
85         NodeIndex nodeIndex;
86         DataFormat format;
87     };
88     RegisterInfo m_gprInfo[numberOfGPRs];
89     NodeIndex m_fprInfo[numberOfFPRs];
90 };
91 typedef SegmentedVector<SpeculationCheck, 16> SpeculationCheckVector;
92 
93 
94 // === SpeculativeJIT ===
95 //
96 // The SpeculativeJIT is used to generate a fast, but potentially
97 // incomplete code path for the dataflow. When code generating
98 // we may make assumptions about operand types, dynamically check,
99 // and bail-out to an alternate code path if these checks fail.
100 // Importantly, the speculative code path cannot be reentered once
101 // a speculative check has failed. This allows the SpeculativeJIT
102 // to propagate type information (including information that has
103 // only speculatively been asserted) through the dataflow.
104 class SpeculativeJIT : public JITCodeGenerator {
105     friend struct SpeculationCheck;
106 public:
SpeculativeJIT(JITCompiler & jit)107     SpeculativeJIT(JITCompiler& jit)
108         : JITCodeGenerator(jit, true)
109         , m_didTerminate(false)
110     {
111     }
112 
113     bool compile();
114 
115     // Retrieve the list of bail-outs from the speculative path,
116     // and additional recovery information.
speculationChecks()117     SpeculationCheckVector& speculationChecks()
118     {
119         return m_speculationChecks;
120     }
speculationRecovery(size_t index)121     SpeculationRecovery* speculationRecovery(size_t index)
122     {
123         // SpeculationCheck::m_recoveryIndex is offset by 1,
124         // 0 means no recovery.
125         return index ? &m_speculationRecoveryList[index - 1] : 0;
126     }
127 
128     // Called by the speculative operand types, below, to fill operand to
129     // machine registers, implicitly generating speculation checks as needed.
130     GPRReg fillSpeculateInt(NodeIndex, DataFormat& returnFormat);
131     GPRReg fillSpeculateIntStrict(NodeIndex);
132     GPRReg fillSpeculateCell(NodeIndex);
133 
134 private:
135     bool compile(Node&);
136     bool compile(BasicBlock&);
137 
isDoubleConstantWithInt32Value(NodeIndex nodeIndex,int32_t & out)138     bool isDoubleConstantWithInt32Value(NodeIndex nodeIndex, int32_t& out)
139     {
140         if (!m_jit.isDoubleConstant(nodeIndex))
141             return false;
142         double value = m_jit.valueOfDoubleConstant(nodeIndex);
143 
144         int32_t asInt32 = static_cast<int32_t>(value);
145         if (value != asInt32)
146             return false;
147         if (!asInt32 && signbit(value))
148             return false;
149 
150         out = asInt32;
151         return true;
152     }
153 
154     // Add a speculation check without additional recovery.
speculationCheck(MacroAssembler::Jump jumpToFail)155     void speculationCheck(MacroAssembler::Jump jumpToFail)
156     {
157         m_speculationChecks.append(SpeculationCheck(jumpToFail, this));
158     }
159     // Add a speculation check with additional recovery.
speculationCheck(MacroAssembler::Jump jumpToFail,const SpeculationRecovery & recovery)160     void speculationCheck(MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
161     {
162         m_speculationRecoveryList.append(recovery);
163         m_speculationChecks.append(SpeculationCheck(jumpToFail, this, m_speculationRecoveryList.size()));
164     }
165 
166     // Called when we statically determine that a speculation will fail.
terminateSpeculativeExecution()167     void terminateSpeculativeExecution()
168     {
169         // FIXME: in cases where we can statically determine we're going to bail out from the speculative
170         // JIT we should probably rewind code generation and only produce the non-speculative path.
171         m_didTerminate = true;
172         speculationCheck(m_jit.jump());
173     }
174 
175     template<bool strict>
176     GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat);
177 
178     // It is possible, during speculative generation, to reach a situation in which we
179     // can statically determine a speculation will fail (for example, when two nodes
180     // will make conflicting speculations about the same operand). In such cases this
181     // flag is set, indicating no further code generation should take place.
182     bool m_didTerminate;
183     // This vector tracks bail-outs from the speculative path to the non-speculative one.
184     SpeculationCheckVector m_speculationChecks;
185     // Some bail-outs need to record additional information recording specific recovery
186     // to be performed (for example, on detected overflow from an add, we may need to
187     // reverse the addition if an operand is being overwritten).
188     Vector<SpeculationRecovery, 16> m_speculationRecoveryList;
189 };
190 
191 
192 // === Speculative Operand types ===
193 //
194 // SpeculateIntegerOperand, SpeculateStrictInt32Operand and SpeculateCellOperand.
195 //
196 // These are used to lock the operands to a node into machine registers within the
197 // SpeculativeJIT. The classes operate like those provided by the JITCodeGenerator,
198 // however these will perform a speculative check for a more restrictive type than
199 // we can statically determine the operand to have. If the operand does not have
200 // the requested type, a bail-out to the non-speculative path will be taken.
201 
202 class SpeculateIntegerOperand {
203 public:
SpeculateIntegerOperand(SpeculativeJIT * jit,NodeIndex index)204     explicit SpeculateIntegerOperand(SpeculativeJIT* jit, NodeIndex index)
205         : m_jit(jit)
206         , m_index(index)
207         , m_gprOrInvalid(InvalidGPRReg)
208 #ifndef NDEBUG
209         , m_format(DataFormatNone)
210 #endif
211     {
212         ASSERT(m_jit);
213         if (jit->isFilled(index))
214             gpr();
215     }
216 
~SpeculateIntegerOperand()217     ~SpeculateIntegerOperand()
218     {
219         ASSERT(m_gprOrInvalid != InvalidGPRReg);
220         m_jit->unlock(m_gprOrInvalid);
221     }
222 
index()223     NodeIndex index() const
224     {
225         return m_index;
226     }
227 
gpr()228     GPRReg gpr()
229     {
230         if (m_gprOrInvalid == InvalidGPRReg)
231             m_gprOrInvalid = m_jit->fillSpeculateInt(index(), m_format);
232         return m_gprOrInvalid;
233     }
234 
format()235     DataFormat format()
236     {
237         gpr(); // m_format is set when m_gpr is locked.
238         ASSERT(m_format == DataFormatInteger || m_format == DataFormatJSInteger);
239         return m_format;
240     }
241 
registerID()242     MacroAssembler::RegisterID registerID()
243     {
244         return JITCompiler::gprToRegisterID(gpr());
245     }
246 
247 private:
248     SpeculativeJIT* m_jit;
249     NodeIndex m_index;
250     GPRReg m_gprOrInvalid;
251     DataFormat m_format;
252 };
253 
254 class SpeculateStrictInt32Operand {
255 public:
SpeculateStrictInt32Operand(SpeculativeJIT * jit,NodeIndex index)256     explicit SpeculateStrictInt32Operand(SpeculativeJIT* jit, NodeIndex index)
257         : m_jit(jit)
258         , m_index(index)
259         , m_gprOrInvalid(InvalidGPRReg)
260     {
261         ASSERT(m_jit);
262         if (jit->isFilled(index))
263             gpr();
264     }
265 
~SpeculateStrictInt32Operand()266     ~SpeculateStrictInt32Operand()
267     {
268         ASSERT(m_gprOrInvalid != InvalidGPRReg);
269         m_jit->unlock(m_gprOrInvalid);
270     }
271 
index()272     NodeIndex index() const
273     {
274         return m_index;
275     }
276 
gpr()277     GPRReg gpr()
278     {
279         if (m_gprOrInvalid == InvalidGPRReg)
280             m_gprOrInvalid = m_jit->fillSpeculateIntStrict(index());
281         return m_gprOrInvalid;
282     }
283 
registerID()284     MacroAssembler::RegisterID registerID()
285     {
286         return JITCompiler::gprToRegisterID(gpr());
287     }
288 
289 private:
290     SpeculativeJIT* m_jit;
291     NodeIndex m_index;
292     GPRReg m_gprOrInvalid;
293 };
294 
295 class SpeculateCellOperand {
296 public:
SpeculateCellOperand(SpeculativeJIT * jit,NodeIndex index)297     explicit SpeculateCellOperand(SpeculativeJIT* jit, NodeIndex index)
298         : m_jit(jit)
299         , m_index(index)
300         , m_gprOrInvalid(InvalidGPRReg)
301     {
302         ASSERT(m_jit);
303         if (jit->isFilled(index))
304             gpr();
305     }
306 
~SpeculateCellOperand()307     ~SpeculateCellOperand()
308     {
309         ASSERT(m_gprOrInvalid != InvalidGPRReg);
310         m_jit->unlock(m_gprOrInvalid);
311     }
312 
index()313     NodeIndex index() const
314     {
315         return m_index;
316     }
317 
gpr()318     GPRReg gpr()
319     {
320         if (m_gprOrInvalid == InvalidGPRReg)
321             m_gprOrInvalid = m_jit->fillSpeculateCell(index());
322         return m_gprOrInvalid;
323     }
324 
registerID()325     MacroAssembler::RegisterID registerID()
326     {
327         return JITCompiler::gprToRegisterID(gpr());
328     }
329 
330 private:
331     SpeculativeJIT* m_jit;
332     NodeIndex m_index;
333     GPRReg m_gprOrInvalid;
334 };
335 
336 
337 // === SpeculationCheckIndexIterator ===
338 //
339 // This class is used by the non-speculative JIT to check which
340 // nodes require entry points from the speculative path.
341 class SpeculationCheckIndexIterator {
342 public:
SpeculationCheckIndexIterator(SpeculationCheckVector & speculationChecks)343     SpeculationCheckIndexIterator(SpeculationCheckVector& speculationChecks)
344         : m_speculationChecks(speculationChecks)
345         , m_iter(m_speculationChecks.begin())
346         , m_end(m_speculationChecks.end())
347     {
348     }
349 
hasCheckAtIndex(NodeIndex nodeIndex)350     bool hasCheckAtIndex(NodeIndex nodeIndex)
351     {
352         while (m_iter != m_end) {
353             NodeIndex current = m_iter->m_nodeIndex;
354             if (current >= nodeIndex)
355                 return current == nodeIndex;
356             ++m_iter;
357         }
358         return false;
359     }
360 
361 private:
362     SpeculationCheckVector& m_speculationChecks;
363     SpeculationCheckVector::Iterator m_iter;
364     SpeculationCheckVector::Iterator m_end;
365 };
366 
367 
368 } } // namespace JSC::DFG
369 
370 #endif
371 #endif
372 
373