[JSC] 32bit bitwide operation with all-one (-1) is wrong in B3
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Sep 2019 00:46:32 +0000 (00:46 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Sep 2019 00:46:32 +0000 (00:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201634

Reviewed by Mark Lam and Robin Morisset.

This patch includes two things. One is fixing 32bit bitwise operation with allOne constants. Another is fixing the existing bug in BitAnd strength reduction.

1. 32bit bitwise operation with allOne constants

    Accidentally, the B3::Value is ConstInt32(-1), `value->isInt(std::numeric_limits<uint32_t>::max())` returns `false`!
    For example, in BitAnd strength reduction,

        1034             // Turn this: BitAnd(value, all-ones)
        1035             // Into this: value.
        1036             if ((m_value->type() == Int64 && m_value->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
        1037                 || (m_value->type() == Int32 && m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max()))) {
        1038                 replaceWithIdentity(m_value->child(0));
        1039                 break;
        1040             }

    We use `m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max())`. However, Value::isInt is,

        262 inline bool Value::isInt(int64_t value) const
        263 {
        264     return hasInt() && asInt() == value;
        265 }

    So, UINT32_MAX is expanded to int64_t, but it is not -1 since UINT32_MAX can be representable in int64_t. And Value::asInt implementation is,

        257 inline int64_t Value::asInt() const
        258 {
        259     return hasInt32() ? asInt32() : asInt64();
        260 }

    So, we perform `static_cast<int64_t>(-1) == static_cast<int64_t>(UINT32_MAX)`. This is false, but this comparison is not what we want!
    We should use `isInt32` and `isInt64` for bit patterns (like, operands for Bitwise opcodes).

2. BitAnd and BitOr strength reduction bug

    We also fix the following optimization.

        // Turn this: BitAnd(Op(value, constant1), constant2)
        //     where !(constant1 & constant2)
        //       and Op is BitOr or BitXor
        // into this: BitAnd(value, constant2)

    Since we stop further optimization when we match `if (m_value->child(1)->hasInt())`, the following optimization is never taken.

        // Turn this: BitAnd(BitXor(x, allOnes), c)
        // Into this: BitXor(BitOr(x, ~c), allOnes)

    And we also found that this not-used optimization has a bug not inserting a newly produced constant B3::Value. This patch also fixes it.

For both, this patch adds tests. And (2) fix can be ensured that the testb3 does not crash with validate-graph option.

* b3/B3LowerToAir.cpp:
* b3/B3ReduceStrength.cpp:
* b3/testb3.h:
* b3/testb3_2.cpp:
(testBitAndNotNot32):
(testBitAndNotImm):
(testBitAndNotImm32):
(testBitOrAndAndArgs32):
(testBitOrAndSameArgs32):
(testBitOrNotNot32):
(testBitOrNotImm32):
(addBitTests):
* b3/testb3_3.cpp:
(testBitXorAndAndArgs32):
(testBitXorAndSameArgs32):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@249747 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/b3/B3LowerToAir.cpp
Source/JavaScriptCore/b3/B3ReduceStrength.cpp
Source/JavaScriptCore/b3/testb3.h
Source/JavaScriptCore/b3/testb3_2.cpp
Source/JavaScriptCore/b3/testb3_3.cpp

index 6ef876a..e08bd91 100644 (file)
@@ -1,3 +1,76 @@
+2019-09-10  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] 32bit bitwide operation with all-one (-1) is wrong in B3
+        https://bugs.webkit.org/show_bug.cgi?id=201634
+
+        Reviewed by Mark Lam and Robin Morisset.
+
+        This patch includes two things. One is fixing 32bit bitwise operation with allOne constants. Another is fixing the existing bug in BitAnd strength reduction.
+
+        1. 32bit bitwise operation with allOne constants
+
+            Accidentally, the B3::Value is ConstInt32(-1), `value->isInt(std::numeric_limits<uint32_t>::max())` returns `false`!
+            For example, in BitAnd strength reduction,
+
+                1034             // Turn this: BitAnd(value, all-ones)
+                1035             // Into this: value.
+                1036             if ((m_value->type() == Int64 && m_value->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
+                1037                 || (m_value->type() == Int32 && m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max()))) {
+                1038                 replaceWithIdentity(m_value->child(0));
+                1039                 break;
+                1040             }
+
+            We use `m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max())`. However, Value::isInt is,
+
+                262 inline bool Value::isInt(int64_t value) const
+                263 {
+                264     return hasInt() && asInt() == value;
+                265 }
+
+            So, UINT32_MAX is expanded to int64_t, but it is not -1 since UINT32_MAX can be representable in int64_t. And Value::asInt implementation is,
+
+                257 inline int64_t Value::asInt() const
+                258 {
+                259     return hasInt32() ? asInt32() : asInt64();
+                260 }
+
+            So, we perform `static_cast<int64_t>(-1) == static_cast<int64_t>(UINT32_MAX)`. This is false, but this comparison is not what we want!
+            We should use `isInt32` and `isInt64` for bit patterns (like, operands for Bitwise opcodes).
+
+        2. BitAnd and BitOr strength reduction bug
+
+            We also fix the following optimization.
+
+                // Turn this: BitAnd(Op(value, constant1), constant2)
+                //     where !(constant1 & constant2)
+                //       and Op is BitOr or BitXor
+                // into this: BitAnd(value, constant2)
+
+            Since we stop further optimization when we match `if (m_value->child(1)->hasInt())`, the following optimization is never taken.
+
+                // Turn this: BitAnd(BitXor(x, allOnes), c)
+                // Into this: BitXor(BitOr(x, ~c), allOnes)
+
+            And we also found that this not-used optimization has a bug not inserting a newly produced constant B3::Value. This patch also fixes it.
+
+        For both, this patch adds tests. And (2) fix can be ensured that the testb3 does not crash with validate-graph option.
+
+        * b3/B3LowerToAir.cpp:
+        * b3/B3ReduceStrength.cpp:
+        * b3/testb3.h:
+        * b3/testb3_2.cpp:
+        (testBitAndNotNot32):
+        (testBitAndNotImm):
+        (testBitAndNotImm32):
+        (testBitOrAndAndArgs32):
+        (testBitOrAndSameArgs32):
+        (testBitOrNotNot32):
+        (testBitOrNotImm32):
+        (addBitTests):
+        * b3/testb3_3.cpp:
+        (testBitXorAndAndArgs32):
+        (testBitXorAndSameArgs32):
+
 2019-09-10  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r249721.
index 3cc75c3..7d6bbb2 100644 (file)
@@ -2749,7 +2749,7 @@ private:
                 return;
             }
 
