Skip to content

Commit 4ebfd43

Browse files
authored
[InstCombine] Always treat inner and/or as bitwise (#121766)
In #116065, we pass `IsLogical` into `foldBooleanAndOr` when folding inner and/or ops. But it is always safe to treat them as bitwise if the outer ops are bitwise. Alive2: https://alive2.llvm.org/ce/z/hULrgH Closes #121701.
1 parent 27751c3 commit 4ebfd43

File tree

4 files changed

+59
-54
lines changed

4 files changed

+59
-54
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

+32-40
Original file line numberDiff line numberDiff line change
@@ -2363,6 +2363,26 @@ static Value *simplifyAndOrWithOpReplaced(Value *V, Value *Op, Value *RepOp,
23632363
return IC.Builder.CreateBinOp(I->getOpcode(), NewOp0, NewOp1);
23642364
}
23652365

2366+
/// Reassociate and/or expressions to see if we can fold the inner and/or ops.
2367+
/// TODO: Make this recursive; it's a little tricky because an arbitrary
2368+
/// number of and/or instructions might have to be created.
2369+
Value *InstCombinerImpl::reassociateBooleanAndOr(Value *LHS, Value *X, Value *Y,
2370+
Instruction &I, bool IsAnd,
2371+
bool RHSIsLogical) {
2372+
Instruction::BinaryOps Opcode = IsAnd ? Instruction::And : Instruction::Or;
2373+
// LHS bop (X lop Y) --> (LHS bop X) lop Y
2374+
// LHS bop (X bop Y) --> (LHS bop X) bop Y
2375+
if (Value *Res = foldBooleanAndOr(LHS, X, I, IsAnd, /*IsLogical=*/false))
2376+
return RHSIsLogical ? Builder.CreateLogicalOp(Opcode, Res, Y)
2377+
: Builder.CreateBinOp(Opcode, Res, Y);
2378+
// LHS bop (X bop Y) --> X bop (LHS bop Y)
2379+
// LHS bop (X lop Y) --> X lop (LHS bop Y)
2380+
if (Value *Res = foldBooleanAndOr(LHS, Y, I, IsAnd, /*IsLogical=*/false))
2381+
return RHSIsLogical ? Builder.CreateLogicalOp(Opcode, X, Res)
2382+
: Builder.CreateBinOp(Opcode, X, Res);
2383+
return nullptr;
2384+
}
2385+
23662386
// FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
23672387
// here. We should standardize that construct where it is needed or choose some
23682388
// other way to ensure that commutated variants of patterns are not missed.
@@ -2746,31 +2766,17 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
27462766
foldBooleanAndOr(Op0, Op1, I, /*IsAnd=*/true, /*IsLogical=*/false))
27472767
return replaceInstUsesWith(I, Res);
27482768

2749-
// TODO: Make this recursive; it's a little tricky because an arbitrary
2750-
// number of 'and' instructions might have to be created.
27512769
if (match(Op1, m_OneUse(m_LogicalAnd(m_Value(X), m_Value(Y))))) {
27522770
bool IsLogical = isa<SelectInst>(Op1);
2753-
// Op0 & (X && Y) --> (Op0 && X) && Y
2754-
if (Value *Res = foldBooleanAndOr(Op0, X, I, /* IsAnd */ true, IsLogical))
2755-
return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalAnd(Res, Y)
2756-
: Builder.CreateAnd(Res, Y));
2757-
// Op0 & (X && Y) --> X && (Op0 & Y)
2758-
if (Value *Res = foldBooleanAndOr(Op0, Y, I, /* IsAnd */ true,
2759-
/* IsLogical */ false))
2760-
return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalAnd(X, Res)
2761-
: Builder.CreateAnd(X, Res));
2771+
if (auto *V = reassociateBooleanAndOr(Op0, X, Y, I, /*IsAnd=*/true,
2772+
/*RHSIsLogical=*/IsLogical))
2773+
return replaceInstUsesWith(I, V);
27622774
}
27632775
if (match(Op0, m_OneUse(m_LogicalAnd(m_Value(X), m_Value(Y))))) {
27642776
bool IsLogical = isa<SelectInst>(Op0);
2765-
// (X && Y) & Op1 --> (X && Op1) && Y
2766-
if (Value *Res = foldBooleanAndOr(X, Op1, I, /* IsAnd */ true, IsLogical))
2767-
return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalAnd(Res, Y)
2768-
: Builder.CreateAnd(Res, Y));
2769-
// (X && Y) & Op1 --> X && (Y & Op1)
2770-
if (Value *Res = foldBooleanAndOr(Y, Op1, I, /* IsAnd */ true,
2771-
/* IsLogical */ false))
2772-
return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalAnd(X, Res)
2773-
: Builder.CreateAnd(X, Res));
2777+
if (auto *V = reassociateBooleanAndOr(Op1, X, Y, I, /*IsAnd=*/true,
2778+
/*RHSIsLogical=*/IsLogical))
2779+
return replaceInstUsesWith(I, V);
27742780
}
27752781

