[llvm] [KnownFPClass] Rule out non-zero finite results for fmul and fdiv (PR #231052)

Eric Ross via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 11 17:37:56 PDT 2026


https://github.com/ZERICO2005 updated https://github.com/llvm/llvm-project/pull/231052

>From e8bc2f46443c5e2758d2b3af4ffb022049ab6803 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Sun, 11 Oct 2026 18:29:05 -0600
Subject: [PATCH] [KnownFPClass] Rule out non-zero finite results for fmul and
 fdiv

---
 llvm/lib/Analysis/ValueTracking.cpp           |  14 +--
 .../CodeGen/GlobalISel/GISelValueTracking.cpp |  12 +-
 llvm/lib/Support/KnownFPClass.cpp             |  49 +++++---
 .../AArch64/GlobalISel/knownfpclass-ops.mir   |  44 +++++++
 .../Transforms/Attributor/nofpclass-fdiv.ll   | 114 ++++++++++++++++--
 .../Transforms/Attributor/nofpclass-fmul.ll   | 106 +++++++++++++++-
 6 files changed, 291 insertions(+), 48 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1cb2f2eb80a13..bd1a726f12b03 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6143,18 +6143,16 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
     if (!WantNan && !WantNegative && !WantPositive)
       break;
 
-    KnownFPClass KnownLHS, KnownRHS;
+    KnownFPClass KnownRHS;
     computeKnownFPClass(Op->getOperand(1), DemandedElts, fcAllFlags, KnownRHS,
                         Q, Depth + 1);
 
-    bool KnowSomethingUseful =
-        KnownRHS.isKnownNeverNaN() ||
-        KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal) ||
-        KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal);
+    if (KnownRHS.isUnknown())
+      break;
 
-    if (KnowSomethingUseful)
-      computeKnownFPClass(Op->getOperand(0), DemandedElts, fcAllFlags, KnownLHS,
-                          Q, Depth + 1);
+    KnownFPClass KnownLHS;
+    computeKnownFPClass(Op->getOperand(0), DemandedElts, fcAllFlags, KnownLHS,
+                        Q, Depth + 1);
 
     Known = KnownFPClass::fdiv(KnownLHS, KnownRHS, Mode);
     break;
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index 3bd31d8227862..2652d11b8a38f 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -2027,16 +2027,14 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     if (!WantNan && !WantNegative && !WantPositive)
       break;
 
-    KnownFPClass KnownLHS, KnownRHS;
+    KnownFPClass KnownRHS;
     computeKnownFPClass(RHS, DemandedElts, fcAllFlags, KnownRHS, Depth + 1);
 
-    bool KnowSomethingUseful =
-        KnownRHS.isKnownNeverNaN() ||
-        KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal) ||
-        KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal);
+    if (KnownRHS.isUnknown())
+      break;
 
-    if (KnowSomethingUseful)
-      computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownLHS, Depth + 1);
+    KnownFPClass KnownLHS;
+    computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownLHS, Depth + 1);
 
     Known = KnownFPClass::fdiv(KnownLHS, KnownRHS, Mode);
     break;
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 4a756af775930..40fd9999e213f 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -552,21 +552,33 @@ KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS_,
   if ((KnownLHS.isKnownNever(fcNegative) &&
        KnownRHS.isKnownNever(fcNegative)) ||
       (KnownLHS.isKnownNever(fcPositive) && KnownRHS.isKnownNever(fcPositive)))
-    Known.knownNot(fcNegative);
+    Known.knownNot(fcNegInf | fcNegZero);
   if ((KnownLHS.isKnownNever(fcPositive) &&
        KnownRHS.isKnownNever(fcNegative)) ||
       (KnownLHS.isKnownNever(fcNegative) && KnownRHS.isKnownNever(fcPositive)))
