[llvm] [ConstantFolding] Consolidate poison propagation for intrinsics (PR #146878)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 3 06:11:06 PDT 2025
https://github.com/nikic updated https://github.com/llvm/llvm-project/pull/146878
>From c1c32e28abcf87aa2e93bf664a01567d8ad000d4 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Thu, 3 Jul 2025 15:01:27 +0200
Subject: [PATCH 1/3] Add test for ctpop with poison arg
---
llvm/test/Transforms/InstSimplify/fold-intrinsics.ll | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/llvm/test/Transforms/InstSimplify/fold-intrinsics.ll b/llvm/test/Transforms/InstSimplify/fold-intrinsics.ll
index e45aa3fd09ce0..bcb1fc8b1450c 100644
--- a/llvm/test/Transforms/InstSimplify/fold-intrinsics.ll
+++ b/llvm/test/Transforms/InstSimplify/fold-intrinsics.ll
@@ -44,3 +44,11 @@ define void @powi_i16(float %V, ptr%P) {
ret void
}
+
+define i32 @test_ctpop_poison(i32 %a) {
+; CHECK-LABEL: @test_ctpop_poison(
+; CHECK-NEXT: ret i32 0
+;
+ %res = tail call i32 @llvm.ctpop.i32(i32 poison)
+ ret i32 %res
+}
>From f867100025bbb7e11f51cc21cd6343b1a1820087 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Thu, 3 Jul 2025 14:56:30 +0200
Subject: [PATCH 2/3] Consolidate poison constant folding
---
llvm/include/llvm/Analysis/ValueTracking.h | 3 +
llvm/lib/Analysis/ConstantFolding.cpp | 39 ++--------
llvm/lib/Analysis/ValueTracking.cpp | 75 +++++++++++--------
.../InstSimplify/fold-intrinsics.ll | 2 +-
4 files changed, 52 insertions(+), 67 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 17c79790a69f2..02990a3cb44f7 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -727,6 +727,9 @@ LLVM_ABI bool isGuaranteedToExecuteForEveryIteration(const Instruction *I,
/// getGuaranteedNonPoisonOp.
LLVM_ABI bool propagatesPoison(const Use &PoisonOp);
+/// Return whether this intrinsic propagates poison for all operands.
+LLVM_ABI bool intrinsicPropagatesPoison(Intrinsic::ID IID);
+
/// Return true if the given instruction must trigger undefined behavior
/// when I is executed with any operands which appear in KnownPoison holding
/// a poison value at the point of execution.
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 9e3c271f7d93f..9df56d13d0d6c 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2229,17 +2229,6 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
return nullptr;
}
- if (isa<PoisonValue>(Operands[0])) {
- // TODO: All of these operations should probably propagate poison.
- switch (IntrinsicID) {
- case Intrinsic::canonicalize:
- case Intrinsic::sqrt:
- return PoisonValue::get(Ty);
- default:
- break;
- }
- }
-
if (isa<UndefValue>(Operands[0])) {
// cosine(arg) is between -1 and 1. cosine(invalid arg) is NaN.
// ctpop() is between 0 and bitwidth, pick 0 for undef.
@@ -3228,11 +3217,6 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
case Intrinsic::smin:
case Intrinsic::umax:
case Intrinsic::umin:
- // This is the same as for binary ops - poison propagates.
- // TODO: Poison handling should be consolidated.
- if (isa<PoisonValue>(Operands[0]) || isa<PoisonValue>(Operands[1]))
- return PoisonValue::get(Ty);
-
if (!C0 && !C1)
return UndefValue::get(Ty);
if (!C0 || !C1)
@@ -3245,9 +3229,6 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
case Intrinsic::scmp:
case Intrinsic::ucmp:
- if (isa<PoisonValue>(Operands[0]) || isa<PoisonValue>(Operands[1]))
- return PoisonValue::get(Ty);
-
if (!C0 || !C1)
return ConstantInt::get(Ty, 0);
@@ -3314,11 +3295,6 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
}
case Intrinsic::uadd_sat:
case Intrinsic::sadd_sat:
- // This is the same as for binary ops - poison propagates.
- // TODO: Poison handling should be consolidated.
- if (isa<PoisonValue>(Operands[0]) || isa<PoisonValue>(Operands[1]))
- return PoisonValue::get(Ty);
-
if (!C0 && !C1)
return UndefValue::get(Ty);
if (!C0 || !C1)
@@ -3329,11 +3305,6 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
return ConstantInt::get(Ty, C0->sadd_sat(*C1));
case Intrinsic::usub_sat:
case Intrinsic::ssub_sat:
- // This is the same as for binary ops - poison propagates.
- // TODO: Poison handling should be consolidated.
- if (isa<PoisonValue>(Operands[0]) || isa<PoisonValue>(Operands[1]))
- return PoisonValue::get(Ty);
-
if (!C0 && !C1)
return UndefValue::get(Ty);
if (!C0 || !C1)
@@ -3592,11 +3563,6 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
if (IntrinsicID == Intrinsic::smul_fix ||
IntrinsicID == Intrinsic::smul_fix_sat) {
- // poison * C -> poison
- // C * poison -> poison
- if (isa<PoisonValue>(Operands[0]) || isa<PoisonValue>(Operands[1]))
- return PoisonValue::get(Ty);
-
const APInt *C0, *C1;
if (!getConstIntOrUndef(Operands[0], C0) ||
!getConstIntOrUndef(Operands[1], C1))
@@ -3670,6 +3636,11 @@ static Constant *ConstantFoldScalarCall(StringRef Name,
ArrayRef<Constant *> Operands,
const TargetLibraryInfo *TLI,
const CallBase *Call) {
+ if (IntrinsicID != Intrinsic::not_intrinsic &&
+ any_of(Operands, [](Constant *Op) { return isa<PoisonValue>(Op); }) &&
+ intrinsicPropagatesPoison(IntrinsicID))
+ return PoisonValue::get(Ty);
+
if (Operands.size() == 1)
return ConstantFoldScalarCall1(Name, IntrinsicID, Ty, Operands, TLI, Call);
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index e576f4899810a..2b7b1ee273992 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -7879,6 +7879,47 @@ bool llvm::isGuaranteedToExecuteForEveryIteration(const Instruction *I,
llvm_unreachable("Instruction not contained in its own parent basic block.");
}
+bool llvm::intrinsicPropagatesPoison(Intrinsic::ID IID) {
+ switch (IID) {
+ // TODO: Add more intrinsics.
+ case Intrinsic::sadd_with_overflow:
+ case Intrinsic::ssub_with_overflow:
+ case Intrinsic::smul_with_overflow:
+ case Intrinsic::uadd_with_overflow:
+ case Intrinsic::usub_with_overflow:
+ case Intrinsic::umul_with_overflow:
+ // If an input is a vector containing a poison element, the
+ // two output vectors (calculated results, overflow bits)'
+ // corresponding lanes are poison.
+ return true;
+ case Intrinsic::ctpop:
+ case Intrinsic::ctlz:
+ case Intrinsic::cttz:
+ case Intrinsic::abs:
+ case Intrinsic::smax:
+ case Intrinsic::smin:
+ case Intrinsic::umax:
+ case Intrinsic::umin:
+ case Intrinsic::scmp:
+ case Intrinsic::ucmp:
+ case Intrinsic::bitreverse:
+ case Intrinsic::bswap:
+ case Intrinsic::sadd_sat:
+ case Intrinsic::ssub_sat:
+ case Intrinsic::sshl_sat:
+ case Intrinsic::uadd_sat:
+ case Intrinsic::usub_sat:
+ case Intrinsic::ushl_sat:
+ case Intrinsic::smul_fix:
+ case Intrinsic::smul_fix_sat:
+ case Intrinsic::canonicalize:
+ case Intrinsic::sqrt:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool llvm::propagatesPoison(const Use &PoisonOp) {
const Operator *I = cast<Operator>(PoisonOp.getUser());
switch (I->getOpcode()) {
@@ -7889,38 +7930,8 @@ bool llvm::propagatesPoison(const Use &PoisonOp) {
case Instruction::Select:
return PoisonOp.getOperandNo() == 0;
case Instruction::Call:
- if (auto *II = dyn_cast<IntrinsicInst>(I)) {
- switch (II->getIntrinsicID()) {
- // TODO: Add more intrinsics.
- case Intrinsic::sadd_with_overflow:
- case Intrinsic::ssub_with_overflow:
- case Intrinsic::smul_with_overflow:
- case Intrinsic::uadd_with_overflow:
- case Intrinsic::usub_with_overflow:
- case Intrinsic::umul_with_overflow:
- // If an input is a vector containing a poison element, the
- // two output vectors (calculated results, overflow bits)'
- // corresponding lanes are poison.
- return true;
- case Intrinsic::ctpop:
- case Intrinsic::ctlz:
- case Intrinsic::cttz:
- case Intrinsic::abs:
- case Intrinsic::smax:
- case Intrinsic::smin:
- case Intrinsic::umax:
- case Intrinsic::umin:
- case Intrinsic::bitreverse:
- case Intrinsic::bswap:
- case Intrinsic::sadd_sat:
- case Intrinsic::ssub_sat:
- case Intrinsic::sshl_sat:
- case Intrinsic::uadd_sat:
- case Intrinsic::usub_sat:
- case Intrinsic::ushl_sat:
- return true;
- }
- }
+ if (auto *II = dyn_cast<IntrinsicInst>(I))
+ return intrinsicPropagatesPoison(II->getIntrinsicID());
return false;
case Instruction::ICmp:
case Instruction::FCmp:
diff --git a/llvm/test/Transforms/InstSimplify/fold-intrinsics.ll b/llvm/test/Transforms/InstSimplify/fold-intrinsics.ll
index bcb1fc8b1450c..b66d0c76d5ede 100644
--- a/llvm/test/Transforms/InstSimplify/fold-intrinsics.ll
+++ b/llvm/test/Transforms/InstSimplify/fold-intrinsics.ll
@@ -47,7 +47,7 @@ define void @powi_i16(float %V, ptr%P) {
define i32 @test_ctpop_poison(i32 %a) {
; CHECK-LABEL: @test_ctpop_poison(
-; CHECK-NEXT: ret i32 0
+; CHECK-NEXT: ret i32 poison
;
%res = tail call i32 @llvm.ctpop.i32(i32 poison)
ret i32 %res
>From 6c1ed11518b8aa1cb8fd8919a49987c9b9a4122d Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Thu, 3 Jul 2025 15:10:47 +0200
Subject: [PATCH 3/3] Adjust unit test
---
llvm/unittests/Analysis/ValueTrackingTest.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 129052fbe08b8..e283591843748 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -910,7 +910,7 @@ TEST(ValueTracking, propagatesPoison) {
{false, "call i32 @llvm.fshr.i32(i32 %x, i32 %y, i32 %shamt)", 0},
{false, "call i32 @llvm.fshr.i32(i32 %x, i32 %y, i32 %shamt)", 1},
{false, "call i32 @llvm.fshr.i32(i32 %x, i32 %y, i32 %shamt)", 2},
- {false, "call float @llvm.sqrt.f32(float %fx)", 0},
+ {true, "call float @llvm.sqrt.f32(float %fx)", 0},
{false, "call float @llvm.powi.f32.i32(float %fx, i32 %x)", 0},
{false, "call float @llvm.sin.f32(float %fx)", 0},
{false, "call float @llvm.cos.f32(float %fx)", 0},
More information about the llvm-commits
mailing list