[llvm] [ConstFold] Fold poison operands eagerly (PR #203389)
Ramkumar Ramachandra via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 12 04:23:44 PDT 2026
https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/203389
>From 22e3ef1f2df6f4507444a82a8037e4acd90757a8 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Thu, 11 Jun 2026 20:47:59 +0100
Subject: [PATCH 1/2] [ConstFold] Fold poison operands eagerly
This is a preparatory step towards removing undef-handling in
ConstantFolding.
---
llvm/lib/Analysis/ConstantFolding.cpp | 37 ++++++++++++++-----
llvm/lib/Analysis/ValueTracking.cpp | 17 +++++++++
.../ConstProp/active-lane-mask.ll | 4 +-
3 files changed, 46 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 8964dcb9fd553..96d9cdec50964 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2298,8 +2298,9 @@ Constant *ConstantFoldFP128(float128 (*NativeFP)(float128), const APFloat &V,
}
#endif
-Constant *ConstantFoldBinaryFP(double (*NativeFP)(double, double),
- const APFloat &V, const APFloat &W, Type *Ty) {
+static Constant *ConstantFoldBinaryFP(double (*NativeFP)(double, double),
+ const APFloat &V, const APFloat &W,
+ Type *Ty) {
llvm_fenv_clearexcept();
double Result = NativeFP(V.convertToDouble(), W.convertToDouble());
if (llvm_fenv_testexcept()) {
@@ -2310,7 +2311,7 @@ Constant *ConstantFoldBinaryFP(double (*NativeFP)(double, double),
return GetConstantFoldFPValue(Result, Ty);
}
-Constant *constantFoldVectorReduce(Intrinsic::ID IID, Constant *Op) {
+static Constant *constantFoldVectorReduce(Intrinsic::ID IID, Constant *Op) {
auto *OpVT = cast<VectorType>(Op->getType());
// This is the same as the underlying binops - poison propagates.
@@ -2443,6 +2444,14 @@ static bool getConstIntOrUndef(Value *Op, const APInt *&C) {
return false;
}
+template <typename ConstTy>
+static Constant *foldPoisonOperands(Intrinsic::ID ID,
+ ArrayRef<ConstTy *> Operands, Type *Ty) {
+ if (intrinsicPropagatesPoison(ID) && any_of(Operands, IsaPred<PoisonValue>))
+ return PoisonValue::get(Ty);
+ return nullptr;
+}
+
/// Checks if the given intrinsic call, which evaluates to constant, is allowed
/// to be folded.
///
@@ -4252,15 +4261,12 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
}
static Constant *ConstantFoldScalarCall(StringRef Name,
- Intrinsic::ID IntrinsicID,
- Type *Ty,
+ Intrinsic::ID IntrinsicID, Type *Ty,
ArrayRef<Constant *> Operands,
const TargetLibraryInfo *TLI,
const CallBase *Call) {
- if (IntrinsicID != Intrinsic::not_intrinsic &&
- any_of(Operands, IsaPred<PoisonValue>) &&
- intrinsicPropagatesPoison(IntrinsicID))
- return PoisonValue::get(Ty);
+ if (auto *C = foldPoisonOperands<Constant>(IntrinsicID, Operands, Ty))
+ return C;
if (Operands.size() == 1)
return ConstantFoldScalarCall1(Name, IntrinsicID, Ty, Operands, TLI, Call);
@@ -4287,6 +4293,9 @@ static Constant *ConstantFoldFixedVectorCall(
SmallVector<Constant *, 4> Lane(Operands.size());
Type *Ty = FVTy->getElementType();
+ if (auto *C = foldPoisonOperands<Constant>(IntrinsicID, Operands, FVTy))
+ return C;
+
switch (IntrinsicID) {
case Intrinsic::masked_load: {
auto *SrcPtr = Operands[0];
@@ -4496,6 +4505,9 @@ static Constant *ConstantFoldScalableVectorCall(
StringRef Name, Intrinsic::ID IntrinsicID, ScalableVectorType *SVTy,
ArrayRef<Constant *> Operands, const DataLayout &DL,
const TargetLibraryInfo *TLI, const CallBase *Call) {
+ if (auto *C = foldPoisonOperands<Constant>(IntrinsicID, Operands, SVTy))
+ return C;
+
switch (IntrinsicID) {
case Intrinsic::aarch64_sve_convert_from_svbool: {
Constant *Src = Operands[0];
@@ -4698,11 +4710,15 @@ ConstantFoldStructCall(StringRef Name, Intrinsic::ID IntrinsicID,
Constant *llvm::ConstantFoldUnaryIntrinsic(Intrinsic::ID ID, Constant *Op,
Type *Ty) {
+ if (auto *C = foldPoisonOperands<Constant>(ID, Op, Ty))
+ return C;
return ConstantFoldScalarCall1("", ID, Ty, Op, nullptr, nullptr);
}
Constant *llvm::ConstantFoldBinaryIntrinsic(Intrinsic::ID ID, Constant *LHS,
Constant *RHS, Type *Ty) {
+ if (auto *C = foldPoisonOperands<Constant>(ID, {LHS, RHS}, Ty))
+ return C;
return ConstantFoldIntrinsicCall2(ID, Ty, {LHS, RHS}, nullptr);
}
@@ -4731,6 +4747,9 @@ Constant *llvm::ConstantFoldCall(const CallBase *Call, Function *F,
if (!AllowNonDeterministic && Ty->isFPOrFPVectorTy())
return nullptr;
+ if (auto *C = foldPoisonOperands(IID, Operands, Ty))
+ return C;
+
StringRef Name = F->getName();
if (auto *FVTy = dyn_cast<FixedVectorType>(Ty))
return ConstantFoldFixedVectorCall(
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 763620b353bb5..54bfb5e1a5916 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8178,6 +8178,23 @@ bool llvm::intrinsicPropagatesPoison(Intrinsic::ID IID) {
case Intrinsic::llrint:
case Intrinsic::fshl:
case Intrinsic::fshr:
+ case Intrinsic::frexp:
+ case Intrinsic::get_active_lane_mask:
+ case Intrinsic::vector_reduce_add:
+ case Intrinsic::vector_reduce_mul:
+ case Intrinsic::vector_reduce_and:
+ case Intrinsic::vector_reduce_or:
+ case Intrinsic::vector_reduce_xor:
+ case Intrinsic::vector_reduce_smin:
+ case Intrinsic::vector_reduce_smax:
+ case Intrinsic::vector_reduce_umin:
+ case Intrinsic::vector_reduce_umax:
+ case Intrinsic::amdgcn_wave_reduce_umin:
+ case Intrinsic::amdgcn_wave_reduce_umax:
+ case Intrinsic::amdgcn_wave_reduce_max:
+ case Intrinsic::amdgcn_wave_reduce_min:
+ case Intrinsic::amdgcn_wave_reduce_and:
+ case Intrinsic::amdgcn_wave_reduce_or:
return true;
default:
return false;
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/active-lane-mask.ll b/llvm/test/Transforms/InstSimplify/ConstProp/active-lane-mask.ll
index e9d9ac040ea1d..e272b3f0682ef 100644
--- a/llvm/test/Transforms/InstSimplify/ConstProp/active-lane-mask.ll
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/active-lane-mask.ll
@@ -297,9 +297,7 @@ entry:
define <4 x float> @poisonc(<4 x float> %a, i32 %n) {
; CHECK-LABEL: @poisonc(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[VAR27:%.*]] = call <4 x i1> @llvm.get.active.lane.mask.v4i1.i32(i32 poison, i32 1024)
-; CHECK-NEXT: [[VAR33:%.*]] = select <4 x i1> [[VAR27]], <4 x float> [[A:%.*]], <4 x float> zeroinitializer
-; CHECK-NEXT: ret <4 x float> [[VAR33]]
+; CHECK-NEXT: ret <4 x float> poison
;
entry:
%new0 = shl i1 0, 1
>From 165179827bb363a28a3650e540975b322c9190ec Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Fri, 12 Jun 2026 11:36:57 +0100
Subject: [PATCH 2/2] [ConstFold] Eliminate some undefs
---
llvm/lib/Analysis/ConstantFolding.cpp | 17 +-
.../InstSimplify/ConstProp/cttz-elts.ll | 16 --
.../ConstProp/saturating-add-sub.ll | 194 ------------------
3 files changed, 4 insertions(+), 223 deletions(-)
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 96d9cdec50964..0d202e882aff3 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1277,7 +1277,7 @@ Constant *llvm::ConstantFoldInstruction(const Instruction *I,
// skip the value if it is equal to the phi node itself we choose not to
// because that would break the rule that constant folding only applies if
// all operands are constants.
- if (isa<UndefValue>(Incoming))
+ if (isa<PoisonValue>(Incoming))
continue;
// If the incoming value is not a constant, then give up.
auto *C = dyn_cast<Constant>(Incoming);
@@ -1293,7 +1293,7 @@ Constant *llvm::ConstantFoldInstruction(const Instruction *I,
}
// If we reach here, all incoming values are the same constant or undef.
- return CommonValue ? CommonValue : UndefValue::get(PN->getType());
+ return CommonValue ? CommonValue : PoisonValue::get(PN->getType());
}
// Scan the operand list, checking to see if they are all constants, if so,
@@ -3786,8 +3786,6 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
case Intrinsic::smin:
case Intrinsic::umax:
case Intrinsic::umin:
- if (!C0 && !C1)
- return UndefValue::get(Ty);
if (!C0 || !C1)
return MinMaxIntrinsic::getSaturationPoint(IntrinsicID, Ty);
return ConstantInt::get(
@@ -3864,8 +3862,6 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
}
case Intrinsic::uadd_sat:
case Intrinsic::sadd_sat:
- if (!C0 && !C1)
- return UndefValue::get(Ty);
if (!C0 || !C1)
return Constant::getAllOnesValue(Ty);
if (IntrinsicID == Intrinsic::uadd_sat)
@@ -3874,8 +3870,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:
- if (!C0 && !C1)
- return UndefValue::get(Ty);
if (!C0 || !C1)
return Constant::getNullValue(Ty);
if (IntrinsicID == Intrinsic::usub_sat)
@@ -3990,7 +3984,7 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
Constant *Elt = Operands[0]->getAggregateElement(I);
if (!Elt)
return nullptr;
- if (isa<UndefValue>(Elt) || Elt->isNullValue())
+ if (isa<PoisonValue>(Elt) || Elt->isNullValue())
continue;
return ConstantInt::get(Ty, I);
}
@@ -4311,7 +4305,7 @@ static Constant *ConstantFoldFixedVectorCall(
break;
auto *PassthruElt = Passthru->getAggregateElement(I);
auto *VecElt = VecData ? VecData->getAggregateElement(I) : nullptr;
- if (isa<UndefValue>(MaskElt)) {
+ if (isa<PoisonValue>(MaskElt)) {
if (PassthruElt)
NewElements.push_back(PassthruElt);
else if (VecElt)
@@ -4570,9 +4564,6 @@ static Constant *ConstantFoldScalableVectorCall(
static std::pair<Constant *, Constant *>
ConstantFoldScalarFrexpCall(Constant *Op, Type *IntTy) {
- if (isa<PoisonValue>(Op))
- return {Op, PoisonValue::get(IntTy)};
-
auto *ConstFP = dyn_cast<ConstantFP>(Op);
if (!ConstFP)
return {};
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll b/llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll
index db91fd68fcbe6..2faac784d0a66 100644
--- a/llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/cttz-elts.ll
@@ -25,22 +25,6 @@ define i32 @cttz_elts_v4i32() {
ret i32 %res
}
-define i32 @cttz_elts_v4i32_unused_lane_undef() {
-; CHECK-LABEL: @cttz_elts_v4i32_unused_lane_undef(
-; CHECK-NEXT: ret i32 1
-;
- %res = call i32 @llvm.experimental.cttz.elts.i32.v4i32(<4 x i32> <i32 0, i32 1, i32 undef, i32 3>, i1 false)
- ret i32 %res
-}
-
-define i32 @cttz_elts_v4i32_used_lane_undef() {
-; CHECK-LABEL: @cttz_elts_v4i32_used_lane_undef(
-; CHECK-NEXT: ret i32 3
-;
- %res = call i32 @llvm.experimental.cttz.elts.i32.v4i32(<4 x i32> <i32 0, i32 0, i32 undef, i32 3>, i1 false)
- ret i32 %res
-}
-
define i32 @cttz_elts_v4i32_unused_lane_poison() {
; CHECK-LABEL: @cttz_elts_v4i32_unused_lane_poison(
; CHECK-NEXT: ret i32 1
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/saturating-add-sub.ll b/llvm/test/Transforms/InstSimplify/ConstProp/saturating-add-sub.ll
index fde98c43e7687..59e6e433e7663 100644
--- a/llvm/test/Transforms/InstSimplify/ConstProp/saturating-add-sub.ll
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/saturating-add-sub.ll
@@ -171,200 +171,6 @@ define <2 x i8> @test_ssub_vector_sat_neg(<2 x i8> %a) {
ret <2 x i8> %x
}
-; Tests for undef handling
-
-define i8 @test_uadd_scalar_both_undef() {
-; CHECK-LABEL: @test_uadd_scalar_both_undef(
-; CHECK-NEXT: ret i8 undef
-;
- %x = call i8 @llvm.uadd.sat.i8(i8 undef, i8 undef)
- ret i8 %x
-}
-
-define i8 @test_sadd_scalar_both_undef() {
-; CHECK-LABEL: @test_sadd_scalar_both_undef(
-; CHECK-NEXT: ret i8 undef
-;
- %x = call i8 @llvm.sadd.sat.i8(i8 undef, i8 undef)
- ret i8 %x
-}
-
-define i8 @test_usub_scalar_both_undef() {
-; CHECK-LABEL: @test_usub_scalar_both_undef(
-; CHECK-NEXT: ret i8 undef
-;
- %x = call i8 @llvm.usub.sat.i8(i8 undef, i8 undef)
- ret i8 %x
-}
-
-define i8 @test_ssub_scalar_both_undef() {
-; CHECK-LABEL: @test_ssub_scalar_both_undef(
-; CHECK-NEXT: ret i8 undef
-;
- %x = call i8 @llvm.ssub.sat.i8(i8 undef, i8 undef)
- ret i8 %x
-}
-
-define i8 @test_uadd_scalar_op2_undef() {
-; CHECK-LABEL: @test_uadd_scalar_op2_undef(
-; CHECK-NEXT: ret i8 -1
-;
- %x = call i8 @llvm.uadd.sat.i8(i8 10, i8 undef)
- ret i8 %x
-}
-
-define i8 @test_sadd_scalar_op1_undef() {
-; CHECK-LABEL: @test_sadd_scalar_op1_undef(
-; CHECK-NEXT: ret i8 -1
-;
- %x = call i8 @llvm.sadd.sat.i8(i8 undef, i8 10)
- ret i8 %x
-}
-
-define i8 @test_usub_scalar_op2_undef() {
-; CHECK-LABEL: @test_usub_scalar_op2_undef(
-; CHECK-NEXT: ret i8 0
-;
- %x = call i8 @llvm.usub.sat.i8(i8 10, i8 undef)
- ret i8 %x
-}
-
-define i8 @test_usub_scalar_op1_undef() {
-; CHECK-LABEL: @test_usub_scalar_op1_undef(
-; CHECK-NEXT: ret i8 0
-;
- %x = call i8 @llvm.usub.sat.i8(i8 undef, i8 10)
- ret i8 %x
-}
-
-define <2 x i8> @test_uadd_vector_both_undef_splat() {
-; CHECK-LABEL: @test_uadd_vector_both_undef_splat(
-; CHECK-NEXT: ret <2 x i8> undef
-;
- %x = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> undef, <2 x i8> undef)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_sadd_vector_both_undef_splat() {
-; CHECK-LABEL: @test_sadd_vector_both_undef_splat(
-; CHECK-NEXT: ret <2 x i8> undef
-;
- %x = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> undef, <2 x i8> undef)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_usub_vector_both_undef_splat() {
-; CHECK-LABEL: @test_usub_vector_both_undef_splat(
-; CHECK-NEXT: ret <2 x i8> undef
-;
- %x = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> undef, <2 x i8> undef)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_ssub_vector_both_undef_splat() {
-; CHECK-LABEL: @test_ssub_vector_both_undef_splat(
-; CHECK-NEXT: ret <2 x i8> undef
-;
- %x = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> undef, <2 x i8> undef)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_uadd_vector_op2_undef_splat() {
-; CHECK-LABEL: @test_uadd_vector_op2_undef_splat(
-; CHECK-NEXT: ret <2 x i8> splat (i8 -1)
-;
- %x = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> <i8 10, i8 20>, <2 x i8> undef)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_sadd_vector_op1_undef_splat() {
-; CHECK-LABEL: @test_sadd_vector_op1_undef_splat(
-; CHECK-NEXT: ret <2 x i8> splat (i8 -1)
-;
- %x = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> undef, <2 x i8> <i8 10, i8 20>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_usub_vector_op2_undef_splat() {
-; CHECK-LABEL: @test_usub_vector_op2_undef_splat(
-; CHECK-NEXT: ret <2 x i8> zeroinitializer
-;
- %x = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> <i8 10, i8 20>, <2 x i8> undef)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_ssub_vector_op1_undef_splat() {
-; CHECK-LABEL: @test_ssub_vector_op1_undef_splat(
-; CHECK-NEXT: ret <2 x i8> zeroinitializer
-;
- %x = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> undef, <2 x i8> <i8 10, i8 20>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_uadd_vector_op2_undef_mix1() {
-; CHECK-LABEL: @test_uadd_vector_op2_undef_mix1(
-; CHECK-NEXT: ret <2 x i8> <i8 30, i8 undef>
-;
- %x = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> <i8 10, i8 undef>, <2 x i8> <i8 20, i8 undef>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_uadd_vector_op2_undef_mix2() {
-; CHECK-LABEL: @test_uadd_vector_op2_undef_mix2(
-; CHECK-NEXT: ret <2 x i8> splat (i8 -1)
-;
- %x = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> <i8 10, i8 undef>, <2 x i8> <i8 undef, i8 20>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_sadd_vector_op1_undef_mix1() {
-; CHECK-LABEL: @test_sadd_vector_op1_undef_mix1(
-; CHECK-NEXT: ret <2 x i8> <i8 undef, i8 30>
-;
- %x = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> <i8 undef, i8 10>, <2 x i8> <i8 undef, i8 20>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_sadd_vector_op1_undef_mix2() {
-; CHECK-LABEL: @test_sadd_vector_op1_undef_mix2(
-; CHECK-NEXT: ret <2 x i8> splat (i8 -1)
-;
- %x = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> <i8 undef, i8 10>, <2 x i8> <i8 20, i8 undef>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_usub_vector_op2_undef_mix1() {
-; CHECK-LABEL: @test_usub_vector_op2_undef_mix1(
-; CHECK-NEXT: ret <2 x i8> <i8 0, i8 undef>
-;
- %x = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> <i8 10, i8 undef>, <2 x i8> <i8 20, i8 undef>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_usub_vector_op2_undef_mix2() {
-; CHECK-LABEL: @test_usub_vector_op2_undef_mix2(
-; CHECK-NEXT: ret <2 x i8> zeroinitializer
-;
- %x = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> <i8 10, i8 undef>, <2 x i8> <i8 undef, i8 20>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_ssub_vector_op1_undef_mix1() {
-; CHECK-LABEL: @test_ssub_vector_op1_undef_mix1(
-; CHECK-NEXT: ret <2 x i8> <i8 undef, i8 -10>
-;
- %x = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> <i8 undef, i8 10>, <2 x i8> <i8 undef, i8 20>)
- ret <2 x i8> %x
-}
-
-define <2 x i8> @test_ssub_vector_op1_undef_mix2() {
-; CHECK-LABEL: @test_ssub_vector_op1_undef_mix2(
-; CHECK-NEXT: ret <2 x i8> zeroinitializer
-;
- %x = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> <i8 undef, i8 10>, <2 x i8> <i8 20, i8 undef>)
- ret <2 x i8> %x
-}
-
; Tests for poison handling
define i8 @test_uadd_scalar_both_poison() {
More information about the llvm-commits
mailing list