-    Known.knownNot(fcPositive);
+    Known.knownNot(fcPosInf | fcPosZero);
+
+  // Normal and subnormal results require two non-zero finite operands.
+  if ((KnownLHS.isKnownNever(fcPosSubnormal | fcPosNormal) ||
+       KnownRHS.isKnownNever(fcPosSubnormal | fcPosNormal)) &&
+      (KnownLHS.isKnownNever(fcNegSubnormal | fcNegNormal) ||
+       KnownRHS.isKnownNever(fcNegSubnormal | fcNegNormal)))
+    Known.knownNot(fcPosSubnormal | fcPosNormal);
+  if ((KnownLHS.isKnownNever(fcPosSubnormal | fcPosNormal) ||
+       KnownRHS.isKnownNever(fcNegSubnormal | fcNegNormal)) &&
+      (KnownLHS.isKnownNever(fcNegSubnormal | fcNegNormal) ||
+       KnownRHS.isKnownNever(fcPosSubnormal | fcPosNormal)))
+    Known.knownNot(fcNegSubnormal | fcNegNormal);
 
   // Inf * Y => Inf or NaN
   if (KnownLHS.isKnownAlways(fcInf | fcNan) ||
       KnownRHS.isKnownAlways(fcInf | fcNan))
-    Known.knownNot(fcNormal | fcSubnormal | fcZero);
+    Known.knownNot(fcZero);
 
   // 0 * Y => 0 or NaN
   if (KnownRHS.isKnownAlways(fcZero | fcNan) ||
       KnownLHS.isKnownAlways(fcZero | fcNan))
-    Known.knownNot(fcNormal | fcSubnormal | fcInf);
+    Known.knownNot(fcInf);
 
   // 0 * +/-inf => NaN
   if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() &&
@@ -623,37 +635,36 @@ KnownFPClass KnownFPClass::fdiv(const KnownFPClass &KnownLHS_,
     Known.knownNot(fcNan);
   }
 
-  //  X / -0.0 => -Inf (or NaN)
   // +X / +Y or -X / -Y => +Q
   // +X / -Y or -X / +Y => -Q
   if ((KnownLHS.isKnownNever(fcNegative) &&
        KnownRHS.isKnownNever(fcNegative)) ||
       (KnownLHS.isKnownNever(fcPositive) && KnownRHS.isKnownNever(fcPositive)))
-    Known.knownNot(fcNegative);
+    Known.knownNot(fcNegInf | fcNegZero);
   if ((KnownLHS.isKnownNever(fcPositive) &&
        KnownRHS.isKnownNever(fcNegative)) ||
       (KnownLHS.isKnownNever(fcNegative) && KnownRHS.isKnownNever(fcPositive)))
-    Known.knownNot(fcPositive);
+    Known.knownNot(fcPosInf | fcPosZero);
 
   // Normal and subnormal results require two non-zero finite operands.
-  if ((KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal) &&
-       KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal)) ||
-      (KnownLHS.isKnownNever(fcPosNormal | fcPosSubnormal) &&
-       KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal)))
-    Known.knownNot(fcNegNormal | fcNegSubnormal);
-  if ((KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal) &&
-       KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal)) ||
-      (KnownLHS.isKnownNever(fcPosNormal | fcPosSubnormal) &&
-       KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal)))
-    Known.knownNot(fcPosNormal | fcPosSubnormal);
+  if ((KnownLHS.isKnownNever(fcPosSubnormal | fcPosNormal) ||
+       KnownRHS.isKnownNever(fcPosSubnormal | fcPosNormal)) &&
+      (KnownLHS.isKnownNever(fcNegSubnormal | fcNegNormal) ||
+       KnownRHS.isKnownNever(fcNegSubnormal | fcNegNormal)))
+    Known.knownNot(fcPosSubnormal | fcPosNormal);
+  if ((KnownLHS.isKnownNever(fcPosSubnormal | fcPosNormal) ||
+       KnownRHS.isKnownNever(fcNegSubnormal | fcNegNormal)) &&
+      (KnownLHS.isKnownNever(fcNegSubnormal | fcNegNormal) ||
+       KnownRHS.isKnownNever(fcPosSubnormal | fcPosNormal)))
+    Known.knownNot(fcNegSubnormal | fcNegNormal);
 
   // 0 / X => 0 or NaN
   if (KnownLHS.isKnownAlways(fcZero))
-    Known.knownNot(fcSubnormal | fcNormal | fcInf);
+    Known.knownNot(fcInf);
 
   // X / 0 => NaN or Inf
   if (KnownRHS.isKnownAlways(fcZero))
-    Known.knownNot(fcFinite);
+    Known.knownNot(fcZero);
 
   return applyOutputDenormalMode(Known, Mode);
 }
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownfpclass-ops.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownfpclass-ops.mir
index c3ad8c3881e0b..ebaf21f3126d2 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownfpclass-ops.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownfpclass-ops.mir
@@ -572,6 +572,28 @@ body:             |
     %copy:_(f32) = COPY %fmul
 ...
 ---
