[InstCombine] Handle logical and/or in recursive and/or of icmps fold

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
This commit is contained in:
Nikita Popov 2022-05-24 10:07:00 +02:00
parent 973c7e0654
commit c0e06c7448
3 changed files with 101 additions and 81 deletions

View File

@ -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));
}
}
@ -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));
}
}

View File

@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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

View File

@ -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