-            if (m_value->child(1)->isInt(0xffffffff)) {
+            if (m_value->child(1)->isInt64(0xffffffff) || m_value->child(1)->isInt32(0xffffffff)) {
                 appendUnOp<Move32, Move32>(m_value->child(0));
                 return;
             }
index fdc6ffd..14fd9d4 100644 (file)
@@ -1033,8 +1033,8 @@ private:
 
             // Turn this: BitAnd(value, all-ones)
             // Into this: value.
-            if ((m_value->type() == Int64 && m_value->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
-                || (m_value->type() == Int32 && m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max()))) {
+            if ((m_value->type() == Int64 && m_value->child(1)->isInt64(std::numeric_limits<uint64_t>::max()))
+                || (m_value->type() == Int32 && m_value->child(1)->isInt32(std::numeric_limits<uint32_t>::max()))) {
                 replaceWithIdentity(m_value->child(0));
                 break;
             }
@@ -1083,6 +1083,7 @@ private:
             //       and Op is BitOr or BitXor
             // into this: BitAnd(value, constant2)
             if (m_value->child(1)->hasInt()) {
+                bool replaced = false;
                 int64_t constant2 = m_value->child(1)->asInt();
                 switch (m_value->child(0)->opcode()) {
                 case BitOr:
@@ -1091,13 +1092,15 @@ private:
                         && !(m_value->child(0)->child(1)->asInt() & constant2)) {
                         m_value->child(0) = m_value->child(0)->child(0);
                         m_changed = true;
+                        replaced = true;
                         break;
                     }
                     break;
                 default:
                     break;
                 }
-                break;
+                if (replaced)
+                    break;
             }
 
             // Turn this: BitAnd(BitXor(x1, allOnes), BitXor(x2, allOnes)
@@ -1106,11 +1109,11 @@ private:
             if (m_value->child(0)->opcode() == BitXor
                 && m_value->child(1)->opcode() == BitXor
                 && ((m_value->type() == Int64
-                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint64_t>::max())
-                        && m_value->child(1)->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
+                        && m_value->child(0)->child(1)->isInt64(std::numeric_limits<uint64_t>::max())
+                        && m_value->child(1)->child(1)->isInt64(std::numeric_limits<uint64_t>::max()))
                     || (m_value->type() == Int32
-                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint32_t>::max())
-                        && m_value->child(1)->child(1)->isInt(std::numeric_limits<uint32_t>::max())))) {
+                        && m_value->child(0)->child(1)->isInt32(std::numeric_limits<uint32_t>::max())
+                        && m_value->child(1)->child(1)->isInt32(std::numeric_limits<uint32_t>::max())))) {
                 Value* bitOr = m_insertionSet.insert<Value>(m_index, BitOr, m_value->origin(), m_value->child(0)->child(0), m_value->child(1)->child(0));
                 replaceWithNew<Value>(BitXor, m_value->origin(), bitOr, m_value->child(1)->child(1));
                 break;
@@ -1123,10 +1126,13 @@ private:
             if (m_value->child(0)->opcode() == BitXor
                 && m_value->child(1)->hasInt()
                 && ((m_value->type() == Int64
-                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
+                        && m_value->child(0)->child(1)->isInt64(std::numeric_limits<uint64_t>::max()))
                     || (m_value->type() == Int32
-                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint32_t>::max())))) {
-                Value* bitOr = m_insertionSet.insert<Value>(m_index, BitOr, m_value->origin(), m_value->child(0)->child(0), m_value->child(1)->bitXorConstant(m_proc, m_value->child(0)->child(1)));
+                        && m_value->child(0)->child(1)->isInt32(std::numeric_limits<uint32_t>::max())))) {
+                Value* newConstant = m_value->child(1)->bitXorConstant(m_proc, m_value->child(0)->child(1));
+                ASSERT(newConstant);
+                m_insertionSet.insertValue(m_index, newConstant);
+                Value* bitOr = m_insertionSet.insert<Value>(m_index, BitOr, m_value->origin(), m_value->child(0)->child(0), newConstant);
                 replaceWithNew<Value>(BitXor, m_value->origin(), bitOr, m_value->child(0)->child(1));
                 break;
             }