+# G_FCANONICALIZE turns sNaN into qNaN. Here we are testing to make sure that
+# both the LHS and RHS are demanded/queried to rule out sNaN.
+name:            FMulNoSNaN
+body:             |
+  bb.0:
+  ; CHECK-LABEL: name: @FMulNoSNaN
+  ; CHECK-NEXT: %ptr:_ FPClasses:(all) SignBitKnown:?
+  ; CHECK-NEXT: %lhs:_ FPClasses:(all) SignBitKnown:?
+  ; CHECK-NEXT: %rhs:_ FPClasses:(all) SignBitKnown:?
+  ; CHECK-NEXT: %lhs_no_snan:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+  ; CHECK-NEXT: %rhs_no_snan:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+  ; CHECK-NEXT: %fmul:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+  ; CHECK-NEXT: %copy:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %lhs:_(f32) = G_LOAD %ptr(p0) :: (load (f32))
+    %rhs:_(f32) = G_LOAD %ptr(p0) :: (load (f32))
+    %lhs_no_snan:_(f32) = G_FCANONICALIZE %lhs
+    %rhs_no_snan:_(f32) = G_FCANONICALIZE %rhs
+    %fmul:_(f32) = G_FMUL %lhs_no_snan, %rhs_no_snan
+    %copy:_(f32) = COPY %fmul
+...
+---
 name:            FLog
 body:             |
   bb.0:
@@ -889,6 +911,28 @@ body:             |
     %copy:_(f32) = COPY %fdiv
 ...
 ---
