@@ -1487,24 +1487,55 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
14871487 BlockRange ().Remove (node);
14881488 op3 = userIntrin->Op (2 );
14891489
1490+ // Tracks which two operands get used first
1491+ TernaryLogicUseFlags firstOpUseFlags = TernaryLogicUseFlags::AB;
1492+
14901493 if (op3 == node)
14911494 {
1492- op3 = userIntrin->Op (1 );
1495+ if (userOper == GT_AND_NOT)
1496+ {
1497+ op3 = op2;
1498+ op2 = op1;
1499+ op1 = userIntrin->Op (1 );
1500+
1501+ // AND_NOT isn't commutative so we need to shift parameters down
1502+ firstOpUseFlags = TernaryLogicUseFlags::BC;
1503+ }
1504+ else
1505+ {
1506+ op3 = userIntrin->Op (1 );
1507+ }
14931508 }
14941509
14951510 uint8_t controlByte = 0x00 ;
14961511
14971512 if ((userOper == GT_XOR) && op3->IsVectorAllBitsSet ())
14981513 {
1499- // We're being used by what is actually GT_NOT, so we
1500- // need to shift parameters down so that A is unused
1514+ // We have XOR(OP(A, B), AllBitsSet)
1515+ // A: op1
1516+ // B: op2
1517+ // C: op3 (AllBitsSet)
1518+ //
1519+ // We want A to be the unused parameter so swap it around
1520+ // A: op3 (AllBitsSet)
1521+ // B: op1
1522+ // C: op2
1523+ //
1524+ // This gives us NOT(OP(B, C))
1525+
1526+ assert (firstOpUseFlags == TernaryLogicUseFlags::AB);
15011527
15021528 std::swap (op2, op3);
15031529 std::swap (op1, op2);
15041530
15051531 if (isOperNot)
15061532 {
1507- // We have what is actually a double not, so just return op2
1533+ // We have NOT(XOR(B, AllBitsSet))
1534+ // A: op3 (AllBitsSet)
1535+ // B: op1
1536+ // C: op2 (AllBitsSet)
1537+ //
1538+ // This represents a double not, so so just return op2
15081539 // which is the only actual value now that the parameters
15091540 // were shifted around
15101541
@@ -1538,20 +1569,64 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
15381569 }
15391570 else if (isOperNot)
15401571 {
1541- // A is unused, so we just want OP(NOT(B), C)
1572+ if (firstOpUseFlags == TernaryLogicUseFlags::AB)
1573+ {
1574+ // We have OP(XOR(A, AllBitsSet), C)
1575+ // A: op1
1576+ // B: op2 (AllBitsSet)
1577+ // C: op3
1578+ //
1579+ // We want A to be the unused parameter so swap it around
1580+ // A: op2 (AllBitsSet)
1581+ // B: op1
1582+ // C: op3
1583+ //
1584+ // This gives us OP(NOT(B), C)
15421585
1543- assert (op2->IsVectorAllBitsSet ());
1544- std::swap (op1, op2);
1586+ assert (op2->IsVectorAllBitsSet ());
1587+ std::swap (op1, op2);
15451588
1546- controlByte = static_cast <uint8_t >(~B);
1547- controlByte = TernaryLogicInfo::GetTernaryControlByte (userOper, controlByte, C);
1589+ controlByte = static_cast <uint8_t >(~B);
1590+ controlByte = TernaryLogicInfo::GetTernaryControlByte (userOper, controlByte, C);
1591+ }
1592+ else
1593+ {
1594+ // We have OP(A, XOR(B, AllBitsSet))
1595+ // A: op1
1596+ // B: op2
1597+ // C: op3 (AllBitsSet)
1598+ //
1599+ // We want A to be the unused parameter so swap it around
1600+ // A: op3 (AllBitsSet)
1601+ // B: op1
1602+ // C: op2
1603+ //
1604+ // This gives us OP(B, NOT(C))
1605+
1606+ assert (firstOpUseFlags == TernaryLogicUseFlags::BC);
1607+
1608+ assert (op3->IsVectorAllBitsSet ());
1609+ std::swap (op2, op3);
1610+ std::swap (op1, op2);
1611+
1612+ controlByte = static_cast <uint8_t >(~C);
1613+ controlByte = TernaryLogicInfo::GetTernaryControlByte (userOper, B, controlByte);
1614+ }
15481615 }
1549- else
1616+ else if (firstOpUseFlags == TernaryLogicUseFlags::AB)
15501617 {
15511618 // We have OP2(OP1(A, B), C)
15521619 controlByte = TernaryLogicInfo::GetTernaryControlByte (oper, A, B);
15531620 controlByte = TernaryLogicInfo::GetTernaryControlByte (userOper, controlByte, C);
15541621 }
1622+ else
1623+ {
1624+ // We have OP2(A, OP1(B, C))
1625+ assert (firstOpUseFlags == TernaryLogicUseFlags::BC);
1626+
1627+ controlByte = TernaryLogicInfo::GetTernaryControlByte (oper, B, C);
1628+ controlByte = TernaryLogicInfo::GetTernaryControlByte (userOper, A, controlByte);
1629+ }
15551630
15561631 NamedIntrinsic ternaryLogicId = NI_AVX512F_TernaryLogic;
15571632
0 commit comments