1 | /* |
2 | * Copyright (C) 2011-2019 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 | #pragma once |
27 | |
28 | #include "DFGCompilationMode.h" |
29 | |
30 | #if ENABLE(DFG_JIT) |
31 | |
32 | #include "Options.h" |
33 | #include <limits.h> |
34 | #include <wtf/text/StringImpl.h> |
35 | |
36 | namespace JSC { namespace DFG { |
37 | |
38 | struct Node; |
39 | |
40 | typedef uint32_t BlockIndex; |
41 | static constexpr BlockIndex NoBlock = UINT_MAX; |
42 | |
43 | // Use RefChildren if the child ref counts haven't already been adjusted using |
44 | // other means and either of the following is true: |
45 | // - The node you're creating is MustGenerate. |
46 | // - The place where you're inserting a reference to the node you're creating |
47 | // will not also do RefChildren. |
48 | enum RefChildrenMode { |
49 | RefChildren, |
50 | DontRefChildren |
51 | }; |
52 | |
53 | // Use RefNode if you know that the node will be used from another node, and you |
54 | // will not already be ref'ing the node to account for that use. |
55 | enum RefNodeMode { |
56 | RefNode, |
57 | DontRefNode |
58 | }; |
59 | |
60 | enum SwitchKind { |
61 | SwitchImm, |
62 | SwitchChar, |
63 | SwitchString, |
64 | SwitchCell |
65 | }; |
66 | |
67 | inline bool verboseCompilationEnabled(CompilationMode mode = DFGMode) |
68 | { |
69 | return Options::verboseCompilation() || Options::dumpGraphAtEachPhase() || (isFTL(mode) && Options::verboseFTLCompilation()); |
70 | } |
71 | |
72 | inline bool logCompilationChanges(CompilationMode mode = DFGMode) |
73 | { |
74 | return verboseCompilationEnabled(mode) || Options::logCompilationChanges(); |
75 | } |
76 | |
77 | inline bool shouldDumpGraphAtEachPhase(CompilationMode mode) |
78 | { |
79 | if (isFTL(mode)) |
80 | return Options::dumpGraphAtEachPhase() || Options::dumpDFGFTLGraphAtEachPhase(); |
81 | return Options::dumpGraphAtEachPhase() || Options::dumpDFGGraphAtEachPhase(); |
82 | } |
83 | |
84 | inline bool validationEnabled() |
85 | { |
86 | #if !ASSERT_DISABLED |
87 | return true; |
88 | #else |
89 | return Options::validateGraph() || Options::validateGraphAtEachPhase(); |
90 | #endif |
91 | } |
92 | |
93 | inline bool enableInt52() |
94 | { |
95 | #if USE(JSVALUE64) |
96 | return true; |
97 | #else |
98 | return false; |
99 | #endif |
100 | } |
101 | |
102 | // The prediction propagator effectively does four passes, with the last pass |
103 | // being done by the separate FixuPhase. |
104 | enum PredictionPass { |
105 | // We're converging in a straight-forward forward flow fixpoint. This is the |
106 | // most conventional part of the propagator - it makes only monotonic decisions |
107 | // based on value profiles and rare case profiles. It ignores baseline JIT rare |
108 | // case profiles. The goal here is to develop a good guess of which variables |
109 | // are likely to be purely numerical, which generally doesn't require knowing |
110 | // the rare case profiles. |
111 | PrimaryPass, |
112 | |
113 | // At this point we know what is numerical and what isn't. Non-numerical inputs |
114 | // to arithmetic operations will not have useful information in the Baseline JIT |
115 | // rare case profiles because Baseline may take slow path on non-numerical |
116 | // inputs even if the DFG could handle the input on the fast path. Boolean |
117 | // inputs are the most obvious example. This pass of prediction propagation will |
118 | // use Baseline rare case profiles for purely numerical operations and it will |
119 | // ignore them for everything else. The point of this pass is to develop a good |
120 | // guess of which variables are likely to be doubles. |
121 | // |
122 | // This pass is intentionally weird and goes against what is considered good |
123 | // form when writing a static analysis: a new data flow of booleans will cause |
124 | // us to ignore rare case profiles except that by then, we will have already |
125 | // propagated double types based on our prior assumption that we shouldn't |
126 | // ignore rare cases. This probably won't happen because the PrimaryPass is |
127 | // almost certainly going to establish what is and isn't numerical. But it's |
128 | // conceivable that during this pass we will discover a new boolean data flow. |
129 | // This ends up being sound because the prediction propagator could literally |
130 | // make any guesses it wants and still be sound (worst case, we OSR exit more |
131 | // often or use too general of types are run a bit slower). This will converge |
132 | // because we force monotonicity on the types of nodes and variables. So, the |
133 | // worst thing that can happen is that we violate basic laws of theoretical |
134 | // decency. |
135 | RareCasePass, |
136 | |
137 | // At this point we know what is numerical and what isn't, and we also know what |
138 | // is a double and what isn't. So, we start forcing variables to be double. |
139 | // Doing so may have a cascading effect so this is a fixpoint. It's monotonic |
140 | // in the sense that once a variable is forced double, it cannot be forced in |
141 | // the other direction. |
142 | DoubleVotingPass, |
143 | |
144 | // This pass occurs once we have converged. At this point we are just installing |
145 | // type checks based on the conclusions we have already reached. It's important |
146 | // for this pass to reach the same conclusions that DoubleVotingPass reached. |
147 | FixupPass |
148 | }; |
149 | |
150 | enum StructureRegistrationState { HaveNotStartedRegistering, AllStructuresAreRegistered }; |
151 | |
152 | enum StructureRegistrationResult { StructureRegisteredNormally, StructureRegisteredAndWatched }; |
153 | |
154 | enum OptimizationFixpointState { BeforeFixpoint, FixpointNotConverged, FixpointConverged }; |
155 | |
156 | // Describes the form you can expect the entire graph to be in. |
157 | enum GraphForm { |
158 | // LoadStore form means that basic blocks may freely use GetLocal, SetLocal, |
159 | // and Flush for accessing local variables and indicating where their live |
160 | // ranges ought to be. Data flow between local accesses is implicit. Liveness |
161 | // is only explicit at block heads (variablesAtHead). This is only used by |
162 | // the DFG simplifier and is only preserved by same. |
163 | // |
164 | // For example, LoadStore form gives no easy way to determine which SetLocal's |
165 | // flow into a GetLocal. As well, LoadStore form implies no restrictions on |
166 | // redundancy: you can freely emit multiple GetLocals, or multiple SetLocals |
167 | // (or any combination thereof) to the same local in the same block. LoadStore |
168 | // form does not require basic blocks to declare how they affect or use locals, |
169 | // other than implicitly by using the local ops and by preserving |
170 | // variablesAtHead. Finally, LoadStore allows flexibility in how liveness of |
171 | // locals is extended; for example you can replace a GetLocal with a Phantom |
172 | // and so long as the Phantom retains the GetLocal's children (i.e. the Phi |
173 | // most likely) then it implies that the local is still live but that it need |
174 | // not be stored to the stack necessarily. This implies that Phantom can |
175 | // reference nodes that have no result, as long as those nodes are valid |
176 | // GetLocal children (i.e. Phi, SetLocal, SetArgumentDefinitely, SetArgumentMaybe). |
177 | // |
178 | // LoadStore form also implies that Phis need not have children. By default, |
179 | // they end up having no children if you enter LoadStore using the canonical |
180 | // way (call Graph::dethread). |
181 | // |
182 | // LoadStore form is suitable for CFG transformations, as well as strength |
183 | // reduction, folding, and CSE. |
184 | LoadStore, |
185 | |
186 | // ThreadedCPS form means that basic blocks list up-front which locals they |
187 | // expect to be live at the head, and which locals they make available at the |
188 | // tail. ThreadedCPS form also implies that: |
189 | // |
190 | // - GetLocals and SetLocals are not redundant within a basic block. |
191 | // |
192 | // - All GetLocals and Flushes are linked directly to the last access point |
193 | // of the variable, which must not be another GetLocal. |
194 | // |
195 | // - Phantom(Phi) is not legal, but PhantomLocal is. |
196 | // |
197 | // ThreadedCPS form is suitable for data flow analysis (CFA, prediction |
198 | // propagation), register allocation, and code generation. |
199 | ThreadedCPS, |
200 | |
201 | // SSA form. See DFGSSAConversionPhase.h for a description. |
202 | SSA |
203 | }; |
204 | |
205 | // Describes the state of the UnionFind structure of VariableAccessData's. |
206 | enum UnificationState { |
207 | // BasicBlock-local accesses to variables are appropriately unified with each other. |
208 | LocallyUnified, |
209 | |
210 | // Unification has been performed globally. |
211 | GloballyUnified |
212 | }; |
213 | |
214 | // Describes how reference counts in the graph behave. |
215 | enum RefCountState { |
216 | // Everything has refCount() == 1. |
217 | EverythingIsLive, |
218 | |
219 | // Set after DCE has run. |
220 | ExactRefCount |
221 | }; |
222 | |
223 | enum OperandSpeculationMode { AutomaticOperandSpeculation, ManualOperandSpeculation }; |
224 | |
225 | enum ProofStatus { NeedsCheck, IsProved }; |
226 | |
227 | inline bool isProved(ProofStatus proofStatus) |
228 | { |
229 | ASSERT(proofStatus == IsProved || proofStatus == NeedsCheck); |
230 | return proofStatus == IsProved; |
231 | } |
232 | |
233 | inline ProofStatus proofStatusForIsProved(bool isProved) |
234 | { |
235 | return isProved ? IsProved : NeedsCheck; |
236 | } |
237 | |
238 | enum KillStatus { DoesNotKill, DoesKill }; |
239 | |
240 | inline bool doesKill(KillStatus killStatus) |
241 | { |
242 | ASSERT(killStatus == DoesNotKill || killStatus == DoesKill); |
243 | return killStatus == DoesKill; |
244 | } |
245 | |
246 | inline KillStatus killStatusForDoesKill(bool doesKill) |
247 | { |
248 | return doesKill ? DoesKill : DoesNotKill; |
249 | } |
250 | |
251 | enum class PlanStage { |
252 | Initial, |
253 | AfterFixup |
254 | }; |
255 | |
256 | // If possible, this will acquire a lock to make sure that if multiple threads |
257 | // start crashing at the same time, you get coherent dump output. Use this only |
258 | // when you're forcing a crash with diagnostics. |
259 | void startCrashing(); |
260 | |
261 | JS_EXPORT_PRIVATE bool isCrashing(); |
262 | |
263 | struct NodeAndIndex { |
264 | NodeAndIndex() |
265 | : node(nullptr) |
266 | , index(UINT_MAX) |
267 | { |
268 | } |
269 | |
270 | NodeAndIndex(Node* node, unsigned index) |
271 | : node(node) |
272 | , index(index) |
273 | { |
274 | ASSERT(!node == (index == UINT_MAX)); |
275 | } |
276 | |
277 | bool operator!() const |
278 | { |
279 | return !node; |
280 | } |
281 | |
282 | Node* node; |
283 | unsigned index; |
284 | }; |
285 | |
286 | // A less-than operator for strings that is useful for generating string switches. Sorts by < |
287 | // relation on characters. Ensures that if a is a prefix of b, then a < b. |
288 | bool stringLessThan(StringImpl& a, StringImpl& b); |
289 | |
290 | } } // namespace JSC::DFG |
291 | |
292 | namespace WTF { |
293 | |
294 | void printInternal(PrintStream&, JSC::DFG::OptimizationFixpointState); |
295 | void printInternal(PrintStream&, JSC::DFG::GraphForm); |
296 | void printInternal(PrintStream&, JSC::DFG::UnificationState); |
297 | void printInternal(PrintStream&, JSC::DFG::RefCountState); |
298 | void printInternal(PrintStream&, JSC::DFG::ProofStatus); |
299 | |
300 | } // namespace WTF |
301 | |
302 | #endif // ENABLE(DFG_JIT) |
303 | |
304 | namespace JSC { namespace DFG { |
305 | |
306 | // Put things here that must be defined even if ENABLE(DFG_JIT) is false. |
307 | |
308 | enum CapabilityLevel { |
309 | CannotCompile, |
310 | CanCompile, |
311 | CanCompileAndInline, |
312 | CapabilityLevelNotSet |
313 | }; |
314 | |
315 | inline bool canCompile(CapabilityLevel level) |
316 | { |
317 | switch (level) { |
318 | case CanCompile: |
319 | case CanCompileAndInline: |
320 | return true; |
321 | default: |
322 | return false; |
323 | } |
324 | } |
325 | |
326 | inline bool canInline(CapabilityLevel level) |
327 | { |
328 | switch (level) { |
329 | case CanCompileAndInline: |
330 | return true; |
331 | default: |
332 | return false; |
333 | } |
334 | } |
335 | |
336 | inline CapabilityLevel leastUpperBound(CapabilityLevel a, CapabilityLevel b) |
337 | { |
338 | switch (a) { |
339 | case CannotCompile: |
340 | return CannotCompile; |
341 | case CanCompile: |
342 | switch (b) { |
343 | case CanCompile: |
344 | case CanCompileAndInline: |
345 | return CanCompile; |
346 | default: |
347 | return CannotCompile; |
348 | } |
349 | case CanCompileAndInline: |
350 | return b; |
351 | case CapabilityLevelNotSet: |
352 | ASSERT_NOT_REACHED(); |
353 | return CannotCompile; |
354 | } |
355 | ASSERT_NOT_REACHED(); |
356 | return CannotCompile; |
357 | } |
358 | |
359 | // Unconditionally disable DFG disassembly support if the DFG is not compiled in. |
360 | inline bool shouldDumpDisassembly(CompilationMode mode = DFGMode) |
361 | { |
362 | #if ENABLE(DFG_JIT) |
363 | return Options::dumpDisassembly() || Options::dumpDFGDisassembly() || (isFTL(mode) && Options::dumpFTLDisassembly()); |
364 | #else |
365 | UNUSED_PARAM(mode); |
366 | return false; |
367 | #endif |
368 | } |
369 | |
370 | } } // namespace JSC::DFG |
371 | |
372 | namespace WTF { |
373 | |
374 | void printInternal(PrintStream&, JSC::DFG::CapabilityLevel); |
375 | |
376 | } // namespace WTF |
377 | |