@@ -1171,8 +1177,8 @@ private:
 
             // Turn this: BitOr(value, all-ones)
             // Into this: all-ones.
-            if ((m_value->type() == Int64 && m_value->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
-                || (m_value->type() == Int32 && m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max()))) {
+            if ((m_value->type() == Int64 && m_value->child(1)->isInt64(std::numeric_limits<uint64_t>::max()))
+                || (m_value->type() == Int32 && m_value->child(1)->isInt32(std::numeric_limits<uint32_t>::max()))) {
                 replaceWithIdentity(m_value->child(1));
                 break;
             }
@@ -1183,11 +1189,11 @@ private:
             if (m_value->child(0)->opcode() == BitXor
                 && m_value->child(1)->opcode() == BitXor
                 && ((m_value->type() == Int64
-                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint64_t>::max())
-                        && m_value->child(1)->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
+                        && m_value->child(0)->child(1)->isInt64(std::numeric_limits<uint64_t>::max())
+                        && m_value->child(1)->child(1)->isInt64(std::numeric_limits<uint64_t>::max()))
                     || (m_value->type() == Int32
-                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint32_t>::max())
-                        && m_value->child(1)->child(1)->isInt(std::numeric_limits<uint32_t>::max())))) {
+                        && m_value->child(0)->child(1)->isInt32(std::numeric_limits<uint32_t>::max())
+                        && m_value->child(1)->child(1)->isInt32(std::numeric_limits<uint32_t>::max())))) {
                 Value* bitAnd = m_insertionSet.insert<Value>(m_index, BitAnd, m_value->origin(), m_value->child(0)->child(0), m_value->child(1)->child(0));
                 replaceWithNew<Value>(BitXor, m_value->origin(), bitAnd, m_value->child(1)->child(1));
                 break;
@@ -1200,10 +1206,13 @@ private:
             if (m_value->child(0)->opcode() == BitXor
                 && m_value->child(1)->hasInt()
                 && ((m_value->type() == Int64
-                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
+                        && m_value->child(0)->child(1)->isInt64(std::numeric_limits<uint64_t>::max()))
                     || (m_value->type() == Int32
-                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint32_t>::max())))) {
-                Value* bitAnd = m_insertionSet.insert<Value>(m_index, BitAnd, m_value->origin(), m_value->child(0)->child(0), m_value->child(1)->bitXorConstant(m_proc, m_value->child(0)->child(1)));
+                        && m_value->child(0)->child(1)->isInt32(std::numeric_limits<uint32_t>::max())))) {
+                Value* newConstant = m_value->child(1)->bitXorConstant(m_proc, m_value->child(0)->child(1));
+                ASSERT(newConstant);
+                m_insertionSet.insertValue(m_index, newConstant);
+                Value* bitAnd = m_insertionSet.insert<Value>(m_index, BitAnd, m_value->origin(), m_value->child(0)->child(0), newConstant);
                 replaceWithNew<Value>(BitXor, m_value->origin(), bitAnd, m_value->child(0)->child(1));
                 break;
             }
