[llvm] bcb4e7d - [InstCombine] Fix 0.0 / x -> 0 folds when the divisor may be zero (#202482)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jul 18 14:10:07 PDT 2026
Author: Justin Lebar
Date: 2026-07-18T21:10:01Z
New Revision: bcb4e7d061ac22bce4ac65f5158a4746fb0edc0f
URL: https://github.com/llvm/llvm-project/commit/bcb4e7d061ac22bce4ac65f5158a4746fb0edc0f
DIFF: https://github.com/llvm/llvm-project/commit/bcb4e7d061ac22bce4ac65f5158a4746fb0edc0f.diff
LOG: [InstCombine] Fix 0.0 / x -> 0 folds when the divisor may be zero (#202482)
SimplifyDemandedUseFPClass folded 0/x to a signed zero, guarded only on
the divisor being non-NaN. But when x==0, the result is is NaN, not 0!
Require the divisor to be never (logical) zero, unless a NaN result
isn't demanded.
Added:
Modified:
llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 26f5fb01a942c..0a7c0375106f2 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2796,12 +2796,22 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
SimplifyDemandedFPClass(I, 1, RHSDemandedMask, KnownRHS, SQ, Depth + 1))
return I;
+ bool ResultNotNan = (DemandedMask & fcNan) == fcNone;
+ bool ResultNotInf = (DemandedMask & fcInf) == fcNone;
+
+ // Replacing 0/x with a zero is only valid when the divisor can't be
+ // (logical) zero, since 0/0 is NaN -- unless NaN results aren't demanded. A
+ // subnormal divisor can flush to zero under a flushing denormal mode.
+ bool CanIgnoreZeroByZeroNan =
+ ResultNotNan || KnownRHS.isKnownNeverLogicalZero(Mode);
+
// nsz [+-]0 / x -> 0
if (FMF.noSignedZeros() && KnownLHS.isKnownAlways(fcZero) &&
- KnownRHS.isKnownNeverNaN())
+ KnownRHS.isKnownNeverNaN() && CanIgnoreZeroByZeroNan)
return ConstantFP::getZero(VTy);
- if (KnownLHS.isKnownAlways(fcPosZero) && KnownRHS.isKnownNeverNaN()) {
+ if (KnownLHS.isKnownAlways(fcPosZero) && KnownRHS.isKnownNeverNaN() &&
+ CanIgnoreZeroByZeroNan) {
IRBuilderBase::InsertPointGuard Guard(Builder);
Builder.SetInsertPoint(I);
@@ -2812,9 +2822,6 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
return Copysign;
}
- bool ResultNotNan = (DemandedMask & fcNan) == fcNone;
- bool ResultNotInf = (DemandedMask & fcInf) == fcNone;
-
if (!ResultNotInf &&
((ResultNotNan || (KnownLHS.isKnownNeverNaN() &&
KnownLHS.isKnownNeverLogicalZero(Mode))) &&
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
index 2a3487a1b9ca7..957a8ec082e8c 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
@@ -1997,6 +1997,111 @@ define nofpclass(snan) half @ret__unknown__fdiv_nsz__zero_or_nan(half %unknown,
ret half %div
}
+; The return is allowed to be nan (only snan is excluded), so 0/0 = nan must
+; be preserved. The divisor may be zero, so nsz +0/x -> 0 must NOT fire.
+define nofpclass(snan) half @nsz_pzero_fdiv_maybe_zero(half nofpclass(nan) %x) {
+; CHECK-LABEL: define nofpclass(snan) half @nsz_pzero_fdiv_maybe_zero(
+; CHECK-SAME: half nofpclass(nan) [[X:%.*]]) {
+; CHECK-NEXT: [[PZERO:%.*]] = call half @returns_pzero()
+; CHECK-NEXT: [[DIV:%.*]] = fdiv nsz half 0.000000e+00, [[X]]
+; CHECK-NEXT: ret half [[DIV]]
+;
+ %pzero = call half @returns_pzero()
+ %div = fdiv nsz half %pzero, %x
+ ret half %div
+}
+
+; Same, for the +0/x -> copysign(0, x) fold.
+define nofpclass(snan) half @pzero_fdiv_maybe_zero(half nofpclass(nan) %x) {
+; CHECK-LABEL: define nofpclass(snan) half @pzero_fdiv_maybe_zero(
+; CHECK-SAME: half nofpclass(nan) [[X:%.*]]) {
+; CHECK-NEXT: [[PZERO:%.*]] = call half @returns_pzero()
+; CHECK-NEXT: [[DIV:%.*]] = fdiv half 0.000000e+00, [[X]]
+; CHECK-NEXT: ret half [[DIV]]
+;
+ %pzero = call half @returns_pzero()
+ %div = fdiv half %pzero, %x
+ ret half %div
+}
+
+; nnan on the fdiv makes 0/0 = nan a poison don't-care, so nsz +0/x -> 0 fires
+; even though the divisor may be zero.
+define nofpclass(snan) half @nsz_nnan_pzero_fdiv_maybe_zero(half nofpclass(nan) %x) {
+; CHECK-LABEL: define nofpclass(snan) half @nsz_nnan_pzero_fdiv_maybe_zero(
+; CHECK-SAME: half nofpclass(nan) [[X:%.*]]) {
+; CHECK-NEXT: [[PZERO:%.*]] = call half @returns_pzero()
+; CHECK-NEXT: ret half 0.000000e+00
+;
+ %pzero = call half @returns_pzero()
+ %div = fdiv nnan nsz half %pzero, %x
+ ret half %div
+}
+
+; Same, for the +0/x -> copysign(0, x) fold.
+define nofpclass(snan) half @nnan_pzero_fdiv_maybe_zero(half nofpclass(nan) %x) {
+; CHECK-LABEL: define nofpclass(snan) half @nnan_pzero_fdiv_maybe_zero(
+; CHECK-SAME: half nofpclass(nan) [[X:%.*]]) {
+; CHECK-NEXT: [[PZERO:%.*]] = call half @returns_pzero()
+; CHECK-NEXT: [[DIV:%.*]] = call nnan half @llvm.copysign.f16(half 0.000000e+00, half [[X]])
+; CHECK-NEXT: ret half [[DIV]]
+;
+ %pzero = call half @returns_pzero()
+ %div = fdiv nnan half %pzero, %x
+ ret half %div
+}
+
+; Divisor never zero/nan: nsz +0/x -> 0 fires.
+define nofpclass(snan) half @nsz_pzero_fdiv_never_zero(half nofpclass(nan zero) %x) {
+; CHECK-LABEL: define nofpclass(snan) half @nsz_pzero_fdiv_never_zero(
+; CHECK-SAME: half nofpclass(nan zero) [[X:%.*]]) {
+; CHECK-NEXT: [[PZERO:%.*]] = call half @returns_pzero()
+; CHECK-NEXT: ret half 0.000000e+00
+;
+ %pzero = call half @returns_pzero()
+ %div = fdiv nsz half %pzero, %x
+ ret half %div
+}
+
+; Divisor never zero/nan: +0/x -> copysign(0, x) fires.
+define nofpclass(snan) half @pzero_fdiv_never_zero(half nofpclass(nan zero) %x) {
+; CHECK-LABEL: define nofpclass(snan) half @pzero_fdiv_never_zero(
+; CHECK-SAME: half nofpclass(nan zero) [[X:%.*]]) {
+; CHECK-NEXT: [[PZERO:%.*]] = call half @returns_pzero()
+; CHECK-NEXT: [[DIV:%.*]] = call half @llvm.copysign.f16(half 0.000000e+00, half [[X]])
+; CHECK-NEXT: ret half [[DIV]]
+;
+ %pzero = call half @returns_pzero()
+ %div = fdiv half %pzero, %x
+ ret half %div
+}
+
+; Divisor never zero but may be subnormal; under preservesign a subnormal flushes
+; to zero, so 0/0 = nan and the fold must NOT fire.
+define nofpclass(snan) half @pzero_fdiv_subnormal_preservesign(half nofpclass(nan zero inf) %x) #0 {
+; CHECK-LABEL: define nofpclass(snan) half @pzero_fdiv_subnormal_preservesign(
+; CHECK-SAME: half nofpclass(nan inf zero) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[PZERO:%.*]] = call half @returns_pzero()
+; CHECK-NEXT: [[DIV:%.*]] = fdiv ninf half 0.000000e+00, [[X]]
+; CHECK-NEXT: ret half [[DIV]]
+;
+ %pzero = call half @returns_pzero()
+ %div = fdiv half %pzero, %x
+ ret half %div
+}
+
+; Same divisor under the default ieee mode (no flush): fold fires.
+define nofpclass(snan) half @pzero_fdiv_subnormal_ieee(half nofpclass(nan zero inf) %x) {
+; CHECK-LABEL: define nofpclass(snan) half @pzero_fdiv_subnormal_ieee(
+; CHECK-SAME: half nofpclass(nan inf zero) [[X:%.*]]) {
+; CHECK-NEXT: [[PZERO:%.*]] = call half @returns_pzero()
+; CHECK-NEXT: [[DIV:%.*]] = call half @llvm.copysign.f16(half 0.000000e+00, half [[X]])
+; CHECK-NEXT: ret half [[DIV]]
+;
+ %pzero = call half @returns_pzero()
+ %div = fdiv half %pzero, %x
+ ret half %div
+}
+
define nofpclass(snan) half @ret__unknown__fdiv__pzero_or_nan(half %unknown, half nofpclass(inf sub norm nzero) %pzero.or.nan) {
; CHECK-LABEL: define nofpclass(snan) half @ret__unknown__fdiv__pzero_or_nan(
; CHECK-SAME: half [[UNKNOWN:%.*]], half nofpclass(inf nzero sub norm) [[PZERO_OR_NAN:%.*]]) {
More information about the llvm-commits
mailing list