Title: [191762] trunk/Source
Revision
191762
Author
[email protected]
Date
2015-10-29 16:42:04 -0700 (Thu, 29 Oct 2015)

Log Message

StoreOpLoad pattern matching should check effects between the Store and Load
https://bugs.webkit.org/show_bug.cgi?id=150534

Reviewed by Geoffrey Garen.

If we turn:

    a = Load(addr)
    b = Add(a, 42)
    Store(b, addr)

Into:

    Add $42, (addr)

Then we must make sure that we didn't really have this to begin with:

    a = Load(addr)
    Store(666, addr)
    b = Add(a, 42)
    Store(b, addr)

That's because pattern matching doesn't care about control flow, and it finds the Load
just using data flow. This patch fleshes out B3's aliasing analysis, and makes it powerful
enough to broadly ask questions about whether such a code motion of the Load is legal.

* b3/B3Effects.cpp:
(JSC::B3::Effects::interferes):
(JSC::B3::Effects::dump):
* b3/B3Effects.h:
(JSC::B3::Effects::mustExecute):
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::run):
(JSC::B3::Air::LowerToAir::commitInternal):
(JSC::B3::Air::LowerToAir::crossesInterference):
(JSC::B3::Air::LowerToAir::effectiveAddr):
(JSC::B3::Air::LowerToAir::loadAddr):
* b3/B3Procedure.cpp:
(JSC::B3::Procedure::addBlock):
(JSC::B3::Procedure::resetValueOwners):
(JSC::B3::Procedure::resetReachability):
* b3/B3Procedure.h:
* b3/B3Value.cpp:
(JSC::B3::Value::effects):
* b3/B3Value.h:
* b3/testb3.cpp:
(JSC::B3::testStoreAddLoad):
(JSC::B3::testStoreAddLoadInterference):
(JSC::B3::testStoreAddAndLoad):
(JSC::B3::testLoadOffsetUsingAdd):
(JSC::B3::testLoadOffsetUsingAddInterference):
(JSC::B3::testLoadOffsetUsingAddNotConstant):
(JSC::B3::run):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (191761 => 191762)


--- trunk/Source/_javascript_Core/ChangeLog	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-10-29 23:42:04 UTC (rev 191762)
@@ -1,3 +1,59 @@
+2015-10-29  Filip Pizlo  <[email protected]>
+
+        StoreOpLoad pattern matching should check effects between the Store and Load
+        https://bugs.webkit.org/show_bug.cgi?id=150534
+
+        Reviewed by Geoffrey Garen.
+
+        If we turn:
+
+            a = Load(addr)
+            b = Add(a, 42)
+            Store(b, addr)
+
+        Into:
+
+            Add $42, (addr)
+
+        Then we must make sure that we didn't really have this to begin with:
+
+            a = Load(addr)
+            Store(666, addr)
+            b = Add(a, 42)
+            Store(b, addr)
+
+        That's because pattern matching doesn't care about control flow, and it finds the Load
+        just using data flow. This patch fleshes out B3's aliasing analysis, and makes it powerful
+        enough to broadly ask questions about whether such a code motion of the Load is legal.
+
+        * b3/B3Effects.cpp:
+        (JSC::B3::Effects::interferes):
+        (JSC::B3::Effects::dump):
+        * b3/B3Effects.h:
+        (JSC::B3::Effects::mustExecute):
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::run):
+        (JSC::B3::Air::LowerToAir::commitInternal):
+        (JSC::B3::Air::LowerToAir::crossesInterference):
+        (JSC::B3::Air::LowerToAir::effectiveAddr):
+        (JSC::B3::Air::LowerToAir::loadAddr):
+        * b3/B3Procedure.cpp:
+        (JSC::B3::Procedure::addBlock):
+        (JSC::B3::Procedure::resetValueOwners):
+        (JSC::B3::Procedure::resetReachability):
+        * b3/B3Procedure.h:
+        * b3/B3Value.cpp:
+        (JSC::B3::Value::effects):
+        * b3/B3Value.h:
+        * b3/testb3.cpp:
+        (JSC::B3::testStoreAddLoad):
+        (JSC::B3::testStoreAddLoadInterference):
+        (JSC::B3::testStoreAddAndLoad):
+        (JSC::B3::testLoadOffsetUsingAdd):
+        (JSC::B3::testLoadOffsetUsingAddInterference):
+        (JSC::B3::testLoadOffsetUsingAddNotConstant):
+        (JSC::B3::run):
+
 2015-10-29  Brady Eidson  <[email protected]>
 
         Modern IDB: deleteObjectStore support.

