[InstCombine] Convert logical and/or with trunc nuw to i1 into bitwise ops#198178
Merged
Conversation
…e ops iff trunc nuw to i1 can not be poison.
|
@llvm/pr-subscribers-llvm-transforms Author: Andreas Jonson (andjo403) Changesif it is know that regression seen in dtcxzyw/llvm-opt-benchmark#3528 (comment) from #184182 proof https://alive2.llvm.org/ce/z/xQ2Sj- Full diff: https://github.com/llvm/llvm-project/pull/198178.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index a064aa1016399..4a47a2a06809b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3653,7 +3653,7 @@ static Instruction *foldNestedSelects(SelectInst &OuterSelVal,
/// 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) {
+ bool Expected, const SimplifyQuery &SQ) {
if (impliesPoison(ValAssumedPoison, V))
return true;
@@ -3678,6 +3678,16 @@ static bool impliesPoisonOrCond(const Value *ValAssumedPoison, const Value *V,
*RHSC2);
}
}
+ Value *A;
+ if (match(ValAssumedPoison, m_NUWTrunc(m_Value(A))) &&
+ isGuaranteedNotToBePoison(A)) {
+ assert(ValAssumedPoison->getType()->isIntOrIntVectorTy(1));
+ unsigned BitWidth = A->getType()->getScalarSizeInBits();
+ return ConstantRange(APInt::getZero(BitWidth), APInt(BitWidth, 2))
+ .contains(computeConstantRangeIncludingKnownBits(
+ A, false,
+ SQ.getWithInstruction(cast<Instruction>(ValAssumedPoison))));
+ }
return false;
}
@@ -3703,13 +3713,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 (impliesPoisonOrCond(FalseVal, CondVal, /*Expected=*/false)) {
+ if (impliesPoisonOrCond(FalseVal, CondVal, /*Expected=*/false, SQ)) {
// 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)))) &&
- impliesPoisonOrCond(FalseVal, B, /*Expected=*/false)) {
+ impliesPoisonOrCond(FalseVal, B, /*Expected=*/false, SQ)) {
// (A || B) || C --> A || (B | C)
Value *LOr = Builder.CreateLogicalOr(A, Builder.CreateOr(B, FalseVal));
if (auto *I = dyn_cast<Instruction>(LOr)) {
@@ -3749,13 +3759,13 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
}
if (match(FalseVal, m_Zero())) {
- if (impliesPoisonOrCond(TrueVal, CondVal, /*Expected=*/true)) {
+ if (impliesPoisonOrCond(TrueVal, CondVal, /*Expected=*/true, SQ)) {
// 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()))) &&
- impliesPoisonOrCond(TrueVal, B, /*Expected=*/true)) {
+ impliesPoisonOrCond(TrueVal, B, /*Expected=*/true, SQ)) {
// (A && B) && C --> A && (B & C)
Value *LAnd = Builder.CreateLogicalAnd(A, Builder.CreateAnd(B, TrueVal));
if (auto *I = dyn_cast<Instruction>(LAnd)) {
diff --git a/llvm/test/Transforms/InstCombine/logical-select.ll b/llvm/test/Transforms/InstCombine/logical-select.ll
index e6de063969a6a..85e8c98455c91 100644
--- a/llvm/test/Transforms/InstCombine/logical-select.ll
+++ b/llvm/test/Transforms/InstCombine/logical-select.ll
@@ -1637,3 +1637,58 @@ define <2 x i1> @test_logical_and_icmp_samesign_vec_with_poison_tv(<2 x i8> %x)
%and = select <2 x i1> %cmp1, <2 x i1> %cmp2, <2 x i1> zeroinitializer
ret <2 x i1> %and
}
+
+define i1 @test_logical_and_trunc_nuw(i1 %c, i8 noundef range(i8 0,2) %x) {
+; CHECK-LABEL: @test_logical_and_trunc_nuw(
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[X:%.*]] to i1
+; CHECK-NEXT: [[AND:%.*]] = and i1 [[C:%.*]], [[TRUNC]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %trunc = trunc nuw i8 %x to i1
+ %and = select i1 %c, i1 %trunc, i1 false
+ ret i1 %and
+}
+
+define i1 @test_logical_or_trunc_nuw(i1 %c, i8 noundef range(i8 0,2) %x) {
+; CHECK-LABEL: @test_logical_or_trunc_nuw(
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[X:%.*]] to i1
+; CHECK-NEXT: [[OR:%.*]] = or i1 [[C:%.*]], [[TRUNC]]
+; CHECK-NEXT: ret i1 [[OR]]
+;
+ %trunc = trunc nuw i8 %x to i1
+ %or = select i1 %c, i1 true, i1 %trunc
+ ret i1 %or
+}
+
+define <2 x i1> @test_logical_and_trunc_nuw_vec(<2 x i1> %c, <2 x i8> noundef range(i8 0,2) %x) {
+; CHECK-LABEL: @test_logical_and_trunc_nuw_vec(
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw <2 x i8> [[X:%.*]] to <2 x i1>
+; CHECK-NEXT: [[AND:%.*]] = and <2 x i1> [[C:%.*]], [[TRUNC]]
+; CHECK-NEXT: ret <2 x i1> [[AND]]
+;
+ %trunc = trunc nuw <2 x i8> %x to <2 x i1>
+ %and = select <2 x i1> %c, <2 x i1> %trunc, <2 x i1> zeroinitializer
+ ret <2 x i1> %and
+}
+
+define i1 @neg_test_logical_and_trunc_nuw_no_range(i1 %c, i8 noundef %x) {
+; CHECK-LABEL: @neg_test_logical_and_trunc_nuw_no_range(
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[X:%.*]] to i1
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C:%.*]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %trunc = trunc nuw i8 %x to i1
+ %and = select i1 %c, i1 %trunc, i1 false
+ ret i1 %and
+}
+
+define i1 @neg_test_logical_and_trunc_nuw_no_noundef(i1 %c, i8 range(i8 0,2) %x) {
+; CHECK-LABEL: @neg_test_logical_and_trunc_nuw_no_noundef(
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i8 [[X:%.*]] to i1
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C:%.*]], i1 [[TRUNC]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %trunc = trunc nuw i8 %x to i1
+ %and = select i1 %c, i1 %trunc, i1 false
+ ret i1 %and
+}
|
nikic
reviewed
May 17, 2026
| assert(ValAssumedPoison->getType()->isIntOrIntVectorTy(1)); | ||
| unsigned BitWidth = A->getType()->getScalarSizeInBits(); | ||
| return ConstantRange(APInt::getZero(BitWidth), APInt(BitWidth, 2)) | ||
| .contains(computeConstantRangeIncludingKnownBits( |
Contributor
There was a problem hiding this comment.
Can you use just computeKnownBits here? I don't think computeConstantRange() adds any precision here, as it's a power of 2 range.
Contributor
Author
There was a problem hiding this comment.
no changes in local run of llvm-opt-benchmark with the change so fix pushed
MaxGraey
reviewed
May 17, 2026
pedroMVicente
pushed a commit
to pedroMVicente/llvm-project
that referenced
this pull request
May 19, 2026
…e ops (llvm#198178) if it is know that `trunc nuw to i1 ` can not be poison logical and/or can be folded to bitwise ops. proof https://alive2.llvm.org/ce/z/xQ2Sj-
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
if it is know that
trunc nuw to i1can not be poison logical and/or can be folded to bitwise ops.regression seen in dtcxzyw/llvm-opt-benchmark#3528 (comment) from #184182
proof https://alive2.llvm.org/ce/z/xQ2Sj-