Skip to content

Commit 9d655c6

Browse files
committed
[InstCombine] Simplify and/or by replacing operands with constants
1 parent 06b0426 commit 9d655c6

File tree

4 files changed

+85
-49
lines changed

4 files changed

+85
-49
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,6 +2159,61 @@ Instruction *InstCombinerImpl::foldBinOpOfDisplacedShifts(BinaryOperator &I) {
21592159
return BinaryOperator::Create(ShiftOp, NewC, ShAmt);
21602160
}
21612161

2162+
// Try to simplify X | Y by replacing occurrences of Y in X with 0.
2163+
// Similarly, simplify X & Y by replacing occurrences of Y in X with -1.
2164+
static Value *simplifyAndOrWithOpReplaced(Value *X, Value *Y, bool IsAnd,
2165+
InstCombinerImpl &IC,
2166+
unsigned Depth = 0) {
2167+
if (isa<Constant>(X) || X == Y)
2168+
return nullptr;
2169+
2170+
auto RecursivelyReplaceUses = [&](Instruction::BinaryOps Opcode, Value *Op0,
2171+
Value *Op1) -> Value * {
2172+
if (Depth == 2)
2173+
return nullptr;
2174+
2175+
// TODO: Relax the one-use constraint to clean up existing hard-coded
2176+
// simplifications.
2177+
if (!X->hasOneUse())
2178+
return nullptr;
2179+
Value *NewOp0 = simplifyAndOrWithOpReplaced(Op0, Y, IsAnd, IC, Depth + 1);
2180+
Value *NewOp1 = simplifyAndOrWithOpReplaced(Op1, Y, IsAnd, IC, Depth + 1);
2181+
if (!NewOp0 && !NewOp1)
2182+
return nullptr;
2183+
return IC.Builder.CreateBinOp(Opcode, NewOp0 ? NewOp0 : Op0,
2184+
NewOp1 ? NewOp1 : Op1);
2185+
};
2186+
2187+
Value *Op0, *Op1;
2188+
if (match(X, m_And(m_Value(Op0), m_Value(Op1)))) {
2189+
if (Op0 == Y || Op1 == Y)
2190+
return IsAnd ? (Op0 == Y ? Op1 : Op0)
2191+
: Constant::getNullValue(X->getType());
2192+
return RecursivelyReplaceUses(Instruction::And, Op0, Op1);
2193+
} else if (match(X, m_Or(m_Value(Op0), m_Value(Op1)))) {
2194+
if (Op0 == Y || Op1 == Y)
2195+
return IsAnd ? Constant::getAllOnesValue(X->getType())
2196+
: (Op0 == Y ? Op1 : Op0);
2197+
return RecursivelyReplaceUses(Instruction::Or, Op0, Op1);
2198+
} else if (match(X, m_Xor(m_Value(Op0), m_Value(Op1)))) {
2199+
if (Op0 == Y || Op1 == Y) {
2200+
Value *V = Op0 == Y ? Op1 : Op0;
2201+
if (IsAnd) {
2202+
if (Value *NotV =
2203+
simplifyXorInst(V, Constant::getAllOnesValue(V->getType()),
2204+
IC.getSimplifyQuery()))
2205+
return NotV;
2206+
if (X->hasOneUse())
2207+
return IC.Builder.CreateNot(V);
2208+
} else
2209+
return V;
2210+
}
2211+
// FIXME: Is it correct to decompose xor if Y may be undef?
2212+
return RecursivelyReplaceUses(Instruction::Xor, Op0, Op1);
2213+
}
2214+
return nullptr;
2215+
}
2216+
21622217
// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
21632218
// here. We should standardize that construct where it is needed or choose some
21642219
// other way to ensure that commutated variants of patterns are not missed.
@@ -2488,13 +2543,6 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
24882543

24892544
{
24902545
Value *A, *B, *C;
2491-
// A & (A ^ B) --> A & ~B
2492-
if (match(Op1, m_OneUse(m_c_Xor(m_Specific(Op0), m_Value(B)))))
2493-
return BinaryOperator::CreateAnd(Op0, Builder.CreateNot(B));
2494-
// (A ^ B) & A --> A & ~B
2495-
if (match(Op0, m_OneUse(m_c_Xor(m_Specific(Op1), m_Value(B)))))
2496-
return BinaryOperator::CreateAnd(Op1, Builder.CreateNot(B));
2497-
24982546
// A & ~(A ^ B) --> A & B
24992547
if (match(Op1, m_Not(m_c_Xor(m_Specific(Op0), m_Value(B)))))
25002548
return BinaryOperator::CreateAnd(Op0, B);
@@ -2688,6 +2736,11 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
26882736
if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
26892737
return Res;
26902738

2739+
if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ true, *this))
2740+
return BinaryOperator::CreateAnd(Op1, V);
2741+
if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ true, *this))
2742+
return BinaryOperator::CreateAnd(Op0, V);
2743+
26912744
return nullptr;
26922745
}
26932746

@@ -3399,10 +3452,6 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
33993452
return BinaryOperator::CreateMul(X, IncrementY);
34003453
}
34013454