index b504096..924c9c5 100644 (file)
@@ -459,7 +459,9 @@ void testBitOrArgsFloatWithUselessDoubleConversion(float, float);
 void testBitXorArgs(int64_t, int64_t);
 void testBitXorSameArg(int64_t);
 void testBitXorAndAndArgs(int64_t, int64_t, int64_t c);
+void testBitXorAndAndArgs32(int32_t, int32_t, int32_t c);
 void testBitXorAndSameArgs(int64_t, int64_t);
+void testBitXorAndSameArgs32(int32_t, int32_t);
 void testBitXorImms(int64_t, int64_t);
 void testBitXorArgImm(int64_t, int64_t);
 void testBitXorImmArg(int64_t, int64_t);
index bc61b54..7cb34c8 100644 (file)
@@ -2565,6 +2565,24 @@ static void testBitAndNotNot(int64_t a, int64_t b)
     CHECK_EQ(compileAndRun<int64_t>(proc, a, b), (~a & ~b));
 }
 
+static void testBitAndNotNot32(int32_t a, int32_t b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* argB = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+    Value* notA = root->appendNew<Value>(proc, BitXor, Origin(), argA, root->appendNew<Const32Value>(proc, Origin(), -1));
+    Value* notB = root->appendNew<Value>(proc, BitXor, Origin(), argB, root->appendNew<Const32Value>(proc, Origin(), -1));
+    root->appendNewControlValue(
+        proc, Return, Origin(),
+        root->appendNew<Value>(
+            proc, BitAnd, Origin(),
+            notA,
+            notB));
+
+    CHECK_EQ(compileAndRun<int32_t>(proc, a, b), (~a & ~b));
+}
+
 static void testBitAndNotImm(int64_t a, int64_t b)
 {
     Procedure proc;
@@ -2579,7 +2597,24 @@ static void testBitAndNotImm(int64_t a, int64_t b)
             notA,
             cstB));
 
-    CHECK_EQ(compileAndRun<int64_t>(proc, a, b), (~a & b));
+    CHECK_EQ(compileAndRun<int64_t>(proc, a), (~a & b));
+}
+
+static void testBitAndNotImm32(int32_t a, int32_t b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* notA = root->appendNew<Value>(proc, BitXor, Origin(), argA, root->appendNew<Const32Value>(proc, Origin(), -1));
+    Value* cstB = root->appendNew<Const32Value>(proc, Origin(), b);
+    root->appendNewControlValue(
+        proc, Return, Origin(),
+        root->appendNew<Value>(
+            proc, BitAnd, Origin(),
+            notA,
+            cstB));
+
+    CHECK_EQ(compileAndRun<int32_t>(proc, a), (~a & b));
 }
 
 static void testBitAndImms(int64_t a, int64_t b)
@@ -2994,6 +3029,34 @@ static void testBitOrAndAndArgs(int64_t a, int64_t b, int64_t c)
     }
 }
 
