]>
Commit | Line | Data |
---|---|---|
6fe7ccc8 A |
1 | /* |
2 | * Copyright (C) 2011 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 "DFGCSEPhase.h" | |
28 | ||
29 | #if ENABLE(DFG_JIT) | |
30 | ||
31 | #include "DFGGraph.h" | |
32 | #include "DFGPhase.h" | |
33 | ||
34 | namespace JSC { namespace DFG { | |
35 | ||
36 | class CSEPhase : public Phase { | |
37 | public: | |
38 | CSEPhase(Graph& graph) | |
39 | : Phase(graph, "common subexpression elimination") | |
40 | { | |
41 | // Replacements are used to implement local common subexpression elimination. | |
42 | m_replacements.resize(m_graph.size()); | |
43 | ||
44 | for (unsigned i = 0; i < m_graph.size(); ++i) | |
45 | m_replacements[i] = NoNode; | |
46 | } | |
47 | ||
48 | void run() | |
49 | { | |
50 | for (unsigned block = 0; block < m_graph.m_blocks.size(); ++block) | |
51 | performBlockCSE(*m_graph.m_blocks[block]); | |
52 | } | |
53 | ||
54 | private: | |
55 | ||
56 | NodeIndex canonicalize(NodeIndex nodeIndex) | |
57 | { | |
58 | if (nodeIndex == NoNode) | |
59 | return NoNode; | |
60 | ||
61 | if (m_graph[nodeIndex].op() == ValueToInt32) | |
62 | nodeIndex = m_graph[nodeIndex].child1().index(); | |
63 | ||
64 | return nodeIndex; | |
65 | } | |
66 | NodeIndex canonicalize(Edge nodeUse) | |
67 | { | |
68 | return canonicalize(nodeUse.indexUnchecked()); | |
69 | } | |
70 | ||
71 | unsigned endIndexForPureCSE() | |
72 | { | |
73 | unsigned result = m_lastSeen[m_graph[m_compileIndex].op()]; | |
74 | if (result == UINT_MAX) | |
75 | result = 0; | |
76 | else | |
77 | result++; | |
78 | ASSERT(result <= m_indexInBlock); | |
79 | #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) | |
80 | dataLog(" limit %u: ", result); | |
81 | #endif | |
82 | return result; | |
83 | } | |
84 | ||
85 | NodeIndex pureCSE(Node& node) | |
86 | { | |
87 | NodeIndex child1 = canonicalize(node.child1()); | |
88 | NodeIndex child2 = canonicalize(node.child2()); | |
89 | NodeIndex child3 = canonicalize(node.child3()); | |
90 | ||
91 | for (unsigned i = endIndexForPureCSE(); i--;) { | |
92 | NodeIndex index = m_currentBlock->at(i); | |
93 | if (index == child1 || index == child2 || index == child3) | |
94 | break; | |
95 | ||
96 | Node& otherNode = m_graph[index]; | |
97 | if (node.op() != otherNode.op()) | |
98 | continue; | |
99 | ||
100 | if (node.arithNodeFlags() != otherNode.arithNodeFlags()) | |
101 | continue; | |
102 | ||
103 | NodeIndex otherChild = canonicalize(otherNode.child1()); | |
104 | if (otherChild == NoNode) | |
105 | return index; | |
106 | if (otherChild != child1) | |
107 | continue; | |
108 | ||
109 | otherChild = canonicalize(otherNode.child2()); | |
110 | if (otherChild == NoNode) | |
111 | return index; | |
112 | if (otherChild != child2) | |
113 | continue; | |
114 | ||
115 | otherChild = canonicalize(otherNode.child3()); | |
116 | if (otherChild == NoNode) | |
117 | return index; | |
118 | if (otherChild != child3) | |
119 | continue; | |
120 | ||
121 | return index; | |
122 | } | |
123 | return NoNode; | |
124 | } | |
125 | ||
126 | bool isPredictedNumerical(Node& node) | |
127 | { | |
128 | PredictedType left = m_graph[node.child1()].prediction(); | |
129 | PredictedType right = m_graph[node.child2()].prediction(); | |
130 | return isNumberPrediction(left) && isNumberPrediction(right); | |
131 | } | |
132 | ||
133 | bool logicalNotIsPure(Node& node) | |
134 | { | |
135 | PredictedType prediction = m_graph[node.child1()].prediction(); | |
136 | return isBooleanPrediction(prediction) || !prediction; | |
137 | } | |
138 | ||
139 | bool byValIsPure(Node& node) | |
140 | { | |
141 | return m_graph[node.child2()].shouldSpeculateInteger() | |
142 | && ((node.op() == PutByVal || node.op() == PutByValAlias) | |
143 | ? isActionableMutableArrayPrediction(m_graph[node.child1()].prediction()) | |
144 | : isActionableArrayPrediction(m_graph[node.child1()].prediction())); | |
145 | } | |
146 | ||
147 | bool clobbersWorld(NodeIndex nodeIndex) | |
148 | { | |
149 | Node& node = m_graph[nodeIndex]; | |
150 | if (node.flags() & NodeClobbersWorld) | |
151 | return true; | |
152 | if (!(node.flags() & NodeMightClobber)) | |
153 | return false; | |
154 | switch (node.op()) { | |
155 | case ValueAdd: | |
156 | case CompareLess: | |
157 | case CompareLessEq: | |
158 | case CompareGreater: | |
159 | case CompareGreaterEq: | |
160 | case CompareEq: | |
161 | return !isPredictedNumerical(node); | |
162 | case LogicalNot: | |
163 | return !logicalNotIsPure(node); | |
164 | case GetByVal: | |
165 | return !byValIsPure(node); | |
166 | default: | |
167 | ASSERT_NOT_REACHED(); | |
168 | return true; // If by some oddity we hit this case in release build it's safer to have CSE assume the worst. | |
169 | } | |
170 | } | |
171 | ||
172 | NodeIndex impureCSE(Node& node) | |
173 | { | |
174 | NodeIndex child1 = canonicalize(node.child1()); | |
175 | NodeIndex child2 = canonicalize(node.child2()); | |
176 | NodeIndex child3 = canonicalize(node.child3()); | |
177 | ||
178 | for (unsigned i = m_indexInBlock; i--;) { | |
179 | NodeIndex index = m_currentBlock->at(i); | |
180 | if (index == child1 || index == child2 || index == child3) | |
181 | break; | |
182 | ||
183 | Node& otherNode = m_graph[index]; | |
184 | if (node.op() == otherNode.op() | |
185 | && node.arithNodeFlags() == otherNode.arithNodeFlags()) { | |
186 | NodeIndex otherChild = canonicalize(otherNode.child1()); | |
187 | if (otherChild == NoNode) | |
188 | return index; | |
189 | if (otherChild == child1) { | |
190 | otherChild = canonicalize(otherNode.child2()); | |
191 | if (otherChild == NoNode) | |
192 | return index; | |
193 | if (otherChild == child2) { | |
194 | otherChild = canonicalize(otherNode.child3()); | |
195 | if (otherChild == NoNode) | |
196 | return index; | |
197 | if (otherChild == child3) | |
198 | return index; | |
199 | } | |
200 | } | |
201 | } | |
202 | if (clobbersWorld(index)) | |
203 | break; | |
204 | } | |
205 | return NoNode; | |
206 | } | |
207 | ||
208 | NodeIndex globalVarLoadElimination(unsigned varNumber, JSGlobalObject* globalObject) | |
209 | { | |
210 | for (unsigned i = m_indexInBlock; i--;) { | |
211 | NodeIndex index = m_currentBlock->at(i); | |
212 | Node& node = m_graph[index]; | |
213 | switch (node.op()) { | |
214 | case GetGlobalVar: | |
215 | if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject) | |
216 | return index; | |
217 | break; | |
218 | case PutGlobalVar: | |
219 | if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject) | |
220 | return node.child1().index(); | |
221 | break; | |
222 | default: | |
223 | break; | |
224 | } | |
225 | if (clobbersWorld(index)) | |
226 | break; | |
227 | } | |
228 | return NoNode; | |
229 | } | |
230 | ||
231 | NodeIndex getByValLoadElimination(NodeIndex child1, NodeIndex child2) | |
232 | { | |
233 | for (unsigned i = m_indexInBlock; i--;) { | |
234 | NodeIndex index = m_currentBlock->at(i); | |
235 | if (index == child1 || index == canonicalize(child2)) | |
236 | break; | |
237 | ||
238 | Node& node = m_graph[index]; | |
239 | switch (node.op()) { | |
240 | case GetByVal: | |
241 | if (!byValIsPure(node)) | |
242 | return NoNode; | |
243 | if (node.child1() == child1 && canonicalize(node.child2()) == canonicalize(child2)) | |
244 | return index; | |
245 | break; | |
246 | case PutByVal: | |
247 | case PutByValAlias: | |
248 | if (!byValIsPure(node)) | |
249 | return NoNode; | |
250 | if (node.child1() == child1 && canonicalize(node.child2()) == canonicalize(child2)) | |
251 | return node.child3().index(); | |
252 | // We must assume that the PutByVal will clobber the location we're getting from. | |
253 | // FIXME: We can do better; if we know that the PutByVal is accessing an array of a | |
254 | // different type than the GetByVal, then we know that they won't clobber each other. | |
255 | return NoNode; | |
256 | case PutStructure: | |
257 | case PutByOffset: | |
258 | // GetByVal currently always speculates that it's accessing an | |
259 | // array with an integer index, which means that it's impossible | |
260 | // for a structure change or a put to property storage to affect | |
261 | // the GetByVal. | |
262 | break; | |
263 | case ArrayPush: | |
264 | // A push cannot affect previously existing elements in the array. | |
265 | break; | |
266 | default: | |
267 | if (clobbersWorld(index)) | |
268 | return NoNode; | |
269 | break; | |
270 | } | |
271 | } | |
272 | return NoNode; | |
273 | } | |
274 | ||
275 | bool checkFunctionElimination(JSFunction* function, NodeIndex child1) | |
276 | { | |
277 | for (unsigned i = endIndexForPureCSE(); i--;) { | |
278 | NodeIndex index = m_currentBlock->at(i); | |
279 | if (index == child1) | |
280 | break; | |
281 | ||
282 | Node& node = m_graph[index]; | |
283 | if (node.op() == CheckFunction && node.child1() == child1 && node.function() == function) | |
284 | return true; | |
285 | } | |
286 | return false; | |
287 | } | |
288 | ||
289 | bool checkStructureLoadElimination(const StructureSet& structureSet, NodeIndex child1) | |
290 | { | |
291 | for (unsigned i = m_indexInBlock; i--;) { | |
292 | NodeIndex index = m_currentBlock->at(i); | |
293 | if (index == child1) | |
294 | break; | |
295 | ||
296 | Node& node = m_graph[index]; | |
297 | switch (node.op()) { | |
298 | case CheckStructure: | |
299 | if (node.child1() == child1 | |
300 | && structureSet.isSupersetOf(node.structureSet())) | |
301 | return true; | |
302 | break; | |
303 | ||
304 | case PutStructure: | |
305 | if (node.child1() == child1 | |
306 | && structureSet.contains(node.structureTransitionData().newStructure)) | |
307 | return true; | |
308 | if (structureSet.contains(node.structureTransitionData().previousStructure)) | |
309 | return false; | |
310 | break; | |
311 | ||
312 | case PutByOffset: | |
313 | // Setting a property cannot change the structure. | |
314 | break; | |
315 | ||
316 | case PutByVal: | |
317 | case PutByValAlias: | |
318 | if (byValIsPure(node)) { | |
319 | // If PutByVal speculates that it's accessing an array with an | |
320 | // integer index, then it's impossible for it to cause a structure | |
321 | // change. | |
322 | break; | |
323 | } | |
324 | return false; | |
325 | ||
326 | default: | |
327 | if (clobbersWorld(index)) | |
328 | return false; | |
329 | break; | |
330 | } | |
331 | } | |
332 | return false; | |
333 | } | |
334 | ||
335 | NodeIndex getByOffsetLoadElimination(unsigned identifierNumber, NodeIndex child1) | |
336 | { | |
337 | for (unsigned i = m_indexInBlock; i--;) { | |
338 | NodeIndex index = m_currentBlock->at(i); | |
339 | if (index == child1) | |
340 | break; | |
341 | ||
342 | Node& node = m_graph[index]; | |
343 | switch (node.op()) { | |
344 | case GetByOffset: | |
345 | if (node.child1() == child1 | |
346 | && m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber) | |
347 | return index; | |
348 | break; | |
349 | ||
350 | case PutByOffset: | |
351 | if (m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber) { | |
352 | if (node.child2() == child1) | |
353 | return node.child3().index(); | |
354 | return NoNode; | |
355 | } | |
356 | break; | |
357 | ||
358 | case PutStructure: | |
359 | // Changing the structure cannot change the outcome of a property get. | |
360 | break; | |
361 | ||
362 | case PutByVal: | |
363 | case PutByValAlias: | |
364 | if (byValIsPure(node)) { | |
365 | // If PutByVal speculates that it's accessing an array with an | |
366 | // integer index, then it's impossible for it to cause a structure | |
367 | // change. | |
368 | break; | |
369 | } | |
370 | return NoNode; | |
371 | ||
372 | default: | |
373 | if (clobbersWorld(index)) | |
374 | return NoNode; | |
375 | break; | |
376 | } | |
377 | } | |
378 | return NoNode; | |
379 | } | |
380 | ||
381 | NodeIndex getPropertyStorageLoadElimination(NodeIndex child1) | |
382 | { | |
383 | for (unsigned i = m_indexInBlock; i--;) { | |
384 | NodeIndex index = m_currentBlock->at(i); | |
385 | if (index == child1) | |
386 | break; | |
387 | ||
388 | Node& node = m_graph[index]; | |
389 | switch (node.op()) { | |
390 | case GetPropertyStorage: | |
391 | if (node.child1() == child1) | |
392 | return index; | |
393 | break; | |
394 | ||
395 | case PutByOffset: | |
396 | case PutStructure: | |
397 | // Changing the structure or putting to the storage cannot | |
398 | // change the property storage pointer. | |
399 | break; | |
400 | ||
401 | case PutByVal: | |
402 | case PutByValAlias: | |
403 | if (byValIsPure(node)) { | |
404 | // If PutByVal speculates that it's accessing an array with an | |
405 | // integer index, then it's impossible for it to cause a structure | |
406 | // change. | |
407 | break; | |
408 | } | |
409 | return NoNode; | |
410 | ||
411 | default: | |
412 | if (clobbersWorld(index)) | |
413 | return NoNode; | |
414 | break; | |
415 | } | |
416 | } | |
417 | return NoNode; | |
418 | } | |
419 | ||
420 | NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, bool hasIntegerIndexPrediction) | |
421 | { | |
422 | for (unsigned i = m_indexInBlock; i--;) { | |
423 | NodeIndex index = m_currentBlock->at(i); | |
424 | if (index == child1) | |
425 | break; | |
426 | ||
427 | Node& node = m_graph[index]; | |
428 | switch (node.op()) { | |
429 | case GetIndexedPropertyStorage: { | |
430 | PredictedType basePrediction = m_graph[node.child2()].prediction(); | |
431 | bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction); | |
432 | if (node.child1() == child1 && hasIntegerIndexPrediction == nodeHasIntegerIndexPrediction) | |
433 | return index; | |
434 | break; | |
435 | } | |
436 | ||
437 | case PutByOffset: | |
438 | case PutStructure: | |
439 | // Changing the structure or putting to the storage cannot | |
440 | // change the property storage pointer. | |
441 | break; | |
442 | ||
443 | case PutByValAlias: | |
444 | // PutByValAlias can't change the indexed storage pointer | |
445 | break; | |
446 | ||
447 | case PutByVal: | |
448 | if (isFixedIndexedStorageObjectPrediction(m_graph[node.child1()].prediction()) && byValIsPure(node)) | |
449 | break; | |
450 | return NoNode; | |
451 | ||
452 | default: | |
453 | if (clobbersWorld(index)) | |
454 | return NoNode; | |
455 | break; | |
456 | } | |
457 | } | |
458 | return NoNode; | |
459 | } | |
460 | ||
461 | NodeIndex getScopeChainLoadElimination(unsigned depth) | |
462 | { | |
463 | for (unsigned i = endIndexForPureCSE(); i--;) { | |
464 | NodeIndex index = m_currentBlock->at(i); | |
465 | Node& node = m_graph[index]; | |
466 | if (node.op() == GetScopeChain | |
467 | && node.scopeChainDepth() == depth) | |
468 | return index; | |
469 | } | |
470 | return NoNode; | |
471 | } | |
472 | ||
473 | void performSubstitution(Edge& child, bool addRef = true) | |
474 | { | |
475 | // Check if this operand is actually unused. | |
476 | if (!child) | |
477 | return; | |
478 | ||
479 | // Check if there is any replacement. | |
480 | NodeIndex replacement = m_replacements[child.index()]; | |
481 | if (replacement == NoNode) | |
482 | return; | |
483 | ||
484 | child.setIndex(replacement); | |
485 | ||
486 | // There is definitely a replacement. Assert that the replacement does not | |
487 | // have a replacement. | |
488 | ASSERT(m_replacements[child.index()] == NoNode); | |
489 | ||
490 | if (addRef) | |
491 | m_graph[child].ref(); | |
492 | } | |
493 | ||
494 | void setReplacement(NodeIndex replacement) | |
495 | { | |
496 | if (replacement == NoNode) | |
497 | return; | |
498 | ||
499 | // Be safe. Don't try to perform replacements if the predictions don't | |
500 | // agree. | |
501 | if (m_graph[m_compileIndex].prediction() != m_graph[replacement].prediction()) | |
502 | return; | |
503 | ||
504 | #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) | |
505 | dataLog(" Replacing @%u -> @%u", m_compileIndex, replacement); | |
506 | #endif | |
507 | ||
508 | Node& node = m_graph[m_compileIndex]; | |
509 | node.setOpAndDefaultFlags(Phantom); | |
510 | node.setRefCount(1); | |
511 | ||
512 | // At this point we will eliminate all references to this node. | |
513 | m_replacements[m_compileIndex] = replacement; | |
514 | } | |
515 | ||
516 | void eliminate() | |
517 | { | |
518 | #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) | |
519 | dataLog(" Eliminating @%u", m_compileIndex); | |
520 | #endif | |
521 | ||
522 | Node& node = m_graph[m_compileIndex]; | |
523 | ASSERT(node.refCount() == 1); | |
524 | ASSERT(node.mustGenerate()); | |
525 | node.setOpAndDefaultFlags(Phantom); | |
526 | } | |
527 | ||
528 | void performNodeCSE(Node& node) | |
529 | { | |
530 | bool shouldGenerate = node.shouldGenerate(); | |
531 | ||
532 | if (node.flags() & NodeHasVarArgs) { | |
533 | for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) | |
534 | performSubstitution(m_graph.m_varArgChildren[childIdx], shouldGenerate); | |
535 | } else { | |
536 | performSubstitution(node.children.child1(), shouldGenerate); | |
537 | performSubstitution(node.children.child2(), shouldGenerate); | |
538 | performSubstitution(node.children.child3(), shouldGenerate); | |
539 | } | |
540 | ||
541 | if (!shouldGenerate) | |
542 | return; | |
543 | ||
544 | #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) | |
545 | dataLog(" %s @%u: ", Graph::opName(m_graph[m_compileIndex].op()), m_compileIndex); | |
546 | #endif | |
547 | ||
548 | // NOTE: there are some nodes that we deliberately don't CSE even though we | |
549 | // probably could, like StrCat and ToPrimitive. That's because there is no | |
550 | // evidence that doing CSE on these nodes would result in a performance | |
551 | // progression. Hence considering these nodes in CSE would just mean that this | |
552 | // code does more work with no win. Of course, we may want to reconsider this, | |
553 | // since StrCat is trivially CSE-able. It's not trivially doable for | |
554 | // ToPrimitive, but we could change that with some speculations if we really | |
555 | // needed to. | |
556 | ||
557 | switch (node.op()) { | |
558 | ||
559 | // Handle the pure nodes. These nodes never have any side-effects. | |
560 | case BitAnd: | |
561 | case BitOr: | |
562 | case BitXor: | |
563 | case BitRShift: | |
564 | case BitLShift: | |
565 | case BitURShift: | |
566 | case ArithAdd: | |
567 | case ArithSub: | |
568 | case ArithNegate: | |
569 | case ArithMul: | |
570 | case ArithMod: | |
571 | case ArithDiv: | |
572 | case ArithAbs: | |
573 | case ArithMin: | |
574 | case ArithMax: | |
575 | case ArithSqrt: | |
576 | case GetInt8ArrayLength: | |
577 | case GetInt16ArrayLength: | |
578 | case GetInt32ArrayLength: | |
579 | case GetUint8ArrayLength: | |
580 | case GetUint8ClampedArrayLength: | |
581 | case GetUint16ArrayLength: | |
582 | case GetUint32ArrayLength: | |
583 | case GetFloat32ArrayLength: | |
584 | case GetFloat64ArrayLength: | |
585 | case GetCallee: | |
586 | case GetStringLength: | |
587 | case StringCharAt: | |
588 | case StringCharCodeAt: | |
589 | case Int32ToDouble: | |
590 | case IsUndefined: | |
591 | case IsBoolean: | |
592 | case IsNumber: | |
593 | case IsString: | |
594 | case IsObject: | |
595 | case IsFunction: | |
596 | case DoubleAsInt32: | |
597 | setReplacement(pureCSE(node)); | |
598 | break; | |
599 | ||
600 | case GetArrayLength: | |
601 | setReplacement(impureCSE(node)); | |
602 | break; | |
603 | ||
604 | case GetScopeChain: | |
605 | setReplacement(getScopeChainLoadElimination(node.scopeChainDepth())); | |
606 | break; | |
607 | ||
608 | // Handle nodes that are conditionally pure: these are pure, and can | |
609 | // be CSE'd, so long as the prediction is the one we want. | |
610 | case ValueAdd: | |
611 | case CompareLess: | |
612 | case CompareLessEq: | |
613 | case CompareGreater: | |
614 | case CompareGreaterEq: | |
615 | case CompareEq: { | |
616 | if (isPredictedNumerical(node)) { | |
617 | NodeIndex replacementIndex = pureCSE(node); | |
618 | if (replacementIndex != NoNode && isPredictedNumerical(m_graph[replacementIndex])) | |
619 | setReplacement(replacementIndex); | |
620 | } | |
621 | break; | |
622 | } | |
623 | ||
624 | case LogicalNot: { | |
625 | if (logicalNotIsPure(node)) { | |
626 | NodeIndex replacementIndex = pureCSE(node); | |
627 | if (replacementIndex != NoNode && logicalNotIsPure(m_graph[replacementIndex])) | |
628 | setReplacement(replacementIndex); | |
629 | } | |
630 | break; | |
631 | } | |
632 | ||
633 | // Finally handle heap accesses. These are not quite pure, but we can still | |
634 | // optimize them provided that some subtle conditions are met. | |
635 | case GetGlobalVar: | |
636 | setReplacement(globalVarLoadElimination(node.varNumber(), codeBlock()->globalObjectFor(node.codeOrigin))); | |
637 | break; | |
638 | ||
639 | case GetByVal: | |
640 | if (byValIsPure(node)) | |
641 | setReplacement(getByValLoadElimination(node.child1().index(), node.child2().index())); | |
642 | break; | |
643 | ||
644 | case PutByVal: | |
645 | if (byValIsPure(node) && getByValLoadElimination(node.child1().index(), node.child2().index()) != NoNode) | |
646 | node.setOp(PutByValAlias); | |
647 | break; | |
648 | ||
649 | case CheckStructure: | |
650 | if (checkStructureLoadElimination(node.structureSet(), node.child1().index())) | |
651 | eliminate(); | |
652 | break; | |
653 | ||
654 | case CheckFunction: | |
655 | if (checkFunctionElimination(node.function(), node.child1().index())) | |
656 | eliminate(); | |
657 | break; | |
658 | ||
659 | case GetIndexedPropertyStorage: { | |
660 | PredictedType basePrediction = m_graph[node.child2()].prediction(); | |
661 | bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction); | |
662 | setReplacement(getIndexedPropertyStorageLoadElimination(node.child1().index(), nodeHasIntegerIndexPrediction)); | |
663 | break; | |
664 | } | |
665 | ||
666 | case GetPropertyStorage: | |
667 | setReplacement(getPropertyStorageLoadElimination(node.child1().index())); | |
668 | break; | |
669 | ||
670 | case GetByOffset: | |
671 | setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber, node.child1().index())); | |
672 | break; | |
673 | ||
674 | default: | |
675 | // do nothing. | |
676 | break; | |
677 | } | |
678 | ||
679 | m_lastSeen[node.op()] = m_indexInBlock; | |
680 | #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) | |
681 | dataLog("\n"); | |
682 | #endif | |
683 | } | |
684 | ||
685 | void performBlockCSE(BasicBlock& block) | |
686 | { | |
687 | m_currentBlock = █ | |
688 | for (unsigned i = 0; i < LastNodeType; ++i) | |
689 | m_lastSeen[i] = UINT_MAX; | |
690 | ||
691 | for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) { | |
692 | m_compileIndex = block[m_indexInBlock]; | |
693 | performNodeCSE(m_graph[m_compileIndex]); | |
694 | } | |
695 | } | |
696 | ||
697 | BasicBlock* m_currentBlock; | |
698 | NodeIndex m_compileIndex; | |
699 | unsigned m_indexInBlock; | |
700 | Vector<NodeIndex, 16> m_replacements; | |
701 | FixedArray<unsigned, LastNodeType> m_lastSeen; | |
702 | }; | |
703 | ||
704 | void performCSE(Graph& graph) | |
705 | { | |
706 | runPhase<CSEPhase>(graph); | |
707 | } | |
708 | ||
709 | } } // namespace JSC::DFG | |
710 | ||
711 | #endif // ENABLE(DFG_JIT) | |
712 | ||
713 |