Skip to content

Commit 89f6bfe

Browse files
committed
Ensure that TernaryLogic lowering accounts for AND_NOT since it is not commutative
1 parent 31733b9 commit 89f6bfe

File tree

1 file changed

+85
-10
lines changed

1 file changed

+85
-10
lines changed

src/coreclr/jit/lowerxarch.cpp

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)