]> git.saurik.com Git - apple/javascriptcore.git/blame - dfg/DFGCSEPhase.cpp
JavaScriptCore-1097.13.tar.gz
[apple/javascriptcore.git] / dfg / DFGCSEPhase.cpp
CommitLineData
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
34namespace JSC { namespace DFG {
35
36class CSEPhase : public Phase {
37public:
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
54private:
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 = &block;
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
704void performCSE(Graph& graph)
705{
706 runPhase<CSEPhase>(graph);
707}
708
709} } // namespace JSC::DFG
710
711#endif // ENABLE(DFG_JIT)
712
713