Skip to content

Commit b6c0124

Browse files
committed
[InstCombine] Simplify select if it combinated and/or/xor
`and/or/xor` operations can each be changed to sum of logical operations including operators other than themselves. `x&y -> (x|y) ^ (x^y)` `x|y -> (x&y) | (x^y)` `x^y -> (x|y) ^ (x&y)` if left of condition of `SelectInst` is `and/or/xor` logical operation and right is equal to `0, -1`, or a `constant`, and if `TrueVal` consist of `and/or/xor` logical operation then we can optimize this case. This patch implements this combination. Proof: https://alive2.llvm.org/ce/z/WW8iRR Fixed #71792.
1 parent edeaf1c commit b6c0124

File tree

2 files changed

+192
-135
lines changed

2 files changed

+192
-135
lines changed

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,6 +1672,142 @@ static Value *foldSelectInstWithICmpConst(SelectInst &SI, ICmpInst *ICI,
16721672
return nullptr;
16731673
}
16741674

1675+
static Instruction *foldSelectICmpEq(SelectInst &SI, ICmpInst *ICI,
1676+
InstCombinerImpl &IC) {
1677+
ICmpInst::Predicate Pred = ICI->getPredicate();
1678+
if (!ICmpInst::isEquality(Pred))
1679+
return nullptr;
1680+
1681+
Value *TrueVal = SI.getTrueValue();
1682+
Value *FalseVal = SI.getFalseValue();
1683+
Value *CmpLHS = ICI->getOperand(0);
1684+
Value *CmpRHS = ICI->getOperand(1);
1685+
1686+
if (Pred == ICmpInst::ICMP_NE) {
1687+
Pred = ICmpInst::ICMP_EQ;
1688+
std::swap(TrueVal, FalseVal);
1689+
}
1690+
1691+
// Transform (X == C) ? X : Y -> (X == C) ? C : Y
1692+
// specific handling for Bitwise operation.
1693+
// https://alive2.llvm.org/ce/z/mW3eYR
1694+
// x&y -> (x|y) ^ (x^y) or (x|y) & ~(x^y)
1695+
// x|y -> (x&y) | (x^y) or (x&y) ^ (x^y)
1696+
// x^y -> (x|y) ^ (x&y) or (x|y) & ~(x&y)
1697+
Value *X, *Y;
1698+
if (!(match(CmpLHS, m_BitwiseLogic(m_Value(X), m_Value(Y)))) ||
1699+
!(match(TrueVal, m_BitwiseLogic(m_Specific(X), m_Specific(Y)))))
1700+
return nullptr;
1701+
1702+
Value *AllOnes = Constant::getAllOnesValue(TrueVal->getType());
1703+
Value *Null = Constant::getNullValue(TrueVal->getType());
1704+
Instruction *ISI = &cast<Instruction>(SI);
1705+
const unsigned AndOps = Instruction::And, OrOps = Instruction::Or,
1706+
XorOps = Instruction::Xor, NoOps = 0;
1707+
enum NotMask { None = 0, NotInner, NotRHS };
1708+
1709+
auto matchFalseVal = [&](Value *FalseVal, Value *CmpRHS, Value *X, Value *Y,
1710+
unsigned OuterOpc, unsigned InnerOpc,
1711+
unsigned NotMask) {
1712+
auto matchInner = m_c_BinOp(InnerOpc, m_Specific(X), m_Specific(Y));
1713+
if (OuterOpc == NoOps) {
1714+
if (match(CmpRHS, m_Zero()))
1715+
return match(FalseVal, matchInner);
1716+
else
1717+
return false;
1718+
}
1719+
1720+
if (NotMask == NotInner) {
1721+
return match(FalseVal,
1722+
m_c_BinOp(OuterOpc, m_Not(matchInner), m_Specific(CmpRHS)));
1723+
} else if (NotMask == NotRHS) {
1724+
return match(FalseVal,
1725+
m_c_BinOp(OuterOpc, matchInner, m_Not(m_Specific(CmpRHS))));
1726+
} else {
1727+
return match(FalseVal,
1728+
m_c_BinOp(OuterOpc, matchInner, m_Specific(CmpRHS)));
1729+
}
1730+
};
1731+
1732+
// https://alive2.llvm.org/ce/z/rw7XWv
1733+
// (X&Y)==C ? X|Y : X^Y -> (X^Y)|C : X^Y or (X^Y)^ C : X^Y
1734+
// (X&Y)==C ? X^Y : X|Y -> (X|Y)^C : X|Y or (X|Y)&~C : X|Y
1735+
if (match(CmpLHS, m_And(m_Value(X), m_Value(Y)))) {
1736+
if (match(TrueVal, m_c_Or(m_Specific(X), m_Specific(Y)))) {
1737+
// (X&Y)==0 ? X|Y : X^Y -> (X^Y)|0 : X^Y -> X^Y
1738+
if (matchFalseVal(FalseVal, CmpRHS, X, Y, NoOps, XorOps, None) ||
1739+
// (X&Y)==C ? X|Y : (X^Y)|C -> (X^Y)|C : (X^Y)|C -> (X^Y)|C
1740+
matchFalseVal(FalseVal, CmpRHS, X, Y, OrOps, XorOps, None) ||
1741+
// (X&Y)==C ? X|Y : (X^Y)^C -> (X^Y)^C : (X^Y)^C -> (X^Y)^C
1742+
matchFalseVal(FalseVal, CmpRHS, X, Y, XorOps, XorOps, None))
1743+
return IC.replaceInstUsesWith(*ISI, FalseVal);
1744+
// (X&Y)==-1 ? X|Y : FV -> -1 : FV
1745+
else if (match(CmpRHS, m_AllOnes()))
1746+
return IC.replaceOperand(SI, 1, AllOnes);
1747+
} else if (match(TrueVal, m_c_Xor(m_Specific(X), m_Specific(Y)))) {
1748+
// (X&Y)==0 ? X^Y : X|Y -> (X|Y)^0 : X|Y -> X|Y
1749+
if (matchFalseVal(FalseVal, CmpRHS, X, Y, NoOps, OrOps, None) ||
1750+
// (X&Y)==C ? X^Y : (X|Y)^ C -> (X|Y)^ C : (X|Y)^ C -> (X|Y)^ C
1751+
matchFalseVal(FalseVal, CmpRHS, X, Y, XorOps, OrOps, None) ||
1752+
// (X&Y)==C ? X^Y : (X|Y)&~C -> (X|Y)&~C : (X|Y)&~C -> (X|Y)&~C
1753+
matchFalseVal(FalseVal, CmpRHS, X, Y, AndOps, OrOps, NotRHS))
1754+
return IC.replaceInstUsesWith(*ISI, FalseVal);
1755+
// (X&Y)==-1 ? X^Y : FV -> 0 : FV
1756+
else if (match(CmpRHS, m_AllOnes()))
1757+
return IC.replaceOperand(SI, 1, Null);
1758+
}
1759+
}
1760+
1761+
// https://alive2.llvm.org/ce/z/5yaUzR
1762+
// (X|Y)==C ? X&Y : X^Y -> (X^Y)^C : X^Y or ~(X^Y)&C : X^Y
1763+
// (X|Y)==C ? X^Y : X&Y -> (X&Y)^C : X&Y or ~(X&Y)&C : X&Y
1764+
if (match(CmpLHS, m_Or(m_Value(X), m_Value(Y)))) {
1765+
if (match(TrueVal, m_c_And(m_Specific(X), m_Specific(Y)))) {
1766+
// (X|Y)==0 ? X&Y : X^Y -> (X^Y)^0 : X^Y -> X^Y
1767+
if (matchFalseVal(FalseVal, CmpRHS, X, Y, NoOps, XorOps, None) ||
1768+
// (X|Y)==C ? X&Y: (X^Y)^C -> (X^Y)^C: (X^Y)^C -> (X^Y)^C
1769+
matchFalseVal(FalseVal, CmpRHS, X, Y, XorOps, XorOps, None) ||
1770+
// (X|Y)==C ? X&Y:~(X^Y)&C ->~(X^Y)&C:~(X^Y)&C -> ~(X^Y)&C
1771+
matchFalseVal(FalseVal, CmpRHS, X, Y, AndOps, XorOps, NotInner))
1772+
return IC.replaceInstUsesWith(*ISI, FalseVal);
1773+
} else if (match(TrueVal, m_c_Xor(m_Specific(X), m_Specific(Y)))) {
1774+
// (X|Y)==0 ? X^Y : X&Y -> (X&Y)^0 : X&Y -> X&Y
1775+
if (matchFalseVal(FalseVal, CmpRHS, X, Y, NoOps, AndOps, None) ||
1776+
// (X|Y)==C ? X^Y : (X&Y)^C -> (X&Y)^C : (X&Y)^C -> (X&Y)^C
1777+
matchFalseVal(FalseVal, CmpRHS, X, Y, XorOps, AndOps, None) ||
1778+
// (X|Y)==C ? X^Y :~(X&Y)&C -> ~(X&Y)&C :~(X&Y)&C -> ~(X&Y)&C
1779+
matchFalseVal(FalseVal, CmpRHS, X, Y, AndOps, AndOps, NotInner))
1780+
return IC.replaceInstUsesWith(*ISI, FalseVal);
1781+
} else if (match(CmpRHS, m_Zero()))
1782+
// (X|Y)==0 ? (X BitwiseLogic Y) : FV -> (X|Y)==0 ? 0 : FV
1783+
return IC.replaceOperand(SI, 1, Null);
1784+
}
1785+
1786+
// https://alive2.llvm.org/ce/z/dA9Hy-
1787+
// (X^Y)==C ? X&Y : X|Y -> (X|Y)^C : X|Y or (X|Y)&~C : X|Y
1788+
// (X^Y)==C ? X|Y : X&Y -> (X&Y)|C : X&Y or (X&Y)^ C : X&Y
1789+
if (match(CmpLHS, m_Xor(m_Value(X), m_Value(Y)))) {
1790+
if ((match(TrueVal, m_c_And(m_Specific(X), m_Specific(Y))))) {
1791+
// (X^Y)==C ? X&Y : (X|Y)^C -> (X|Y)^C
1792+
if (matchFalseVal(FalseVal, CmpRHS, X, Y, XorOps, OrOps, None) ||
1793+
// (X^Y)==C ? X&Y : (X|Y)&~C -> (X|Y)&~C
1794+
matchFalseVal(FalseVal, CmpRHS, X, Y, AndOps, OrOps, NotRHS))
1795+
return IC.replaceInstUsesWith(*ISI, FalseVal);
1796+
} else if (match(TrueVal, m_c_Or(m_Specific(X), m_Specific(Y)))) {
1797+
// (X^Y)==C ? (X|Y) : (X&Y)|C -> (X&Y)|C
1798+
if (matchFalseVal(FalseVal, CmpRHS, X, Y, OrOps, AndOps, None) ||
1799+
// (X^Y)==C ? (X|Y) : (X&Y)^C -> (X&Y)^C
1800+
matchFalseVal(FalseVal, CmpRHS, X, Y, XorOps, AndOps, None))
1801+
return IC.replaceInstUsesWith(*ISI, FalseVal);
1802+
// (X^Y)==-1 ? (X|Y) : FV -> -1 : FV
1803+
if (match(CmpRHS, m_AllOnes()))
1804+
return IC.replaceOperand(SI, 1, AllOnes);
1805+
}
1806+
}
1807+
1808+
return nullptr;
1809+
}
1810+
16751811
/// Visit a SelectInst that has an ICmpInst as its first operand.
16761812
Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
16771813
ICmpInst *ICI) {
@@ -1714,6 +1850,9 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
17141850
}
17151851
}
17161852

1853+
if (Instruction *NewSel = foldSelectICmpEq(SI, ICI, *this))
1854+
return NewSel;
1855+
17171856
// Canonicalize a signbit condition to use zero constant by swapping:
17181857
// (CmpLHS > -1) ? TV : FV --> (CmpLHS < 0) ? FV : TV
17191858
// To avoid conflicts (infinite loops) with other canonicalizations, this is

0 commit comments

Comments
 (0)