From c1c32e28abcf87aa2e93bf664a01567d8ad000d4 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 3 Jul 2025 15:01:27 +0200 Subject: [PATCH 1/4] 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 Date: Thu, 3 Jul 2025 14:56:30 +0200 Subject: [PATCH 2/4] 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(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(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(Operands[0]) || isa(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(Operands[0]) || isa(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(Operands[0]) || isa(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(Operands[0]) || isa(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(Operands[0]) || isa(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 Operands, const TargetLibraryInfo *TLI, const CallBase *Call) { + if (IntrinsicID != Intrinsic::not_intrinsic && + any_of(Operands, [](Constant *Op) { return isa(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(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(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(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 Date: Thu, 3 Jul 2025 15:10:47 +0200 Subject: [PATCH 3/4] 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}, From 95ab15cdcc181160926cd5420715921e8b9cba3a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 3 Jul 2025 15:23:28 +0200 Subject: [PATCH 4/4] Use IsaPred --- llvm/lib/Analysis/ConstantFolding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index 9df56d13d0d6c..af955f202ac28 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -3637,7 +3637,7 @@ static Constant *ConstantFoldScalarCall(StringRef Name, const TargetLibraryInfo *TLI, const CallBase *Call) { if (IntrinsicID != Intrinsic::not_intrinsic && - any_of(Operands, [](Constant *Op) { return isa(Op); }) && + any_of(Operands, IsaPred) && intrinsicPropagatesPoison(IntrinsicID)) return PoisonValue::get(Ty);