-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[InstCombine] Convert logical and/or with icmp samesign
into bitwise ops
#116983
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-llvm-transforms Author: Yingwei Zheng (dtcxzyw) ChangesSee the following case:
Currently we cannot convert this logical and into a bitwise and due to the This patch converts a logical and into a bitwise and iff TV is poison implies that Cond is either poison or true. Likewise, we convert a logical or into a bitwise or iff FV is poison implies that Cond is either poison or false. Note:
Alive2: https://alive2.llvm.org/ce/z/eorFfa Full diff: https://github.com/llvm/llvm-project/pull/116983.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 010b77548c152a..6f0ec66420a51d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3115,6 +3115,39 @@ static Instruction *foldNestedSelects(SelectInst &OuterSelVal,
!IsAndVariant ? SelInner : InnerSel.FalseVal);
}
+/// Return true if V is poison or \p Expected given that ValAssumedPoison is
+/// already poison. For example, if ValAssumedPoison is `icmp samesign X, 10`
+/// and V is `icmp ne X, 5`, impliesPoisonOrCond returns true.
+static bool impliesPoisonOrCond(const Value *ValAssumedPoison, const Value *V,
+ bool Expected) {
+ if (impliesPoison(ValAssumedPoison, V))
+ return true;
+
+ // Handle the case that ValAssumedPoison is `icmp samesign pred X, C1` and V
+ // is `icmp pred X, C2`, where C1 is well-defined.
+ if (auto *ICmp = dyn_cast<ICmpInst>(ValAssumedPoison)) {
+ Value *LHS = ICmp->getOperand(0);
+ const APInt *RHSC1;
+ const APInt *RHSC2;
+ ICmpInst::Predicate Pred;
+ if (ICmp->hasSameSign() &&
+ match(ICmp->getOperand(1), m_APIntForbidPoison(RHSC1)) &&
+ match(V, m_ICmp(Pred, m_Specific(LHS), m_APIntAllowPoison(RHSC2)))) {
+ unsigned BitWidth = RHSC1->getBitWidth();
+ ConstantRange CRX =
+ RHSC1->isNonNegative()
+ ? ConstantRange(APInt::getSignedMinValue(BitWidth),
+ APInt::getZero(BitWidth))
+ : ConstantRange(APInt::getZero(BitWidth),
+ APInt::getSignedMinValue(BitWidth));
+ return CRX.icmp(Expected ? Pred : ICmpInst::getInversePredicate(Pred),
+ *RHSC2);
+ }
+ }
+
+ return false;
+}
+
Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
Value *CondVal = SI.getCondition();
Value *TrueVal = SI.getTrueValue();
@@ -3136,13 +3169,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
// checks whether folding it does not convert a well-defined value into
// poison.
if (match(TrueVal, m_One())) {
- if (impliesPoison(FalseVal, CondVal)) {
+ if (impliesPoisonOrCond(FalseVal, CondVal, /*Expected=*/false)) {
// Change: A = select B, true, C --> A = or B, C
return BinaryOperator::CreateOr(CondVal, FalseVal);
}
if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_One(), m_Value(B)))) &&
- impliesPoison(FalseVal, B)) {
+ impliesPoisonOrCond(FalseVal, B, /*Expected=*/false)) {
// (A || B) || C --> A || (B | C)
return replaceInstUsesWith(
SI, Builder.CreateLogicalOr(A, Builder.CreateOr(B, FalseVal)));
@@ -3178,13 +3211,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
}
if (match(FalseVal, m_Zero())) {
- if (impliesPoison(TrueVal, CondVal)) {
+ if (impliesPoisonOrCond(TrueVal, CondVal, /*Expected=*/true)) {
// Change: A = select B, C, false --> A = and B, C
return BinaryOperator::CreateAnd(CondVal, TrueVal);
}
if (match(CondVal, m_OneUse(m_Select(m_Value(A), m_Value(B), m_Zero()))) &&
- impliesPoison(TrueVal, B)) {
+ impliesPoisonOrCond(TrueVal, B, /*Expected=*/true)) {
// (A && B) && C --> A && (B & C)
return replaceInstUsesWith(
SI, Builder.CreateLogicalAnd(A, Builder.CreateAnd(B, TrueVal)));
diff --git a/llvm/test/Transforms/InstCombine/logical-select.ll b/llvm/test/Transforms/InstCombine/logical-select.ll
index 1b6e816d2e624e..050a53406a9c59 100644
--- a/llvm/test/Transforms/InstCombine/logical-select.ll
+++ b/llvm/test/Transforms/InstCombine/logical-select.ll
@@ -1521,3 +1521,121 @@ bb:
%and2 = or i1 %and1, %cmp
ret i1 %and2
}
+
+define i1 @test_logical_and_icmp_samesign(i8 %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i8 [[X]], 11
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %cmp1 = icmp ne i8 %x, 9
+ %cmp2 = icmp samesign ult i8 %x, 11
+ %and = select i1 %cmp1, i1 %cmp2, i1 false
+ ret i1 %and
+}
+
+define i1 @test_logical_or_icmp_samesign(i8 %x) {
+; CHECK-LABEL: @test_logical_or_icmp_samesign(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X:%.*]], -9
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i8 [[X]], -11
+; CHECK-NEXT: [[OR:%.*]] = or i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[OR]]
+;
+ %cmp1 = icmp eq i8 %x, -9
+ %cmp2 = icmp samesign ult i8 %x, -11
+ %or = select i1 %cmp1, i1 true, i1 %cmp2
+ ret i1 %or
+}
+
+define i1 @test_double_logical_and_icmp_samesign1(i1 %cond, i32 %y) {
+; CHECK-LABEL: @test_double_logical_and_icmp_samesign1(
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i32 [[Y:%.*]], 4
+; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[SEL1:%.*]], i1 [[CMP2]], i1 false
+; CHECK-NEXT: ret i1 [[SEL2]]
+;
+ %cmp1 = icmp ne i32 %y, 5
+ %sel1 = select i1 %cond, i1 %cmp1, i1 false
+ %cmp2 = icmp samesign ult i32 %y, 4
+ %sel2 = select i1 %sel1, i1 %cmp2, i1 false
+ ret i1 %sel2
+}
+
+define i1 @test_double_logical_and_icmp_samesign2(i1 %cond, i32 %y) {
+; CHECK-LABEL: @test_double_logical_and_icmp_samesign2(
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[Y:%.*]], -65536
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i32 [[TMP1]], 1048576
+; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[SEL1:%.*]], i1 [[CMP2]], i1 false
+; CHECK-NEXT: ret i1 [[SEL2]]
+;
+ %cmp1 = icmp samesign ugt i32 %y, 65535
+ %sel1 = select i1 %cond, i1 %cmp1, i1 false
+ %cmp2 = icmp samesign ult i32 %y, 1114112
+ %sel2 = select i1 %sel1, i1 %cmp2, i1 false
+ ret i1 %sel2
+}
+
+define <2 x i1> @test_logical_and_icmp_samesign_vec(<2 x i8> %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign_vec(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9)
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11)
+; CHECK-NEXT: [[AND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
+; CHECK-NEXT: ret <2 x i1> [[AND]]
+;
+ %cmp1 = icmp ne <2 x i8> %x, splat(i8 9)
+ %cmp2 = icmp samesign ult <2 x i8> %x, splat(i8 11)
+ %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
+ ret <2 x i1> %and
+}
+
+define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_cond(<2 x i8> %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_cond(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], <i8 9, i8 poison>
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], splat (i8 11)
+; CHECK-NEXT: [[AND:%.*]] = and <2 x i1> [[CMP1]], [[CMP2]]
+; CHECK-NEXT: ret <2 x i1> [[AND]]
+;
+ %cmp1 = icmp ne <2 x i8> %x, <i8 9, i8 poison>
+ %cmp2 = icmp samesign ult <2 x i8> %x, splat(i8 11)
+ %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
+ ret <2 x i1> %and
+}
+
+define i1 @test_logical_and_icmp_samesign_do_not_imply(i8 %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign_do_not_imply(
+; CHECK-NEXT: [[AND:%.*]] = icmp ult i8 [[X:%.*]], 11
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %cmp1 = icmp ne i8 %x, -9
+ %cmp2 = icmp samesign ult i8 %x, 11
+ %and = select i1 %cmp1, i1 %cmp2, i1 false
+ ret i1 %and
+}
+
+define i1 @test_logical_and_icmp_no_samesign(i8 %x) {
+; CHECK-LABEL: @test_logical_and_icmp_no_samesign(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[X:%.*]], 9
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 [[X]], 11
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %cmp1 = icmp ne i8 %x, 9
+ %cmp2 = icmp ult i8 %x, 11
+ %and = select i1 %cmp1, i1 %cmp2, i1 false
+ ret i1 %and
+}
+
+; Negative tests
+
+define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_tv(<2 x i8> %x) {
+; CHECK-LABEL: @test_logical_and_icmp_samesign_vec_with_poison_tv(
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ne <2 x i8> [[X:%.*]], splat (i8 9)
+; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult <2 x i8> [[X]], <i8 11, i8 poison>
+; CHECK-NEXT: [[AND:%.*]] = select <2 x i1> [[CMP1]], <2 x i1> [[CMP2]], <2 x i1> zeroinitializer
+; CHECK-NEXT: ret <2 x i1> [[AND]]
+;
+ %cmp1 = icmp ne <2 x i8> %x, splat(i8 9)
+ %cmp2 = icmp samesign ult <2 x i8> %x, <i8 11, i8 poison>
+ %and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
+ ret <2 x i1> %and
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/73/builds/8910 Here is the relevant piece of the build log for the reference
|
See the following case:
Currently we cannot convert this logical and into a bitwise and due to the
samesign
flag. But if%cmp2
evaluates topoison
, we can infer that%cmp1
is eitherpoison
ortrue
(samesign
violation indicates that X is negative). Therefore,%and
still evaluates topoison
.This patch converts a logical and into a bitwise and iff TV is poison implies that Cond is either poison or true. Likewise, we convert a logical or into a bitwise or iff FV is poison implies that Cond is either poison or false.
Note:
impliesPoison(TV/FV, Sel)
instead.ValAssumedPoison
isicmp samesign pred X, C1
andV
isicmp pred X, C2
. There are no suitable variants forisImpliedCondition
to pass the fact that X is [non-]negative.Alive2: https://alive2.llvm.org/ce/z/eorFfa
Motivation: fix a major regression to unblock #112742.