Changeset 241964 in webkit


Ignore:
Timestamp:
Feb 22, 2019 2:54:23 PM (5 years ago)
Author:
rmorisset@apple.com
Message:

B3ReduceStrength: missing peephole optimizations for binary operations
https://bugs.webkit.org/show_bug.cgi?id=194252

Reviewed by Saam Barati.

Adds several sets of optimizations for BitAnd, BitOr and BitXor.
Using BitAnd distributivity over BitOr and BitXor:

Turn any of these (for Op == BitOr
Op == BitXor):

Op(BitAnd(x1, x2), BitAnd(x1, x3))
Op(BitAnd(x2, x1), BitAnd(x1, x3))
Op(BitAnd(x1, x2), BitAnd(x3, x1))
Op(BitAnd(x2, x1), BitAnd(x3, x1))

Into this: BitAnd(Op(x2, x3), x1)
And any of these:

Op(BitAnd(x1, x2), x1)
Op(BitAnd(x2, x1), x1)
Op(x1, BitAnd(x1, x2))
Op(x1, BitAnd(x2, x1))

Into this: BitAnd(Op(x2, x1), x1)
This second set is equivalent to doing x1 => BitAnd(x1, x1), and then applying the first set.

Using de Morgan laws (we represent not as BitXor with allOnes):

