B3 should support multiple entrypoints
[WebKit-https.git] / Source / JavaScriptCore / b3 / B3Procedure.h
1 /*
2  * Copyright (C) 2015-2016 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 B3Procedure_h
27 #define B3Procedure_h
28
29 #if ENABLE(B3_JIT)
30
31 #include "B3OpaqueByproducts.h"
32 #include "B3Origin.h"
33 #include "B3PCToOriginMap.h"
34 #include "B3SparseCollection.h"
35 #include "B3Type.h"
36 #include "B3ValueKey.h"
37 #include "CCallHelpers.h"
38 #include "PureNaN.h"
39 #include "RegisterAtOffsetList.h"
40 #include <wtf/Bag.h>
41 #include <wtf/FastMalloc.h>
42 #include <wtf/HashSet.h>
43 #include <wtf/Noncopyable.h>
44 #include <wtf/PrintStream.h>
45 #include <wtf/SharedTask.h>
46 #include <wtf/TriState.h>
47 #include <wtf/Vector.h>
48
49 namespace JSC { namespace B3 {
50
51 class BasicBlock;
52 class BlockInsertionSet;
53 class CFG;
54 class Dominators;
55 class StackSlot;
56 class Value;
57 class Variable;
58
59 namespace Air { class Code; }
60
61 // This represents B3's view of a piece of code. Note that this object must exist in a 1:1
62 // relationship with Air::Code. B3::Procedure and Air::Code are just different facades of the B3
63 // compiler's knowledge about a piece of code. Some kinds of state aren't perfect fits for either
64 // Procedure or Code, and are placed in one or the other based on convenience. Procedure always
65 // allocates a Code, and a Code cannot be allocated without an owning Procedure and they always
66 // have references to each other.
67
68 class Procedure {
69     WTF_MAKE_NONCOPYABLE(Procedure);
70     WTF_MAKE_FAST_ALLOCATED;
71 public:
72
73     JS_EXPORT_PRIVATE Procedure();
74     JS_EXPORT_PRIVATE ~Procedure();
75
76     template<typename Callback>
77     void setOriginPrinter(Callback&& callback)
78     {
79         m_originPrinter = createSharedTask<void(PrintStream&, Origin)>(
80             std::forward<Callback>(callback));
81     }
82
83     // Usually you use this via OriginDump, though it's cool to use it directly.
84     void printOrigin(PrintStream& out, Origin origin) const;
85
86     // This is a debugging hack. Sometimes while debugging B3 you need to break the abstraction
87     // and get at the DFG Graph, or whatever data structure the frontend used to describe the
88     // program. The FTL passes the DFG Graph.
89     void setFrontendData(const void* value) { m_frontendData = value; }
90     const void* frontendData() const { return m_frontendData; }
91
92     JS_EXPORT_PRIVATE BasicBlock* addBlock(double frequency = 1);
93
94     // Changes the order of basic blocks to be as in the supplied vector. The vector does not
95     // need to mention every block in the procedure. Blocks not mentioned will be placed after
96     // these blocks in the same order as they were in originally.
97     template<typename BlockIterable>
98     void setBlockOrder(const BlockIterable& iterable)
99     {
100         Vector<BasicBlock*> blocks;
101         for (BasicBlock* block : iterable)
102             blocks.append(block);
103         setBlockOrderImpl(blocks);
104     }
105
106     JS_EXPORT_PRIVATE StackSlot* addStackSlot(unsigned byteSize);
107     JS_EXPORT_PRIVATE Variable* addVariable(Type);
108     
109     template<typename ValueType, typename... Arguments>
110     ValueType* add(Arguments...);
111
112     Value* clone(Value*);
113
114     Value* addIntConstant(Origin, Type, int64_t value);
115     Value* addIntConstant(Value*, int64_t value);
116
117     Value* addBottom(Origin, Type);
118     Value* addBottom(Value*);
119
120     // Returns null for MixedTriState.
121     Value* addBoolConstant(Origin, TriState);
122
123     void resetValueOwners();
124     JS_EXPORT_PRIVATE void resetReachability();
125
126     // This destroys CFG analyses. If we ask for them again, we will recompute them. Usually you
127     // should call this anytime you call resetReachability().
128     void invalidateCFG();
129
130     JS_EXPORT_PRIVATE void dump(PrintStream&) const;
131
132     unsigned size() const { return m_blocks.size(); }
133     BasicBlock* at(unsigned index) const { return m_blocks[index].get(); }
134     BasicBlock* operator[](unsigned index) const { return at(index); }
135
136     class iterator {
137     public:
138         iterator()
139             : m_procedure(nullptr)
140             , m_index(0)
141         {
142         }
143
144         iterator(const Procedure& procedure, unsigned index)
145             : m_procedure(&procedure)
146             , m_index(findNext(index))
147         {
148         }
149
150         BasicBlock* operator*()
151         {
152             return m_procedure->at(m_index);
153         }
154
155         iterator& operator++()
156         {
157             m_index = findNext(m_index + 1);
158             return *this;
159         }
160
161         bool operator==(const iterator& other) const
162         {
163             ASSERT(m_procedure == other.m_procedure);
164             return m_index == other.m_index;
165         }
166         
167         bool operator!=(const iterator& other) const
168         {
169             return !(*this == other);
170         }
171
172     private:
173         unsigned findNext(unsigned index)
174         {
175             while (index < m_procedure->size() && !m_procedure->at(index))
176                 index++;
177             return index;
178         }
179
180         const Procedure* m_procedure;
181         unsigned m_index;
182     };
183
184     iterator begin() const { return iterator(*this, 0); }
185     iterator end() const { return iterator(*this, size()); }
186
187     Vector<BasicBlock*> blocksInPreOrder();
188     Vector<BasicBlock*> blocksInPostOrder();
189
190     SparseCollection<StackSlot>& stackSlots() { return m_stackSlots; }
191     const SparseCollection<StackSlot>& stackSlots() const { return m_stackSlots; }
192
193     // Short for stackSlots().remove(). It's better to call this method since it's out of line.
194     void deleteStackSlot(StackSlot*);
195
196     SparseCollection<Variable>& variables() { return m_variables; }
197     const SparseCollection<Variable>& variables() const { return m_variables; }
198
199     // Short for variables().remove(). It's better to call this method since it's out of line.
200     void deleteVariable(Variable*);
201
202     SparseCollection<Value>& values() { return m_values; }
203     const SparseCollection<Value>& values() const { return m_values; }
204
205     // Short for values().remove(). It's better to call this method since it's out of line.
206     void deleteValue(Value*);
207
208     // A valid procedure cannot contain any orphan values. An orphan is a value that is not in
209     // any basic block. It is possible to create an orphan value during code generation or during
210     // transformation. If you know that you may have created some, you can call this method to
211     // delete them, making the procedure valid again.
212     void deleteOrphans();
213
214     CFG& cfg() const { return *m_cfg; }
215
216     Dominators& dominators();
217
218     void addFastConstant(const ValueKey&);
219     bool isFastConstant(const ValueKey&);
220     
221     unsigned numEntrypoints() const { return m_numEntrypoints; }
222     void setNumEntrypoints(unsigned numEntrypoints) { m_numEntrypoints = numEntrypoints; }
223     
224     // Only call this after code generation is complete. Note that the label for the 0th entrypoint
225     // should point to exactly where the code generation cursor was before you started generating
226     // code.
227     JS_EXPORT_PRIVATE CCallHelpers::Label entrypointLabel(unsigned entrypointIndex) const;
228
229     // The name has to be a string literal, since we don't do any memory management for the string.
230     void setLastPhaseName(const char* name)
231     {
232         m_lastPhaseName = name;
233     }
234
235     const char* lastPhaseName() const { return m_lastPhaseName; }
236
237     // Allocates a slab of memory that will be kept alive by anyone who keeps the resulting code
238     // alive. Great for compiler-generated data sections, like switch jump tables and constant pools.
239     // This returns memory that has been zero-initialized.
240     JS_EXPORT_PRIVATE void* addDataSection(size_t);
241
242     OpaqueByproducts& byproducts() { return *m_byproducts; }
243
244     // Below are methods that make sense to call after you have generated code for the procedure.
245
246     // You have to call this method after calling generate(). The code generated by B3::generate()
247     // will require you to keep this object alive for as long as that code is runnable. Usually, this
248     // just keeps alive things like the double constant pool and switch lookup tables. If this sounds
249     // confusing, you should probably be using the B3::Compilation API to compile code. If you use
250     // that API, then you don't have to worry about this.
251     std::unique_ptr<OpaqueByproducts> releaseByproducts() { return WTFMove(m_byproducts); }
252
253     // This gives you direct access to Code. However, the idea is that clients of B3 shouldn't have to
254     // call this. So, Procedure has some methods (below) that expose some Air::Code functionality.
255     const Air::Code& code() const { return *m_code; }
256     Air::Code& code() { return *m_code; }
257
258     unsigned callArgAreaSize() const;
259     void requestCallArgAreaSize(unsigned size);
260
261     JS_EXPORT_PRIVATE unsigned frameSize() const;
262     const RegisterAtOffsetList& calleeSaveRegisters() const;
263
264     PCToOriginMap& pcToOriginMap() { return m_pcToOriginMap; }
265     PCToOriginMap releasePCToOriginMap() { return WTFMove(m_pcToOriginMap); }
266
267 private:
268     friend class BlockInsertionSet;
269
270     JS_EXPORT_PRIVATE Value* addValueImpl(Value*);
271     void setBlockOrderImpl(Vector<BasicBlock*>&);
272
273     SparseCollection<StackSlot> m_stackSlots;
274     SparseCollection<Variable> m_variables;
275     Vector<std::unique_ptr<BasicBlock>> m_blocks;
276     SparseCollection<Value> m_values;
277     std::unique_ptr<CFG> m_cfg;
278     std::unique_ptr<Dominators> m_dominators;
279     HashSet<ValueKey> m_fastConstants;
280     unsigned m_numEntrypoints { 1 };
281     const char* m_lastPhaseName;
282     std::unique_ptr<OpaqueByproducts> m_byproducts;
283     std::unique_ptr<Air::Code> m_code;
284     RefPtr<SharedTask<void(PrintStream&, Origin)>> m_originPrinter;
285     const void* m_frontendData;
286     PCToOriginMap m_pcToOriginMap;
287 };
288
289 } } // namespace JSC::B3
290
291 #endif // ENABLE(B3_JIT)
292
293 #endif // B3Procedure_h
294