Skip to content

Commit

Permalink
[InstCombine] Handle logical and/or in recursive and/or of icmps fold
Browse files Browse the repository at this point in the history
The and/or of icmps fold is also applied in reassociated form.
However, this currently only happens for bitwise and of bitwise
and, but not for bitwise and of logical and (or other combinations,
but this is the one being addressed here).

We can do this for bitwise+logical combinations as well, but need
to be a bit careful about which of the resulting ands are logical:
https://alive2.llvm.org/ce/z/WYSjGh
https://alive2.llvm.org/ce/z/guxYnz
https://alive2.llvm.org/ce/z/S5SYxY
https://alive2.llvm.org/ce/z/2rAWeW
  • Loading branch information
nikic committed May 24, 2022
1 parent 973c7e0 commit c0e06c7
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 81 deletions.
76 changes: 56 additions & 20 deletions llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1989,21 +1989,39 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {

// TODO: Make this recursive; it's a little tricky because an arbitrary
// number of 'and' instructions might have to be created.
if (LHS && match(Op1, m_OneUse(m_And(m_Value(X), m_Value(Y))))) {
if (LHS && match(Op1, m_OneUse(m_LogicalAnd(m_Value(X), m_Value(Y))))) {
bool IsLogical = isa<SelectInst>(Op1);
// LHS & (X && Y) --> (LHS && X) && Y
if (auto *Cmp = dyn_cast<ICmpInst>(X))
if (Value *Res = foldAndOrOfICmps(LHS, Cmp, I, /* IsAnd */ true))
return replaceInstUsesWith(I, Builder.CreateAnd(Res, Y));
if (Value *Res =
foldAndOrOfICmps(LHS, Cmp, I, /* IsAnd */ true, IsLogical))
return replaceInstUsesWith(I, IsLogical
? Builder.CreateLogicalAnd(Res, Y)
: Builder.CreateAnd(Res, Y));
// LHS & (X && Y) --> X && (LHS & Y)
if (auto *Cmp = dyn_cast<ICmpInst>(Y))
if (Value *Res = foldAndOrOfICmps(LHS, Cmp, I, /* IsAnd */ true))
return replaceInstUsesWith(I, Builder.CreateAnd(X, Res));
if (Value *Res = foldAndOrOfICmps(LHS, Cmp, I, /* IsAnd */ true,
/* IsLogical */ false))
return replaceInstUsesWith(I, IsLogical
? Builder.CreateLogicalAnd(X, Res)
: Builder.CreateAnd(X, Res));
}
if (RHS && match(Op0, m_OneUse(m_And(m_Value(X), m_Value(Y))))) {
if (RHS && match(Op0, m_OneUse(m_LogicalAnd(m_Value(X), m_Value(Y))))) {
bool IsLogical = isa<SelectInst>(Op0);
// (X && Y) & RHS --> (X && RHS) && Y
if (auto *Cmp = dyn_cast<ICmpInst>(X))
if (Value *Res = foldAndOrOfICmps(Cmp, RHS, I, /* IsAnd */ true))
return replaceInstUsesWith(I, Builder.CreateAnd(Res, Y));
if (Value *Res =
foldAndOrOfICmps(Cmp, RHS, I, /* IsAnd */ true, IsLogical))
return replaceInstUsesWith(I, IsLogical
? Builder.CreateLogicalAnd(Res, Y)
: Builder.CreateAnd(Res, Y));
// (X && Y) & RHS --> X && (Y & RHS)
if (auto *Cmp = dyn_cast<ICmpInst>(Y))
if (Value *Res = foldAndOrOfICmps(Cmp, RHS, I, /* IsAnd */ true))
return replaceInstUsesWith(I, Builder.CreateAnd(X, Res));
if (Value *Res = foldAndOrOfICmps(Cmp, RHS, I, /* IsAnd */ true,
/* IsLogical */ false))
return replaceInstUsesWith(I, IsLogical
? Builder.CreateLogicalAnd(X, Res)
: Builder.CreateAnd(X, Res));
}
}

Expand Down Expand Up @@ -2788,21 +2806,39 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
// TODO: Make this recursive; it's a little tricky because an arbitrary
// number of 'or' instructions might have to be created.
Value *X, *Y;
if (LHS && match(Op1, m_OneUse(m_Or(m_Value(X), m_Value(Y))))) {
if (LHS && match(Op1, m_OneUse(m_LogicalOr(m_Value(X), m_Value(Y))))) {
bool IsLogical = isa<SelectInst>(Op1);
// LHS | (X || Y) --> (LHS || X) || Y
if (auto *Cmp = dyn_cast<ICmpInst>(X))
if (Value *Res = foldAndOrOfICmps(LHS, Cmp, I, /* IsAnd */ false))
return replaceInstUsesWith(I, Builder.CreateOr(Res, Y));
if (Value *Res =
foldAndOrOfICmps(LHS, Cmp, I, /* IsAnd */ false, IsLogical))
return replaceInstUsesWith(I, IsLogical
? Builder.CreateLogicalOr(Res, Y)
: Builder.CreateOr(Res, Y));
// LHS | (X || Y) --> X || (LHS | Y)
if (auto *Cmp = dyn_cast<ICmpInst>(Y))
if (Value *Res = foldAndOrOfICmps(LHS, Cmp, I, /* IsAnd */ false))
return replaceInstUsesWith(I, Builder.CreateOr(X, Res));
if (Value *Res = foldAndOrOfICmps(LHS, Cmp, I, /* IsAnd */ false,
/* IsLogical */ false))
return replaceInstUsesWith(I, IsLogical
? Builder.CreateLogicalOr(X, Res)
: Builder.CreateOr(X, Res));
}
if (RHS && match(Op0, m_OneUse(m_Or(m_Value(X), m_Value(Y))))) {
if (RHS && match(Op0, m_OneUse(m_LogicalOr(m_Value(X), m_Value(Y))))) {
bool IsLogical = isa<SelectInst>(Op0);
// (X || Y) | RHS --> (X || RHS) || Y
if (auto *Cmp = dyn_cast<ICmpInst>(X))
if (Value *Res = foldAndOrOfICmps(Cmp, RHS, I, /* IsAnd */ false))
return replaceInstUsesWith(I, Builder.CreateOr(Res, Y));
if (Value *Res =
foldAndOrOfICmps(Cmp, RHS, I, /* IsAnd */ false, IsLogical))
return replaceInstUsesWith(I, IsLogical
? Builder.CreateLogicalOr(Res, Y)
: Builder.CreateOr(Res, Y));
// (X || Y) | RHS --> X || (Y | RHS)
if (auto *Cmp = dyn_cast<ICmpInst>(Y))
if (Value *Res = foldAndOrOfICmps(Cmp, RHS, I, /* IsAnd */ false))
return replaceInstUsesWith(I, Builder.CreateOr(X, Res));
if (Value *Res = foldAndOrOfICmps(Cmp, RHS, I, /* IsAnd */ false,
/* IsLogical */ false))
return replaceInstUsesWith(I, IsLogical
? Builder.CreateLogicalOr(X, Res)
: Builder.CreateOr(X, Res));
}
}

Expand Down
98 changes: 42 additions & 56 deletions llvm/test/Transforms/InstCombine/and-or-icmps.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1364,14 +1364,12 @@ define i1 @bitwise_and_bitwise_and_icmps_comm3(i8 %x, i8 %y, i8 %z) {
define i1 @bitwise_and_logical_and_icmps(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @bitwise_and_logical_and_icmps(
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
; CHECK-NEXT: [[X_M1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl i8 1, [[Z:%.*]]
; CHECK-NEXT: [[X_M2:%.*]] = and i8 [[Z_SHIFT]], [[X]]
; CHECK-NEXT: [[C2:%.*]] = icmp ne i8 [[X_M1]], 0
; CHECK-NEXT: [[C3:%.*]] = icmp ne i8 [[X_M2]], 0
; CHECK-NEXT: [[AND1:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
; CHECK-NEXT: [[AND2:%.*]] = and i1 [[AND1]], [[C3]]
; CHECK-NEXT: ret i1 [[AND2]]
; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]]
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP2]], [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[C1]], i1 [[TMP3]], i1 false
; CHECK-NEXT: ret i1 [[TMP4]]
;
%c1 = icmp eq i8 %y, 42
%x.m1 = and i8 %x, 1
Expand All @@ -1387,14 +1385,12 @@ define i1 @bitwise_and_logical_and_icmps(i8 %x, i8 %y, i8 %z) {
define i1 @bitwise_and_logical_and_icmps_comm1(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @bitwise_and_logical_and_icmps_comm1(
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
; CHECK-NEXT: [[X_M1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl i8 1, [[Z:%.*]]
; CHECK-NEXT: [[X_M2:%.*]] = and i8 [[Z_SHIFT]], [[X]]
; CHECK-NEXT: [[C2:%.*]] = icmp ne i8 [[X_M1]], 0
; CHECK-NEXT: [[C3:%.*]] = icmp ne i8 [[X_M2]], 0
; CHECK-NEXT: [[AND1:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
; CHECK-NEXT: [[AND2:%.*]] = and i1 [[C3]], [[AND1]]
; CHECK-NEXT: ret i1 [[AND2]]
; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]]
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP2]], [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[C1]], i1 [[TMP3]], i1 false
; CHECK-NEXT: ret i1 [[TMP4]]
;
%c1 = icmp eq i8 %y, 42
%x.m1 = and i8 %x, 1
Expand All @@ -1410,14 +1406,13 @@ define i1 @bitwise_and_logical_and_icmps_comm1(i8 %x, i8 %y, i8 %z) {
define i1 @bitwise_and_logical_and_icmps_comm2(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @bitwise_and_logical_and_icmps_comm2(
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
; CHECK-NEXT: [[X_M1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl i8 1, [[Z:%.*]]
; CHECK-NEXT: [[X_M2:%.*]] = and i8 [[Z_SHIFT]], [[X]]
; CHECK-NEXT: [[C2:%.*]] = icmp ne i8 [[X_M1]], 0
; CHECK-NEXT: [[C3:%.*]] = icmp ne i8 [[X_M2]], 0
; CHECK-NEXT: [[AND1:%.*]] = select i1 [[C2]], i1 [[C1]], i1 false
; CHECK-NEXT: [[AND2:%.*]] = and i1 [[AND1]], [[C3]]
; CHECK-NEXT: ret i1 [[AND2]]
; CHECK-NEXT: [[TMP1:%.*]] = freeze i8 [[Z_SHIFT]]
; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], 1
; CHECK-NEXT: [[TMP3:%.*]] = and i8 [[TMP2]], [[X:%.*]]
; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i8 [[TMP3]], [[TMP2]]
; CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i1 [[C1]], i1 false
; CHECK-NEXT: ret i1 [[TMP5]]
;
%c1 = icmp eq i8 %y, 42
%x.m1 = and i8 %x, 1
Expand All @@ -1433,14 +1428,12 @@ define i1 @bitwise_and_logical_and_icmps_comm2(i8 %x, i8 %y, i8 %z) {
define i1 @bitwise_and_logical_and_icmps_comm3(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @bitwise_and_logical_and_icmps_comm3(
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
; CHECK-NEXT: [[X_M1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl i8 1, [[Z:%.*]]
; CHECK-NEXT: [[X_M2:%.*]] = and i8 [[Z_SHIFT]], [[X]]
; CHECK-NEXT: [[C2:%.*]] = icmp ne i8 [[X_M1]], 0
; CHECK-NEXT: [[C3:%.*]] = icmp ne i8 [[X_M2]], 0
; CHECK-NEXT: [[AND1:%.*]] = select i1 [[C2]], i1 [[C1]], i1 false
; CHECK-NEXT: [[AND2:%.*]] = and i1 [[C3]], [[AND1]]
; CHECK-NEXT: ret i1 [[AND2]]
; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]]
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP2]], [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i1 [[C1]], i1 false
; CHECK-NEXT: ret i1 [[TMP4]]
;
%c1 = icmp eq i8 %y, 42
%x.m1 = and i8 %x, 1
Expand Down Expand Up @@ -1722,14 +1715,12 @@ define i1 @bitwise_or_bitwise_or_icmps_comm3(i8 %x, i8 %y, i8 %z) {
define i1 @bitwise_or_logical_or_icmps(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @bitwise_or_logical_or_icmps(
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
; CHECK-NEXT: [[X_M1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl i8 1, [[Z:%.*]]
; CHECK-NEXT: [[X_M2:%.*]] = and i8 [[Z_SHIFT]], [[X]]
; CHECK-NEXT: [[C2:%.*]] = icmp eq i8 [[X_M1]], 0
; CHECK-NEXT: [[C3:%.*]] = icmp eq i8 [[X_M2]], 0
; CHECK-NEXT: [[OR1:%.*]] = select i1 [[C1]], i1 true, i1 [[C2]]
; CHECK-NEXT: [[OR2:%.*]] = or i1 [[OR1]], [[C3]]
; CHECK-NEXT: ret i1 [[OR2]]
; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]]
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP2]], [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[C1]], i1 true, i1 [[TMP3]]
; CHECK-NEXT: ret i1 [[TMP4]]
;
%c1 = icmp eq i8 %y, 42
%x.m1 = and i8 %x, 1
Expand All @@ -1745,14 +1736,12 @@ define i1 @bitwise_or_logical_or_icmps(i8 %x, i8 %y, i8 %z) {
define i1 @bitwise_or_logical_or_icmps_comm1(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @bitwise_or_logical_or_icmps_comm1(
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
; CHECK-NEXT: [[X_M1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl i8 1, [[Z:%.*]]
; CHECK-NEXT: [[X_M2:%.*]] = and i8 [[Z_SHIFT]], [[X]]
; CHECK-NEXT: [[C2:%.*]] = icmp eq i8 [[X_M1]], 0
; CHECK-NEXT: [[C3:%.*]] = icmp eq i8 [[X_M2]], 0
; CHECK-NEXT: [[OR1:%.*]] = select i1 [[C1]], i1 true, i1 [[C2]]
; CHECK-NEXT: [[OR2:%.*]] = or i1 [[C3]], [[OR1]]
; CHECK-NEXT: ret i1 [[OR2]]
; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]]
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP2]], [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[C1]], i1 true, i1 [[TMP3]]
; CHECK-NEXT: ret i1 [[TMP4]]
;
%c1 = icmp eq i8 %y, 42
%x.m1 = and i8 %x, 1
Expand All @@ -1768,14 +1757,13 @@ define i1 @bitwise_or_logical_or_icmps_comm1(i8 %x, i8 %y, i8 %z) {
define i1 @bitwise_or_logical_or_icmps_comm2(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @bitwise_or_logical_or_icmps_comm2(
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
; CHECK-NEXT: [[X_M1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl i8 1, [[Z:%.*]]
; CHECK-NEXT: [[X_M2:%.*]] = and i8 [[Z_SHIFT]], [[X]]
; CHECK-NEXT: [[C2:%.*]] = icmp eq i8 [[X_M1]], 0
; CHECK-NEXT: [[C3:%.*]] = icmp eq i8 [[X_M2]], 0
; CHECK-NEXT: [[OR1:%.*]] = select i1 [[C2]], i1 true, i1 [[C1]]
; CHECK-NEXT: [[OR2:%.*]] = or i1 [[OR1]], [[C3]]
; CHECK-NEXT: ret i1 [[OR2]]
; CHECK-NEXT: [[TMP1:%.*]] = freeze i8 [[Z_SHIFT]]
; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], 1
; CHECK-NEXT: [[TMP3:%.*]] = and i8 [[TMP2]], [[X:%.*]]
; CHECK-NEXT: [[TMP4:%.*]] = icmp ne i8 [[TMP3]], [[TMP2]]
; CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i1 true, i1 [[C1]]
; CHECK-NEXT: ret i1 [[TMP5]]
;
%c1 = icmp eq i8 %y, 42
%x.m1 = and i8 %x, 1
Expand All @@ -1791,14 +1779,12 @@ define i1 @bitwise_or_logical_or_icmps_comm2(i8 %x, i8 %y, i8 %z) {
define i1 @bitwise_or_logical_or_icmps_comm3(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @bitwise_or_logical_or_icmps_comm3(
; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
; CHECK-NEXT: [[X_M1:%.*]] = and i8 [[X:%.*]], 1
; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl i8 1, [[Z:%.*]]
; CHECK-NEXT: [[X_M2:%.*]] = and i8 [[Z_SHIFT]], [[X]]
; CHECK-NEXT: [[C2:%.*]] = icmp eq i8 [[X_M1]], 0
; CHECK-NEXT: [[C3:%.*]] = icmp eq i8 [[X_M2]], 0
; CHECK-NEXT: [[OR1:%.*]] = select i1 [[C2]], i1 true, i1 [[C1]]
; CHECK-NEXT: [[OR2:%.*]] = or i1 [[C3]], [[OR1]]
; CHECK-NEXT: ret i1 [[OR2]]
; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X:%.*]]
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP2]], [[TMP1]]
; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i1 true, i1 [[C1]]
; CHECK-NEXT: ret i1 [[TMP4]]
;
%c1 = icmp eq i8 %y, 42
%x.m1 = and i8 %x, 1
Expand Down
8 changes: 3 additions & 5 deletions llvm/test/Transforms/InstCombine/and2.ll
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,9 @@ define i1 @test7(i32 %i, i1 %b) {

define i1 @test7_logical(i32 %i, i1 %b) {
; CHECK-LABEL: @test7_logical(
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[I:%.*]], 1
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i32 [[I]], -1
; CHECK-NEXT: [[AND1:%.*]] = select i1 [[CMP1]], i1 [[B:%.*]], i1 false
; CHECK-NEXT: [[AND2:%.*]] = and i1 [[AND1]], [[CMP2]]
; CHECK-NEXT: ret i1 [[AND2]]
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[I:%.*]], 0
; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i1 [[B:%.*]], i1 false
; CHECK-NEXT: ret i1 [[TMP2]]
;
%cmp1 = icmp slt i32 %i, 1
%cmp2 = icmp sgt i32 %i, -1
Expand Down

0 comments on commit c0e06c7

Please sign in to comment.