+static void testBitOrAndAndArgs32(int32_t a, int32_t b, int32_t c)
+{
+    // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
+    // ((a & b) | (a & c))
+    // ((a & b) | (c & a))
+    // ((b & a) | (a & c))
+    // ((b & a) | (c & a))
+    for (int i = 0; i < 4; ++i) {
+        Procedure proc;
+        BasicBlock* root = proc.addBlock();
+        Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        Value* argB = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+        Value* argC = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2));
+        Value* andAB = i & 2 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
+            : root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
+        Value* andAC = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argC)
+            : root->appendNew<Value>(proc, BitAnd, Origin(), argC, argA);
+        root->appendNewControlValue(
+            proc, Return, Origin(),
+            root->appendNew<Value>(
+                proc, BitOr, Origin(),
+                andAB,
+                andAC));
+
+        CHECK_EQ(compileAndRun<int32_t>(proc, a, b, c), ((a & b) | (a & c)));
+    }
+}
+
 static void testBitOrAndSameArgs(int64_t a, int64_t b)
 {
     // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
@@ -3016,6 +3079,28 @@ static void testBitOrAndSameArgs(int64_t a, int64_t b)
     }
 }
 
+static void testBitOrAndSameArgs32(int32_t a, int32_t b)
+{
+    // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
+    // ((a & b) | a)
+    // ((b & a) | a)
+    // (a | (a & b))
+    // (a | (b & a))
+    for (int i = 0; i < 4; ++i) {
+        Procedure proc;
+        BasicBlock* root = proc.addBlock();
+        Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        Value* argB = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+        Value* andAB = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
+            : root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
+        Value* result = i & 2 ? root->appendNew<Value>(proc, BitOr, Origin(), andAB, argA)
+            : root->appendNew<Value>(proc, BitOr, Origin(), argA, andAB);
+        root->appendNewControlValue(proc, Return, Origin(), result);
+
+        CHECK_EQ(compileAndRun<int32_t>(proc, a, b), ((a & b) | a));
+    }
+}
+
 static void testBitOrNotNot(int64_t a, int64_t b)
 {
     Procedure proc;
@@ -3034,6 +3119,24 @@ static void testBitOrNotNot(int64_t a, int64_t b)
     CHECK_EQ(compileAndRun<int64_t>(proc, a, b), (~a | ~b));
 }
 
+static void testBitOrNotNot32(int32_t a, int32_t b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* argB = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+    Value* notA = root->appendNew<Value>(proc, BitXor, Origin(), argA, root->appendNew<Const32Value>(proc, Origin(), -1));
+    Value* notB = root->appendNew<Value>(proc, BitXor, Origin(), argB, root->appendNew<Const32Value>(proc, Origin(), -1));
+    root->appendNewControlValue(
+        proc, Return, Origin(),
+        root->appendNew<Value>(
+            proc, BitOr, Origin(),
+            notA,
+            notB));
+
+    CHECK_EQ(compileAndRun<int32_t>(proc, a, b), (~a | ~b));
+}
+
 static void testBitOrNotImm(int64_t a, int64_t b)
 {
     Procedure proc;
@@ -3051,6 +3154,23 @@ static void testBitOrNotImm(int64_t a, int64_t b)
     CHECK_EQ(compileAndRun<int64_t>(proc, a, b), (~a | b));
 }
 