3402-
// X | (X ^ Y) --> X | Y (4 commuted patterns)
3403-
if (match(&I, m_c_Or(m_Value(X), m_c_Xor(m_Deferred(X), m_Value(Y)))))
3404-
return BinaryOperator::CreateOr(X, Y);
3405-
34063455
// (A & C) | (B & D)
34073456
Value *A, *B, *C, *D;
34083457
if (match(Op0, m_And(m_Value(A), m_Value(C))) &&
@@ -3884,6 +3933,11 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
38843933
return BinaryOperator::CreateAnd(X, ConstantInt::get(Ty, *C1 | *C2));
38853934
}
38863935

3936+
if (Value *V = simplifyAndOrWithOpReplaced(Op0, Op1, /*IsAnd*/ false, *this))
3937+
return BinaryOperator::CreateOr(Op1, V);
3938+
if (Value *V = simplifyAndOrWithOpReplaced(Op1, Op0, /*IsAnd*/ false, *this))
3939+
return BinaryOperator::CreateOr(Op0, V);
3940+
38873941
return nullptr;
38883942
}
38893943

llvm/test/Transforms/InstCombine/and-or-icmps.ll

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,16 +369,12 @@ define void @simplify_before_foldAndOfICmps(ptr %p) {
369369
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i16 [[L7]], -1
370370
; CHECK-NEXT: [[B11:%.*]] = zext i1 [[TMP1]] to i16
371371
; CHECK-NEXT: [[C10:%.*]] = icmp ugt i16 [[L7]], [[B11]]
372-
; CHECK-NEXT: [[C5:%.*]] = icmp slt i16 [[L7]], 1
373372
; CHECK-NEXT: [[C7:%.*]] = icmp slt i16 [[L7]], 0
374-
; CHECK-NEXT: [[B15:%.*]] = xor i1 [[C7]], [[C10]]
375-
; CHECK-NEXT: [[C6:%.*]] = xor i1 [[B15]], true
376-
; CHECK-NEXT: [[TMP2:%.*]] = and i1 [[C5]], [[C6]]
377-
; CHECK-NEXT: [[C3:%.*]] = and i1 [[TMP2]], [[C10]]
378-
; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[C10]], true
379-
; CHECK-NEXT: [[C18:%.*]] = or i1 [[C7]], [[TMP3]]
380-
; CHECK-NEXT: [[TMP4:%.*]] = sext i1 [[C3]] to i64
381-
; CHECK-NEXT: [[G26:%.*]] = getelementptr i1, ptr null, i64 [[TMP4]]
373+
; CHECK-NEXT: [[C3:%.*]] = and i1 [[C10]], [[C7]]
374+
; CHECK-NEXT: [[TMP2:%.*]] = xor i1 [[C10]], true
375+
; CHECK-NEXT: [[C18:%.*]] = or i1 [[C7]], [[TMP2]]
376+
; CHECK-NEXT: [[TMP3:%.*]] = sext i1 [[C3]] to i64
377+
; CHECK-NEXT: [[G26:%.*]] = getelementptr i1, ptr null, i64 [[TMP3]]
382378
; CHECK-NEXT: store i16 [[L7]], ptr [[P:%.*]], align 2
383379
; CHECK-NEXT: store i1 [[C18]], ptr [[P]], align 1
384380
; CHECK-NEXT: store ptr [[G26]], ptr [[P]], align 8