Modified: trunk/Source/_javascript_Core/b3/B3Effects.cpp (191761 => 191762)


--- trunk/Source/_javascript_Core/b3/B3Effects.cpp	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/b3/B3Effects.cpp	2015-10-29 23:42:04 UTC (rev 191762)
@@ -29,9 +29,53 @@
 #if ENABLE(B3_JIT)
 
 #include <wtf/CommaPrinter.h>
+#include <wtf/DataLog.h>
 
 namespace JSC { namespace B3 {
 
+namespace {
+
+// These helpers cascade in such a way that after the helper for terminal, we don't have to worry
+// about terminal again, since the terminal case considers all ways that a terminal may interfere
+// with something else. And after the exit sideways case, we don't have to worry about either
+// exitsSideways or terminal. And so on...
+
+bool interferesWithTerminal(const Effects& terminal, const Effects& other)
+{
+    if (!terminal.terminal)
+        return false;
+    return other.terminal || other.controlDependent || other.writesSSAState || other.writes;
+}
+
+bool interferesWithExitSideways(const Effects& exitsSideways, const Effects& other)
+{
+    if (!exitsSideways.exitsSideways)
+        return false;
+    return other.controlDependent || other.writes;
+}
+
+bool interferesWithWritesSSAState(const Effects& writesSSAState, const Effects& other)
+{
+    if (!writesSSAState.writesSSAState)
+        return false;
+    return other.writesSSAState || other.readsSSAState;
+}
+
+} // anonymous namespace
+
+bool Effects::interferes(const Effects& other) const
+{
+    if (interferesWithTerminal(*this, other) || interferesWithTerminal(other, *this))
+        return true;
+    if (interferesWithExitSideways(*this, other) || interferesWithExitSideways(other, *this))
+        return true;
+    if (interferesWithWritesSSAState(*this, other) || interferesWithWritesSSAState(other, *this))
+        return true;
+    return writes.overlaps(other.writes)
+        || writes.overlaps(other.reads)
+        || reads.overlaps(other.writes);
+}
+
 void Effects::dump(PrintStream& out) const
 {
     CommaPrinter comma("|");
@@ -43,6 +87,8 @@
         out.print(comma, "ControlDependent");
     if (writesSSAState)
         out.print(comma, "WritesSSAState");
+    if (readsSSAState)
+        out.print(comma, "ReadsSSAState");
     if (writes)
         out.print(comma, "Writes:", writes);
     if (reads)

Modified: trunk/Source/_javascript_Core/b3/B3Effects.h (191761 => 191762)


--- trunk/Source/_javascript_Core/b3/B3Effects.h	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/b3/B3Effects.h	2015-10-29 23:42:04 UTC (rev 191762)
@@ -49,6 +49,9 @@
     // anyway.
     bool writesSSAState { false };
 
+    // True if this reads from the SSA state. This is only used for Phi.
+    bool readsSSAState { false };
+
     HeapRange writes;
     HeapRange reads;
 
@@ -57,6 +60,10 @@
         return terminal || exitsSideways || writesSSAState || writes;
     }
 
+    // Returns true if reordering instructions with these respective effects would change program
+    // behavior in an observable way.
+    bool interferes(const Effects&) const;
+
     void dump(PrintStream& out) const;
 };
 

Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (191761 => 191762)


--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2015-10-29 23:42:04 UTC (rev 191762)
@@ -74,15 +74,19 @@
                 stackToStack.add(stackSlotValue, code.addStackSlot(stackSlotValue));
         }
 
