[llvm] [Support][KnownFPClass] Refine fdiv and fdiv_self class propagation + some minor fixes for propagateXorSign (PR #215014)

Max Graey via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 8 13:55:13 PDT 2026


https://github.com/MaxGraey updated https://github.com/llvm/llvm-project/pull/215014

>From 874bfdffd1837238f0a9e8cc3ab2db72df0921f7 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 8 Aug 2026 22:30:30 +0300
Subject: [PATCH 1/4] Refine fdiv and fdiv_self class propagation + fix
 propagateXorSign under positive-zero input mode

---
 llvm/include/llvm/Support/KnownFPClass.h      |  26 +-
 llvm/lib/Support/KnownFPClass.cpp             |  49 +++-
 .../Transforms/Attributor/nofpclass-fdiv.ll   | 246 ++++++++++++++++--
 .../simplify-demanded-fpclass-fdiv.ll         |  29 +--
 4 files changed, 293 insertions(+), 57 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 956141b9449a2..00e031a54c02b 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -100,6 +100,10 @@ struct KnownFPClass {
   /// zero.
   LLVM_ABI bool isKnownNeverLogicalPosZero(DenormalMode Mode) const;
 
+  /// Return true if it's known this can never be interpreted as a finite
+  /// nonzero value, accounting for denormals the mode reads as zero.
+  LLVM_ABI bool isKnownNeverLogicalFiniteNonZero(DenormalMode Mode) const;
+
   static constexpr FPClassTest OrderedLessThanZeroMask =
       fcNegSubnormal | fcNegNormal | fcNegInf;
   static constexpr FPClassTest OrderedGreaterThanZeroMask =
@@ -395,13 +399,25 @@ struct KnownFPClass {
   // Propagate knowledge for operations whose result sign is the xor of the
   // operand signs, such as multiply and divide. This only rules out possible
   // non-NaN sign classes. NaNs do not have a constrained sign class here.
-  void propagateXorSign(const KnownFPClass &LHS, const KnownFPClass &RHS) {
-    if ((LHS.isKnownNever(fcNegative) && RHS.isKnownNever(fcNegative)) ||
-        (LHS.isKnownNever(fcPositive) && RHS.isKnownNever(fcPositive)))
+  //
+  // A negative subnormal is read as +0.0 under a positive-zero input mode, so
+  // it counts towards the positive side and not the negative one.
+  void propagateXorSign(const KnownFPClass &LHS, const KnownFPClass &RHS,
+                        DenormalMode Mode) {
+    bool MustFlushNegSub = Mode.Input == DenormalMode::PositiveZero;
+    bool MayFlushNegSub = Mode.inputsMayBePositiveZero();
+
+    FPClassTest NegMask =
+        MustFlushNegSub ? fcNegative & ~fcNegSubnormal : fcNegative;
+    FPClassTest PosMask =
+        MayFlushNegSub ? fcPositive | fcNegSubnormal : fcPositive;
+
+    if ((LHS.isKnownNever(NegMask) && RHS.isKnownNever(NegMask)) ||
+        (LHS.isKnownNever(PosMask) && RHS.isKnownNever(PosMask)))
       knownNot(fcNegative);
 
-    if ((LHS.isKnownNever(fcPositive) && RHS.isKnownNever(fcNegative)) ||
-        (LHS.isKnownNever(fcNegative) && RHS.isKnownNever(fcPositive)))
+    if ((LHS.isKnownNever(PosMask) && RHS.isKnownNever(NegMask)) ||
+        (LHS.isKnownNever(NegMask) && RHS.isKnownNever(PosMask)))
       knownNot(fcPositive);
   }
 
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index eccd83451a05f..1cd08315ff2fd 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -65,6 +65,11 @@ bool KnownFPClass::isKnownNeverLogicalPosZero(DenormalMode Mode) const {
   llvm_unreachable("covered switch over denormal mode");
 }
 
+bool KnownFPClass::isKnownNeverLogicalFiniteNonZero(DenormalMode Mode) const {
+  return isKnownNever(fcNormal) &&
+         (isKnownNeverSubnormal() || Mode.inputsAreZero());
+}
+
 void KnownFPClass::propagateDenormal(const KnownFPClass &Src,
                                      DenormalMode Mode) {
   KnownFPClasses = Src.KnownFPClasses;
@@ -362,7 +367,7 @@ KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS,
 
   // +X * +Y or -X * -Y => +Q
   // +X * -Y or -X * +Y => -Q
-  Known.propagateXorSign(KnownLHS, KnownRHS);
+  Known.propagateXorSign(KnownLHS, KnownRHS, Mode);
 
   // Inf * Y => Inf or NaN
   if (KnownLHS.isKnownAlways(fcInf | fcNan) ||
@@ -432,14 +437,35 @@ KnownFPClass KnownFPClass::fdiv(const KnownFPClass &KnownLHS,
   //  X / -0.0 => -Inf (or NaN)
   // +X / +Y or -X / -Y => +Q
   // +X / -Y or -X / +Y => -Q
-  Known.propagateXorSign(KnownLHS, KnownRHS);
+  Known.propagateXorSign(KnownLHS, KnownRHS, Mode);
 
-  // 0 / X => 0 or NaN
-  if (KnownLHS.isKnownAlways(fcZero))
+  // {0, Inf, NaN} / Y => {0, Inf, NaN}
+  if (KnownLHS.isKnownNeverLogicalFiniteNonZero(Mode))
+    Known.knownNot(fcNormal | fcSubnormal);
+
+  // X / 0   => Inf
+  // X / Sub => Normal or Inf
+  // X / Inf => 0
+  if (KnownRHS.isKnownNever(fcNormal) || Mode.outputsAreZero())
+    Known.knownNot(fcSubnormal);
+
+  // 0 / Y      => 0
+  // X / Inf    => 0
+  // X / Normal => 0 on underflow
+  if (KnownLHS.isKnownNeverLogicalZero(Mode) &&
+      KnownRHS.isKnownNever(fcNormal | fcInf))
+    Known.knownNot(fcZero);
+
+  // {0, NaN} / Y   => 0 or NaN
+  // X / {Inf, NaN} => 0 or NaN
+  if (KnownLHS.isKnownAlways(fcZero | fcNan) ||
+      KnownRHS.isKnownAlways(fcInf | fcNan))
     Known.knownNot(fcSubnormal | fcNormal | fcInf);
 
-  // X / 0 => NaN or Inf
-  if (KnownRHS.isKnownAlways(fcZero))
+  // {Inf, NaN} / Y => Inf or NaN
+  // X / {0, NaN}   => Inf or NaN
+  if (KnownLHS.isKnownAlways(fcInf | fcNan) ||
+      KnownRHS.isKnownAlways(fcZero | fcNan))
     Known.knownNot(fcFinite);
 
   return Known;
@@ -447,16 +473,23 @@ KnownFPClass KnownFPClass::fdiv(const KnownFPClass &KnownLHS,
 
 KnownFPClass KnownFPClass::fdiv_self(const KnownFPClass &KnownSrc,
                                      DenormalMode Mode) {
-  // X / X is always exactly 1.0 or a NaN.
+  // X / X is always exactly +1.0 or NaN.
   KnownFPClass Known(fcNan | fcPosNormal);
 
+  // X / X => +1.0 only for finite nonzero X
+  if (KnownSrc.isKnownNeverLogicalFiniteNonZero(Mode))
+    Known.knownNot(fcPosNormal);
+
+  // X / X => NaN only for 0, Inf and NaN
   if (KnownSrc.isKnownNeverInfOrNaN() && KnownSrc.isKnownNeverLogicalZero(Mode))
     Known.knownNot(fcNan);
-  else if (KnownSrc.isKnownNever(fcSNan))
+
+  if (KnownSrc.isKnownNever(fcSNan))
     Known.knownNot(fcSNan);
 
   return Known;
 }
+
 KnownFPClass KnownFPClass::frem_self(const KnownFPClass &KnownSrc,
                                      DenormalMode Mode) {
   // X % X is always exactly [+-]0.0 or a NaN.
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
index fef871a1e85f7..432a12bb1e86b 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
@@ -589,7 +589,7 @@ define float @ret_fdiv_f32_known_zero_or_nan_lhs(float nofpclass(inf norm sub) %
 }
 
 define float @ret_fdiv_f32_known_zero_or_nan_rhs(float %arg0, float nofpclass(inf norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_zero_or_nan_rhs
+; CHECK-LABEL: define nofpclass(zero sub norm) float @ret_fdiv_f32_known_zero_or_nan_rhs
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(inf sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -629,7 +629,7 @@ define float @ret_fdiv_f32_known_pzero_or_nan_lhs(float nofpclass(inf norm sub n
 }
 
 define float @ret_fdiv_f32_known_pzero_or_nan_rhs(float %arg0, float nofpclass(inf norm sub nzero) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_pzero_or_nan_rhs
+; CHECK-LABEL: define nofpclass(zero sub norm) float @ret_fdiv_f32_known_pzero_or_nan_rhs
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(inf nzero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -669,7 +669,7 @@ define float @ret_fdiv_f32_known_nzero_or_nan_lhs(float nofpclass(inf norm sub p
 }
 
 define float @ret_fdiv_f32_known_nzero_or_nan_rhs(float %arg0, float nofpclass(inf norm sub pzero) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_nzero_or_nan_rhs
+; CHECK-LABEL: define nofpclass(zero sub norm) float @ret_fdiv_f32_known_nzero_or_nan_rhs
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(inf pzero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -709,7 +709,7 @@ define float @ret_fdiv_f32_known_inf_or_nan_lhs(float nofpclass(zero norm sub) %
 }
 
 define float @ret_fdiv_f32_known_inf_or_nan_rhs(float %arg0, float nofpclass(zero norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_inf_or_nan_rhs
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_fdiv_f32_known_inf_or_nan_rhs
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -729,7 +729,7 @@ define float @ret_fdiv_f32_known_inf_lhs(float nofpclass(nan zero norm sub) %arg
 }
 
 define float @ret_fdiv_f32_known_inf_rhs(float %arg0, float nofpclass(nan zero norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_inf_rhs
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_fdiv_f32_known_inf_rhs
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(nan zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -749,7 +749,7 @@ define float @ret_fdiv_f32_known_pinf_or_nan_lhs(float nofpclass(ninf zero norm
 }
 
 define float @ret_fdiv_f32_known_pinf_or_nan_rhs(float %arg0, float nofpclass(ninf zero norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_pinf_or_nan_rhs
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_fdiv_f32_known_pinf_or_nan_rhs
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(ninf zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -769,7 +769,7 @@ define float @ret_fdiv_f32_known_ninf_or_nan_lhs(float nofpclass(pinf zero norm
 }
 
 define float @ret_fdiv_f32_known_ninf_or_nan_rhs(float %arg0, float nofpclass(pinf zero norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_ninf_or_nan_rhs
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_fdiv_f32_known_ninf_or_nan_rhs
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(pinf zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -802,7 +802,7 @@ define float @ret_known_inf_or_nan_fdiv_known_zero(float nofpclass(norm sub zero
 
 ; -> nan
 define float @ret_known_inf_fdiv_known_zero_or_nan(float nofpclass(nan norm sub zero) %arg0, float nofpclass(inf norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_known_inf_fdiv_known_zero_or_nan
+; CHECK-LABEL: define nofpclass(zero sub norm) float @ret_known_inf_fdiv_known_zero_or_nan
 ; CHECK-SAME: (float nofpclass(nan zero sub norm) [[ARG0:%.*]], float nofpclass(inf sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -846,7 +846,7 @@ define float @ret_known_zero_fdiv_known_inf(float nofpclass(nan inf norm sub) %a
 
 ; -> zero
 define float @ret_known_zero_fdiv_known_inf_or_nan(float nofpclass(nan inf norm sub) %arg0, float nofpclass(norm sub zero) %arg1) {
-; CHECK-LABEL: define float @ret_known_zero_fdiv_known_inf_or_nan
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_known_zero_fdiv_known_inf_or_nan
 ; CHECK-SAME: (float nofpclass(nan inf sub norm) [[ARG0:%.*]], float nofpclass(zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -857,7 +857,7 @@ define float @ret_known_zero_fdiv_known_inf_or_nan(float nofpclass(nan inf norm
 
 ; -> zero or nan
 define float @ret_known_zero_or_nan_fdiv_known_inf(float nofpclass(inf norm sub) %arg0, float nofpclass(nan norm sub zero) %arg1) {
-; CHECK-LABEL: define float @ret_known_zero_or_nan_fdiv_known_inf
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_known_zero_or_nan_fdiv_known_inf
 ; CHECK-SAME: (float nofpclass(inf sub norm) [[ARG0:%.*]], float nofpclass(nan zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -870,7 +870,7 @@ define float @ret_known_zero_or_nan_fdiv_known_inf(float nofpclass(inf norm sub)
 define float @ret_fdiv_lhs_known_positive_or_nan(float %lhs, float %rhs) {
 ; CHECK-LABEL: define float @ret_fdiv_lhs_known_positive_or_nan
 ; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6:[0-9]+]]
+; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8:[0-9]+]]
 ; CHECK-NEXT:    [[MUL:%.*]] = fdiv float [[LHS_FABS]], [[RHS]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
@@ -883,7 +883,7 @@ define float @ret_fdiv_lhs_known_positive_or_nan(float %lhs, float %rhs) {
 define float @ret_fdiv_rhs_known_positive_or_nan(float %lhs, float %rhs) {
 ; CHECK-LABEL: define float @ret_fdiv_rhs_known_positive_or_nan
 ; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
 ; CHECK-NEXT:    [[MUL:%.*]] = fdiv float [[LHS]], [[RHS_FABS]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
@@ -896,8 +896,8 @@ define float @ret_fdiv_rhs_known_positive_or_nan(float %lhs, float %rhs) {
 define float @ret_fdiv_both_signs_positive_or_nan(float %lhs, float %rhs) {
 ; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_fdiv_both_signs_positive_or_nan
 ; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6]]
-; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
 ; CHECK-NEXT:    [[MUL:%.*]] = fdiv float [[LHS_FABS]], [[RHS_FABS]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
@@ -911,8 +911,8 @@ define float @ret_fdiv_both_signs_positive_or_nan(float %lhs, float %rhs) {
 define float @ret_fdiv_both_signs_negative_or_nan(float %lhs, float %rhs) {
 ; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_fdiv_both_signs_negative_or_nan
 ; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6]]
-; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
 ; CHECK-NEXT:    [[LHS_NEG_FABS:%.*]] = fneg float [[LHS_FABS]]
 ; CHECK-NEXT:    [[RHS_NEG_FABS:%.*]] = fneg float [[RHS_FABS]]
 ; CHECK-NEXT:    [[MUL:%.*]] = fdiv float [[LHS_NEG_FABS]], [[RHS_NEG_FABS]]
@@ -930,8 +930,8 @@ define float @ret_fdiv_both_signs_negative_or_nan(float %lhs, float %rhs) {
 define float @ret_fdiv_lhs_negative_rhs_positive(float %lhs, float %rhs) {
 ; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_lhs_negative_rhs_positive
 ; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6]]
-; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
 ; CHECK-NEXT:    [[LHS_NEG_FABS:%.*]] = fneg float [[LHS_FABS]]
 ; CHECK-NEXT:    [[MUL:%.*]] = fdiv float [[LHS_NEG_FABS]], [[RHS_FABS]]
 ; CHECK-NEXT:    ret float [[MUL]]
@@ -947,8 +947,8 @@ define float @ret_fdiv_lhs_negative_rhs_positive(float %lhs, float %rhs) {
 define float @ret_fdiv_rhs_negative_lhs_positive(float %lhs, float %rhs) {
 ; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_rhs_negative_lhs_positive
 ; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6]]
-; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT:    [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT:    [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
 ; CHECK-NEXT:    [[RHS_NEG_FABS:%.*]] = fneg float [[RHS_FABS]]
 ; CHECK-NEXT:    [[MUL:%.*]] = fdiv float [[LHS_FABS]], [[RHS_NEG_FABS]]
 ; CHECK-NEXT:    ret float [[MUL]]
@@ -962,7 +962,7 @@ define float @ret_fdiv_rhs_negative_lhs_positive(float %lhs, float %rhs) {
 
 ; Could be inf of either sign
 define float @ret_known_inf_fdiv_known_inf(float nofpclass(norm sub zero nan) %arg0, float nofpclass(norm sub zero nan) %arg1) {
-; CHECK-LABEL: define float @ret_known_inf_fdiv_known_inf
+; CHECK-LABEL: define nofpclass(inf zero sub norm) float @ret_known_inf_fdiv_known_inf
 ; CHECK-SAME: (float nofpclass(nan zero sub norm) [[ARG0:%.*]], float nofpclass(nan zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -973,7 +973,7 @@ define float @ret_known_inf_fdiv_known_inf(float nofpclass(norm sub zero nan) %a
 
 ; Could be inf of either sign, or nan
 define float @ret_known_inf_fdiv_known_inf_or_nan(float nofpclass(norm sub zero nan) %arg0, float nofpclass(norm sub zero) %arg1) {
-; CHECK-LABEL: define float @ret_known_inf_fdiv_known_inf_or_nan
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_known_inf_fdiv_known_inf_or_nan
 ; CHECK-SAME: (float nofpclass(nan zero sub norm) [[ARG0:%.*]], float nofpclass(zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -984,7 +984,7 @@ define float @ret_known_inf_fdiv_known_inf_or_nan(float nofpclass(norm sub zero
 
 ; Could be inf of either sign, or nan
 define float @ret_known_inf_or_nan_fdiv_known_inf(float nofpclass(norm sub zero) %arg0, float nofpclass(norm sub zero nan) %arg1) {
-; CHECK-LABEL: define float @ret_known_inf_or_nan_fdiv_known_inf
+; CHECK-LABEL: define nofpclass(inf zero sub norm) float @ret_known_inf_or_nan_fdiv_known_inf
 ; CHECK-SAME: (float nofpclass(zero sub norm) [[ARG0:%.*]], float nofpclass(nan zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -1006,7 +1006,7 @@ define float @ret_known_zero_fdiv_known_zero(float nofpclass(inf norm sub nan) %
 
 ; Could be zero of either sign, or nan
 define float @ret_known_zero_fdiv_known_zero_or_nan(float nofpclass(inf norm sub nan) %arg0, float nofpclass(inf norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_known_zero_fdiv_known_zero_or_nan
+; CHECK-LABEL: define nofpclass(zero sub norm) float @ret_known_zero_fdiv_known_zero_or_nan
 ; CHECK-SAME: (float nofpclass(nan inf sub norm) [[ARG0:%.*]], float nofpclass(inf sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -1017,7 +1017,7 @@ define float @ret_known_zero_fdiv_known_zero_or_nan(float nofpclass(inf norm sub
 
 ; Could be zero of either sign, or nan
 define float @ret_known_zero_or_nan_fdiv_known_zero(float nofpclass(inf norm sub) %arg0, float nofpclass(inf norm sub nan) %arg1) {
-; CHECK-LABEL: define nofpclass(zero sub norm) float @ret_known_zero_or_nan_fdiv_known_zero
+; CHECK-LABEL: define nofpclass(inf zero sub norm) float @ret_known_zero_or_nan_fdiv_known_zero
 ; CHECK-SAME: (float nofpclass(inf sub norm) [[ARG0:%.*]], float nofpclass(nan inf sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -1037,7 +1037,7 @@ define float @ret_known_inf_or_nan_fdiv_unknown(float nofpclass(norm sub zero) %
 }
 
 define float @ret_unknown_fdiv_known_inf_or_nan(float %arg0, float nofpclass(norm sub zero) %arg1) {
-; CHECK-LABEL: define float @ret_unknown_fdiv_known_inf_or_nan
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_unknown_fdiv_known_inf_or_nan
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -1047,7 +1047,7 @@ define float @ret_unknown_fdiv_known_inf_or_nan(float %arg0, float nofpclass(nor
 }
 
 define float @ret_known_inf_or_nan_fdiv_known_inf_or_nan(float nofpclass(norm sub zero) %arg0, float nofpclass(norm sub zero) %arg1) {
-; CHECK-LABEL: define float @ret_known_inf_or_nan_fdiv_known_inf_or_nan
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_known_inf_or_nan_fdiv_known_inf_or_nan
 ; CHECK-SAME: (float nofpclass(zero sub norm) [[ARG0:%.*]], float nofpclass(zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -1056,9 +1056,201 @@ define float @ret_known_inf_or_nan_fdiv_known_inf_or_nan(float nofpclass(norm su
   ret float %fdiv
 }
 
+define float @ret_fdiv_ieee_all_nonorm(float %arg0, float nofpclass(norm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(sub) float @ret_fdiv_ieee_all_nonorm
+; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(norm) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_ftz_all_all(float %arg0, float %arg1) #4 {
+; CHECK-LABEL: define nofpclass(sub) float @ret_fdiv_ftz_all_all
+; CHECK-SAME: (float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR5:[0-9]+]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_dynout_all_all(float %arg0, float %arg1) #5 {
+; CHECK-LABEL: define float @ret_fdiv_dynout_all_all
+; CHECK-SAME: (float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR6:[0-9]+]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_ieee_nosub_nonorm_nonan(float nofpclass(sub norm) %arg0, float nofpclass(nan) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_ieee_nosub_nonorm_nonan
+; CHECK-SAME: (float nofpclass(sub norm) [[ARG0:%.*]], float nofpclass(nan) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_ieee_nonorm_nonan(float nofpclass(norm) %arg0, float nofpclass(nan) %arg1) #0 {
+; CHECK-LABEL: define float @ret_fdiv_ieee_nonorm_nonan
+; CHECK-SAME: (float nofpclass(norm) [[ARG0:%.*]], float nofpclass(nan) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_daz_nonorm_nonan(float nofpclass(norm) %arg0, float nofpclass(nan) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_daz_nonorm_nonan
+; CHECK-SAME: (float nofpclass(norm) [[ARG0:%.*]], float nofpclass(nan) [[ARG1:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_ieee_nozero_nosub_nonan_noinf_nonorm(float nofpclass(zero sub) %arg0, float nofpclass(nan inf norm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(zero sub) float @ret_fdiv_ieee_nozero_nosub_nonan_noinf_nonorm
+; CHECK-SAME: (float nofpclass(zero sub) [[ARG0:%.*]], float nofpclass(nan inf norm) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_ieee_nozero_nonan_noinf_nonorm(float nofpclass(zero) %arg0, float nofpclass(nan inf norm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(zero sub) float @ret_fdiv_ieee_nozero_nonan_noinf_nonorm
+; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(nan inf norm) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_daz_nozero_nonan_noinf_nonorm(float nofpclass(zero) %arg0, float nofpclass(nan inf norm) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(sub) float @ret_fdiv_daz_nozero_nonan_noinf_nonorm
+; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(nan inf norm) [[ARG1:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_ieee_nozero_nosub_nonan_noinf(float nofpclass(zero sub) %arg0, float nofpclass(nan inf) %arg1) #0 {
+; CHECK-LABEL: define float @ret_fdiv_ieee_nozero_nosub_nonan_noinf
+; CHECK-SAME: (float nofpclass(zero sub) [[ARG0:%.*]], float nofpclass(nan inf) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_ieee_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_ieee_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_daz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_daz_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #2 {
+; CHECK-LABEL: define float @ret_fdiv_dapz_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_dynamic_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #3 {
+; CHECK-LABEL: define float @ret_fdiv_dynamic_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_dapz_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #2 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_dapz_no_pos_nonsub_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_same_operands_nosub_nonorm(float noundef nofpclass(sub norm) %arg) #0 {
+; CHECK-LABEL: define noundef nofpclass(inf zero sub norm) float @ret_fdiv_same_operands_nosub_nonorm
+; CHECK-SAME: (float noundef nofpclass(sub norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg, %arg
+  ret float %fdiv
+}
+
+define float @ret_fdiv_same_operands_nonorm(float noundef nofpclass(norm) %arg) #0 {
+; CHECK-LABEL: define noundef nofpclass(inf zero sub nnorm) float @ret_fdiv_same_operands_nonorm
+; CHECK-SAME: (float noundef nofpclass(norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg, %arg
+  ret float %fdiv
+}
+
+define float @ret_fdiv_same_operands_nonorm_daz(float noundef nofpclass(norm) %arg) #1 {
+; CHECK-LABEL: define noundef nofpclass(inf zero sub norm) float @ret_fdiv_same_operands_nonorm_daz
+; CHECK-SAME: (float noundef nofpclass(norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg, %arg
+  ret float %fdiv
+}
+
+define float @ret_fdiv_same_operands_nonorm_dynamic(float noundef nofpclass(norm) %arg) #3 {
+; CHECK-LABEL: define noundef nofpclass(inf zero sub nnorm) float @ret_fdiv_same_operands_nonorm_dynamic
+; CHECK-SAME: (float noundef nofpclass(norm) [[ARG:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg, %arg
+  ret float %fdiv
+}
+
 attributes #0 = { denormal_fpenv(ieee|ieee) }
 attributes #1 = { denormal_fpenv(ieee|preservesign) }
 attributes #2 = { denormal_fpenv(ieee|positivezero) }
 attributes #3 = { denormal_fpenv(ieee|dynamic) }
+attributes #4 = { denormal_fpenv(preservesign|ieee) }
+attributes #5 = { denormal_fpenv(dynamic|ieee) }
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; TUNIT: {{.*}}
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
index 957a8ec082e8c..af300be6ac806 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fdiv.ll
@@ -665,7 +665,7 @@ define nofpclass(ninf nnorm nsub nzero nan) half @ret_only_positive_results_know
 define nofpclass(nsub) half @ret__known_zero_or_nan__fdiv__not_inf(half nofpclass(inf sub norm) %zero.or.nan, half nofpclass(inf) %not.inf) {
 ; CHECK-LABEL: define nofpclass(nsub) half @ret__known_zero_or_nan__fdiv__not_inf(
 ; CHECK-SAME: half nofpclass(inf sub norm) [[ZERO_OR_NAN:%.*]], half nofpclass(inf) [[NOT_INF:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv half [[ZERO_OR_NAN]], [[NOT_INF]]
+; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf half [[ZERO_OR_NAN]], [[NOT_INF]]
 ; CHECK-NEXT:    ret half [[DIV]]
 ;
   %div = fdiv half %zero.or.nan, %not.inf
@@ -676,7 +676,7 @@ define nofpclass(nsub) half @ret__known_zero_or_nan__fdiv__not_inf(half nofpclas
 define nofpclass(nsub) half @ret__known_zero_or_nan__fdiv__not_inf_or_nan(half nofpclass(inf sub norm) %zero.or.nan, half nofpclass(inf nan) %not.inf.or.nan) {
 ; CHECK-LABEL: define nofpclass(nsub) half @ret__known_zero_or_nan__fdiv__not_inf_or_nan(
 ; CHECK-SAME: half nofpclass(inf sub norm) [[ZERO_OR_NAN:%.*]], half nofpclass(nan inf) [[NOT_INF_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv half [[ZERO_OR_NAN]], [[NOT_INF_OR_NAN]]
+; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf half [[ZERO_OR_NAN]], [[NOT_INF_OR_NAN]]
 ; CHECK-NEXT:    ret half [[DIV]]
 ;
   %div = fdiv half %zero.or.nan, %not.inf.or.nan
@@ -698,7 +698,7 @@ define nofpclass(nsub) half @ret__not_inf_or_nan__fdiv__known_zero_or_nan(half n
 define nofpclass(nsub) half @ret__known_pzero_or_nan__fdiv__not_inf_or_nan(half nofpclass(inf sub norm nzero) %pzero.or.nan, half nofpclass(inf nan) %not.inf.or.nan) {
 ; CHECK-LABEL: define nofpclass(nsub) half @ret__known_pzero_or_nan__fdiv__not_inf_or_nan(
 ; CHECK-SAME: half nofpclass(inf nzero sub norm) [[PZERO_OR_NAN:%.*]], half nofpclass(nan inf) [[NOT_INF_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv half [[PZERO_OR_NAN]], [[NOT_INF_OR_NAN]]
+; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf half [[PZERO_OR_NAN]], [[NOT_INF_OR_NAN]]
 ; CHECK-NEXT:    ret half [[DIV]]
 ;
   %div = fdiv half %pzero.or.nan, %not.inf.or.nan
@@ -1217,7 +1217,7 @@ define nofpclass(ninf) half @ret_ninf__fdiv_nnan_unknown__zero(half %unknown) {
 define nofpclass(snan) half @known__nzero_or_nan__fdiv__not_inf_or_nan(half nofpclass(inf sub norm pzero) %nzero.or.nan, half nofpclass(inf nan) %not.inf.or.nan) {
 ; CHECK-LABEL: define nofpclass(snan) half @known__nzero_or_nan__fdiv__not_inf_or_nan(
 ; CHECK-SAME: half nofpclass(inf pzero sub norm) [[NZERO_OR_NAN:%.*]], half nofpclass(nan inf) [[NOT_INF_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv contract half [[NZERO_OR_NAN]], [[NOT_INF_OR_NAN]]
+; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf contract half [[NZERO_OR_NAN]], [[NOT_INF_OR_NAN]]
 ; CHECK-NEXT:    ret half [[DIV]]
 ;
   %div = fdiv contract half %nzero.or.nan, %not.inf.or.nan
@@ -1239,7 +1239,7 @@ define nofpclass(snan) half @known__not_inf_or_nan__fdiv__nzero_or_nan(half nofp
 define nofpclass(snan) half @known__nzero_or_nan__fdiv__not_inf(half nofpclass(inf sub norm pzero) %nzero.or.nan, half nofpclass(inf) %not.inf) {
 ; CHECK-LABEL: define nofpclass(snan) half @known__nzero_or_nan__fdiv__not_inf(
 ; CHECK-SAME: half nofpclass(inf pzero sub norm) [[NZERO_OR_NAN:%.*]], half nofpclass(inf) [[NOT_INF:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv half [[NZERO_OR_NAN]], [[NOT_INF]]
+; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf half [[NZERO_OR_NAN]], [[NOT_INF]]
 ; CHECK-NEXT:    ret half [[DIV]]
 ;
   %div = fdiv half %nzero.or.nan, %not.inf
@@ -1294,8 +1294,7 @@ define nofpclass(inf) half @ret_noinf__nzero_or_nan__fdiv__not_inf_or_nan(half n
 define nofpclass(inf) half @ret_noinf__not_inf_or_nan__fdiv__nzero_or_nan(half nofpclass(nan) %not.nan, half nofpclass(inf sub norm pzero) %nzero.or.nan) {
 ; CHECK-LABEL: define nofpclass(inf) half @ret_noinf__not_inf_or_nan__fdiv__nzero_or_nan(
 ; CHECK-SAME: half nofpclass(nan) [[NOT_NAN:%.*]], half nofpclass(inf pzero sub norm) [[NZERO_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv half [[NOT_NAN]], [[NZERO_OR_NAN]]
-; CHECK-NEXT:    ret half [[DIV]]
+; CHECK-NEXT:    ret half +qnan
 ;
   %div = fdiv half %not.nan, %nzero.or.nan
   ret half %div
@@ -1338,8 +1337,7 @@ define nofpclass(snan) half @ret__nzero_or_nan__fdiv_ninf__not_inf_or_nan(half n
 define nofpclass(snan) half @ret_not_inf_or_nan__fdiv_ninf__nzero_or_nan(half nofpclass(nan) %not.nan, half nofpclass(inf sub norm pzero) %nzero.or.nan) {
 ; CHECK-LABEL: define nofpclass(snan) half @ret_not_inf_or_nan__fdiv_ninf__nzero_or_nan(
 ; CHECK-SAME: half nofpclass(nan) [[NOT_NAN:%.*]], half nofpclass(inf pzero sub norm) [[NZERO_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf half [[NOT_NAN]], [[NZERO_OR_NAN]]
-; CHECK-NEXT:    ret half [[DIV]]
+; CHECK-NEXT:    ret half +qnan
 ;
   %div = fdiv ninf half %not.nan, %nzero.or.nan
   ret half %div
@@ -1371,7 +1369,7 @@ define nofpclass(snan) half @not_inf_or_nan__fdiv_nnan__nzero_or_nan(half %unkno
 define nofpclass(snan) half @known__pzero_or_nan__fdiv__not_inf_or_nan(half nofpclass(inf sub norm nzero) %pzero.or.nan, half nofpclass(inf nan) %not.inf.or.nan) {
 ; CHECK-LABEL: define nofpclass(snan) half @known__pzero_or_nan__fdiv__not_inf_or_nan(
 ; CHECK-SAME: half nofpclass(inf nzero sub norm) [[PZERO_OR_NAN:%.*]], half nofpclass(nan inf) [[NOT_INF_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv contract half [[PZERO_OR_NAN]], [[NOT_INF_OR_NAN]]
+; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf contract half [[PZERO_OR_NAN]], [[NOT_INF_OR_NAN]]
 ; CHECK-NEXT:    ret half [[DIV]]
 ;
   %div = fdiv contract half %pzero.or.nan, %not.inf.or.nan
@@ -1393,7 +1391,7 @@ define nofpclass(snan) half @known__not_inf_or_nan__fdiv__pzero_or_nan(half nofp
 define nofpclass(snan) half @known__pzero_or_nan__fdiv__not_inf(half nofpclass(inf sub norm nzero) %pzero.or.nan, half nofpclass(inf) %not.inf) {
 ; CHECK-LABEL: define nofpclass(snan) half @known__pzero_or_nan__fdiv__not_inf(
 ; CHECK-SAME: half nofpclass(inf nzero sub norm) [[PZERO_OR_NAN:%.*]], half nofpclass(inf) [[NOT_INF:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv half [[PZERO_OR_NAN]], [[NOT_INF]]
+; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf half [[PZERO_OR_NAN]], [[NOT_INF]]
 ; CHECK-NEXT:    ret half [[DIV]]
 ;
   %div = fdiv half %pzero.or.nan, %not.inf
@@ -1448,8 +1446,7 @@ define nofpclass(inf) half @ret_noinf__pzero_or_nan__fdiv__not_inf_or_nan(half n
 define nofpclass(inf) half @ret_noinf__not_inf_or_nan__fdiv__pzero_or_nan(half nofpclass(nan) %not.nan, half nofpclass(inf sub norm nzero) %pzero.or.nan) {
 ; CHECK-LABEL: define nofpclass(inf) half @ret_noinf__not_inf_or_nan__fdiv__pzero_or_nan(
 ; CHECK-SAME: half nofpclass(nan) [[NOT_NAN:%.*]], half nofpclass(inf nzero sub norm) [[PZERO_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv half [[NOT_NAN]], [[PZERO_OR_NAN]]
-; CHECK-NEXT:    ret half [[DIV]]
+; CHECK-NEXT:    ret half +qnan
 ;
   %div = fdiv half %not.nan, %pzero.or.nan
   ret half %div
@@ -1470,8 +1467,7 @@ define nofpclass(snan) half @ret__pzero_or_nan__fdiv_ninf__not_inf_or_nan(half n
 define nofpclass(snan) half @ret_not_inf_or_nan__fdiv_ninf__pzero_or_nan(half nofpclass(nan) %not.nan, half nofpclass(inf sub norm nzero) %pzero.or.nan) {
 ; CHECK-LABEL: define nofpclass(snan) half @ret_not_inf_or_nan__fdiv_ninf__pzero_or_nan(
 ; CHECK-SAME: half nofpclass(nan) [[NOT_NAN:%.*]], half nofpclass(inf nzero sub norm) [[PZERO_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv ninf half [[NOT_NAN]], [[PZERO_OR_NAN]]
-; CHECK-NEXT:    ret half [[DIV]]
+; CHECK-NEXT:    ret half +qnan
 ;
   %div = fdiv ninf half %not.nan, %pzero.or.nan
   ret half %div
@@ -2346,8 +2342,7 @@ define nofpclass(nan) half @ret_nonan__unknown__fdiv__zero_or_nan(half %unknown,
 define nofpclass(inf) half @ret_noinf__unknown__fdiv__zero_or_nan(half %unknown, half nofpclass(inf sub norm) %zero.or.nan) {
 ; CHECK-LABEL: define nofpclass(inf) half @ret_noinf__unknown__fdiv__zero_or_nan(
 ; CHECK-SAME: half [[UNKNOWN:%.*]], half nofpclass(inf sub norm) [[ZERO_OR_NAN:%.*]]) {
-; CHECK-NEXT:    [[DIV:%.*]] = fdiv half [[UNKNOWN]], [[ZERO_OR_NAN]]
-; CHECK-NEXT:    ret half [[DIV]]
+; CHECK-NEXT:    ret half +qnan
 ;
   %div = fdiv half %unknown, %zero.or.nan
   ret half %div

>From cb77b54ddcedbb2ac43b8bb729bc3825851b4bdd Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 8 Aug 2026 22:35:48 +0300
Subject: [PATCH 2/4] add TODO comment

---
 llvm/include/llvm/Support/KnownFPClass.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 00e031a54c02b..e4217eddb261f 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -402,6 +402,10 @@ struct KnownFPClass {
   //
   // A negative subnormal is read as +0.0 under a positive-zero input mode, so
   // it counts towards the positive side and not the negative one.
+  //
+  // TODO: With a positive-zero output mode a -sub result is flushed to +0.0, so
+  // the result can be positive after all. Fix it in fmul and fdiv by adding
+  // fcPosZero back and dropping SignBit before they rule out fcSubnormal.
   void propagateXorSign(const KnownFPClass &LHS, const KnownFPClass &RHS,
                         DenormalMode Mode) {
     bool MustFlushNegSub = Mode.Input == DenormalMode::PositiveZero;

>From d639dc10e0879f2afa925f0091bf95af3d2d49df Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 8 Aug 2026 23:41:43 +0300
Subject: [PATCH 3/4] restructure and improve precision even more

---
 llvm/lib/Support/KnownFPClass.cpp             | 38 ++++++++++-------
 .../Transforms/Attributor/nofpclass-fdiv.ll   | 42 ++++++++++++++++++-
 2 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 1cd08315ff2fd..4628e7a2c2641 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -440,9 +440,31 @@ KnownFPClass KnownFPClass::fdiv(const KnownFPClass &KnownLHS,
   Known.propagateXorSign(KnownLHS, KnownRHS, Mode);
 
   // {0, Inf, NaN} / Y => {0, Inf, NaN}
-  if (KnownLHS.isKnownNeverLogicalFiniteNonZero(Mode))
+  if (KnownLHS.isKnownNeverLogicalFiniteNonZero(Mode)) {
     Known.knownNot(fcNormal | fcSubnormal);
 
+    // {0, NaN} / Y => 0 or NaN
+    if (KnownLHS.isKnownNever(fcInf))
+      Known.knownNot(fcInf);
+
+    // {Inf, NaN} / Y => Inf or NaN
+    if (KnownLHS.isKnownNeverLogicalZero(Mode))
+      Known.knownNot(fcZero);
+  }
+
+  // X / {0, Inf, NaN} => {0, Inf, NaN}
+  if (KnownRHS.isKnownNeverLogicalFiniteNonZero(Mode)) {
+    Known.knownNot(fcNormal | fcSubnormal);
+
+    // X / {0, NaN} => Inf or NaN
+    if (KnownRHS.isKnownNever(fcInf))
+      Known.knownNot(fcZero);
+
+    // X / {Inf, NaN} => 0 or NaN
+    if (KnownRHS.isKnownNeverLogicalZero(Mode))
+      Known.knownNot(fcInf);
+  }
+
   // X / 0   => Inf
   // X / Sub => Normal or Inf
   // X / Inf => 0
@@ -450,24 +472,12 @@ KnownFPClass KnownFPClass::fdiv(const KnownFPClass &KnownLHS,
     Known.knownNot(fcSubnormal);
 
   // 0 / Y      => 0
-  // X / Inf    => 0
   // X / Normal => 0 on underflow
+  // X / Inf    => 0
   if (KnownLHS.isKnownNeverLogicalZero(Mode) &&
       KnownRHS.isKnownNever(fcNormal | fcInf))
     Known.knownNot(fcZero);
 
-  // {0, NaN} / Y   => 0 or NaN
-  // X / {Inf, NaN} => 0 or NaN
-  if (KnownLHS.isKnownAlways(fcZero | fcNan) ||
-      KnownRHS.isKnownAlways(fcInf | fcNan))
-    Known.knownNot(fcSubnormal | fcNormal | fcInf);
-
-  // {Inf, NaN} / Y => Inf or NaN
-  // X / {0, NaN}   => Inf or NaN
-  if (KnownLHS.isKnownAlways(fcInf | fcNan) ||
-      KnownRHS.isKnownAlways(fcZero | fcNan))
-    Known.knownNot(fcFinite);
-
   return Known;
 }
 
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
index 432a12bb1e86b..15fd01a30ba63 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
@@ -1137,7 +1137,7 @@ define float @ret_fdiv_ieee_nozero_nonan_noinf_nonorm(float nofpclass(zero) %arg
 }
 
 define float @ret_fdiv_daz_nozero_nonan_noinf_nonorm(float nofpclass(zero) %arg0, float nofpclass(nan inf norm) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(sub) float @ret_fdiv_daz_nozero_nonan_noinf_nonorm
+; CHECK-LABEL: define nofpclass(zero sub norm) float @ret_fdiv_daz_nozero_nonan_noinf_nonorm
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(nan inf norm) [[ARG1:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -1246,6 +1246,46 @@ define float @ret_fdiv_same_operands_nonorm_dynamic(float noundef nofpclass(norm
   ret float %fdiv
 }
 
+define float @ret_fdiv_ieee_all_nosub_nonorm(float %arg0, float nofpclass(sub norm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_ieee_all_nosub_nonorm
+; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(sub norm) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_daz_all_nonorm(float %arg0, float nofpclass(norm) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_daz_all_nonorm
+; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(norm) [[ARG1:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_ieee_all_nonan_nosub_nonorm(float %arg0, float nofpclass(nan sub norm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_ieee_all_nonan_nosub_nonorm
+; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(nan sub norm) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
+define float @ret_fdiv_daz_psub_or_nan_nonan(float nofpclass(inf zero nsub norm) %arg0, float nofpclass(nan) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(inf sub norm) float @ret_fdiv_daz_psub_or_nan_nonan
+; CHECK-SAME: (float nofpclass(inf zero nsub norm) [[ARG0:%.*]], float nofpclass(nan) [[ARG1:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg0, %arg1
+  ret float %fdiv
+}
+
 attributes #0 = { denormal_fpenv(ieee|ieee) }
 attributes #1 = { denormal_fpenv(ieee|preservesign) }
 attributes #2 = { denormal_fpenv(ieee|positivezero) }

>From 1a78ed7246c548041d9787762e5e4abd642c0057 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 8 Aug 2026 23:54:57 +0300
Subject: [PATCH 4/4] better test coverage for fdiv_self

---
 .../Transforms/Attributor/nofpclass-fdiv.ll   | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
index 15fd01a30ba63..91de61be81dc8 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
@@ -1246,6 +1246,26 @@ define float @ret_fdiv_same_operands_nonorm_dynamic(float noundef nofpclass(norm
   ret float %fdiv
 }
 
+define float @ret_fdiv_same_operands_nonorm_dapz(float noundef nofpclass(norm) %arg) #2 {
+; CHECK-LABEL: define noundef nofpclass(inf zero sub norm) float @ret_fdiv_same_operands_nonorm_dapz
+; CHECK-SAME: (float noundef nofpclass(norm) [[ARG:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg, %arg
+  ret float %fdiv
+}
+
+define float @ret_fdiv_same_operands_nosnan_nosub_nonorm(float noundef nofpclass(snan sub norm) %arg) #0 {
+; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_fdiv_same_operands_nosnan_nosub_nonorm
+; CHECK-SAME: (float noundef nofpclass(snan sub norm) [[ARG:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %arg, %arg
+  ret float %fdiv
+}
+
 define float @ret_fdiv_ieee_all_nosub_nonorm(float %arg0, float nofpclass(sub norm) %arg1) #0 {
 ; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_ieee_all_nosub_nonorm
 ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(sub norm) [[ARG1:%.*]]) #[[ATTR0]] {



More information about the llvm-commits mailing list