+# G_FCANONICALIZE turns sNaN into qNaN. Here we are testing to make sure that
+# both the LHS and RHS are demanded/queried to rule out sNaN.
+name:            FDivNoSNaN
+body:             |
+  bb.0:
+  ; CHECK-LABEL: name: @FDivNoSNaN
+  ; CHECK-NEXT: %ptr:_ FPClasses:(all) SignBitKnown:?
+  ; CHECK-NEXT: %lhs:_ FPClasses:(all) SignBitKnown:?
+  ; CHECK-NEXT: %rhs:_ FPClasses:(all) SignBitKnown:?
+  ; CHECK-NEXT: %lhs_no_snan:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+  ; CHECK-NEXT: %rhs_no_snan:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+  ; CHECK-NEXT: %fdiv:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+  ; CHECK-NEXT: %copy:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %lhs:_(f32) = G_LOAD %ptr(p0) :: (load (f32))
+    %rhs:_(f32) = G_LOAD %ptr(p0) :: (load (f32))
+    %lhs_no_snan:_(f32) = G_FCANONICALIZE %lhs
+    %rhs_no_snan:_(f32) = G_FCANONICALIZE %rhs
+    %fdiv:_(f32) = G_FDIV %lhs_no_snan, %rhs_no_snan
+    %copy:_(f32) = COPY %fdiv
+...
+---
 name:            FDivSqrt
 body:             |
   bb.0:
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
index 506d082682a0e..12e43495b5426 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(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(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(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(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(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(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(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]]
@@ -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(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]]
@@ -1067,7 +1067,7 @@ define float @fdiv_no_nan(float nofpclass(nan) %arg0, float nofpclass(nan) %arg1
 }
 
 define float @fdiv_no_snan(float nofpclass(snan) %arg0, float nofpclass(snan) %arg1) {
-; CHECK-LABEL: define float @fdiv_no_snan
+; CHECK-LABEL: define nofpclass(snan) float @fdiv_no_snan
 ; CHECK-SAME: (float nofpclass(snan) [[ARG0:%.*]], float nofpclass(snan) [[ARG1:%.*]]) #[[ATTR4]] {
 ; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FDIV]]
@@ -1392,6 +1392,102 @@ define float @ret_fdiv_neginf_posnormal__mode_dynamic_dynamic(float nofpclass(na
   ret float %fdiv
 }
 
+; A non-zero finite result requires two non-zero finite operands, so
+; ruling out one sign class on either operand rules out the matching
+; sign of the result.
+
+define float @ret_fdiv__lhs_no_psub_pnorm__rhs_no_psub_pnorm(float nofpclass(psub pnorm) %lhs, float nofpclass(psub pnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fdiv__lhs_no_psub_pnorm__rhs_no_psub_pnorm
+; CHECK-SAME: (float nofpclass(psub pnorm) [[LHS:%.*]], float nofpclass(psub pnorm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_psub_pnorm__rhs_no_nsub_nnorm(float nofpclass(psub pnorm) %lhs, float nofpclass(nsub nnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fdiv__lhs_no_psub_pnorm__rhs_no_nsub_nnorm
+; CHECK-SAME: (float nofpclass(psub pnorm) [[LHS:%.*]], float nofpclass(nsub nnorm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_nsub_nnorm__rhs_no_psub_pnorm(float nofpclass(nsub nnorm) %lhs, float nofpclass(psub pnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fdiv__lhs_no_nsub_nnorm__rhs_no_psub_pnorm
+; CHECK-SAME: (float nofpclass(nsub nnorm) [[LHS:%.*]], float nofpclass(psub pnorm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_nsub_nnorm__rhs_no_nsub_nnorm(float nofpclass(nsub nnorm) %lhs, float nofpclass(nsub nnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fdiv__lhs_no_nsub_nnorm__rhs_no_nsub_nnorm
+; CHECK-SAME: (float nofpclass(nsub nnorm) [[LHS:%.*]], float nofpclass(nsub nnorm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_norm(float nofpclass(norm) %lhs, float %rhs) {
+; CHECK-LABEL: define float @ret_fdiv__lhs_no_norm
+; CHECK-SAME: (float nofpclass(norm) [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
+define float @ret_fdiv__rhs_no_norm(float %lhs, float nofpclass(norm) %rhs) {
+; CHECK-LABEL: define float @ret_fdiv__rhs_no_norm
+; CHECK-SAME: (float [[LHS:%.*]], float nofpclass(norm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_sub_norm(float nofpclass(sub norm) %lhs, float %rhs) {
+; CHECK-LABEL: define float @ret_fdiv__lhs_no_sub_norm
+; CHECK-SAME: (float nofpclass(sub norm) [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
+define float @ret_fdiv__rhs_no_sub_norm(float %lhs, float nofpclass(sub norm) %rhs) {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv__rhs_no_sub_norm
+; CHECK-SAME: (float [[LHS:%.*]], float nofpclass(sub norm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
+; subnormal / subnormal is always normal, so a non-zero finite result is
+; reachable here.
+define float @ret_fdiv__lhs_no_norm__rhs_no_norm(float nofpclass(norm) %lhs, float nofpclass(norm) %rhs) {
+; CHECK-LABEL: define float @ret_fdiv__lhs_no_norm__rhs_no_norm
+; CHECK-SAME: (float nofpclass(norm) [[LHS:%.*]], float nofpclass(norm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FDIV]]
+;
+  %fdiv = fdiv float %lhs, %rhs
+  ret float %fdiv
+}
+
 attributes #0 = { denormal_fpenv(ieee|ieee) }
 attributes #1 = { denormal_fpenv(ieee|preservesign) }
 attributes #2 = { denormal_fpenv(ieee|positivezero) }
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
index 312bfc76b8874..94f6bdcffacb7 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
@@ -1014,7 +1014,7 @@ define float @ret_fmul_daz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm)
 }
 
 define float @ret_fmul_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_fmul_dapz_no_pos_no_neg(
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul_dapz_no_pos_no_neg(
 ; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR3:[0-9]+]] {
 ; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FMUL]]
@@ -1024,7 +1024,7 @@ define float @ret_fmul_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm)
 }
 
 define float @ret_fmul_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_fmul_dynamic_no_pos_no_neg(
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul_dynamic_no_pos_no_neg(
 ; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR4:[0-9]+]] {
 ; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FMUL]]
@@ -1074,7 +1074,7 @@ define float @ret_fmul_dynamic_no_pos_nonsub_no_neg(float nofpclass(pinf pzero p
 }
 
 define float @ret_fmul_ftpz_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #4 {
-; CHECK-LABEL: define float @ret_fmul_ftpz_dapz_no_pos_no_neg(
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul_ftpz_dapz_no_pos_no_neg(
 ; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR5:[0-9]+]] {
 ; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
 ; CHECK-NEXT:    ret float [[FMUL]]
@@ -1149,7 +1149,7 @@ define float @ret_fmul_negnormal_posnormal_mode_dynamic_dynamic(float nofpclass(
 }
 
 define float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #5 {
-; CHECK-LABEL: define nofpclass(nan) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(
+; CHECK-LABEL: define nofpclass(nan nsub nnorm) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(
 ; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero psub pnorm) [[RHS:%.*]]) #[[ATTR6]] {
 ; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
 ; CHECK-NEXT:    ret float [[FMUL]]
@@ -1159,7 +1159,7 @@ define float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(
 }
 
 define float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #4 {
-; CHECK-LABEL: define nofpclass(nan) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(
+; CHECK-LABEL: define nofpclass(nan nsub nnorm) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(
 ; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero psub pnorm) [[RHS:%.*]]) #[[ATTR5]] {
 ; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
 ; CHECK-NEXT:    ret float [[FMUL]]
@@ -1255,6 +1255,102 @@ define float @ret_fmul_exact_negzero__mode_dynamic_dynamic(float nofpclass(nan i
   ret float %fmul
 }
 
+; A non-zero finite result requires two non-zero finite operands, so
+; ruling out one sign class on either operand rules out the matching
+; sign of the result.
+
+define float @ret_fmul__lhs_no_psub_pnorm__rhs_no_psub_pnorm(float nofpclass(psub pnorm) %lhs, float nofpclass(psub pnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fmul__lhs_no_psub_pnorm__rhs_no_psub_pnorm(
+; CHECK-SAME: float nofpclass(psub pnorm) [[LHS:%.*]], float nofpclass(psub pnorm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_psub_pnorm__rhs_no_nsub_nnorm(float nofpclass(psub pnorm) %lhs, float nofpclass(nsub nnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul__lhs_no_psub_pnorm__rhs_no_nsub_nnorm(
+; CHECK-SAME: float nofpclass(psub pnorm) [[LHS:%.*]], float nofpclass(nsub nnorm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_nsub_nnorm__rhs_no_psub_pnorm(float nofpclass(nsub nnorm) %lhs, float nofpclass(psub pnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul__lhs_no_nsub_nnorm__rhs_no_psub_pnorm(
+; CHECK-SAME: float nofpclass(nsub nnorm) [[LHS:%.*]], float nofpclass(psub pnorm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_nsub_nnorm__rhs_no_nsub_nnorm(float nofpclass(nsub nnorm) %lhs, float nofpclass(nsub nnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fmul__lhs_no_nsub_nnorm__rhs_no_nsub_nnorm(
+; CHECK-SAME: float nofpclass(nsub nnorm) [[LHS:%.*]], float nofpclass(nsub nnorm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_norm(float nofpclass(norm) %lhs, float %rhs) {
+; CHECK-LABEL: define float @ret_fmul__lhs_no_norm(
+; CHECK-SAME: float nofpclass(norm) [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
+define float @ret_fmul__rhs_no_norm(float %lhs, float nofpclass(norm) %rhs) {
+; CHECK-LABEL: define float @ret_fmul__rhs_no_norm(
+; CHECK-SAME: float [[LHS:%.*]], float nofpclass(norm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_sub_norm(float nofpclass(sub norm) %lhs, float %rhs) {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fmul__lhs_no_sub_norm(
+; CHECK-SAME: float nofpclass(sub norm) [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
+define float @ret_fmul__rhs_no_sub_norm(float %lhs, float nofpclass(sub norm) %rhs) {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fmul__rhs_no_sub_norm(
+; CHECK-SAME: float [[LHS:%.*]], float nofpclass(sub norm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
+; The only finite non-zero result is subnormal * subnormal = min-subnormal
+; depending on the rounding mode.
+define float @ret_fmul__lhs_no_norm__rhs_no_norm(float nofpclass(norm) %lhs, float nofpclass(norm) %rhs) {
+; CHECK-LABEL: define float @ret_fmul__lhs_no_norm__rhs_no_norm(
+; CHECK-SAME: float nofpclass(norm) [[LHS:%.*]], float nofpclass(norm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret float [[FMUL]]
+;
+  %fmul = fmul float %lhs, %rhs
+  ret float %fmul
+}
+
 attributes #0 = { denormal_fpenv(ieee|ieee) }
 attributes #1 = { denormal_fpenv(ieee|preservesign) }
 attributes #2 = { denormal_fpenv(ieee|positivezero) }



More information about the llvm-commits mailing list