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