[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