]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGStrengthReductionPhase.cpp
JavaScriptCore-7600.1.4.9.tar.gz
[apple/javascriptcore.git] / dfg / DFGStrengthReductionPhase.cpp
diff --git a/dfg/DFGStrengthReductionPhase.cpp b/dfg/DFGStrengthReductionPhase.cpp
new file mode 100644 (file)
index 0000000..1f837d3
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGStrengthReductionPhase.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGGraph.h"
+#include "DFGInsertionSet.h"
+#include "DFGPhase.h"
+#include "DFGPredictionPropagationPhase.h"
+#include "DFGVariableAccessDataDump.h"
+#include "JSCInlines.h"
+
+namespace JSC { namespace DFG {
+
+class StrengthReductionPhase : public Phase {
+public:
+    StrengthReductionPhase(Graph& graph)
+        : Phase(graph, "strength reduction")
+        , m_insertionSet(graph)
+    {
+    }
+    
+    bool run()
+    {
+        ASSERT(m_graph.m_fixpointState == FixpointNotConverged);
+        
+        m_changed = false;
+        
+        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
+            m_block = m_graph.block(blockIndex);
+            if (!m_block)
+                continue;
+            for (m_nodeIndex = 0; m_nodeIndex < m_block->size(); ++m_nodeIndex) {
+                m_node = m_block->at(m_nodeIndex);
+                handleNode();
+            }
+            m_insertionSet.execute(m_block);
+        }
+        
+        return m_changed;
+    }
+
+private:
+    void handleNode()
+    {
+        switch (m_node->op()) {
+        case BitOr:
+            handleCommutativity();
+
+            if (m_node->child2()->isConstant()) {
+                JSValue op2 = m_graph.valueOfJSConstant(m_node->child2().node());
+                if (op2.isInt32() && !op2.asInt32()) {
+                    convertToIdentityOverChild1();
+                    break;
+                }
+            }
+            break;
+            
+        case BitXor:
+        case BitAnd:
+            handleCommutativity();
+            break;
+            
+        case BitLShift:
+        case BitRShift:
+        case BitURShift:
+            if (m_node->child2()->isConstant()) {
+                JSValue op2 = m_graph.valueOfJSConstant(m_node->child2().node());
+                if (op2.isInt32() && !(op2.asInt32() & 0x1f)) {
+                    convertToIdentityOverChild1();
+                    break;
+                }
+            }
+            break;
+            
+        case UInt32ToNumber:
+            if (m_node->child1()->op() == BitURShift
+                && m_node->child1()->child2()->isConstant()) {
+                JSValue shiftAmount = m_graph.valueOfJSConstant(
+                    m_node->child1()->child2().node());
+                if (shiftAmount.isInt32() && (shiftAmount.asInt32() & 0x1f)
+                    && m_node->arithMode() != Arith::DoOverflow) {
+                    m_node->convertToIdentity();
+                    m_changed = true;
+                    break;
+                }
+            }
+            break;
+            
+        case ArithAdd:
+            handleCommutativity();
+            
+            if (m_graph.isInt32Constant(m_node->child2().node())) {
+                int32_t value = m_graph.valueOfInt32Constant(
+                    m_node->child2().node());
+                if (!value) {
+                    convertToIdentityOverChild1();
+                    break;
+                }
+            }
+            break;
+            
+        case ArithMul:
+            handleCommutativity();
+            break;
+            
+        case ArithSub:
+            if (m_graph.isInt32Constant(m_node->child2().node())
+                && m_node->isBinaryUseKind(Int32Use)) {
+                int32_t value = m_graph.valueOfInt32Constant(m_node->child2().node());
+                if (-value != value) {
+                    m_node->setOp(ArithAdd);
+                    m_node->child2().setNode(
+                        m_insertionSet.insertConstant(
+                            m_nodeIndex, m_node->origin, jsNumber(-value)));
+                    m_changed = true;
+                    break;
+                }
+            }
+            break;
+            
+        case GetArrayLength:
+            if (JSArrayBufferView* view = m_graph.tryGetFoldableViewForChild1(m_node))
+                foldTypedArrayPropertyToConstant(view, jsNumber(view->length()));
+            break;
+            
+        case GetTypedArrayByteOffset:
+            if (JSArrayBufferView* view = m_graph.tryGetFoldableView(m_node->child1().node()))
+                foldTypedArrayPropertyToConstant(view, jsNumber(view->byteOffset()));
+            break;
+            
+        case GetIndexedPropertyStorage:
+            if (JSArrayBufferView* view = m_graph.tryGetFoldableViewForChild1(m_node)) {
+                if (view->mode() != FastTypedArray) {
+                    prepareToFoldTypedArray(view);
+                    m_node->convertToConstantStoragePointer(view->vector());
+                    m_changed = true;
+                    break;
+                } else {
+                    // FIXME: It would be awesome to be able to fold the property storage for
+                    // these GC-allocated typed arrays. For now it doesn't matter because the
+                    // most common use-cases for constant typed arrays involve large arrays with
+                    // aliased buffer views.
+                    // https://bugs.webkit.org/show_bug.cgi?id=125425
+                }
+            }
+            break;
+            
+        case ValueRep:
+        case Int52Rep:
+        case DoubleRep: {
+            // This short-circuits circuitous conversions, like ValueRep(DoubleRep(value)) or
+            // even more complicated things. Like, it can handle a beast like
+            // ValueRep(DoubleRep(Int52Rep(value))).
+            
+            // The only speculation that we would do beyond validating that we have a type that
+            // can be represented a certain way is an Int32 check that would appear on Int52Rep
+            // nodes. For now, if we see this and the final type we want is an Int52, we use it
+            // as an excuse not to fold. The only thing we would need is a Int52RepInt32Use kind.
+            bool hadInt32Check = false;
+            if (m_node->op() == Int52Rep) {
+                if (m_node->child1().useKind() != Int32Use)
+                    break;
+                hadInt32Check = true;
+            }
+            for (Node* node = m_node->child1().node(); ; node = node->child1().node()) {
+                if (canonicalResultRepresentation(node->result()) ==
+                    canonicalResultRepresentation(m_node->result())) {
+                    m_insertionSet.insertNode(
+                        m_nodeIndex, SpecNone, Phantom, m_node->origin, m_node->child1());
+                    if (hadInt32Check) {
+                        // FIXME: Consider adding Int52RepInt32Use or even DoubleRepInt32Use,
+                        // which would be super weird. The latter would only arise in some
+                        // seriously circuitous conversions.
+                        if (canonicalResultRepresentation(node->result()) != NodeResultJS)
+                            break;
+                        
+                        m_insertionSet.insertNode(
+                            m_nodeIndex, SpecNone, Phantom, m_node->origin,
+                            Edge(node, Int32Use));
+                    }
+                    m_node->child1() = node->defaultEdge();
+                    m_node->convertToIdentity();
+                    m_changed = true;
+                    break;
+                }
+                
+                switch (node->op()) {
+                case Int52Rep:
+                    if (node->child1().useKind() != Int32Use)
+                        break;
+                    hadInt32Check = true;
+                    continue;
+                    
+                case DoubleRep:
+                case ValueRep:
+                    continue;
+                    
+                default:
+                    break;
+                }
+                break;
+            }
+            break;
+        }
+            
+        default:
+            break;
+        }
+    }
+            
+    void convertToIdentityOverChild(unsigned childIndex)
+    {
+        m_insertionSet.insertNode(
+            m_nodeIndex, SpecNone, Phantom, m_node->origin, m_node->children);
+        m_node->children.removeEdge(childIndex ^ 1);
+        m_node->convertToIdentity();
+        m_changed = true;
+    }
+    
+    void convertToIdentityOverChild1()
+    {
+        convertToIdentityOverChild(0);
+    }
+    
+    void convertToIdentityOverChild2()
+    {
+        convertToIdentityOverChild(1);
+    }
+    
+    void foldTypedArrayPropertyToConstant(JSArrayBufferView* view, JSValue constant)
+    {
+        prepareToFoldTypedArray(view);
+        m_graph.convertToConstant(m_node, constant);
+        m_changed = true;
+    }
+    
+    void prepareToFoldTypedArray(JSArrayBufferView* view)
+    {
+        m_insertionSet.insertNode(
+            m_nodeIndex, SpecNone, TypedArrayWatchpoint, m_node->origin,
+            OpInfo(view));
+        m_insertionSet.insertNode(
+            m_nodeIndex, SpecNone, Phantom, m_node->origin, m_node->children);
+    }
+    
+    void handleCommutativity()
+    {
+        // If the right side is a constant then there is nothing left to do.
+        if (m_node->child2()->hasConstant())
+            return;
+        
+        // This case ensures that optimizations that look for x + const don't also have
+        // to look for const + x.
+        if (m_node->child1()->hasConstant()) {
+            std::swap(m_node->child1(), m_node->child2());
+            m_changed = true;
+            return;
+        }
+        
+        // This case ensures that CSE is commutativity-aware.
+        if (m_node->child1().node() > m_node->child2().node()) {
+            std::swap(m_node->child1(), m_node->child2());
+            m_changed = true;
+            return;
+        }
+    }
+    
+    InsertionSet m_insertionSet;
+    BasicBlock* m_block;
+    unsigned m_nodeIndex;
+    Node* m_node;
+    bool m_changed;
+};
+    
+bool performStrengthReduction(Graph& graph)
+{
+    SamplingRegion samplingRegion("DFG Strength Reduction Phase");
+    return runPhase<StrengthReductionPhase>(graph);
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+