27762782
if (Instruction *FoldedFCmps = reassociateFCmps(I, Builder))
@@ -3831,31 +3837,17 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
38313837
foldBooleanAndOr(Op0, Op1, I, /*IsAnd=*/false, /*IsLogical=*/false))
38323838
return replaceInstUsesWith(I, Res);
38333839

3834-
// TODO: Make this recursive; it's a little tricky because an arbitrary
3835-
// number of 'or' instructions might have to be created.
38363840
if (match(Op1, m_OneUse(m_LogicalOr(m_Value(X), m_Value(Y))))) {
38373841
bool IsLogical = isa<SelectInst>(Op1);
3838-
// Op0 | (X || Y) --> (Op0 || X) || Y
3839-
if (Value *Res = foldBooleanAndOr(Op0, X, I, /* IsAnd */ false, IsLogical))
3840-
return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalOr(Res, Y)
3841-
: Builder.CreateOr(Res, Y));
3842-
// Op0 | (X || Y) --> X || (Op0 | Y)
3843-
if (Value *Res = foldBooleanAndOr(Op0, Y, I, /* IsAnd */ false,
3844-
/* IsLogical */ false))
3845-
return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalOr(X, Res)
3846-
: Builder.CreateOr(X, Res));
3842+
if (auto *V = reassociateBooleanAndOr(Op0, X, Y, I, /*IsAnd=*/false,
3843+
/*RHSIsLogical=*/IsLogical))
3844+
return replaceInstUsesWith(I, V);
38473845
}
38483846
if (match(Op0, m_OneUse(m_LogicalOr(m_Value(X), m_Value(Y))))) {
38493847
bool IsLogical = isa<SelectInst>(Op0);
3850-
// (X || Y) | Op1 --> (X || Op1) || Y
3851-
if (Value *Res = foldBooleanAndOr(X, Op1, I, /* IsAnd */ false, IsLogical))
3852-
return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalOr(Res, Y)
3853-
: Builder.CreateOr(Res, Y));
3854-
// (X || Y) | Op1 --> X || (Y | Op1)
3855-
if (Value *Res = foldBooleanAndOr(Y, Op1, I, /* IsAnd */ false,
3856-
/* IsLogical */ false))
3857-
return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalOr(X, Res)
3858-
: Builder.CreateOr(X, Res));
3848+
if (auto *V = reassociateBooleanAndOr(Op1, X, Y, I, /*IsAnd=*/false,
3849+
/*RHSIsLogical=*/IsLogical))
3850+
return replaceInstUsesWith(I, V);
38593851
}
38603852

38613853
if (Instruction *FoldedFCmps = reassociateFCmps(I, Builder))

llvm/lib/Transforms/InstCombine/InstCombineInternal.h

+3
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,9 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
429429
Value *foldBooleanAndOr(Value *LHS, Value *RHS, Instruction &I, bool IsAnd,
430430
bool IsLogical);
431431

432+
Value *reassociateBooleanAndOr(Value *LHS, Value *X, Value *Y, Instruction &I,
433+
bool IsAnd, bool RHSIsLogical);
434+
432435
Instruction *
433436
canonicalizeConditionalNegationViaMathToSelect(BinaryOperator &i);
434437

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