+        procedure.resetValueOwners(); // Used by crossesInterference().
+
         // Lower defs before uses on a global level. This is a good heuristic to lock down a
         // hoisted address _expression_ before we duplicate it back into the loop.
         for (B3::BasicBlock* block : procedure.blocksInPreOrder()) {
+            currentBlock = block;
             // Reset some state.
             insts.resize(0);
             
             // Process blocks in reverse order so we see uses before defs. That's what allows us
             // to match patterns effectively.
             for (unsigned i = block->size(); i--;) {
+                currentIndex = i;
                 currentValue = block->at(i);
                 if (locked.contains(currentValue))
                     continue;
@@ -144,6 +148,29 @@
         locked.add(value);
     }
 
+    bool crossesInterference(Value* value)
+    {
+        // If it's in a foreign block, then be conservative. We could handle this if we were
+        // willing to do heavier analysis. For example, if we had liveness, then we could label
+        // values as "crossing interference" if they interfere with anything that they are live
+        // across. But, it's not clear how useful this would be.
+        if (value->owner != currentValue->owner)
+            return true;
+
+        Effects effects = value->effects();
+
+        for (unsigned i = currentIndex; i--;) {
+            Value* otherValue = currentBlock->at(i);
+            if (otherValue == value)
+                return false;
+            if (effects.interferes(otherValue->effects()))
+                return true;
+        }
+
+        ASSERT_NOT_REACHED();
+        return true;
+    }
+
     // This turns the given operand into an address.
     Arg effectiveAddr(Value* address)
     {
@@ -179,11 +206,13 @@
 
     Arg loadAddr(Value* loadValue)
     {
+        if (loadValue->opcode() != Load)
+            return Arg();
         if (!canBeInternal(loadValue))
             return Arg();
-        if (loadValue->opcode() == Load)
-            return addr(loadValue);
-        return Arg();
+        if (crossesInterference(loadValue))
+            return Arg();
+        return addr(loadValue);
     }
 
     Arg imm(Value* value)
@@ -289,11 +318,6 @@
     template<Air::Opcode opcode, Commutativity commutativity = NotCommutative>
     bool tryAppendStoreBinOp(Value* left, Value* right)
     {
-        // FIXME: This fails to check if there are any effects between the Load and the Store that
-        // could clobber the loaded value. We need to check such things because we are effectively
-        // sinking the load.
-        // https://bugs.webkit.org/show_bug.cgi?id=150534
-        
         Arg storeAddr = addr(currentValue);
         ASSERT(storeAddr);
 
@@ -366,6 +390,8 @@
     Vector<Vector<Inst, 4>> insts;
     Vector<Inst> prologue;
 
+    B3::BasicBlock* currentBlock;
+    unsigned currentIndex;
     Value* currentValue;
 
     // The address selector will match any pattern where the input operands are available as Tmps.

Modified: trunk/Source/_javascript_Core/b3/B3Procedure.cpp (191761 => 191762)


--- trunk/Source/_javascript_Core/b3/B3Procedure.cpp	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/b3/B3Procedure.cpp	2015-10-29 23:42:04 UTC (rev 191762)
@@ -52,6 +52,14 @@
     return result;
 }
 
+void Procedure::resetValueOwners()
+{
+    for (BasicBlock* block : *this) {
+        for (Value* value : *block)
+            value->owner = block;
+    }
+}
+
 void Procedure::resetReachability()
 {
     B3::resetReachability(m_blocks);

Modified: trunk/Source/_javascript_Core/b3/B3Procedure.h (191761 => 191762)


--- trunk/Source/_javascript_Core/b3/B3Procedure.h	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/b3/B3Procedure.h	2015-10-29 23:42:04 UTC (rev 191762)
@@ -53,6 +53,7 @@
     template<typename ValueType, typename... Arguments>
     ValueType* add(Arguments...);
 
+    void resetValueOwners();
     void resetReachability();
 
     JS_EXPORT_PRIVATE void dump(PrintStream&) const;

Modified: trunk/Source/_javascript_Core/b3/B3Value.cpp (191761 => 191762)


--- trunk/Source/_javascript_Core/b3/B3Value.cpp	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/b3/B3Value.cpp	2015-10-29 23:42:04 UTC (rev 191762)
@@ -168,7 +168,6 @@
     case Below:
     case AboveEqual:
     case BelowEqual:
-    case Phi:
         break;
     case Div:
         result.controlDependent = true;
@@ -209,6 +208,9 @@
     case Upsilon:
         result.writesSSAState = true;
         break;
+    case Phi:
+        result.readsSSAState = true;
+        break;
     case Jump:
     case Branch:
     case Switch:

Modified: trunk/Source/_javascript_Core/b3/B3Value.h (191761 => 191762)


--- trunk/Source/_javascript_Core/b3/B3Value.h	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/b3/B3Value.h	2015-10-29 23:42:04 UTC (rev 191762)
@@ -197,7 +197,7 @@
     AdjacencyList m_children;
 
 public:
-    BasicBlock* owner { nullptr }; // computed lazily.
+    BasicBlock* owner { nullptr }; // computed by Procedure::resetValueOwners().
 };
 
 class DeepValueDump {

Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (191761 => 191762)


--- trunk/Source/_javascript_Core/b3/testb3.cpp	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp	2015-10-29 23:42:04 UTC (rev 191762)
@@ -235,6 +235,33 @@
     CHECK(slot == 37 + amount);
 }
 
+void testStoreAddLoadInterference(int amount)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int slot = 37;
+    ConstPtrValue* slotPtr = root->appendNew<ConstPtrValue>(proc, Origin(), &slot);
+    ArgumentRegValue* otherSlotPtr =
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    MemoryValue* load = root->appendNew<MemoryValue>(proc, Load, Int32, Origin(), slotPtr);
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 666),
+        otherSlotPtr);
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(),
+            load, root->appendNew<Const32Value>(proc, Origin(), amount)),
+        slotPtr);
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Const32Value>(proc, Origin(), 0));
+
+    CHECK(!compileAndRun<int>(proc, &slot));
+    CHECK(slot == 37 + amount);
+}
+
 void testStoreAddAndLoad(int amount, int mask)
 {
     Procedure proc;
@@ -331,6 +358,39 @@
     CHECK(compileAndRun<int>(proc) == array[0] + array[1]);
 }
 
