Skip to content

Commit a37335e

Browse files
committed
[InstCombine] Factor in op0's usages to decide leniency for one-use in foldComplexAndOrPatterns
If we can eliminate Op0 by replacing, which will happen if Op0 is one use, then we need not check if the other is one-use.
1 parent e129c3c commit a37335e

File tree

2 files changed

+33
-25
lines changed

2 files changed

+33
-25
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,9 +2036,6 @@ static Instruction *foldComplexAndOrPatterns(BinaryOperator &I,
20362036

20372037
// (~(A | B) & C) | ... --> ...
20382038
// (~(A & B) | C) & ... --> ...
2039-
// TODO: One use checks are conservative. We just need to check that a total
2040-
// number of multiple used values does not exceed reduction
2041-
// in operations.
20422039
if (matchNotOrAnd(Op0, m_Value(A), m_Value(B), m_Value(C), X)) {
20432040
// (~(A | B) & C) | (~(A | C) & B) --> (B ^ C) & ~A
20442041
// (~(A & B) | C) & (~(A & C) | B) --> ~((B ^ C) & A)
@@ -2060,25 +2057,37 @@ static Instruction *foldComplexAndOrPatterns(BinaryOperator &I,
20602057
: BinaryOperator::CreateNot(Builder.CreateAnd(Xor, B));
20612058
}
20622059

2060+
bool Op0OneUse = Op0->hasOneUse();
2061+
20632062
// (~(A | B) & C) | ~(A | C) --> ~((B & C) | A)
20642063
// (~(A & B) | C) & ~(A & C) --> ~((B | C) & A)
2065-
if (match(Op1, m_OneUse(m_Not(m_OneUse(
2066-
m_c_BinOp(Opcode, m_Specific(A), m_Specific(C)))))))
2064+
if (!Op0OneUse && match(Op1, m_OneUse(m_Not(m_OneUse(m_c_BinOp(
2065+
Opcode, m_Specific(A), m_Specific(C)))))))
2066+
return BinaryOperator::CreateNot(Builder.CreateBinOp(
2067+
Opcode, Builder.CreateBinOp(FlippedOpcode, B, C), A));
2068+
2069+
if (Op0OneUse &&
2070+
match(Op1, m_Not(m_c_BinOp(Opcode, m_Specific(A), m_Specific(C)))))
20672071
return BinaryOperator::CreateNot(Builder.CreateBinOp(
20682072
Opcode, Builder.CreateBinOp(FlippedOpcode, B, C), A));
20692073

20702074
// (~(A | B) & C) | ~(B | C) --> ~((A & C) | B)
20712075
// (~(A & B) | C) & ~(B & C) --> ~((A | C) & B)
2072-
if (match(Op1, m_OneUse(m_Not(m_OneUse(
2073-
m_c_BinOp(Opcode, m_Specific(B), m_Specific(C)))))))
2076+
if (!Op0OneUse && match(Op1, m_OneUse(m_Not(m_OneUse(m_c_BinOp(
2077+
Opcode, m_Specific(B), m_Specific(C)))))))
2078+
return BinaryOperator::CreateNot(Builder.CreateBinOp(
2079+
Opcode, Builder.CreateBinOp(FlippedOpcode, A, C), B));
2080+
2081+
if (Op0OneUse &&
2082+
match(Op1, m_Not(m_c_BinOp(Opcode, m_Specific(B), m_Specific(C)))))
20742083
return BinaryOperator::CreateNot(Builder.CreateBinOp(
20752084
Opcode, Builder.CreateBinOp(FlippedOpcode, A, C), B));
20762085

20772086
// (~(A | B) & C) | ~(C | (A ^ B)) --> ~((A | B) & (C | (A ^ B)))
20782087
// Note, the pattern with swapped and/or is not handled because the
20792088
// result is more undefined than a source:
20802089
// (~(A & B) | C) & ~(C & (A ^ B)) --> (A ^ B ^ C) | ~(A | C) is invalid.
2081-
if (Opcode == Instruction::Or && Op0->hasOneUse() &&
2090+
if (Opcode == Instruction::Or && Op0OneUse &&
20822091
match(Op1, m_OneUse(m_Not(m_CombineAnd(
20832092
m_Value(Y),
20842093
m_c_BinOp(Opcode, m_Specific(C),
@@ -2104,9 +2113,13 @@ static Instruction *foldComplexAndOrPatterns(BinaryOperator &I,
21042113
m_c_BinOp(FlippedOpcode, m_Value(C),
21052114
m_CombineAnd(m_Value(X), m_Not(m_Value(A)))),
21062115
m_Value(B))))) {
2116+
21072117
// X = ~A
21082118
// (~A & B & C) | ~(A | B | C) --> ~(A | (B ^ C))
21092119
// (~A | B | C) & ~(A & B & C) --> (~A | (B ^ C))
2120+
// TODO: One use checks are conservative. We just need to check that a total
2121+
// number of multiple used values does not exceed reduction
2122+
// in operations.
21102123
if (match(Op1, m_OneUse(m_Not(m_c_BinOp(
21112124
Opcode, m_c_BinOp(Opcode, m_Specific(A), m_Specific(B)),
21122125
m_Specific(C))))) ||

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

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,10 +1862,9 @@ define i32 @or_and_not_not_extra_not_use1(i32 %a, i32 %b, i32 %c) {
18621862
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) {
18631863
; CHECK-NEXT: [[OR1:%.*]] = or i32 [[B]], [[A]]
18641864
; CHECK-NEXT: [[NOT1:%.*]] = xor i32 [[OR1]], -1
1865-
; CHECK-NEXT: [[OR2:%.*]] = or i32 [[A]], [[C]]
1866-
; CHECK-NEXT: [[NOT2:%.*]] = xor i32 [[OR2]], -1
1867-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[B]], [[NOT2]]
1868-
; CHECK-NEXT: [[OR3:%.*]] = or i32 [[AND]], [[NOT1]]
1865+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[C]], [[B]]
1866+
; CHECK-NEXT: [[TMP2:%.*]] = or i32 [[TMP1]], [[A]]
1867+
; CHECK-NEXT: [[OR3:%.*]] = xor i32 [[TMP2]], -1
18691868
; CHECK-NEXT: call void @use(i32 [[NOT1]])
18701869
; CHECK-NEXT: ret i32 [[OR3]]
18711870
;
@@ -1926,11 +1925,9 @@ define i32 @or_and_not_not_extra_or_use1(i32 %a, i32 %b, i32 %c) {
19261925
; CHECK-LABEL: define {{[^@]+}}@or_and_not_not_extra_or_use1
19271926
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) {
19281927
; CHECK-NEXT: [[OR1:%.*]] = or i32 [[B]], [[A]]
1929-
; CHECK-NEXT: [[NOT1:%.*]] = xor i32 [[OR1]], -1
1930-
; CHECK-NEXT: [[OR2:%.*]] = or i32 [[A]], [[C]]
1931-
; CHECK-NEXT: [[NOT2:%.*]] = xor i32 [[OR2]], -1
1932-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[B]], [[NOT2]]
1933-
; CHECK-NEXT: [[OR3:%.*]] = or i32 [[AND]], [[NOT1]]
1928+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[C]], [[B]]
1929+
; CHECK-NEXT: [[TMP2:%.*]] = or i32 [[TMP1]], [[A]]
1930+
; CHECK-NEXT: [[OR3:%.*]] = xor i32 [[TMP2]], -1
19341931
; CHECK-NEXT: call void @use(i32 [[OR1]])
19351932
; CHECK-NEXT: ret i32 [[OR3]]
19361933
;
@@ -2177,10 +2174,9 @@ define i32 @and_or_not_not_extra_not_use1(i32 %a, i32 %b, i32 %c) {
21772174
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) {
21782175
; CHECK-NEXT: [[AND1:%.*]] = and i32 [[B]], [[A]]
21792176
; CHECK-NEXT: [[NOT1:%.*]] = xor i32 [[AND1]], -1
2180-
; CHECK-NEXT: [[AND2:%.*]] = and i32 [[A]], [[C]]
2181-
; CHECK-NEXT: [[NOT2:%.*]] = xor i32 [[AND2]], -1
2182-
; CHECK-NEXT: [[OR:%.*]] = or i32 [[B]], [[NOT2]]
2183-
; CHECK-NEXT: [[AND3:%.*]] = xor i32 [[AND1]], [[OR]]
2177+
; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[C]], [[B]]
2178+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], [[A]]
2179+
; CHECK-NEXT: [[AND3:%.*]] = xor i32 [[TMP2]], -1
21842180
; CHECK-NEXT: call void @use(i32 [[NOT1]])
21852181
; CHECK-NEXT: ret i32 [[AND3]]
21862182
;
@@ -2241,10 +2237,9 @@ define i32 @and_or_not_not_extra_or_use1(i32 %a, i32 %b, i32 %c) {
22412237
; CHECK-LABEL: define {{[^@]+}}@and_or_not_not_extra_or_use1
22422238
; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) {
22432239
; CHECK-NEXT: [[AND1:%.*]] = and i32 [[B]], [[A]]
2244-
; CHECK-NEXT: [[AND2:%.*]] = and i32 [[A]], [[C]]
2245-
; CHECK-NEXT: [[NOT2:%.*]] = xor i32 [[AND2]], -1
2246-
; CHECK-NEXT: [[OR:%.*]] = or i32 [[B]], [[NOT2]]
2247-
; CHECK-NEXT: [[AND3:%.*]] = xor i32 [[AND1]], [[OR]]
2240+
; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[C]], [[B]]
2241+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], [[A]]
2242+
; CHECK-NEXT: [[AND3:%.*]] = xor i32 [[TMP2]], -1
22482243
; CHECK-NEXT: call void @use(i32 [[AND1]])
22492244
; CHECK-NEXT: ret i32 [[AND3]]
22502245
;

0 commit comments

Comments
 (0)