[llvm] [InstSimplify] Avoid poison value for ctz/abs in simplifyWithOpsReplaced() (PR #176168)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jan 15 05:47:30 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Nikita Popov (nikic)
<details>
<summary>Changes</summary>
If we drop flags, we'll set the zero_is_poison/int_min_is_poison flag to false as part of the transform. However, the constant folding was still performed with the value true, which made constant folding return poison. This resulted in the pattern failing to match, as the poison value is not equal to the other select arm.
To avoid this, add some special handling to set the argument to false during constant folding as well.
Fixes https://github.com/llvm/llvm-project/issues/175282.
---
Full diff: https://github.com/llvm/llvm-project/pull/176168.diff
2 Files Affected:
- (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+18-2)
- (modified) llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll (+117)
``````````diff
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index a8823117cdb51..ac95f5b09d812 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4517,15 +4517,31 @@ static Value *simplifyWithOpsReplaced(Value *V,
// TODO: This may be unsound, because it only catches some forms of
// refinement.
if (!AllowRefinement) {
+ auto *II = dyn_cast<IntrinsicInst>(I);
if (canCreatePoison(cast<Operator>(I), !DropFlags)) {
// abs cannot create poison if the value is known to never be int_min.
- if (auto *II = dyn_cast<IntrinsicInst>(I);
- II && II->getIntrinsicID() == Intrinsic::abs) {
+ if (II && II->getIntrinsicID() == Intrinsic::abs) {
if (!ConstOps[0]->isNotMinSignedValue())
return nullptr;
} else
return nullptr;
}
+
+ if (DropFlags && II) {
+ // If we're going to change the poison flag of abs/ctz to false, also
+ // perform constant folding that way, so we get an integer instead of a
+ // poison value here.
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::abs:
+ case Intrinsic::ctlz:
+ case Intrinsic::cttz:
+ ConstOps[1] = ConstantInt::getFalse(I->getContext());
+ break;
+ default:
+ break;
+ }
+ }
+
Constant *Res = ConstantFoldInstOperands(I, ConstOps, Q.DL, Q.TLI,
/*AllowNonDeterministic=*/false);
if (DropFlags && Res && I->hasPoisonGeneratingAnnotations())
diff --git a/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll b/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
index 52a32e19f57ef..0cb3aeaa14b3e 100644
--- a/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
+++ b/llvm/test/Transforms/InstCombine/select-cmp-cttz-ctlz.ll
@@ -718,6 +718,123 @@ define i64 @test_pr128441_commuted4_negative(i64 %x, i64 %y) {
ret i64 %sel
}
+define i32 @test_ctlz_sub_zero_not_poison(i32 %arg) {
+; CHECK-LABEL: @test_ctlz_sub_zero_not_poison(
+; CHECK-NEXT: [[CTZ:%.*]] = call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[ARG:%.*]], i1 false)
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i32 32, [[CTZ]]
+; CHECK-NEXT: ret i32 [[SUB]]
+;
+ %cmp = icmp eq i32 %arg, 0
+ %ctz = call i32 @llvm.ctlz.i32(i32 %arg, i1 false)
+ %sub = sub nuw nsw i32 32, %ctz
+ %sel = select i1 %cmp, i32 0, i32 %sub
+ ret i32 %sel
+}
+
+define i32 @test_ctlz_sub_zero_poison(i32 %arg) {
+; CHECK-LABEL: @test_ctlz_sub_zero_poison(
+; CHECK-NEXT: [[CTZ:%.*]] = call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[ARG:%.*]], i1 false)
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i32 32, [[CTZ]]
+; CHECK-NEXT: ret i32 [[SUB]]
+;
+ %cmp = icmp eq i32 %arg, 0
+ %ctz = call i32 @llvm.ctlz.i32(i32 %arg, i1 true)
+ %sub = sub nuw nsw i32 32, %ctz
+ %sel = select i1 %cmp, i32 0, i32 %sub
+ ret i32 %sel
+}
+
+define i32 @test_ctlz_sub_wrong_const(i32 %arg) {
+; CHECK-LABEL: @test_ctlz_sub_wrong_const(
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[ARG:%.*]], 0
+; CHECK-NEXT: [[CTZ:%.*]] = call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 [[ARG]], i1 true)
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i32 32, [[CTZ]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 [[SUB]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %cmp = icmp eq i32 %arg, 0
+ %ctz = call i32 @llvm.ctlz.i32(i32 %arg, i1 true)
+ %sub = sub nuw nsw i32 32, %ctz
+ %sel = select i1 %cmp, i32 1, i32 %sub
+ ret i32 %sel
+}
+
+define i32 @test_cttz_sub_zero_not_poison(i32 %arg) {
+; CHECK-LABEL: @test_cttz_sub_zero_not_poison(
+; CHECK-NEXT: [[CTZ:%.*]] = call range(i32 0, 33) i32 @llvm.cttz.i32(i32 [[ARG:%.*]], i1 false)
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i32 32, [[CTZ]]
+; CHECK-NEXT: ret i32 [[SUB]]
+;
+ %cmp = icmp eq i32 %arg, 0
+ %ctz = call i32 @llvm.cttz.i32(i32 %arg, i1 false)
+ %sub = sub nuw nsw i32 32, %ctz
+ %sel = select i1 %cmp, i32 0, i32 %sub
+ ret i32 %sel
+}
+
+define i32 @test_cttz_sub_zero_poison(i32 %arg) {
+; CHECK-LABEL: @test_cttz_sub_zero_poison(
+; CHECK-NEXT: [[CTZ:%.*]] = call range(i32 0, 33) i32 @llvm.cttz.i32(i32 [[ARG:%.*]], i1 false)
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i32 32, [[CTZ]]
+; CHECK-NEXT: ret i32 [[SUB]]
+;
+ %cmp = icmp eq i32 %arg, 0
+ %ctz = call i32 @llvm.cttz.i32(i32 %arg, i1 true)
+ %sub = sub nuw nsw i32 32, %ctz
+ %sel = select i1 %cmp, i32 0, i32 %sub
+ ret i32 %sel
+}
+
+define i32 @test_cttz_sub_zero_poison_wrong_const(i32 %arg) {
+; CHECK-LABEL: @test_cttz_sub_zero_poison_wrong_const(
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[ARG:%.*]], 0
+; CHECK-NEXT: [[CTZ:%.*]] = call range(i32 0, 33) i32 @llvm.cttz.i32(i32 [[ARG]], i1 true)
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw nsw i32 32, [[CTZ]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1, i32 [[SUB]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %cmp = icmp eq i32 %arg, 0
+ %ctz = call i32 @llvm.cttz.i32(i32 %arg, i1 true)
+ %sub = sub nuw nsw i32 32, %ctz
+ %sel = select i1 %cmp, i32 1, i32 %sub
+ ret i32 %sel
+}
+
+define i32 @test_abs_int_min_not_poison(i32 %arg) {
+; CHECK-LABEL: @test_abs_int_min_not_poison(
+; CHECK-NEXT: [[ABS:%.*]] = call i32 @llvm.abs.i32(i32 [[ARG:%.*]], i1 false)
+; CHECK-NEXT: ret i32 [[ABS]]
+;
+ %cmp = icmp eq i32 %arg, u0x80000000
+ %abs = call i32 @llvm.abs.i32(i32 %arg, i1 false)
+ %sel = select i1 %cmp, i32 u0x80000000, i32 %abs
+ ret i32 %sel
+}
+
+define i32 @test_abs_int_min_poison(i32 %arg) {
+; CHECK-LABEL: @test_abs_int_min_poison(
+; CHECK-NEXT: [[SEL:%.*]] = call i32 @llvm.abs.i32(i32 [[ARG:%.*]], i1 false)
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %cmp = icmp eq i32 %arg, u0x80000000
+ %abs = call i32 @llvm.abs.i32(i32 %arg, i1 true)
+ %sel = select i1 %cmp, i32 u0x80000000, i32 %abs
+ ret i32 %sel
+}
+
+define i32 @test_abs_int_min_poison_wrong_const(i32 %arg) {
+; CHECK-LABEL: @test_abs_int_min_poison_wrong_const(
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[ARG:%.*]], -2147483648
+; CHECK-NEXT: [[ABS:%.*]] = call i32 @llvm.abs.i32(i32 [[ARG]], i1 true)
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 1879048192, i32 [[ABS]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %cmp = icmp eq i32 %arg, u0x80000000
+ %abs = call i32 @llvm.abs.i32(i32 %arg, i1 true)
+ %sel = select i1 %cmp, i32 u0x70000000, i32 %abs
+ ret i32 %sel
+}
+
declare i16 @llvm.ctlz.i16(i16, i1)
declare i32 @llvm.ctlz.i32(i32, i1)
declare i64 @llvm.ctlz.i64(i64, i1)
``````````
</details>
https://github.com/llvm/llvm-project/pull/176168
More information about the llvm-commits
mailing list