diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index 37a7c4d88b234..e9bb2b8847563 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -2363,6 +2363,26 @@ static Value *simplifyAndOrWithOpReplaced(Value *V, Value *Op, Value *RepOp, return IC.Builder.CreateBinOp(I->getOpcode(), NewOp0, NewOp1); } +/// Reassociate and/or expressions to see if we can fold the inner and/or ops. +/// TODO: Make this recursive; it's a little tricky because an arbitrary +/// number of and/or instructions might have to be created. +Value *InstCombinerImpl::reassociateBooleanAndOr(Value *LHS, Value *X, Value *Y, + Instruction &I, bool IsAnd, + bool RHSIsLogical) { + Instruction::BinaryOps Opcode = IsAnd ? Instruction::And : Instruction::Or; + // LHS bop (X lop Y) --> (LHS bop X) lop Y + // LHS bop (X bop Y) --> (LHS bop X) bop Y + if (Value *Res = foldBooleanAndOr(LHS, X, I, IsAnd, /*IsLogical=*/false)) + return RHSIsLogical ? Builder.CreateLogicalOp(Opcode, Res, Y) + : Builder.CreateBinOp(Opcode, Res, Y); + // LHS bop (X bop Y) --> X bop (LHS bop Y) + // LHS bop (X lop Y) --> X lop (LHS bop Y) + if (Value *Res = foldBooleanAndOr(LHS, Y, I, IsAnd, /*IsLogical=*/false)) + return RHSIsLogical ? Builder.CreateLogicalOp(Opcode, X, Res) + : Builder.CreateBinOp(Opcode, X, Res); + return nullptr; +} + // FIXME: We use commutative matchers (m_c_*) for some, but not all, matches // here. We should standardize that construct where it is needed or choose some // other way to ensure that commutated variants of patterns are not missed. @@ -2746,31 +2766,17 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) { foldBooleanAndOr(Op0, Op1, I, /*IsAnd=*/true, /*IsLogical=*/false)) return replaceInstUsesWith(I, Res); - // TODO: Make this recursive; it's a little tricky because an arbitrary - // number of 'and' instructions might have to be created. if (match(Op1, m_OneUse(m_LogicalAnd(m_Value(X), m_Value(Y))))) { bool IsLogical = isa(Op1); - // Op0 & (X && Y) --> (Op0 && X) && Y - if (Value *Res = foldBooleanAndOr(Op0, X, I, /* IsAnd */ true, IsLogical)) - return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalAnd(Res, Y) - : Builder.CreateAnd(Res, Y)); - // Op0 & (X && Y) --> X && (Op0 & Y) - if (Value *Res = foldBooleanAndOr(Op0, Y, I, /* IsAnd */ true, - /* IsLogical */ false)) - return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalAnd(X, Res) - : Builder.CreateAnd(X, Res)); + if (auto *V = reassociateBooleanAndOr(Op0, X, Y, I, /*IsAnd=*/true, + /*RHSIsLogical=*/IsLogical)) + return replaceInstUsesWith(I, V); } if (match(Op0, m_OneUse(m_LogicalAnd(m_Value(X), m_Value(Y))))) { bool IsLogical = isa(Op0); - // (X && Y) & Op1 --> (X && Op1) && Y - if (Value *Res = foldBooleanAndOr(X, Op1, I, /* IsAnd */ true, IsLogical)) - return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalAnd(Res, Y) - : Builder.CreateAnd(Res, Y)); - // (X && Y) & Op1 --> X && (Y & Op1) - if (Value *Res = foldBooleanAndOr(Y, Op1, I, /* IsAnd */ true, - /* IsLogical */ false)) - return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalAnd(X, Res) - : Builder.CreateAnd(X, Res)); + if (auto *V = reassociateBooleanAndOr(Op1, X, Y, I, /*IsAnd=*/true, + /*RHSIsLogical=*/IsLogical)) + return replaceInstUsesWith(I, V); } if (Instruction *FoldedFCmps = reassociateFCmps(I, Builder)) @@ -3831,31 +3837,17 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) { foldBooleanAndOr(Op0, Op1, I, /*IsAnd=*/false, /*IsLogical=*/false)) return replaceInstUsesWith(I, Res); - // TODO: Make this recursive; it's a little tricky because an arbitrary - // number of 'or' instructions might have to be created. if (match(Op1, m_OneUse(m_LogicalOr(m_Value(X), m_Value(Y))))) { bool IsLogical = isa(Op1); - // Op0 | (X || Y) --> (Op0 || X) || Y - if (Value *Res = foldBooleanAndOr(Op0, X, I, /* IsAnd */ false, IsLogical)) - return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalOr(Res, Y) - : Builder.CreateOr(Res, Y)); - // Op0 | (X || Y) --> X || (Op0 | Y) - if (Value *Res = foldBooleanAndOr(Op0, Y, I, /* IsAnd */ false, - /* IsLogical */ false)) - return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalOr(X, Res) - : Builder.CreateOr(X, Res)); + if (auto *V = reassociateBooleanAndOr(Op0, X, Y, I, /*IsAnd=*/false, + /*RHSIsLogical=*/IsLogical)) + return replaceInstUsesWith(I, V); } if (match(Op0, m_OneUse(m_LogicalOr(m_Value(X), m_Value(Y))))) { bool IsLogical = isa(Op0); - // (X || Y) | Op1 --> (X || Op1) || Y - if (Value *Res = foldBooleanAndOr(X, Op1, I, /* IsAnd */ false, IsLogical)) - return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalOr(Res, Y) - : Builder.CreateOr(Res, Y)); - // (X || Y) | Op1 --> X || (Y | Op1) - if (Value *Res = foldBooleanAndOr(Y, Op1, I, /* IsAnd */ false, - /* IsLogical */ false)) - return replaceInstUsesWith(I, IsLogical ? Builder.CreateLogicalOr(X, Res) - : Builder.CreateOr(X, Res)); + if (auto *V = reassociateBooleanAndOr(Op1, X, Y, I, /*IsAnd=*/false, + /*RHSIsLogical=*/IsLogical)) + return replaceInstUsesWith(I, V); } if (Instruction *FoldedFCmps = reassociateFCmps(I, Builder)) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 3a074ee70dc48..b31ae374540bb 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -429,6 +429,9 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final Value *foldBooleanAndOr(Value *LHS, Value *RHS, Instruction &I, bool IsAnd, bool IsLogical); + Value *reassociateBooleanAndOr(Value *LHS, Value *X, Value *Y, Instruction &I, + bool IsAnd, bool RHSIsLogical); + Instruction * canonicalizeConditionalNegationViaMathToSelect(BinaryOperator &i); diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll index fffe1f8426690..9651858a0caef 100644 --- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll +++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll @@ -1445,8 +1445,7 @@ 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: [[Z_SHIFT:%.*]] = shl nuw i8 1, [[Z:%.*]] -; CHECK-NEXT: [[TMP1:%.*]] = freeze i8 [[Z_SHIFT]] -; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[Z_SHIFT]], 1 ; CHECK-NEXT: [[TMP3:%.*]] = and i8 [[X:%.*]], [[TMP2]] ; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i8 [[TMP3]], [[TMP2]] ; 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) { ; CHECK-LABEL: @bitwise_or_logical_or_icmps_comm2( ; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42 ; CHECK-NEXT: [[Z_SHIFT:%.*]] = shl nuw i8 1, [[Z:%.*]] -; CHECK-NEXT: [[TMP1:%.*]] = freeze i8 [[Z_SHIFT]] -; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[TMP1]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = or i8 [[Z_SHIFT]], 1 ; CHECK-NEXT: [[TMP3:%.*]] = and i8 [[X:%.*]], [[TMP2]] ; CHECK-NEXT: [[TMP4:%.*]] = icmp ne i8 [[TMP3]], [[TMP2]] ; 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 define i1 @bitwise_and_logical_and_masked_icmp_allzeros_poison2(i1 %c, i32 %x, i32 %y) { ; CHECK-LABEL: @bitwise_and_logical_and_masked_icmp_allzeros_poison2( -; CHECK-NEXT: [[X_M1:%.*]] = and i32 [[X:%.*]], 8 -; CHECK-NEXT: [[C1:%.*]] = icmp eq i32 [[X_M1]], 0 -; CHECK-NEXT: [[AND1:%.*]] = select i1 [[C1]], i1 [[C:%.*]], i1 false -; CHECK-NEXT: [[X_M2:%.*]] = and i32 [[X]], [[Y:%.*]] +; CHECK-NEXT: [[Y:%.*]] = or i32 [[Y1:%.*]], 8 +; CHECK-NEXT: [[X_M2:%.*]] = and i32 [[X:%.*]], [[Y]] ; CHECK-NEXT: [[C2:%.*]] = icmp eq i32 [[X_M2]], 0 -; CHECK-NEXT: [[AND2:%.*]] = and i1 [[AND1]], [[C2]] +; CHECK-NEXT: [[AND2:%.*]] = select i1 [[C2]], i1 [[C:%.*]], i1 false ; CHECK-NEXT: ret i1 [[AND2]] ; %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 define i1 @bitwise_and_logical_and_masked_icmp_allones_poison2(i1 %c, i32 %x, i32 %y) { ; CHECK-LABEL: @bitwise_and_logical_and_masked_icmp_allones_poison2( -; CHECK-NEXT: [[X_M1:%.*]] = and i32 [[X:%.*]], 8 -; CHECK-NEXT: [[C1:%.*]] = icmp ne i32 [[X_M1]], 0 -; CHECK-NEXT: [[AND1:%.*]] = select i1 [[C1]], i1 [[C:%.*]], i1 false -; CHECK-NEXT: [[X_M2:%.*]] = and i32 [[X]], [[Y:%.*]] +; CHECK-NEXT: [[Y:%.*]] = or i32 [[Y1:%.*]], 8 +; CHECK-NEXT: [[X_M2:%.*]] = and i32 [[X:%.*]], [[Y]] ; CHECK-NEXT: [[C2:%.*]] = icmp eq i32 [[X_M2]], [[Y]] -; CHECK-NEXT: [[AND2:%.*]] = and i1 [[AND1]], [[C2]] +; CHECK-NEXT: [[AND2:%.*]] = select i1 [[C2]], i1 [[C:%.*]], i1 false ; CHECK-NEXT: ret i1 [[AND2]] ; %x.m1 = and i32 %x, 8 diff --git a/llvm/test/Transforms/InstCombine/bit-checks.ll b/llvm/test/Transforms/InstCombine/bit-checks.ll index 43cd6dd1211b0..936c02c9f9052 100644 --- a/llvm/test/Transforms/InstCombine/bit-checks.ll +++ b/llvm/test/Transforms/InstCombine/bit-checks.ll @@ -1335,6 +1335,22 @@ define i1 @no_masks_with_logical_or(i32 %a, i32 %b, i32 noundef %c) { ret i1 %or2 } +define i1 @no_masks_with_logical_or_commuted(i32 %a, i32 %b, i32 noundef %c) { +; CHECK-LABEL: @no_masks_with_logical_or_commuted( +; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 63 +; CHECK-NEXT: [[C:%.*]] = or i32 [[C1:%.*]], [[A:%.*]] +; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i32 [[C]], 0 +; CHECK-NEXT: [[OR2:%.*]] = select i1 [[CMP3]], i1 true, i1 [[CMP2]] +; CHECK-NEXT: ret i1 [[OR2]] +; + %cmp1 = icmp ne i32 %a, 0 + %cmp2 = icmp ne i32 %b, 63 + %or1 = select i1 %cmp1, i1 true, i1 %cmp2 + %cmp3 = icmp ne i32 %c, 0 + %or2 = or i1 %cmp3, %or1 + ret i1 %or2 +} + define i1 @no_masks_with_logical_or2(i32 %a, i32 %b, i32 noundef %c) { ; CHECK-LABEL: @no_masks_with_logical_or2( ; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[B:%.*]], 63