1 | /*
|
---|
2 | * Copyright (C) 2013-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 | #include "config.h"
|
---|
27 | #include "DFGLICMPhase.h"
|
---|
28 |
|
---|
29 | #if ENABLE(DFG_JIT)
|
---|
30 |
|
---|
31 | #include "DFGAbstractInterpreterInlines.h"
|
---|
32 | #include "DFGAtTailAbstractState.h"
|
---|
33 | #include "DFGClobberSet.h"
|
---|
34 | #include "DFGClobberize.h"
|
---|
35 | #include "DFGControlEquivalenceAnalysis.h"
|
---|
36 | #include "DFGEdgeDominates.h"
|
---|
37 | #include "DFGGraph.h"
|
---|
38 | #include "DFGMayExit.h"
|
---|
39 | #include "DFGNaturalLoops.h"
|
---|
40 | #include "DFGPhase.h"
|
---|
41 | #include "DFGSafeToExecute.h"
|
---|
42 | #include "JSCInlines.h"
|
---|
43 |
|
---|
44 | namespace JSC { namespace DFG {
|
---|
45 |
|
---|
46 | class LICMPhase : public Phase {
|
---|
47 | static constexpr bool verbose = false;
|
---|
48 |
|
---|
49 | using NaturalLoop = SSANaturalLoop;
|
---|
50 |
|
---|
51 | struct LoopData {
|
---|
52 | ClobberSet writes;
|
---|
53 | BasicBlock* preHeader { nullptr };
|
---|
54 | };
|
---|
55 |
|
---|
56 | public:
|
---|
57 | LICMPhase(Graph& graph)
|
---|
58 | : Phase(graph, "LICM")
|
---|
59 | , m_state(graph)
|
---|
60 | , m_interpreter(graph, m_state)
|
---|
61 | {
|
---|
62 | }
|
---|
63 |
|
---|
64 | bool run()
|
---|
65 | {
|
---|
66 | DFG_ASSERT(m_graph, nullptr, m_graph.m_form == SSA);
|
---|
67 |
|
---|
68 | m_graph.ensureSSADominators();
|
---|
69 | m_graph.ensureSSANaturalLoops();
|
---|
70 | m_graph.ensureControlEquivalenceAnalysis();
|
---|
71 |
|
---|
72 | if (verbose) {
|
---|
73 | dataLog("Graph before LICM:\n");
|
---|
74 | m_graph.dump();
|
---|
75 | }
|
---|
76 |
|
---|
77 | m_data.resize(m_graph.m_ssaNaturalLoops->numLoops());
|
---|
78 |
|
---|
79 | // Figure out the set of things each loop writes to, not including blocks that
|
---|
80 | // belong to inner loops. We fix this later.
|
---|
81 | for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
|
---|
82 | BasicBlock* block = m_graph.block(blockIndex);
|
---|
83 | if (!block)
|
---|
84 | continue;
|
---|
85 |
|
---|
86 | // Skip blocks that are proved to not execute.
|
---|
87 | // FIXME: This shouldn't be needed.
|
---|
88 | // https://bugs.webkit.org/show_bug.cgi?id=128584
|
---|
89 | if (!block->cfaHasVisited)
|
---|
90 | continue;
|
---|
91 |
|
---|
92 | const NaturalLoop* loop = m_graph.m_ssaNaturalLoops->innerMostLoopOf(block);
|
---|
93 | if (!loop)
|
---|
94 | continue;
|
---|
95 | LoopData& data = m_data[loop->index()];
|
---|
96 | for (auto* node : *block) {
|
---|
97 | // Don't look beyond parts of the code that definitely always exit.
|
---|
98 | // FIXME: This shouldn't be needed.
|
---|
99 | // https://bugs.webkit.org/show_bug.cgi?id=128584
|
---|
100 | if (node->op() == ForceOSRExit)
|
---|
101 | break;
|
---|
102 |
|
---|
103 | addWrites(m_graph, node, data.writes);
|
---|
104 | }
|
---|
105 | }
|
---|
106 |
|
---|
107 | // For each loop:
|
---|
108 | // - Identify its pre-header.
|
---|
109 | // - Make sure its outer loops know what it clobbers.
|
---|
110 | for (unsigned loopIndex = m_graph.m_ssaNaturalLoops->numLoops(); loopIndex--;) {
|
---|
111 | const NaturalLoop& loop = m_graph.m_ssaNaturalLoops->loop(loopIndex);
|
---|
112 | LoopData& data = m_data[loop.index()];
|
---|
113 |
|
---|
114 | for (
|
---|
115 | const NaturalLoop* outerLoop = m_graph.m_ssaNaturalLoops->innerMostOuterLoop(loop);
|
---|
116 | outerLoop;
|
---|
117 | outerLoop = m_graph.m_ssaNaturalLoops->innerMostOuterLoop(*outerLoop))
|
---|
118 | m_data[outerLoop->index()].writes.addAll(data.writes);
|
---|
119 |
|
---|
120 | BasicBlock* header = loop.header();
|
---|
121 | BasicBlock* preHeader = nullptr;
|
---|
122 | unsigned numberOfPreHeaders = 0; // We're cool if this is 1.
|
---|
123 |
|
---|
124 | // This is guaranteed because we expect the CFG not to have unreachable code. Therefore, a
|
---|
125 | // loop header must have a predecessor. (Also, we don't allow the root block to be a loop,
|
---|
126 | // which cuts out the one other way of having a loop header with only one predecessor.)
|
---|
127 | DFG_ASSERT(m_graph, header->at(0), header->predecessors.size() > 1, header->predecessors.size());
|
---|
128 |
|
---|
129 | for (unsigned i = header->predecessors.size(); i--;) {
|
---|
130 | BasicBlock* predecessor = header->predecessors[i];
|
---|
131 | if (m_graph.m_ssaDominators->dominates(header, predecessor))
|
---|
132 | continue;
|
---|
133 |
|
---|
134 | preHeader = predecessor;
|
---|
135 | ++numberOfPreHeaders;
|
---|
136 | }
|
---|
137 |
|
---|
138 | // We need to validate the pre-header. There are a bunch of things that could be wrong
|
---|
139 | // about it:
|
---|
140 | //
|
---|
141 | // - There might be more than one. This means that pre-header creation either did not run,
|
---|
142 | // or some CFG transformation destroyed the pre-headers.
|
---|
143 | //
|
---|
144 | // - It may not be legal to exit at the pre-header. That would be a real bummer. Currently,
|
---|
145 | // LICM assumes that it can always hoist checks. See
|
---|
146 | // https://bugs.webkit.org/show_bug.cgi?id=148545. Though even with that fixed, we anyway
|
---|
147 | // would need to check if it's OK to exit at the pre-header since if we can't then we
|
---|
148 | // would have to restrict hoisting to non-exiting nodes.
|
---|
149 |
|
---|
150 | if (numberOfPreHeaders != 1)
|
---|
151 | continue;
|
---|
152 |
|
---|
153 | // This is guaranteed because the header has multiple predecessors and critical edges are
|
---|
154 | // broken. Therefore the predecessors must all have one successor, which implies that they
|
---|
155 | // must end in a Jump.
|
---|
156 | DFG_ASSERT(m_graph, preHeader->terminal(), preHeader->terminal()->op() == Jump, preHeader->terminal()->op());
|
---|
157 |
|
---|
158 | if (!preHeader->terminal()->origin.exitOK)
|
---|
159 | continue;
|
---|
160 |
|
---|
161 | data.preHeader = preHeader;
|
---|
162 | }
|
---|
163 |
|
---|
164 | m_graph.initializeNodeOwners();
|
---|
165 |
|
---|
166 | // Walk all basic blocks that belong to loops, looking for hoisting opportunities.
|
---|
167 | // We try to hoist to the outer-most loop that permits it. Hoisting is valid if:
|
---|
168 | // - The node doesn't write anything.
|
---|
169 | // - The node doesn't read anything that the loop writes.
|
---|
170 | // - The preHeader is valid (i.e. it passed the validation above).
|
---|
171 | // - The preHeader's state at tail makes the node safe to execute.
|
---|
172 | // - The loop's children all belong to nodes that strictly dominate the loop header.
|
---|
173 | // - The preHeader's state at tail is still valid. This is mostly to save compile
|
---|
174 | // time and preserve some kind of sanity, if we hoist something that must exit.
|
---|
175 | //
|
---|
176 | // Also, we need to remember to:
|
---|
177 | // - Update the state-at-tail with the node we hoisted, so future hoist candidates
|
---|
178 | // know about any type checks we hoisted.
|
---|
179 | //
|
---|
180 | // For maximum profit, we walk blocks in DFS order to ensure that we generally
|
---|
181 | // tend to hoist dominators before dominatees.
|
---|
182 | Vector<const NaturalLoop*> loopStack;
|
---|
183 | bool changed = false;
|
---|
184 |
|
---|
185 | WeakRandom random { Options::seedForLICMFuzzer() };
|
---|
186 |
|
---|
187 | for (BasicBlock* block : m_graph.blocksInPreOrder()) {
|
---|
188 | if (!block->cfaHasVisited)
|
---|
189 | continue;
|
---|
190 |
|
---|
191 | const NaturalLoop* loop = m_graph.m_ssaNaturalLoops->innerMostLoopOf(block);
|
---|
192 | if (!loop)
|
---|
193 | continue;
|
---|
194 |
|
---|
195 | loopStack.shrink(0);
|
---|
196 | for (
|
---|
197 | const NaturalLoop* current = loop;
|
---|
198 | current;
|
---|
199 | current = m_graph.m_ssaNaturalLoops->innerMostOuterLoop(*current))
|
---|
200 | loopStack.append(current);
|
---|
201 |
|
---|
202 | // Remember: the loop stack has the inner-most loop at index 0, so if we want
|
---|
203 | // to bias hoisting to outer loops then we need to use a reverse loop.
|
---|
204 |
|
---|
205 | if (verbose) {
|
---|
206 | dataLog(
|
---|
207 | "Attempting to hoist out of block ", *block, " in loops:\n");
|
---|
208 | for (unsigned stackIndex = loopStack.size(); stackIndex--;) {
|
---|
209 | dataLog(
|
---|
210 | " ", *loopStack[stackIndex], ", which writes ",
|
---|
211 | m_data[loopStack[stackIndex]->index()].writes, "\n");
|
---|
212 | }
|
---|
213 | }
|
---|
214 |
|
---|
215 | for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
|
---|
216 | Node*& nodeRef = block->at(nodeIndex);
|
---|
217 | if (nodeRef->op() == ForceOSRExit)
|
---|
218 | break;
|
---|
219 | for (unsigned stackIndex = loopStack.size(); stackIndex--;) {
|
---|
220 | if (UNLIKELY(Options::useLICMFuzzing())) {
|
---|
221 | bool shouldAttemptHoist = random.returnTrueWithProbability(Options::allowHoistingLICMProbability());
|
---|
222 | if (!shouldAttemptHoist)
|
---|
223 | continue;
|
---|
224 | }
|
---|
225 |
|
---|
226 | changed |= attemptHoist(block, nodeRef, loopStack[stackIndex]);
|
---|
227 | }
|
---|
228 | }
|
---|
229 | }
|
---|
230 |
|
---|
231 | return changed;
|
---|
232 | }
|
---|
233 |
|
---|
234 | private:
|
---|
235 | bool attemptHoist(BasicBlock* fromBlock, Node*& nodeRef, const NaturalLoop* loop)
|
---|
236 | {
|
---|
237 | Node* node = nodeRef;
|
---|
238 | LoopData& data = m_data[loop->index()];
|
---|
239 |
|
---|
240 | if (!data.preHeader) {
|
---|
241 | if (verbose)
|
---|
242 | dataLog(" Not hoisting ", node, " because the pre-header is invalid.\n");
|
---|
243 | return false;
|
---|
244 | }
|
---|
245 |
|
---|
246 | if (!data.preHeader->cfaDidFinish) {
|
---|
247 | if (verbose)
|
---|
248 | dataLog(" Not hoisting ", node, " because CFA is invalid.\n");
|
---|
249 | return false;
|
---|
250 | }
|
---|
251 |
|
---|
252 | m_state.initializeTo(data.preHeader);
|
---|
253 | ASSERT(m_state.isValid());
|
---|
254 | NodeOrigin originalOrigin = node->origin;
|
---|
255 | bool canSpeculateBlindly = !m_graph.hasGlobalExitSite(originalOrigin.semantic, HoistingFailed);
|
---|
256 |
|
---|
257 | // NOTE: We could just use BackwardsDominators here directly, since we already know that the
|
---|
258 | // preHeader dominates fromBlock. But we wouldn't get anything from being so clever, since
|
---|
259 | // dominance checks are O(1) and only a few integer compares.
|
---|
260 | bool isControlEquivalent = m_graph.m_controlEquivalenceAnalysis->dominatesEquivalently(data.preHeader, fromBlock);
|
---|
261 |
|
---|
262 | bool addsBlindSpeculation = !isControlEquivalent;
|
---|
263 | NodeOrigin terminalOrigin = data.preHeader->terminal()->origin;
|
---|
264 | Vector<Node*, 2> hoistedNodes; // This is sorted in the program order they will appear in the basic block we're hoisting to.
|
---|
265 |
|
---|
266 | auto insertHoistedNode = [&] (Node* node) {
|
---|
267 | data.preHeader->insertBeforeTerminal(node);
|
---|
268 | node->owner = data.preHeader;
|
---|
269 | node->origin = terminalOrigin.withSemantic(node->origin.semantic);
|
---|
270 | node->origin.wasHoisted |= addsBlindSpeculation;
|
---|
271 | hoistedNodes.append(node);
|
---|
272 | };
|
---|
273 |
|
---|
274 | auto updateAbstractState = [&] {
|
---|
275 | auto invalidate = [&] (const NaturalLoop* loop) {
|
---|
276 | LoopData& data = m_data[loop->index()];
|
---|
277 | data.preHeader->cfaDidFinish = false;
|
---|
278 |
|
---|
279 | for (unsigned bodyIndex = loop->size(); bodyIndex--;) {
|
---|
280 | BasicBlock* block = loop->at(bodyIndex);
|
---|
281 | if (block != data.preHeader)
|
---|
282 | block->cfaHasVisited = false;
|
---|
283 | block->cfaDidFinish = false;
|
---|
284 | }
|
---|
285 | };
|
---|
286 |
|
---|
287 | // We can trust what AI proves about edge proof statuses when hoisting to the preheader.
|
---|
288 | m_state.trustEdgeProofs();
|
---|
289 | for (unsigned i = 0; i < hoistedNodes.size(); ++i) {
|
---|
290 | if (!m_interpreter.execute(hoistedNodes[i])) {
|
---|
291 | invalidate(loop);
|
---|
292 | return;
|
---|
293 | }
|
---|
294 | }
|
---|
295 |
|
---|
296 | // However, when walking various inner loops below, the proof status of
|
---|
297 | // an edge may be trivially true, even if it's not true in the preheader
|
---|
298 | // we hoist to. We don't allow the below node executions to change the
|
---|
299 | // state of edge proofs. An example of where a proof is trivially true
|
---|
300 | // is if we have two loops, L1 and L2, where L2 is nested inside L1. The
|
---|
301 | // header for L1 dominates L2. We hoist a Check from L1's header into L1's
|
---|
302 | // preheader. However, inside L2's preheader, we can't trust that AI will
|
---|
303 | // tell us this edge is proven. It's proven in L2's preheader because L2
|
---|
304 | // is dominated by L1's header. However, the edge is not guaranteed to be
|
---|
305 | // proven inside L1's preheader.
|
---|
306 | m_state.dontTrustEdgeProofs();
|
---|
307 |
|
---|
308 | // Modify the states at the end of the preHeader of the loop we hoisted to,
|
---|
309 | // and all pre-headers inside the loop. This isn't a stability bottleneck right now
|
---|
310 | // because most loops are small and most blocks belong to few loops.
|
---|
311 | for (unsigned bodyIndex = loop->size(); bodyIndex--;) {
|
---|
312 | BasicBlock* subBlock = loop->at(bodyIndex);
|
---|
313 | const NaturalLoop* subLoop = m_graph.m_ssaNaturalLoops->headerOf(subBlock);
|
---|
314 | if (!subLoop)
|
---|
315 | continue;
|
---|
316 | BasicBlock* subPreHeader = m_data[subLoop->index()].preHeader;
|
---|
317 | // We may not have given this loop a pre-header because either it didn't have exitOK
|
---|
318 | // or the header had multiple predecessors that it did not dominate. In that case the
|
---|
319 | // loop wouldn't be a hoisting candidate anyway, so we don't have to do anything.
|
---|
320 | if (!subPreHeader)
|
---|
321 | continue;
|
---|
322 | // The pre-header's tail may be unreachable, in which case we have nothing to do.
|
---|
323 | if (!subPreHeader->cfaDidFinish)
|
---|
324 | continue;
|
---|
325 | // We handled this above.
|
---|
326 | if (subPreHeader == data.preHeader)
|
---|
327 | continue;
|
---|
328 | m_state.initializeTo(subPreHeader);
|
---|
329 | for (unsigned i = 0; i < hoistedNodes.size(); ++i) {
|
---|
330 | if (!m_interpreter.execute(hoistedNodes[i])) {
|
---|
331 | invalidate(subLoop);
|
---|
332 | break;
|
---|
333 | }
|
---|
334 | }
|
---|
335 | }
|
---|
336 | };
|
---|
337 |
|
---|
338 | auto tryHoistChecks = [&] {
|
---|
339 | if (addsBlindSpeculation && !canSpeculateBlindly)
|
---|
340 | return false;
|
---|
341 |
|
---|
342 | ASSERT(hoistedNodes.isEmpty());
|
---|
343 |
|
---|
344 | Vector<Edge, 3> checks;
|
---|
345 | m_graph.doToChildren(node, [&] (Edge edge) {
|
---|
346 | if (!m_graph.m_ssaDominators->dominates(edge.node()->owner, data.preHeader))
|
---|
347 | return;
|
---|
348 |
|
---|
349 | if (!edge.willHaveCheck())
|
---|
350 | return;
|
---|
351 |
|
---|
352 | if ((m_state.forNode(edge).m_type & SpecEmpty) && checkMayCrashIfInputIsEmpty(edge.useKind())) {
|
---|
353 | if (!canSpeculateBlindly)
|
---|
354 | return;
|
---|
355 | Node* checkNotEmpty = m_graph.addNode(CheckNotEmpty, originalOrigin, Edge(edge.node(), UntypedUse));
|
---|
356 | insertHoistedNode(checkNotEmpty);
|
---|
357 | }
|
---|
358 |
|
---|
359 | checks.append(edge);
|
---|
360 | });
|
---|
361 |
|
---|
362 | if (checks.isEmpty())
|
---|
363 | return false;
|
---|
364 |
|
---|
365 | AdjacencyList children;
|
---|
366 | NodeType checkOp = Check;
|
---|
367 | if (checks.size() <= AdjacencyList::Size) {
|
---|
368 | children = AdjacencyList(AdjacencyList::Fixed);
|
---|
369 | for (unsigned i = 0; i < checks.size(); ++i)
|
---|
370 | children.setChild(i, checks[i]);
|
---|
371 | } else {
|
---|
372 | checkOp = CheckVarargs;
|
---|
373 | unsigned firstChild = m_graph.m_varArgChildren.size();
|
---|
374 | for (Edge edge : checks)
|
---|
375 | m_graph.m_varArgChildren.append(edge);
|
---|
376 | children = AdjacencyList(AdjacencyList::Variable, firstChild, checks.size());
|
---|
377 | }
|
---|
378 |
|
---|
379 | Node* check = m_graph.addNode(checkOp, originalOrigin, children);
|
---|
380 | insertHoistedNode(check);
|
---|
381 | updateAbstractState();
|
---|
382 |
|
---|
383 | if (verbose)
|
---|
384 | dataLogLn(" Hoisted some checks from ", node, " and created a new Check ", check, ". Hoisted from ", *fromBlock, " to ", *data.preHeader);
|
---|
385 |
|
---|
386 | return true;
|
---|
387 | };
|
---|
388 |
|
---|
389 | if (!edgesDominate(m_graph, node, data.preHeader)) {
|
---|
390 | if (verbose) {
|
---|
391 | dataLog(
|
---|
392 | " Not hoisting ", node, " because it isn't loop invariant.\n");
|
---|
393 | }
|
---|
394 | return tryHoistChecks();
|
---|
395 | }
|
---|
396 |
|
---|
397 | if (doesWrites(m_graph, node)) {
|
---|
398 | if (verbose)
|
---|
399 | dataLog(" Not hoisting ", node, " because it writes things.\n");
|
---|
400 | return tryHoistChecks();
|
---|
401 | }
|
---|
402 |
|
---|
403 | // It's not safe to consult the AbstractState inside mayExit until we prove all edges
|
---|
404 | // dominate the pre-header we're hoisting to. We are more conservative above when assigning
|
---|
405 | // to this variable since we hadn't yet proven all edges dominate the pre-header. Above, we
|
---|
406 | // just assume mayExit is true. We refine that here since we can now consult the AbstractState.
|
---|
407 | addsBlindSpeculation = mayExit(m_graph, node, m_state) && !isControlEquivalent;
|
---|
408 |
|
---|
409 | if (readsOverlap(m_graph, node, data.writes)) {
|
---|
410 | if (verbose) {
|
---|
411 | dataLog(
|
---|
412 | " Not hoisting ", node,
|
---|
413 | " because it reads things that the loop writes.\n");
|
---|
414 | }
|
---|
415 | return tryHoistChecks();
|
---|
416 | }
|
---|
417 |
|
---|
418 | if (addsBlindSpeculation && !canSpeculateBlindly) {
|
---|
419 | if (verbose) {
|
---|
420 | dataLog(
|
---|
421 | " Not hoisting ", node, " because it may exit and the pre-header (",
|
---|
422 | *data.preHeader, ") is not control equivalent to the node's original block (",
|
---|
423 | *fromBlock, ") and hoisting had previously failed.\n");
|
---|
424 | }
|
---|
425 | return tryHoistChecks();
|
---|
426 | }
|
---|
427 |
|
---|
428 | if (!safeToExecute(m_state, m_graph, node)) {
|
---|
429 | // See if we can rescue the situation by inserting blind speculations.
|
---|
430 | bool ignoreEmptyChildren = true;
|
---|
431 | if (canSpeculateBlindly
|
---|
432 | && safeToExecute(m_state, m_graph, node, ignoreEmptyChildren)) {
|
---|
433 | if (verbose) {
|
---|
434 | dataLog(
|
---|
435 | " Rescuing hoisting by inserting empty checks.\n");
|
---|
436 | }
|
---|
437 | m_graph.doToChildren(
|
---|
438 | node,
|
---|
439 | [&] (Edge& edge) {
|
---|
440 | if (!(m_state.forNode(edge).m_type & SpecEmpty))
|
---|
441 | return;
|
---|
442 |
|
---|
443 | Node* check = m_graph.addNode(CheckNotEmpty, originalOrigin, Edge(edge.node(), UntypedUse));
|
---|
444 | insertHoistedNode(check);
|
---|
445 | });
|
---|
446 | } else {
|
---|
447 | if (verbose) {
|
---|
448 | dataLog(
|
---|
449 | " Not hoisting ", node, " because it isn't safe to execute.\n");
|
---|
450 | }
|
---|
451 | return tryHoistChecks();
|
---|
452 | }
|
---|
453 | }
|
---|
454 |
|
---|
455 | if (verbose) {
|
---|
456 | dataLog(
|
---|
457 | " Hoisting ", node, " from ", *fromBlock, " to ", *data.preHeader,
|
---|
458 | "\n");
|
---|
459 | }
|
---|
460 |
|
---|
461 | insertHoistedNode(node);
|
---|
462 | updateAbstractState();
|
---|
463 |
|
---|
464 | if (node->flags() & NodeHasVarArgs)
|
---|
465 | nodeRef = m_graph.addNode(CheckVarargs, originalOrigin, m_graph.copyVarargChildren(node));
|
---|
466 | else
|
---|
467 | nodeRef = m_graph.addNode(Check, originalOrigin, node->children);
|
---|
468 |
|
---|
469 | return true;
|
---|
470 | }
|
---|
471 |
|
---|
472 | AtTailAbstractState m_state;
|
---|
473 | AbstractInterpreter<AtTailAbstractState> m_interpreter;
|
---|
474 | Vector<LoopData> m_data;
|
---|
475 | };
|
---|
476 |
|
---|
477 | bool performLICM(Graph& graph)
|
---|
478 | {
|
---|
479 | return runPhase<LICMPhase>(graph);
|
---|
480 | }
|
---|
481 |
|
---|
482 | } } // namespace JSC::DFG
|
---|
483 |
|
---|
484 | #endif // ENABLE(DFG_JIT)
|
---|
485 |
|
---|