+8-14
Original file line numberDiff line numberDiff line change
@@ -1445,8 +1445,7 @@ define i1 @bitwise_and_logical_and_icmps_comm2(i8 %x, i8 %y, i8 %z) {
14451445
; CHECK-LABEL: @bitwise_and_logical_and_icmps_comm2(
14461446
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
14471447
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl nuw i8 1, [[Z:%.*]]
1448-
; CHECK-NEXT: [[TMP1:%.*]] = freeze i8 [[Z_SHIFT]]
1449-
; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], 1
1448+
; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[Z_SHIFT]], 1
14501449
; CHECK-NEXT: [[TMP3:%.*]] = and i8 [[X:%.*]], [[TMP2]]
14511450
; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i8 [[TMP3]], [[TMP2]]
14521451
; CHECK-NEXT: [[AND2:%.*]] = select i1 [[TMP4]], i1 [[C1]], i1 false
@@ -1796,8 +1795,7 @@ define i1 @bitwise_or_logical_or_icmps_comm2(i8 %x, i8 %y, i8 %z) {
17961795
; CHECK-LABEL: @bitwise_or_logical_or_icmps_comm2(
17971796
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
17981797
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl nuw i8 1, [[Z:%.*]]
1799-
; CHECK-NEXT: [[TMP1:%.*]] = freeze i8 [[Z_SHIFT]]
1800-
; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], 1
1798+
; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[Z_SHIFT]], 1
18011799
; CHECK-NEXT: [[TMP3:%.*]] = and i8 [[X:%.*]], [[TMP2]]
18021800
; CHECK-NEXT: [[TMP4:%.*]] = icmp ne i8 [[TMP3]], [[TMP2]]
18031801
; CHECK-NEXT: [[OR2:%.*]] = select i1 [[TMP4]], i1 true, i1 [[C1]]
@@ -2068,12 +2066,10 @@ define i1 @bitwise_and_logical_and_masked_icmp_allzeros_poison1(i1 %c, i32 %x, i
20682066

20692067
define i1 @bitwise_and_logical_and_masked_icmp_allzeros_poison2(i1 %c, i32 %x, i32 %y) {
20702068
; CHECK-LABEL: @bitwise_and_logical_and_masked_icmp_allzeros_poison2(
2071-
; CHECK-NEXT: [[X_M1:%.*]] = and i32 [[X:%.*]], 8
2072-
; CHECK-NEXT: [[C1:%.*]] = icmp eq i32 [[X_M1]], 0
2073-
; CHECK-NEXT: [[AND1:%.*]] = select i1 [[C1]], i1 [[C:%.*]], i1 false
2074-
; CHECK-NEXT: [[X_M2:%.*]] = and i32 [[X]], [[Y:%.*]]
2069+
; CHECK-NEXT: [[Y:%.*]] = or i32 [[Y1:%.*]], 8
2070+
; CHECK-NEXT: [[X_M2:%.*]] = and i32 [[X:%.*]], [[Y]]
20752071
; CHECK-NEXT: [[C2:%.*]] = icmp eq i32 [[X_M2]], 0
2076-
; CHECK-NEXT: [[AND2:%.*]] = and i1 [[AND1]], [[C2]]
2072+
; CHECK-NEXT: [[AND2:%.*]] = select i1 [[C2]], i1 [[C:%.*]], i1 false
20772073
; CHECK-NEXT: ret i1 [[AND2]]
20782074
;
20792075
%x.m1 = and i32 %x, 8
@@ -2120,12 +2116,10 @@ define i1 @bitwise_and_logical_and_masked_icmp_allones_poison1(i1 %c, i32 %x, i3
21202116

21212117
define i1 @bitwise_and_logical_and_masked_icmp_allones_poison2(i1 %c, i32 %x, i32 %y) {
21222118
; CHECK-LABEL: @bitwise_and_logical_and_masked_icmp_allones_poison2(
2123-
; CHECK-NEXT: [[X_M1:%.*]] = and i32 [[X:%.*]], 8
2124-
; CHECK-NEXT: [[C1:%.*]] = icmp ne i32 [[X_M1]], 0
2125-
; CHECK-NEXT: [[AND1:%.*]] = select i1 [[C1]], i1 [[C:%.*]], i1 false
2126-
; CHECK-NEXT: [[X_M2:%.*]] = and i32 [[X]], [[Y:%.*]]
2119+
; CHECK-NEXT: [[Y:%.*]] = or i32 [[Y1:%.*]], 8
2120+
; CHECK-NEXT: [[X_M2:%.*]] = and i32 [[X:%.*]], [[Y]]
21272121
; CHECK-NEXT: [[C2:%.*]] = icmp eq i32 [[X_M2]], [[Y]]
2128-
; CHECK-NEXT: [[AND2:%.*]] = and i1 [[AND1]], [[C2]]
2122+
; CHECK-NEXT: [[AND2:%.*]] = select i1 [[C2]], i1 [[C:%.*]], i1 false
21292123
; CHECK-NEXT: ret i1 [[AND2]]
21302124
;
21312125
%x.m1 = and i32 %x, 8

llvm/test/Transforms/InstCombine/bit-checks.ll

+16
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,22 @@ define i1 @no_masks_with_logical_or(i32 %a, i32 %b, i32 noundef %c) {
13351335
ret i1 %or2
13361336
}
13371337

1338+
define i1 @no_masks_with_logical_or_commuted(i32 %a, i32 %b, i32 noundef %c) {
1339+
; CHECK-LABEL: @no_masks_with_logical_or_commuted(
1340+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 63
1341+
; CHECK-NEXT: [[C:%.*]] = or i32 [[C1:%.*]], [[A:%.*]]
1342+
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i32 [[C]], 0
1343+
; CHECK-NEXT: [[OR2:%.*]] = select i1 [[CMP3]], i1 true, i1 [[CMP2]]
1344+
; CHECK-NEXT: ret i1 [[OR2]]
1345+
;
1346+
%cmp1 = icmp ne i32 %a, 0
1347+
%cmp2 = icmp ne i32 %b, 63
1348+
%or1 = select i1 %cmp1, i1 true, i1 %cmp2
1349+
%cmp3 = icmp ne i32 %c, 0
1350+
%or2 = or i1 %cmp3, %or1
1351+
ret i1 %or2
1352+
}
1353+
13381354
define i1 @no_masks_with_logical_or2(i32 %a, i32 %b, i32 noundef %c) {
13391355
; CHECK-LABEL: @no_masks_with_logical_or2(
13401356
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 63

0 commit comments

Comments
 (0)