BitAnd(BitXor(x1, allOnes), BitXor(x2, allOnes)) => BitXor(BitOr(x1, x2), allOnes)
BitOr(BitXor(x1, allOnes), BitXor(x2, allOnes) => BitXor(BitAnd(x1, x2), allOnes)
BitOr(BitXor(x, allOnes), c) => BitXor(BitAnd(x, ~c), allOnes)
BitAnd(BitXor(x, allOnes), c) => BitXor(BitOr(x, ~c), allOnes)

The latter two are equivalent to doing c => BitXor(~c, allOnes), and then applying the former two.

All of these transformations either reduce the number of operations (which we always do when possible), or bring the expression closer to having:

  • BitXor with all ones at the outermost
  • then BitAnd
  • then other BitXor
  • then BitOr at the innermost.

These transformations that don't directly reduce the number of operations are still useful for normalization (helping things like CSE), and also can enable
more optimizations (for example BitXor with all ones can easily cancel each other once they are all at the outermost level).

  • b3/B3ReduceStrength.cpp:
  • b3/testb3.cpp:

(JSC::B3::testBitAndNotNot):
(JSC::B3::testBitAndNotImm):
(JSC::B3::testBitOrAndAndArgs):
(JSC::B3::testBitOrAndSameArgs):
(JSC::B3::testBitOrNotNot):
(JSC::B3::testBitOrNotImm):
(JSC::B3::testBitXorAndAndArgs):
(JSC::B3::testBitXorAndSameArgs):
(JSC::B3::run):

Location:
trunk/Source/JavaScriptCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r241956 r241964  
     12019-02-22  Robin Morisset  <rmorisset@apple.com>
     2
     3        B3ReduceStrength: missing peephole optimizations for binary operations
     4        https://bugs.webkit.org/show_bug.cgi?id=194252
     5
     6        Reviewed by Saam Barati.
     7
     8        Adds several sets of optimizations for BitAnd, BitOr and BitXor.
     9        Using BitAnd distributivity over BitOr and BitXor:
     10          Turn any of these (for Op == BitOr || Op == BitXor):
     11                Op(BitAnd(x1, x2), BitAnd(x1, x3))
     12                Op(BitAnd(x2, x1), BitAnd(x1, x3))
     13                Op(BitAnd(x1, x2), BitAnd(x3, x1))
     14                Op(BitAnd(x2, x1), BitAnd(x3, x1))
     15           Into this: BitAnd(Op(x2, x3), x1)
     16           And any of these:
     17                Op(BitAnd(x1, x2), x1)
     18                Op(BitAnd(x2, x1), x1)
     19                Op(x1, BitAnd(x1, x2))
     20                Op(x1, BitAnd(x2, x1))
     21           Into this: BitAnd(Op(x2, x1), x1)
     22           This second set is equivalent to doing x1 => BitAnd(x1, x1), and then applying the first set.
     23        Using de Morgan laws (we represent not as BitXor with allOnes):
     24          BitAnd(BitXor(x1, allOnes), BitXor(x2, allOnes)) => BitXor(BitOr(x1, x2), allOnes)
     25          BitOr(BitXor(x1, allOnes), BitXor(x2, allOnes) => BitXor(BitAnd(x1, x2), allOnes)
     26          BitOr(BitXor(x, allOnes), c) => BitXor(BitAnd(x, ~c), allOnes)
     27          BitAnd(BitXor(x, allOnes), c) => BitXor(BitOr(x, ~c), allOnes)
     28        The latter two are equivalent to doing c => BitXor(~c, allOnes), and then applying the former two.
     29
     30        All of these transformations either reduce the number of operations (which we always do when possible), or bring the expression closer to having:
     31          - BitXor with all ones at the outermost
     32          - then BitAnd
     33          - then other BitXor
     34          - then BitOr at the innermost.
     35        These transformations that don't directly reduce the number of operations are still useful for normalization (helping things like CSE), and also can enable
     36        more optimizations (for example BitXor with all ones can easily cancel each other once they are all at the outermost level).
     37
     38        * b3/B3ReduceStrength.cpp:
     39        * b3/testb3.cpp:
     40        (JSC::B3::testBitAndNotNot):
     41        (JSC::B3::testBitAndNotImm):
     42        (JSC::B3::testBitOrAndAndArgs):
     43        (JSC::B3::testBitOrAndSameArgs):
     44        (JSC::B3::testBitOrNotNot):
     45        (JSC::B3::testBitOrNotImm):
     46        (JSC::B3::testBitXorAndAndArgs):
     47        (JSC::B3::testBitXorAndSameArgs):
     48        (JSC::B3::run):
     49
    1502019-02-22  Yusuke Suzuki  <ysuzuki@apple.com>
    251
  • trunk/Source/JavaScriptCore/b3/B3ReduceStrength.cpp

    r241768 r241964  
    963963            // Turn this: BitAnd(value, all-ones)
    964964            // Into this: value.
    965             if ((m_value->type() == Int64 && m_value->child(1)->isInt(0xffffffffffffffff))
    966                 || (m_value->type() == Int32 && m_value->child(1)->isInt(0xffffffff))) {
     965            if ((m_value->type() == Int64 && m_value->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
     966                || (m_value->type() == Int32 && m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max()))) {
    967967                replaceWithIdentity(m_value->child(0));
    968968                break;
     
    985985                m_value->child(0) = m_value->child(0)->child(0);
    986986                m_changed = true;
     987                break;
    987988            }
    988989
     
    993994                m_value->child(0) = m_value->child(0)->child(0);
    994995                m_changed = true;
     996                break;
    995997            }
    996998
     
    10031005                    m_value->child(0)->child(0), m_value->child(0)->child(1));
    10041006                m_changed = true;
     1007                break;
    10051008            }
    10061009
     
    10241027                    break;
    10251028                }
    1026             }
     1029                break;
     1030            }
     1031
     1032            // Turn this: BitAnd(BitXor(x1, allOnes), BitXor(x2, allOnes)
     1033            // Into this: BitXor(BitOr(x1, x2), allOnes)
     1034            // By applying De Morgan laws
     1035            if (m_value->child(0)->opcode() == BitXor
     1036                && m_value->child(1)->opcode() == BitXor
     1037                && ((m_value->type() == Int64
     1038                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint64_t>::max())
     1039                        && m_value->child(1)->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
     1040                    || (m_value->type() == Int32
     1041                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint32_t>::max())
     1042                        && m_value->child(1)->child(1)->isInt(std::numeric_limits<uint32_t>::max())))) {
     1043                Value* bitOr = m_insertionSet.insert<Value>(m_index, BitOr, m_value->origin(), m_value->child(0)->child(0), m_value->child(1)->child(0));
     1044                replaceWithNew<Value>(BitXor, m_value->origin(), bitOr, m_value->child(1)->child(1));
     1045                break;
     1046            }
     1047
     1048            // Turn this: BitAnd(BitXor(x, allOnes), c)
     1049            // Into this: BitXor(BitOr(x, ~c), allOnes)
     1050            // This is a variation on the previous optimization, treating c as if it were BitXor(~c, allOnes)
     1051            // It does not reduce the number of operations, but provides some normalization (we try to get BitXor by allOnes at the outermost point), and some chance to float Xors to a place where they might get eliminated.
     1052            if (m_value->child(0)->opcode() == BitXor
     1053                && m_value->child(1)->hasInt()
     1054                && ((m_value->type() == Int64
     1055                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
     1056                    || (m_value->type() == Int32
     1057                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint32_t>::max())))) {
     1058                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)));
     1059                replaceWithNew<Value>(BitXor, m_value->origin(), bitOr, m_value->child(0)->child(1));
     1060                break;
     1061            }
     1062
    10271063            break;
    10281064
     
    10651101            // Turn this: BitOr(value, all-ones)
    10661102            // Into this: all-ones.
    1067             if ((m_value->type() == Int64 && m_value->child(1)->isInt(0xffffffffffffffff))
    1068                 || (m_value->type() == Int32 && m_value->child(1)->isInt(0xffffffff))) {
     1103            if ((m_value->type() == Int64 && m_value->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
     1104                || (m_value->type() == Int32 && m_value->child(1)->isInt(std::numeric_limits<uint32_t>::max()))) {
    10691105                replaceWithIdentity(m_value->child(1));
    10701106                break;
    10711107            }
     1108
     1109            // Turn this: BitOr(BitXor(x1, allOnes), BitXor(x2, allOnes)
     1110            // Into this: BitXor(BitAnd(x1, x2), allOnes)
     1111            // By applying De Morgan laws
     1112            if (m_value->child(0)->opcode() == BitXor
     1113                && m_value->child(1)->opcode() == BitXor
     1114                && ((m_value->type() == Int64
     1115                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint64_t>::max())
     1116                        && m_value->child(1)->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
     1117                    || (m_value->type() == Int32
     1118                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint32_t>::max())
     1119                        && m_value->child(1)->child(1)->isInt(std::numeric_limits<uint32_t>::max())))) {
     1120                Value* bitAnd = m_insertionSet.insert<Value>(m_index, BitAnd, m_value->origin(), m_value->child(0)->child(0), m_value->child(1)->child(0));
     1121                replaceWithNew<Value>(BitXor, m_value->origin(), bitAnd, m_value->child(1)->child(1));
     1122                break;
     1123            }
     1124
     1125            // Turn this: BitOr(BitXor(x, allOnes), c)
     1126            // Into this: BitXor(BitAnd(x, ~c), allOnes)
     1127            // This is a variation on the previous optimization, treating c as if it were BitXor(~c, allOnes)
     1128            // It does not reduce the number of operations, but provides some normalization (we try to get BitXor by allOnes at the outermost point), and some chance to float Xors to a place where they might get eliminated.
     1129            if (m_value->child(0)->opcode() == BitXor
     1130                && m_value->child(1)->hasInt()
     1131                && ((m_value->type() == Int64
     1132                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint64_t>::max()))
     1133                    || (m_value->type() == Int32
     1134                        && m_value->child(0)->child(1)->isInt(std::numeric_limits<uint32_t>::max())))) {
     1135                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)));
     1136                replaceWithNew<Value>(BitXor, m_value->origin(), bitAnd, m_value->child(0)->child(1));
     1137                break;
     1138            }
     1139
     1140            if (handleBitAndDistributivity())
     1141                break;
    10721142
    10731143            break;
     
    11171187                break;
    11181188            }
     1189               
     1190            if (handleBitAndDistributivity())
     1191                break;
    11191192
    11201193            break;
     
    22112284    }
    22122285
     2286    // For Op==BitOr or BitXor, turn any of these:
     2287    //      Op(BitAnd(x1, x2), BitAnd(x1, x3))
     2288    //      Op(BitAnd(x2, x1), BitAnd(x1, x3))
     2289    //      Op(BitAnd(x1, x2), BitAnd(x3, x1))
     2290    //      Op(BitAnd(x2, x1), BitAnd(x3, x1))
     2291    // Into this: BitAnd(Op(x2, x3), x1)
     2292    // And any of these:
     2293    //      Op(BitAnd(x1, x2), x1)
     2294    //      Op(BitAnd(x2, x1), x1)
     2295    //      Op(x1, BitAnd(x1, x2))
     2296    //      Op(x1, BitAnd(x2, x1))
     2297    // Into this: BitAnd(Op(x2, x1), x1)
     2298    // This second set is equivalent to doing x1 => BitAnd(x1, x1), and then applying the first set.
     2299    // It does not reduce the number of operations executed, but provides some useful normalization: we prefer to have BitAnd at the outermost, then BitXor, and finally BitOr at the innermost
     2300    bool handleBitAndDistributivity()
     2301    {
     2302        ASSERT(m_value->opcode() == BitOr || m_value->opcode() == BitXor);
     2303        Value* x1 = nullptr;
     2304        Value* x2 = nullptr;
     2305        Value* x3 = nullptr;
     2306        if (m_value->child(0)->opcode() == BitAnd && m_value->child(1)->opcode() == BitAnd) {
     2307            if (m_value->child(0)->child(0) == m_value->child(1)->child(0)) {
     2308                x1 = m_value->child(0)->child(0);
     2309                x2 = m_value->child(0)->child(1);
     2310                x3 = m_value->child(1)->child(1);
     2311            } else if (m_value->child(0)->child(1) == m_value->child(1)->child(0)) {
     2312                x1 = m_value->child(0)->child(1);
     2313                x2 = m_value->child(0)->child(0);
     2314                x3 = m_value->child(1)->child(1);
     2315            } else if (m_value->child(0)->child(0) == m_value->child(1)->child(1)) {
     2316                x1 = m_value->child(0)->child(0);
     2317                x2 = m_value->child(0)->child(1);
     2318                x3 = m_value->child(1)->child(0);
     2319            } else if (m_value->child(0)->child(1) == m_value->child(1)->child(1)) {
     2320                x1 = m_value->child(0)->child(1);
     2321                x2 = m_value->child(0)->child(0);
     2322                x3 = m_value->child(1)->child(0);
     2323            }
     2324        } else if (m_value->child(0)->opcode() == BitAnd) {
     2325            if (m_value->child(0)->child(0) == m_value->child(1)) {
     2326                x1 = x3 = m_value->child(1);
     2327                x2 = m_value->child(0)->child(1);
     2328            } else if (m_value->child(0)->child(1) == m_value->child(1)) {
     2329                x1 = x3 = m_value->child(1);
     2330                x2 = m_value->child(0)->child(0);
     2331            }
     2332        } else if (m_value->child(1)->opcode() == BitAnd) {
     2333            if (m_value->child(1)->child(0) == m_value->child(0)) {
     2334                x1 = x3 = m_value->child(0);
     2335                x2 = m_value->child(1)->child(1);
     2336            } else if (m_value->child(1)->child(1) == m_value->child(0)) {
     2337                x1 = x3 = m_value->child(0);
     2338                x2 = m_value->child(1)->child(0);
     2339            }
     2340        }
     2341        if (x1 != nullptr) {
     2342            ASSERT(x2 != nullptr && x3 != nullptr);
     2343            Value* bitOp = m_insertionSet.insert<Value>(m_index, m_value->opcode(), m_value->origin(), x2, x3);
     2344            replaceWithNew<Value>(BitAnd, m_value->origin(), bitOp, x1);
     2345            return true;
     2346        }
     2347        return false;
     2348    }
     2349
    22132350    struct CanonicalizedComparison {
    22142351        Opcode opcode;
  • trunk/Source/JavaScriptCore/b3/testb3.cpp

    r241783 r241964  
    24872487}
    24882488
     2489void testBitAndNotNot(int64_t a, int64_t b)
     2490{
     2491    Procedure proc;
     2492    BasicBlock* root = proc.addBlock();
     2493    Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     2494    Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
     2495    Value* notA = root->appendNew<Value>(proc, BitXor, Origin(), argA, root->appendNew<Const64Value>(proc, Origin(), -1));
     2496    Value* notB = root->appendNew<Value>(proc, BitXor, Origin(), argB, root->appendNew<Const64Value>(proc, Origin(), -1));
     2497    root->appendNewControlValue(
     2498        proc, Return, Origin(),
     2499        root->appendNew<Value>(
     2500            proc, BitAnd, Origin(),
     2501            notA,
     2502            notB));
     2503
     2504    CHECK_EQ(compileAndRun<int64_t>(proc, a, b), (~a & ~b));
     2505}
     2506
     2507void testBitAndNotImm(int64_t a, int64_t b)
     2508{
     2509    Procedure proc;
     2510    BasicBlock* root = proc.addBlock();
     2511    Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     2512    Value* notA = root->appendNew<Value>(proc, BitXor, Origin(), argA, root->appendNew<Const64Value>(proc, Origin(), -1));
     2513    Value* cstB = root->appendNew<Const64Value>(proc, Origin(), b);
     2514    root->appendNewControlValue(
     2515        proc, Return, Origin(),
     2516        root->appendNew<Value>(
     2517            proc, BitAnd, Origin(),
     2518            notA,
     2519            cstB));
     2520
     2521    CHECK_EQ(compileAndRun<int64_t>(proc, a, b), (~a & b));
     2522}
     2523
    24892524void testBitAndImms(int64_t a, int64_t b)
    24902525{
     
    28712906}
    28722907
     2908void testBitOrAndAndArgs(int64_t a, int64_t b, int64_t c)
     2909{
     2910    // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
     2911    // ((a & b) | (a & c))
     2912    // ((a & b) | (c & a))
     2913    // ((b & a) | (a & c))
     2914    // ((b & a) | (c & a))
     2915    for (int i = 0; i < 4; ++i) {
     2916        Procedure proc;
     2917        BasicBlock* root = proc.addBlock();
     2918        Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     2919        Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
     2920        Value* argC = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
     2921        Value* andAB = i & 2 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
     2922            : root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
     2923        Value* andAC = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argC)
     2924            : root->appendNew<Value>(proc, BitAnd, Origin(), argC, argA);
     2925        root->appendNewControlValue(
     2926            proc, Return, Origin(),
     2927            root->appendNew<Value>(
     2928                proc, BitOr, Origin(),
     2929                andAB,
     2930                andAC));
     2931
     2932        CHECK_EQ(compileAndRun<int64_t>(proc, a, b, c), ((a & b) | (a & c)));
     2933    }
     2934}
     2935
     2936void testBitOrAndSameArgs(int64_t a, int64_t b)
     2937{
     2938    // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
     2939    // ((a & b) | a)
     2940    // ((b & a) | a)
     2941    // (a | (a & b))
     2942    // (a | (b & a))
     2943    for (int i = 0; i < 4; ++i) {
     2944        Procedure proc;
     2945        BasicBlock* root = proc.addBlock();
     2946        Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     2947        Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
     2948        Value* andAB = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
     2949            : root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
     2950        Value* result = i & 2 ? root->appendNew<Value>(proc, BitOr, Origin(), andAB, argA)
     2951            : root->appendNew<Value>(proc, BitOr, Origin(), argA, andAB);
     2952        root->appendNewControlValue(proc, Return, Origin(), result);
     2953
     2954        CHECK_EQ(compileAndRun<int64_t>(proc, a, b), ((a & b) | a));
     2955    }
     2956}
     2957
     2958void testBitOrNotNot(int64_t a, int64_t b)
     2959{
     2960    Procedure proc;
     2961    BasicBlock* root = proc.addBlock();
     2962    Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     2963    Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
     2964    Value* notA = root->appendNew<Value>(proc, BitXor, Origin(), argA, root->appendNew<Const64Value>(proc, Origin(), -1));
     2965    Value* notB = root->appendNew<Value>(proc, BitXor, Origin(), argB, root->appendNew<Const64Value>(proc, Origin(), -1));
     2966    root->appendNewControlValue(
     2967        proc, Return, Origin(),
     2968        root->appendNew<Value>(
     2969            proc, BitOr, Origin(),
     2970            notA,
     2971            notB));
     2972
     2973    CHECK_EQ(compileAndRun<int64_t>(proc, a, b), (~a | ~b));
     2974}
     2975
     2976void testBitOrNotImm(int64_t a, int64_t b)
     2977{
     2978    Procedure proc;
     2979    BasicBlock* root = proc.addBlock();
     2980    Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     2981    Value* notA = root->appendNew<Value>(proc, BitXor, Origin(), argA, root->appendNew<Const64Value>(proc, Origin(), -1));
     2982    Value* cstB = root->appendNew<Const64Value>(proc, Origin(), b);
     2983    root->appendNewControlValue(
     2984        proc, Return, Origin(),
     2985        root->appendNew<Value>(
     2986            proc, BitOr, Origin(),
     2987            notA,
     2988            cstB));
     2989   
     2990    CHECK_EQ(compileAndRun<int64_t>(proc, a, b), (~a | b));
     2991}
     2992
    28732993void testBitOrImms(int64_t a, int64_t b)
    28742994{
     
    32313351
    32323352    CHECK(!compileAndRun<int64_t>(proc, a));
     3353}
     3354
     3355void testBitXorAndAndArgs(int64_t a, int64_t b, int64_t c)
     3356{
     3357    // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
     3358    // ((a & b) ^ (a & c))
     3359    // ((a & b) ^ (c & a))
     3360    // ((b & a) ^ (a & c))
     3361    // ((b & a) ^ (c & a))
     3362    for (int i = 0; i < 4; ++i) {
     3363        Procedure proc;
     3364        BasicBlock* root = proc.addBlock();
     3365        Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     3366        Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
     3367        Value* argC = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
     3368        Value* andAB = i & 2 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
     3369            : root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
     3370        Value* andAC = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argC)
     3371            : root->appendNew<Value>(proc, BitAnd, Origin(), argC, argA);
     3372        root->appendNewControlValue(
     3373            proc, Return, Origin(),
     3374            root->appendNew<Value>(
     3375                proc, BitXor, Origin(),
     3376                andAB,
     3377                andAC));
     3378
     3379        CHECK_EQ(compileAndRun<int64_t>(proc, a, b, c), ((a & b) ^ (a & c)));
     3380    }
     3381}
     3382
     3383void testBitXorAndSameArgs(int64_t a, int64_t b)
     3384{
     3385    // We want to check every possible ordering of arguments (to properly check every path in B3ReduceStrength):
     3386    // ((a & b) ^ a)
     3387    // ((b & a) ^ a)
     3388    // (a ^ (a & b))
     3389    // (a ^ (b & a))
     3390    for (int i = 0; i < 4; ++i) {
     3391        Procedure proc;
     3392        BasicBlock* root = proc.addBlock();
     3393        Value* argA = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
     3394        Value* argB = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
     3395        Value* andAB = i & 1 ? root->appendNew<Value>(proc, BitAnd, Origin(), argA, argB)
     3396            : root->appendNew<Value>(proc, BitAnd, Origin(), argB, argA);
     3397        Value* result = i & 2 ? root->appendNew<Value>(proc, BitXor, Origin(), andAB, argA)
     3398            : root->appendNew<Value>(proc, BitXor, Origin(), argA, andAB);
     3399        root->appendNewControlValue(proc, Return, Origin(), result);
     3400       
     3401        CHECK_EQ(compileAndRun<int64_t>(proc, a, b), ((a & b) ^ a));
     3402    }
    32333403}
    32343404
     
    1645216622                }));                                        \
    1645316623        }                                                   \
     16624    }
     16625#define RUN_TERNARY(test, valuesA, valuesB, valuesC) \
     16626    for (auto a : valuesA) {                                    \
     16627        for (auto b : valuesB) {                                \
     16628            for (auto c : valuesC) {                            \
     16629                CString testStr = toCString(#test, "(", a.name, ", ", b.name, ",", c.name, ")"); \
     16630                if (!shouldRun(testStr.data()))                 \
     16631                    continue;                                   \
     16632                tasks.append(createSharedTask<void()>(          \
     16633                    [=] () {                                    \
     16634                        dataLog(toCString(testStr, "...\n"));   \
     16635                        test(a.value, b.value, c.value);        \
     16636                        dataLog(toCString(testStr, ": OK!\n")); \
     16637                    }));                                        \
     16638            }                                                   \
     16639        }                                                       \
    1645416640    }
    1645516641
     
    1678116967    RUN_BINARY(testBitAndImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
    1678216968    RUN_BINARY(testBitAndArgsFloatWithUselessDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
     16969    RUN_BINARY(testBitAndNotNot, int64Operands(), int64Operands());
     16970    RUN_BINARY(testBitAndNotImm, int64Operands(), int64Operands());
    1678316971
    1678416972    RUN(testBitOrArgs(43, 43));
     
    1684317031    RUN_BINARY(testBitOrImmsFloat, floatingPointOperands<float>(), floatingPointOperands<float>());
    1684417032    RUN_BINARY(testBitOrArgsFloatWithUselessDoubleConversion, floatingPointOperands<float>(), floatingPointOperands<float>());
     17033    RUN_TERNARY(testBitOrAndAndArgs, int64Operands(), int64Operands(), int64Operands());
     17034    RUN_BINARY(testBitOrAndSameArgs, int64Operands(), int64Operands());
     17035    RUN_BINARY(testBitOrNotNot, int64Operands(), int64Operands());
     17036    RUN_BINARY(testBitOrNotImm, int64Operands(), int64Operands());
    1684517037
    1684617038    RUN_BINARY(testBitXorArgs, int64Operands(), int64Operands());
     
    1688117073    RUN(testBitXorImmBitXorArgImm32(6, 1, 6));
    1688217074    RUN(testBitXorImmBitXorArgImm32(24, 0xffff, 7));
     17075    RUN_TERNARY(testBitXorAndAndArgs, int64Operands(), int64Operands(), int64Operands());
     17076    RUN_BINARY(testBitXorAndSameArgs, int64Operands(), int64Operands());
    1688317077
    1688417078    RUN_UNARY(testBitNotArg, int64Operands());
Note: See TracChangeset for help on using the changeset viewer.