llvm/test/Transforms/InstCombine/and-or-not.ll

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -761,11 +761,7 @@ define i4 @simplify_and_common_op_use1(i4 %x, i4 %y, i4 %z) {
761761
define i4 @simplify_and_common_op_use2(i4 %x, i4 %y, i4 %z) {
762762
; CHECK-LABEL: @simplify_and_common_op_use2(
763763
; CHECK-NEXT: call void @use(i4 [[Y:%.*]])
764-
; CHECK-NEXT: [[TMP1:%.*]] = or i4 [[X:%.*]], [[Z:%.*]]
765-
; CHECK-NEXT: [[XYZ:%.*]] = or i4 [[TMP1]], [[Y]]
766-
; CHECK-NEXT: [[NOT_XYZ:%.*]] = xor i4 [[XYZ]], -1
767-
; CHECK-NEXT: [[R:%.*]] = and i4 [[NOT_XYZ]], [[X]]
768-
; CHECK-NEXT: ret i4 [[R]]
764+
; CHECK-NEXT: ret i4 0
769765
;
770766
%xy = or i4 %y, %x
771767
call void @use(i4 %y)
@@ -779,12 +775,8 @@ define i4 @simplify_and_common_op_use2(i4 %x, i4 %y, i4 %z) {
779775

780776
define i4 @simplify_and_common_op_use3(i4 %x, i4 %y, i4 %z) {
781777
; CHECK-LABEL: @simplify_and_common_op_use3(
782-
; CHECK-NEXT: [[XY:%.*]] = or i4 [[X:%.*]], [[Y:%.*]]
783-
; CHECK-NEXT: [[XYZ:%.*]] = or i4 [[XY]], [[Z:%.*]]
784-
; CHECK-NEXT: call void @use(i4 [[Z]])
785-
; CHECK-NEXT: [[NOT_XYZ:%.*]] = xor i4 [[XYZ]], -1
786-
; CHECK-NEXT: [[R:%.*]] = and i4 [[NOT_XYZ]], [[X]]
787-
; CHECK-NEXT: ret i4 [[R]]
778+
; CHECK-NEXT: call void @use(i4 [[Z:%.*]])
779+
; CHECK-NEXT: ret i4 0
788780
;
789781
%xy = or i4 %x, %y
790782
%xyz = or i4 %xy, %z

llvm/test/Transforms/InstCombine/or.ll

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,10 +1781,8 @@ if.else:
17811781
; Tests from PR76554
17821782
define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
17831783
; CHECK-LABEL: @test_or_and_xor_constant(
1784-
; CHECK-NEXT: [[A:%.*]] = and i32 [[X:%.*]], -2147483648
1785-
; CHECK-NEXT: [[B:%.*]] = xor i32 [[A]], -2147483648
1786-
; CHECK-NEXT: [[C:%.*]] = and i32 [[B]], [[Y:%.*]]
1787-
; CHECK-NEXT: [[D:%.*]] = or i32 [[C]], [[A]]
1784+
; CHECK-NEXT: [[A1:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
1785+
; CHECK-NEXT: [[D:%.*]] = and i32 [[A1]], -2147483648
17881786
; CHECK-NEXT: ret i32 [[D]]
17891787
;
17901788
%a = and i32 %x, -2147483648
@@ -1796,9 +1794,8 @@ define i32 @test_or_and_xor_constant(i32 %x, i32 %y) {
17961794

17971795
define i32 @test_or_and_xor(i32 %a, i32 %b, i32 %c) {
17981796
; CHECK-LABEL: @test_or_and_xor(
1799-
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
1800-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
1801-
; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
1797+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
1798+
; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
18021799
; CHECK-NEXT: ret i32 [[OR]]
18031800
;
18041801
%xor = xor i32 %a, %b
@@ -1809,9 +1806,8 @@ define i32 @test_or_and_xor(i32 %a, i32 %b, i32 %c) {
18091806

18101807
define i32 @test_or_and_xor_commuted1(i32 %a, i32 %b, i32 %c) {
18111808
; CHECK-LABEL: @test_or_and_xor_commuted1(
1812-
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[B:%.*]], [[A:%.*]]
1813-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
1814-
; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
1809+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
1810+
; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
18151811
; CHECK-NEXT: ret i32 [[OR]]
18161812
;
18171813
%xor = xor i32 %b, %a
@@ -1823,9 +1819,8 @@ define i32 @test_or_and_xor_commuted1(i32 %a, i32 %b, i32 %c) {
18231819
define i32 @test_or_and_xor_commuted2(i32 %a, i32 %b, i32 %c) {
18241820
; CHECK-LABEL: @test_or_and_xor_commuted2(
18251821
; CHECK-NEXT: [[CC:%.*]] = mul i32 [[C:%.*]], [[C]]
1826-
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
1827-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[CC]], [[XOR]]
1828-
; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
1822+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[CC]], [[B:%.*]]
1823+
; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[A:%.*]]
18291824
; CHECK-NEXT: ret i32 [[OR]]
18301825
;
18311826
%cc = mul i32 %c, %c
@@ -1838,9 +1833,8 @@ define i32 @test_or_and_xor_commuted2(i32 %a, i32 %b, i32 %c) {
18381833
define i32 @test_or_and_xor_commuted3(i32 %a, i32 %b, i32 %c) {
18391834
; CHECK-LABEL: @test_or_and_xor_commuted3(
18401835
; CHECK-NEXT: [[AA:%.*]] = mul i32 [[A:%.*]], [[A]]
1841-
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[AA]], [[B:%.*]]
1842-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
1843-
; CHECK-NEXT: [[OR:%.*]] = or i32 [[AA]], [[AND]]
1836+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[B:%.*]], [[C:%.*]]
1837+
; CHECK-NEXT: [[OR:%.*]] = or i32 [[AA]], [[TMP1]]
18441838
; CHECK-NEXT: ret i32 [[OR]]
18451839
;
18461840
%aa = mul i32 %a, %a
@@ -1854,8 +1848,8 @@ define i32 @test_or_and_xor_multiuse1(i32 %a, i32 %b, i32 %c) {
18541848
; CHECK-LABEL: @test_or_and_xor_multiuse1(
18551849
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[A:%.*]], [[B:%.*]]
18561850
; CHECK-NEXT: call void @use(i32 [[XOR]])
1857-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[XOR]], [[C:%.*]]
1858-
; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[A]]
1851+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[B]], [[C:%.*]]
1852+
; CHECK-NEXT: [[OR:%.*]] = or i32 [[TMP1]], [[A]]
18591853
; CHECK-NEXT: ret i32 [[OR]]
18601854
;
18611855
%xor = xor i32 %a, %b

0 commit comments

Comments
 (0)