97cf02793985d88771e259700d27cfeb8113e820
[WebKit-https.git] / Source / JavaScriptCore / b3 / B3StackmapValue.h
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 #ifndef B3StackmapValue_h
27 #define B3StackmapValue_h
28
29 #if ENABLE(B3_JIT)
30
31 #include "B3ConstrainedValue.h"
32 #include "B3Value.h"
33 #include "B3ValueRep.h"
34 #include "CCallHelpers.h"
35 #include "RegisterSet.h"
36 #include <wtf/SharedTask.h>
37
38 namespace JSC { namespace B3 {
39
40 class StackmapGenerationParams;
41
42 typedef void StackmapGeneratorFunction(CCallHelpers&, const StackmapGenerationParams&);
43 typedef SharedTask<StackmapGeneratorFunction> StackmapGenerator;
44
45 class JS_EXPORT_PRIVATE StackmapValue : public Value {
46 public:
47     static bool accepts(Opcode opcode)
48     {
49         // This needs to include opcodes of all subclasses.
50         switch (opcode) {
51         case CheckAdd:
52         case CheckSub:
53         case CheckMul:
54         case Check:
55         case Patchpoint:
56             return true;
57         default:
58             return false;
59         }
60     }
61
62     ~StackmapValue();
63
64     // Use this to add children. Note that you could also add children by doing
65     // children().append(). That will work fine, but it's not recommended.
66     void append(const ConstrainedValue&);
67
68     template<typename VectorType>
69     void appendVector(const VectorType& vector)
70     {
71         for (const auto& value : vector)
72             append(value);
73     }
74
75     // Helper for appending cold any's. This often used by clients to implement OSR.
76     template<typename VectorType>
77     void appendColdAnys(const VectorType& vector)
78     {
79         for (Value* value : vector)
80             append(ConstrainedValue(value, ValueRep::ColdAny));
81     }
82
83     // This is a helper for something you might do a lot of: append a value that should be constrained
84     // to SomeRegister.
85     void appendSomeRegister(Value*);
86
87     const Vector<ValueRep>& reps() const { return m_reps; }
88
89     // Stackmaps allow you to specify that the operation may clobber some registers. Clobbering a register
90     // means that the operation appears to store a value into the register, but the compiler doesn't
91     // assume to know anything about what kind of value might have been stored. In B3's model of
92     // execution, registers are read or written at instruction boundaries rather than inside the
93     // instructions themselves. A register could be read or written immediately before the instruction
94     // executes, or immediately after. Note that at a boundary between instruction A and instruction B we
95     // simultaneously look at what A does after it executes and what B does before it executes. This is
96     // because when the compiler considers what happens to registers, it views the boundary between two
97     // instructions as a kind of atomic point where the late effects of A happen at the same time as the
98     // early effects of B.
99     //
100     // The compiler views a stackmap as a single instruction, even though of course the stackmap may be
101     // composed of any number of instructions (if it's a Patchpoint). You can claim that a stackmap value
102     // clobbers a set of registers before the stackmap's instruction or after. Clobbering before is called
103     // early clobber, while clobbering after is called late clobber.
104     //
105     // This is quite flexible but it has its limitations. Any register listed as an early clobber will
106     // interfere with all uses of the stackmap. Any register listed as a late clobber will interfere with
107     // all defs of the stackmap (i.e. the result). This means that it's currently not possible to claim
108     // to clobber a register while still allowing that register to be used for both an input and an output
109     // of the instruction. It just so happens that B3's sole client (the FTL) currently never wants to
110     // convey such a constraint, but it will want it eventually (FIXME:
111     // https://bugs.webkit.org/show_bug.cgi?id=151823).
112     //
113     // Note that a common use case of early clobber sets is to indicate that this is the set of registers
114     // that shall not be used for inputs to the value. But B3 supports two different ways of specifying
115     // this, the other being LateUse in combination with late clobber (not yet available to stackmaps
116     // directly, FIXME: https://bugs.webkit.org/show_bug.cgi?id=151335). A late use makes the use of that
117     // value appear to happen after the instruction. This means that a late use cannot use the same
118     // register as the result and it cannot use the same register as either early or late clobbered
119     // registers. Late uses are usually a better way of saying that a clobbered register cannot be used
120     // for an input. Early clobber means that some register(s) interfere with *all* inputs, while LateUse
121     // means that some value interferes with whatever is live after the instruction. Below is a list of
122     // examples of how the FTL can handle its various kinds of scenarios using a combination of early
123     // clobber, late clobber, and late use. These examples are for X86_64, w.l.o.g.
124     //
125     // Basic ById patchpoint: Early and late clobber of r11. Early clobber prevents any inputs from using
126     // r11 since that would mess with the MacroAssembler's assumptions when we
127     // AllowMacroScratchRegisterUsage. Late clobber tells B3 that the patchpoint may overwrite r11.
128     //
129     // ById patchpoint in a try block with some live state: This might throw an exception after already
130     // assigning to the result. So, this should LateUse all stackmap values to ensure that the stackmap
131     // values don't interfere with the result. Note that we do not LateUse the non-OSR inputs of the ById
132     // since LateUse implies that the use is cold: the register allocator will assume that the use is not
133     // important for the critical path. Also, early and late clobber of r11.
134     //
135     // Basic ByIdFlush patchpoint: We could do Flush the same way we did it with LLVM: ignore it and let
136     // PolymorphicAccess figure it out. Or, we could add internal clobber support (FIXME:
137     // https://bugs.webkit.org/show_bug.cgi?id=151823). Or, we could do it by early clobbering r11, late
138     // clobbering all volatile registers, and constraining the result to some register. Or, we could do
139     // that but leave the result constrained to SomeRegister, which will cause it to use a callee-save
140     // register. Internal clobber support would allow us to use SomeRegister while getting the result into
141     // a volatile register.
142     //
143     // ByIdFlush patchpoint in a try block with some live state: LateUse all for-OSR stackmap values,
144     // early clobber of r11 to prevent the other inputs from using r11, and late clobber of all volatile
145     // registers to make way for the call. To handle the result, we could do any of what is listed in the
146     // previous paragraph.
147     //
148     // Basic JS call: Force all non-OSR inputs into specific locations (register, stack, whatever).
149     // All volatile registers are late-clobbered. The output is constrained to a register as well.
150     //
151     // JS call in a try block with some live state: LateUse all for-OSR stackmap values, fully constrain
152     // all non-OSR inputs and the result, and late clobber all volatile registers.
153     //
154     // JS tail call: Pass all inputs as a warm variant of Any (FIXME:
155     // https://bugs.webkit.org/show_bug.cgi?id=151811).
156     //
157     // Note that we cannot yet do all of these things because although Air already supports all of these
158     // various forms of uses (LateUse and warm unconstrained use), B3 doesn't yet expose all of it. The
159     // bugs are:
160     // https://bugs.webkit.org/show_bug.cgi?id=151335 (LateUse)
161     // https://bugs.webkit.org/show_bug.cgi?id=151811 (warm Any)
162     void clobberEarly(const RegisterSet& set)
163     {
164         m_earlyClobbered.merge(set);
165     }
166
167     void clobberLate(const RegisterSet& set)
168     {
169         m_lateClobbered.merge(set);
170     }
171
172     void clobber(const RegisterSet& set)
173     {
174         clobberEarly(set);
175         clobberLate(set);
176     }
177
178     const RegisterSet& earlyClobbered() const { return m_earlyClobbered; }
179     const RegisterSet& lateClobbered() const { return m_lateClobbered; }
180
181     void setGenerator(RefPtr<StackmapGenerator> generator)
182     {
183         m_generator = generator;
184     }
185
186     template<typename Functor>
187     void setGenerator(const Functor& functor)
188     {
189         m_generator = createSharedTask<StackmapGeneratorFunction>(functor);
190     }
191
192     ConstrainedValue constrainedChild(unsigned index) const
193     {
194         return ConstrainedValue(child(index), index < m_reps.size() ? m_reps[index] : ValueRep::ColdAny);
195     }
196
197     void setConstrainedChild(unsigned index, const ConstrainedValue&);
198     
199     void setConstraint(unsigned index, const ValueRep&);
200
201     class ConstrainedValueCollection {
202     public:
203         ConstrainedValueCollection(const StackmapValue& value)
204             : m_value(value)
205         {
206         }
207
208         unsigned size() const { return m_value.numChildren(); }
209         
210         ConstrainedValue at(unsigned index) const { return m_value.constrainedChild(index); }
211
212         ConstrainedValue operator[](unsigned index) const { return at(index); }
213
214         class iterator {
215         public:
216             iterator()
217                 : m_collection(nullptr)
218                 , m_index(0)
219             {
220             }
221
222             iterator(const ConstrainedValueCollection& collection, unsigned index)
223                 : m_collection(&collection)
224                 , m_index(index)
225             {
226             }
227
228             ConstrainedValue operator*() const
229             {
230                 return m_collection->at(m_index);
231             }
232
233             iterator& operator++()
234             {
235                 m_index++;
236                 return *this;
237             }
238
239             bool operator==(const iterator& other) const
240             {
241                 ASSERT(m_collection == other.m_collection);
242                 return m_index == other.m_index;
243             }
244
245             bool operator!=(const iterator& other) const
246             {
247                 return !(*this == other);
248             }
249             
250         private:
251             const ConstrainedValueCollection* m_collection;
252             unsigned m_index;
253         };
254
255         iterator begin() const { return iterator(*this, 0); }
256         iterator end() const { return iterator(*this, size()); }
257
258     private:
259         const StackmapValue& m_value;
260     };
261
262     ConstrainedValueCollection constrainedChildren() const
263     {
264         return ConstrainedValueCollection(*this);
265     }
266
267 protected:
268     void dumpChildren(CommaPrinter&, PrintStream&) const override;
269     void dumpMeta(CommaPrinter&, PrintStream&) const override;
270
271     StackmapValue(unsigned index, CheckedOpcodeTag, Opcode, Type, Origin);
272
273 private:
274     friend class CheckSpecial;
275     friend class PatchpointSpecial;
276     friend class StackmapGenerationParams;
277     friend class StackmapSpecial;
278     
279     Vector<ValueRep> m_reps;
280     RefPtr<StackmapGenerator> m_generator;
281     RegisterSet m_earlyClobbered;
282     RegisterSet m_lateClobbered;
283     RegisterSet m_usedRegisters; // Stackmaps could be further duplicated by Air, but that's unlikely, so we just merge the used registers sets if that were to happen.
284 };
285
286 } } // namespace JSC::B3
287
288 #endif // ENABLE(B3_JIT)
289
290 #endif // B3StackmapValue_h
291