+void testLoadOffsetUsingAddInterference()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    int array[] = { 1, 2 };
+    ConstPtrValue* arrayPtr = root->appendNew<ConstPtrValue>(proc, Origin(), array);
+    ArgumentRegValue* otherArrayPtr =
+        root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+    Const32Value* theNumberOfTheBeast = root->appendNew<Const32Value>(proc, Origin(), 666);
+    MemoryValue* left = root->appendNew<MemoryValue>(
+        proc, Load, Int32, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(), arrayPtr,
+            root->appendNew<ConstPtrValue>(proc, Origin(), 0)));
+    MemoryValue* right = root->appendNew<MemoryValue>(
+        proc, Load, Int32, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(), arrayPtr,
+            root->appendNew<ConstPtrValue>(proc, Origin(), sizeof(int))));
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(), theNumberOfTheBeast, otherArrayPtr, 0);
+    root->appendNew<MemoryValue>(
+        proc, Store, Origin(), theNumberOfTheBeast, otherArrayPtr, sizeof(int));
+    root->appendNew<ControlValue>(
+        proc, Return, Origin(),
+        root->appendNew<Value>(
+            proc, Add, Origin(), left, right));
+    
+    CHECK(compileAndRun<int>(proc, &array[0]) == 1 + 2);
+    CHECK(array[0] == 666);
+    CHECK(array[1] == 666);
+}
+
 void testLoadOffsetUsingAddNotConstant()
 {
     Procedure proc;
@@ -443,12 +503,14 @@
     RUN(testTrunc((static_cast<int64_t>(1) << 40) + 42));
     RUN(testAdd1(45));
     RUN(testStoreAddLoad(46));
+    RUN(testStoreAddLoadInterference(52));
     RUN(testStoreAddAndLoad(47, 0xffff));
     RUN(testStoreAddAndLoad(470000, 0xffff));
     RUN(testAdd1Uncommuted(48));
     RUN(testLoadOffset());
     RUN(testLoadOffsetNotConstant());
     RUN(testLoadOffsetUsingAdd());
+    RUN(testLoadOffsetUsingAddInterference());
     RUN(testLoadOffsetUsingAddNotConstant());
     RUN(testFramePointer());
     RUN(testStackSlot());

Modified: trunk/Source/WTF/wtf/MathExtras.h (191761 => 191762)


--- trunk/Source/WTF/wtf/MathExtras.h	2015-10-29 23:13:10 UTC (rev 191761)
+++ trunk/Source/WTF/wtf/MathExtras.h	2015-10-29 23:42:04 UTC (rev 191762)
@@ -428,6 +428,13 @@
 {
     ASSERT(leftMin <= leftMax);
     ASSERT(rightMin <= rightMax);
+    
+    // Empty ranges interfere with nothing.
+    if (leftMin == leftMax)
+        return false;
+    if (rightMin == rightMax)
+        return false;
+    
     if (leftMin <= rightMin && leftMax > rightMin)
         return true;
     if (rightMin <= leftMin && rightMax > leftMin)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to