+static void testBitOrNotImm32(int32_t a, int32_t b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* notA = root->appendNew<Value>(proc, BitXor, Origin(), argA, root->appendNew<Const32Value>(proc, Origin(), -1));
+    Value* cstB = root->appendNew<Const32Value>(proc, Origin(), b);
+    root->appendNewControlValue(
+        proc, Return, Origin(),
+        root->appendNew<Value>(
+            proc, BitOr, Origin(),
+            notA,
+            cstB));
+
+    CHECK_EQ(compileAndRun<int32_t>(proc, a), (~a | b));
+}
+
 static void testBitOrImms(int64_t a, int64_t b)
 {
     Procedure proc;
@@ -3287,7 +3407,9 @@ void addBitTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
     RUN_BINARY(testBitAndImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
     RUN_BINARY(testBitAndArgsFloatWithUselessDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
     RUN_BINARY(testBitAndNotNot, int64Operands(), int64Operands());
+    RUN_BINARY(testBitAndNotNot32, int32Operands(), int32Operands());
     RUN_BINARY(testBitAndNotImm, int64Operands(), int64Operands());
+    RUN_BINARY(testBitAndNotImm32, int32Operands(), int32Operands());
     
     RUN(testBitOrArgs(43, 43));
     RUN(testBitOrArgs(43, 0));
@@ -3351,9 +3473,13 @@ void addBitTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
     RUN_BINARY(testBitOrImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
     RUN_BINARY(testBitOrArgsFloatWithUselessDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
     RUN_TERNARY(testBitOrAndAndArgs, int64Operands(), int64Operands(), int64Operands());
+    RUN_TERNARY(testBitOrAndAndArgs32, int32Operands(), int32Operands(), int32Operands());
     RUN_BINARY(testBitOrAndSameArgs, int64Operands(), int64Operands());
+    RUN_BINARY(testBitOrAndSameArgs32, int32Operands(), int32Operands());
     RUN_BINARY(testBitOrNotNot, int64Operands(), int64Operands());
+    RUN_BINARY(testBitOrNotNot32, int32Operands(), int32Operands());
     RUN_BINARY(testBitOrNotImm, int64Operands(), int64Operands());
+    RUN_BINARY(testBitOrNotImm32, int32Operands(), int32Operands());
     
     RUN_BINARY(testBitXorArgs, int64Operands(), int64Operands());
     RUN_UNARY(testBitXorSameArg, int64Operands());
@@ -3393,7 +3519,9 @@ void addBitTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
     RUN(testBitXorImmBitXorArgImm32(6, 1, 6));
     RUN(testBitXorImmBitXorArgImm32(24, 0xffff, 7));
     RUN_TERNARY(testBitXorAndAndArgs, int64Operands(), int64Operands(), int64Operands());
+    RUN_TERNARY(testBitXorAndAndArgs32, int32Operands(), int32Operands(), int32Operands());
     RUN_BINARY(testBitXorAndSameArgs, int64Operands(), int64Operands());
+    RUN_BINARY(testBitXorAndSameArgs32, int32Operands(), int32Operands());
     
     RUN_UNARY(testBitNotArg, int64Operands());
     RUN_UNARY(testBitNotImm, int64Operands());
index 336c822..7091672 100644 (file)
@@ -259,6 +259,34 @@ void testBitXorAndAndArgs(int64_t a, int64_t b, int64_t c)
     }
 }
 
+void testBitXorAndAndArgs32(int32_t a, int32_t b, int32_t c)
+{
+    // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
+    // ((a & b) ^ (a & c))
+    // ((a & b) ^ (c & a))
+    // ((b & a) ^ (a & c))
+    // ((b & a) ^ (c & a))
+    for (int i = 0; i < 4; ++i) {
+        Procedure proc;
+        BasicBlock* root = proc.addBlock();
+        Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        Value* argB = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+        Value* argC = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2));
+        Value* andAB = i & 2 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
+            : root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
+        Value* andAC = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argC)
+            : root->appendNew<Value>(proc, BitAnd, Origin(), argC, argA);
+        root->appendNewControlValue(
+            proc, Return, Origin(),
+            root->appendNew<Value>(
+                proc, BitXor, Origin(),
+                andAB,
+                andAC));
+
+        CHECK_EQ(compileAndRun<int32_t>(proc, a, b, c), ((a & b) ^ (a & c)));
+    }
+}
+
 void testBitXorAndSameArgs(int64_t a, int64_t b)
 {
     // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
@@ -281,6 +309,28 @@ void testBitXorAndSameArgs(int64_t a, int64_t b)
     }
 }
 
+void testBitXorAndSameArgs32(int32_t a, int32_t b)
+{
+    // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
+    // ((a & b) ^ a)
+    // ((b & a) ^ a)
+    // (a ^ (a & b))
+    // (a ^ (b & a))
+    for (int i = 0; i < 4; ++i) {
+        Procedure proc;
+        BasicBlock* root = proc.addBlock();
+        Value* argA = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        Value* argB = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+        Value* andAB = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
+            : root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
+        Value* result = i & 2 ? root->appendNew<Value>(proc, BitXor, Origin(), andAB, argA)
+            : root->appendNew<Value>(proc, BitXor, Origin(), argA, andAB);
+        root->appendNewControlValue(proc, Return, Origin(), result);
+
+        CHECK_EQ(compileAndRun<int32_t>(proc, a, b), ((a & b) ^ a));
+    }
+}
+
 void testBitXorImms(int64_t a, int64_t b)
 {
     Procedure proc;