[llvm] [KnownFPClass] Correct denormal mode handling for `KnownFPClass::minMaxLike` (PR #220148)

Eric Ross via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 10 21:19:34 PDT 2026


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

>From 21b7eb0422e7640df2b2d9cc21f310e24d0b9945 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Mon, 31 Aug 2026 20:43:09 -0600
Subject: [PATCH] [KnownFPClass] Correct denormal mode handling for
 KnownFPClass::minMaxLike

---
 llvm/lib/Support/KnownFPClass.cpp             | 26 +++----
 .../InstCombineSimplifyDemanded.cpp           | 33 +++++---
 .../Attributor/nofpclass-minimum-maximum.ll   | 77 +++++++++++++-----
 .../nofpclass-minimumnum-maximumnum.ll        | 78 ++++++++++++++-----
 .../Attributor/nofpclass-minnum-maxnum.ll     | 77 +++++++++++++-----
 .../simplify-demanded-fpclass-maximum.ll      | 56 +++++++++++++
 .../simplify-demanded-fpclass-maximumnum.ll   | 56 +++++++++++++
 .../simplify-demanded-fpclass-maxnum.ll       | 56 +++++++++++++
 .../simplify-demanded-fpclass-minimum.ll      | 56 +++++++++++++
 .../simplify-demanded-fpclass-minimumnum.ll   | 56 +++++++++++++
 .../simplify-demanded-fpclass-minnum.ll       | 56 +++++++++++++
 11 files changed, 550 insertions(+), 77 deletions(-)

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 1f596edd74100..7f341d3fb04d4 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -128,19 +128,6 @@ KnownFPClass KnownFPClass::minMaxLike(const KnownFPClass &LHS_,
   } else
     llvm_unreachable("unhandled intrinsic");
 
-  // Fixup zero handling if denormals could be returned as a zero.
-  //
-  // As there's no spec for denormal flushing, be conservative with the
-  // treatment of denormals that could be flushed to zero. For older
-  // subtargets on AMDGPU the min/max instructions would not flush the
-  // output and return the original value.
-  //
-  if ((Known.getKnownFPClasses() & fcZero) != fcNone &&
-      !Known.isKnownNeverSubnormal()) {
-    if (Mode != DenormalMode::getIEEE())
-      Known.setKnownFPClasses(Known.getKnownFPClasses() | fcZero);
-  }
-
   if (Known.isKnownNeverNaN()) {
     if (KnownLHS.getSignBit() && KnownRHS.getSignBit() &&
         *KnownLHS.getSignBit() == *KnownRHS.getSignBit()) {
@@ -172,6 +159,19 @@ KnownFPClass KnownFPClass::minMaxLike(const KnownFPClass &LHS_,
     }
   }
 
+  // Fixup zero handling if denormal outputs/inputs could be treated as zero.
+  //
+  // As there's no spec for denormal flushing, be conservative with the
+  // treatment of denormals that could be flushed to zero. For older
+  // subtargets on AMDGPU the min/max instructions would not flush the
+  // output and return the original value.
+  //
+  if (Mode != DenormalMode::getIEEE() && (!KnownLHS.isKnownNeverSubnormal() ||
+                                          !KnownRHS.isKnownNeverSubnormal())) {
+    Known.setKnownFPClasses(Known.getKnownFPClasses() | fcZero);
+    Known.setSignBit(std::nullopt);
+  }
+
   return Known;
 }
 
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 34a2da3bf03f2..30a5b352e8847 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2258,6 +2258,13 @@ simplifyDemandedFPClassMinMax(KnownFPClass &Known, Intrinsic::ID IID,
                               KnownFPClass KnownLHS, KnownFPClass KnownRHS,
                               const Function &F, bool NSZ) {
   bool OrderedZeroSign = !NSZ;
+  Type *EltTy = CI->getType()->getScalarType();
+  DenormalMode Mode = F.getDenormalMode(EltTy->getFltSemantics());
+  // Prevent folding of the operand if a subnormal output/input could flush to
+  // zero.
+  bool CanReturnOperand =
+      Mode == DenormalMode::getIEEE() ||
+      (KnownLHS.isKnownNeverSubnormal() && KnownRHS.isKnownNeverSubnormal());
 
   KnownFPClass::MinMaxKind OpKind;
   switch (IID) {
@@ -2266,13 +2273,15 @@ simplifyDemandedFPClassMinMax(KnownFPClass &Known, Intrinsic::ID IID,
 
     // If one operand is known greater than the other, it must be that
     // operand unless the other is a nan.
-    if (cannotOrderStrictlyLess(KnownLHS.getKnownFPClasses(),
+    if (CanReturnOperand &&
+        cannotOrderStrictlyLess(KnownLHS.getKnownFPClasses(),
                                 KnownRHS.getKnownFPClasses(),
                                 OrderedZeroSign) &&
         KnownRHS.isKnownNever(fcNan))
       return CI->getArgOperand(0);
 
-    if (cannotOrderStrictlyGreater(KnownLHS.getKnownFPClasses(),
+    if (CanReturnOperand &&
+        cannotOrderStrictlyGreater(KnownLHS.getKnownFPClasses(),
                                    KnownRHS.getKnownFPClasses(),
                                    OrderedZeroSign) &&
         KnownLHS.isKnownNever(fcNan))
@@ -2285,13 +2294,15 @@ simplifyDemandedFPClassMinMax(KnownFPClass &Known, Intrinsic::ID IID,
 
     // If one operand is known less than the other, it must be that operand
     // unless the other is a nan.
-    if (cannotOrderStrictlyGreater(KnownLHS.getKnownFPClasses(),
+    if (CanReturnOperand &&
+        cannotOrderStrictlyGreater(KnownLHS.getKnownFPClasses(),
                                    KnownRHS.getKnownFPClasses(),
                                    OrderedZeroSign) &&
         KnownRHS.isKnownNever(fcNan))
       return CI->getArgOperand(0);
 
-    if (cannotOrderStrictlyLess(KnownLHS.getKnownFPClasses(),
+    if (CanReturnOperand &&
+        cannotOrderStrictlyLess(KnownLHS.getKnownFPClasses(),
                                 KnownRHS.getKnownFPClasses(),
                                 OrderedZeroSign) &&
         KnownLHS.isKnownNever(fcNan))
@@ -2304,13 +2315,15 @@ simplifyDemandedFPClassMinMax(KnownFPClass &Known, Intrinsic::ID IID,
     OpKind = IID == Intrinsic::maxnum ? KnownFPClass::MinMaxKind::maxnum
                                       : KnownFPClass::MinMaxKind::maximumnum;
 
-    if (cannotOrderStrictlyLess(KnownLHS.getKnownFPClasses(),
+    if (CanReturnOperand &&
+        cannotOrderStrictlyLess(KnownLHS.getKnownFPClasses(),
                                 KnownRHS.getKnownFPClasses(),
                                 OrderedZeroSign) &&
         KnownLHS.isKnownNever(fcNan))
       return CI->getArgOperand(0);
 
-    if (cannotOrderStrictlyGreater(KnownLHS.getKnownFPClasses(),
+    if (CanReturnOperand &&
+        cannotOrderStrictlyGreater(KnownLHS.getKnownFPClasses(),
                                    KnownRHS.getKnownFPClasses(),
                                    OrderedZeroSign) &&
         KnownRHS.isKnownNever(fcNan))
@@ -2323,13 +2336,15 @@ simplifyDemandedFPClassMinMax(KnownFPClass &Known, Intrinsic::ID IID,
     OpKind = IID == Intrinsic::minnum ? KnownFPClass::MinMaxKind::minnum
                                       : KnownFPClass::MinMaxKind::minimumnum;
 
-    if (cannotOrderStrictlyGreater(KnownLHS.getKnownFPClasses(),
+    if (CanReturnOperand &&
+        cannotOrderStrictlyGreater(KnownLHS.getKnownFPClasses(),
                                    KnownRHS.getKnownFPClasses(),
                                    OrderedZeroSign) &&
         KnownLHS.isKnownNever(fcNan))
       return CI->getArgOperand(0);
 
-    if (cannotOrderStrictlyLess(KnownLHS.getKnownFPClasses(),
+    if (CanReturnOperand &&
+        cannotOrderStrictlyLess(KnownLHS.getKnownFPClasses(),
                                 KnownRHS.getKnownFPClasses(),
                                 OrderedZeroSign) &&
         KnownRHS.isKnownNever(fcNan))
@@ -2341,8 +2356,6 @@ simplifyDemandedFPClassMinMax(KnownFPClass &Known, Intrinsic::ID IID,
     llvm_unreachable("not a min/max intrinsic");
   }
 
-  Type *EltTy = CI->getType()->getScalarType();
-  DenormalMode Mode = F.getDenormalMode(EltTy->getFltSemantics());
   Known = KnownFPClass::minMaxLike(KnownLHS, KnownRHS, OpKind, Mode);
   Known.knownNot(~DemandedMask);
 
diff --git a/llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll b/llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll
index 093cbbe19e2ce..9bae46398baef 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll
@@ -175,9 +175,9 @@ define float @ret_minimum_dynamic_dynamic(float %arg0, float %arg1) #3 {
 }
 
 define float @ret_minimum_noinf_nozero__noinf_nozero(float nofpclass(inf zero) %arg0, float nofpclass(inf zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(inf zero) float @ret_minimum_noinf_nozero__noinf_nozero
+; CHECK-LABEL: define nofpclass(inf) float @ret_minimum_noinf_nozero__noinf_nozero
 ; CHECK-SAME: (float nofpclass(inf zero) [[ARG0:%.*]], float nofpclass(inf zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero) float @llvm.minimum.f32(float nofpclass(inf zero) [[ARG0]], float nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.minimum.f32(float nofpclass(inf zero) [[ARG0]], float nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimum.f32(float %arg0, float %arg1)
@@ -185,9 +185,9 @@ define float @ret_minimum_noinf_nozero__noinf_nozero(float nofpclass(inf zero) %
 }
 
 define <2 x float> @ret_minimum_noinf_nozero__noinf_nozero_v2f32(<2 x float> nofpclass(inf zero) %arg0, <2 x float> nofpclass(inf zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(inf zero) <2 x float> @ret_minimum_noinf_nozero__noinf_nozero_v2f32
+; CHECK-LABEL: define nofpclass(inf) <2 x float> @ret_minimum_noinf_nozero__noinf_nozero_v2f32
 ; CHECK-SAME: (<2 x float> nofpclass(inf zero) [[ARG0:%.*]], <2 x float> nofpclass(inf zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero) <2 x float> @llvm.minimum.v2f32(<2 x float> nofpclass(inf zero) [[ARG0]], <2 x float> nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) <2 x float> @llvm.minimum.v2f32(<2 x float> nofpclass(inf zero) [[ARG0]], <2 x float> nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret <2 x float> [[CALL]]
 ;
   %call = call <2 x float> @llvm.minimum.v2f32(<2 x float> %arg0, <2 x float> %arg1)
@@ -195,9 +195,9 @@ define <2 x float> @ret_minimum_noinf_nozero__noinf_nozero_v2f32(<2 x float> nof
 }
 
 define float @ret_minimum_daz_daz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimum_daz_daz_nozero__nozero
+; CHECK-LABEL: define float @ret_minimum_daz_daz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimum.f32(float %arg0, float %arg1)
@@ -205,9 +205,9 @@ define float @ret_minimum_daz_daz_nozero__nozero(float nofpclass(zero) %arg0, fl
 }
 
 define float @ret_minimum_dapz_dapz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #2 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimum_dapz_dapz_nozero__nozero
+; CHECK-LABEL: define float @ret_minimum_dapz_dapz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimum.f32(float %arg0, float %arg1)
@@ -215,9 +215,9 @@ define float @ret_minimum_dapz_dapz_nozero__nozero(float nofpclass(zero) %arg0,
 }
 
 define float @ret_minimum_dynamic_dynamic_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #3 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimum_dynamic_dynamic_nozero__nozero
+; CHECK-LABEL: define float @ret_minimum_dynamic_dynamic_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimum.f32(float %arg0, float %arg1)
@@ -305,9 +305,9 @@ define float @ret_minimum_dapz_dapz_nonzero__nonzero(float nofpclass(nzero) %arg
 }
 
 define float @ret_minimum_ieee_daz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #4 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimum_ieee_daz_nozero__nozero
+; CHECK-LABEL: define float @ret_minimum_ieee_daz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR5:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimum.f32(float %arg0, float %arg1)
@@ -315,9 +315,9 @@ define float @ret_minimum_ieee_daz_nozero__nozero(float nofpclass(zero) %arg0, f
 }
 
 define float @ret_minimum_daz_ieee_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #5 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimum_daz_ieee_nozero__nozero
+; CHECK-LABEL: define float @ret_minimum_daz_ieee_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR6:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimum.f32(float %arg0, float %arg1)
@@ -325,9 +325,9 @@ define float @ret_minimum_daz_ieee_nozero__nozero(float nofpclass(zero) %arg0, f
 }
 
 define float @ret_minimum_ieee_dapz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #6 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimum_ieee_dapz_nozero__nozero
+; CHECK-LABEL: define float @ret_minimum_ieee_dapz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR7:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimum.f32(float %arg0, float %arg1)
@@ -335,9 +335,9 @@ define float @ret_minimum_ieee_dapz_nozero__nozero(float nofpclass(zero) %arg0,
 }
 
 define float @ret_minimum_dapz_ieee_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #7 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimum_dapz_ieee_nozero__nozero
+; CHECK-LABEL: define float @ret_minimum_dapz_ieee_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR8:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimum.f32(float %arg0, float %arg1)
@@ -504,6 +504,47 @@ define float @ret_maximum_any__nopos(float %arg0, float nofpclass(pinf psub pnor
   ret float %call
 }
 
+; Note that a negative subnormal argument may flush to positive zero.
+define float @ret_maximum_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #3 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_maximum_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:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.maximum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.maximum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_maximum_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #2 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_maximum_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:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.maximum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.maximum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_minimum_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #3 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_minimum_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:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.minimum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.minimum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_minimum_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #2 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_minimum_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:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.minimum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.minimum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
 attributes #0 = { denormal_fpenv(ieee|ieee) }
 attributes #1 = { denormal_fpenv(preservesign) }
 attributes #2 = { denormal_fpenv(positivezero|positivezero) }
diff --git a/llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll b/llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll
index 1b971f2874f74..532534f350ac1 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-minimumnum-maximumnum.ll
@@ -175,9 +175,9 @@ define float @ret_minimumnum_dynamic_dynamic(float %arg0, float %arg1) #3 {
 }
 
 define float @ret_minimumnum_noinf_nozero__noinf_nozero(float nofpclass(inf zero) %arg0, float nofpclass(inf zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(inf zero) float @ret_minimumnum_noinf_nozero__noinf_nozero
+; CHECK-LABEL: define nofpclass(inf) float @ret_minimumnum_noinf_nozero__noinf_nozero
 ; CHECK-SAME: (float nofpclass(inf zero) [[ARG0:%.*]], float nofpclass(inf zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero) float @llvm.minimumnum.f32(float nofpclass(inf zero) [[ARG0]], float nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.minimumnum.f32(float nofpclass(inf zero) [[ARG0]], float nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimumnum.f32(float %arg0, float %arg1)
@@ -185,9 +185,9 @@ define float @ret_minimumnum_noinf_nozero__noinf_nozero(float nofpclass(inf zero
 }
 
 define <2 x float> @ret_minimumnum_noinf_nozero__noinf_nozero_v2f32(<2 x float> nofpclass(inf zero) %arg0, <2 x float> nofpclass(inf zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(inf zero) <2 x float> @ret_minimumnum_noinf_nozero__noinf_nozero_v2f32
+; CHECK-LABEL: define nofpclass(inf) <2 x float> @ret_minimumnum_noinf_nozero__noinf_nozero_v2f32
 ; CHECK-SAME: (<2 x float> nofpclass(inf zero) [[ARG0:%.*]], <2 x float> nofpclass(inf zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero) <2 x float> @llvm.minimumnum.v2f32(<2 x float> nofpclass(inf zero) [[ARG0]], <2 x float> nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) <2 x float> @llvm.minimumnum.v2f32(<2 x float> nofpclass(inf zero) [[ARG0]], <2 x float> nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret <2 x float> [[CALL]]
 ;
   %call = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %arg0, <2 x float> %arg1)
@@ -195,9 +195,9 @@ define <2 x float> @ret_minimumnum_noinf_nozero__noinf_nozero_v2f32(<2 x float>
 }
 
 define float @ret_minimumnum_daz_daz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimumnum_daz_daz_nozero__nozero
+; CHECK-LABEL: define float @ret_minimumnum_daz_daz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimumnum.f32(float %arg0, float %arg1)
@@ -205,9 +205,9 @@ define float @ret_minimumnum_daz_daz_nozero__nozero(float nofpclass(zero) %arg0,
 }
 
 define float @ret_minimumnum_dapz_dapz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #2 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimumnum_dapz_dapz_nozero__nozero
+; CHECK-LABEL: define float @ret_minimumnum_dapz_dapz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimumnum.f32(float %arg0, float %arg1)
@@ -215,9 +215,9 @@ define float @ret_minimumnum_dapz_dapz_nozero__nozero(float nofpclass(zero) %arg
 }
 
 define float @ret_minimumnum_dynamic_dynamic_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #3 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimumnum_dynamic_dynamic_nozero__nozero
+; CHECK-LABEL: define float @ret_minimumnum_dynamic_dynamic_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimumnum.f32(float %arg0, float %arg1)
@@ -305,9 +305,9 @@ define float @ret_minimumnum_dapz_dapz_nonzero__nonzero(float nofpclass(nzero) %
 }
 
 define float @ret_minimumnum_ieee_daz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #4 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimumnum_ieee_daz_nozero__nozero
+; CHECK-LABEL: define float @ret_minimumnum_ieee_daz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR5:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimumnum.f32(float %arg0, float %arg1)
@@ -315,9 +315,9 @@ define float @ret_minimumnum_ieee_daz_nozero__nozero(float nofpclass(zero) %arg0
 }
 
 define float @ret_minimumnum_daz_ieee_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #5 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimumnum_daz_ieee_nozero__nozero
+; CHECK-LABEL: define float @ret_minimumnum_daz_ieee_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR6:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimumnum.f32(float %arg0, float %arg1)
@@ -325,9 +325,9 @@ define float @ret_minimumnum_daz_ieee_nozero__nozero(float nofpclass(zero) %arg0
 }
 
 define float @ret_minimumnum_ieee_dapz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #6 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimumnum_ieee_dapz_nozero__nozero
+; CHECK-LABEL: define float @ret_minimumnum_ieee_dapz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR7:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimumnum.f32(float %arg0, float %arg1)
@@ -335,9 +335,9 @@ define float @ret_minimumnum_ieee_dapz_nozero__nozero(float nofpclass(zero) %arg
 }
 
 define float @ret_minimumnum_dapz_ieee_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #7 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minimumnum_dapz_ieee_nozero__nozero
+; CHECK-LABEL: define float @ret_minimumnum_dapz_ieee_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR8:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minimumnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minimumnum.f32(float %arg0, float %arg1)
@@ -784,6 +784,48 @@ define float @ret_maximumnum_noqnan__nosnan(float nofpclass(qnan) %arg0, float n
   ret float %call
 }
 
+; Note that a negative subnormal argument may flush to positive zero.
+define float @ret_maximumnum_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #3 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_maximumnum_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:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.maximumnum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.maximumnum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_maximumnum_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #2 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_maximumnum_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:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.maximumnum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.maximumnum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_minimumnum_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #3 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_minimumnum_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:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.minimumnum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.minimumnum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_minimumnum_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #2 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_minimumnum_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:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.minimumnum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.minimumnum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+
 attributes #0 = { denormal_fpenv(ieee|ieee) }
 attributes #1 = { denormal_fpenv(preservesign) }
 attributes #2 = { denormal_fpenv(positivezero|positivezero) }
diff --git a/llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll b/llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll
index 8963a7a362c98..a795a35adbba8 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll
@@ -175,9 +175,9 @@ define float @ret_minnum_dynamic_dynamic(float %arg0, float %arg1) #3 {
 }
 
 define float @ret_minnum_noinf_nozero__noinf_nozero(float nofpclass(inf zero) %arg0, float nofpclass(inf zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(inf zero) float @ret_minnum_noinf_nozero__noinf_nozero
+; CHECK-LABEL: define nofpclass(inf) float @ret_minnum_noinf_nozero__noinf_nozero
 ; CHECK-SAME: (float nofpclass(inf zero) [[ARG0:%.*]], float nofpclass(inf zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero) float @llvm.minnum.f32(float nofpclass(inf zero) [[ARG0]], float nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.minnum.f32(float nofpclass(inf zero) [[ARG0]], float nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minnum.f32(float %arg0, float %arg1)
@@ -185,9 +185,9 @@ define float @ret_minnum_noinf_nozero__noinf_nozero(float nofpclass(inf zero) %a
 }
 
 define <2 x float> @ret_minnum_noinf_nozero__noinf_nozero_v2f32(<2 x float> nofpclass(inf zero) %arg0, <2 x float> nofpclass(inf zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(inf zero) <2 x float> @ret_minnum_noinf_nozero__noinf_nozero_v2f32
+; CHECK-LABEL: define nofpclass(inf) <2 x float> @ret_minnum_noinf_nozero__noinf_nozero_v2f32
 ; CHECK-SAME: (<2 x float> nofpclass(inf zero) [[ARG0:%.*]], <2 x float> nofpclass(inf zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero) <2 x float> @llvm.minnum.v2f32(<2 x float> nofpclass(inf zero) [[ARG0]], <2 x float> nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) <2 x float> @llvm.minnum.v2f32(<2 x float> nofpclass(inf zero) [[ARG0]], <2 x float> nofpclass(inf zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret <2 x float> [[CALL]]
 ;
   %call = call <2 x float> @llvm.minnum.v2f32(<2 x float> %arg0, <2 x float> %arg1)
@@ -195,9 +195,9 @@ define <2 x float> @ret_minnum_noinf_nozero__noinf_nozero_v2f32(<2 x float> nofp
 }
 
 define float @ret_minnum_daz_daz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #1 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minnum_daz_daz_nozero__nozero
+; CHECK-LABEL: define float @ret_minnum_daz_daz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minnum.f32(float %arg0, float %arg1)
@@ -205,9 +205,9 @@ define float @ret_minnum_daz_daz_nozero__nozero(float nofpclass(zero) %arg0, flo
 }
 
 define float @ret_minnum_dapz_dapz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #2 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minnum_dapz_dapz_nozero__nozero
+; CHECK-LABEL: define float @ret_minnum_dapz_dapz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR3]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minnum.f32(float %arg0, float %arg1)
@@ -215,9 +215,9 @@ define float @ret_minnum_dapz_dapz_nozero__nozero(float nofpclass(zero) %arg0, f
 }
 
 define float @ret_minnum_dynamic_dynamic_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #3 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minnum_dynamic_dynamic_nozero__nozero
+; CHECK-LABEL: define float @ret_minnum_dynamic_dynamic_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minnum.f32(float %arg0, float %arg1)
@@ -305,9 +305,9 @@ define float @ret_minnum_dapz_dapz_nonzero__nonzero(float nofpclass(nzero) %arg0
 }
 
 define float @ret_minnum_ieee_daz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #4 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minnum_ieee_daz_nozero__nozero
+; CHECK-LABEL: define float @ret_minnum_ieee_daz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR5:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minnum.f32(float %arg0, float %arg1)
@@ -315,9 +315,9 @@ define float @ret_minnum_ieee_daz_nozero__nozero(float nofpclass(zero) %arg0, fl
 }
 
 define float @ret_minnum_daz_ieee_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #5 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minnum_daz_ieee_nozero__nozero
+; CHECK-LABEL: define float @ret_minnum_daz_ieee_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR6:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minnum.f32(float %arg0, float %arg1)
@@ -325,9 +325,9 @@ define float @ret_minnum_daz_ieee_nozero__nozero(float nofpclass(zero) %arg0, fl
 }
 
 define float @ret_minnum_ieee_dapz_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #6 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minnum_ieee_dapz_nozero__nozero
+; CHECK-LABEL: define float @ret_minnum_ieee_dapz_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR7:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minnum.f32(float %arg0, float %arg1)
@@ -335,9 +335,9 @@ define float @ret_minnum_ieee_dapz_nozero__nozero(float nofpclass(zero) %arg0, f
 }
 
 define float @ret_minnum_dapz_ieee_nozero__nozero(float nofpclass(zero) %arg0, float nofpclass(zero) %arg1) #7 {
-; CHECK-LABEL: define nofpclass(zero) float @ret_minnum_dapz_ieee_nozero__nozero
+; CHECK-LABEL: define float @ret_minnum_dapz_ieee_nozero__nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]], float nofpclass(zero) [[ARG1:%.*]]) #[[ATTR8:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(zero) float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
+; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.minnum.f32(float nofpclass(zero) [[ARG0]], float nofpclass(zero) [[ARG1]]) #[[ATTR9]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.minnum.f32(float %arg0, float %arg1)
@@ -504,6 +504,47 @@ define float @ret_maxnum_any__nopos(float %arg0, float nofpclass(pinf psub pnorm
   ret float %call
 }
 
+; Note that a negative subnormal argument may flush to positive zero.
+define float @ret_maxnum_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #3 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_maxnum_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:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.maxnum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.maxnum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_maxnum_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #2 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_maxnum_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:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.maxnum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.maxnum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_minnum_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #3 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_minnum_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:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.minnum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.minnum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
+define float @ret_minnum_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #2 {
+; CHECK-LABEL: define nofpclass(nan inf psub pnorm) float @ret_minnum_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:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf psub pnorm) float @llvm.minnum.f32(float nofpclass(nan inf zero psub pnorm) [[LHS]], float nofpclass(nan inf zero psub pnorm) [[RHS]]) #[[ATTR9]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.minnum.f32(float %lhs, float %rhs)
+  ret float %call
+}
+
 attributes #0 = { denormal_fpenv(ieee|ieee) }
 attributes #1 = { denormal_fpenv(preservesign) }
 attributes #2 = { denormal_fpenv(positivezero|positivezero) }
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll
index 8b8284e8a50ae..0f839c397b94a 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximum.ll
@@ -2064,6 +2064,59 @@ define nofpclass(snan) float @known_nsub__maximum__known_ninf() {
   ret float %result
 }
 
+; A subnormal input is treated as zero in a non-IEEE input denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_psub__maximum__known_nnorm__mode_ieee_positivezero(float nofpclass(nan inf zero nsub norm) %psub) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maximum__known_nnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maximum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maximum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_psub__maximum__known_nnorm__mode_ieee_preservesign(float nofpclass(nan inf zero nsub norm) %psub) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maximum__known_nnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maximum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maximum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+; A subnormal result may be flushed to zero in a non-IEEE output denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_psub__maximum__known_nnorm__mode_positivezero_ieee(float nofpclass(nan inf zero nsub norm) %psub) #4 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maximum__known_nnorm__mode_positivezero_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maximum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maximum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+; The operand fold remains valid when neither operand can be subnormal.
+define nofpclass(snan) float @known_pnorm__maximum__known_nnorm__mode_ieee_positivezero(float nofpclass(nan inf zero sub nnorm) %pnorm) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_pnorm__maximum__known_nnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[PNORM:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    ret float [[PNORM]]
+;
+  %result = call float @llvm.maximum.f32(float %pnorm, float -1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_pnorm__maximum__known_nnorm__mode_ieee_preservesign(float nofpclass(nan inf zero sub nnorm) %pnorm) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_pnorm__maximum__known_nnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[PNORM:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    ret float [[PNORM]]
+;
+  %result = call float @llvm.maximum.f32(float %pnorm, float -1.000000e+00)
+  ret float %result
+}
+
 define nofpclass(snan) float @simplify_multiple_use_maximum(ptr %ptr) {
 ; CHECK-LABEL: define nofpclass(snan) float @simplify_multiple_use_maximum(
 ; CHECK-SAME: ptr [[PTR:%.*]]) {
@@ -2192,3 +2245,6 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 attributes #0 = { denormal_fpenv(preservesign) }
 attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|preservesign) }
+attributes #4 = { denormal_fpenv(positivezero|ieee) }
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll
index 3eeceacbfd1c4..80ba6f1fb853d 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maximumnum.ll
@@ -2069,6 +2069,59 @@ define nofpclass(snan) float @known_nsub__maximumnum__known_ninf() {
   ret float %result
 }
 
+; A subnormal input is treated as zero in a non-IEEE input denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_psub__maximumnum__known_nnorm__mode_ieee_positivezero(float nofpclass(nan inf zero nsub norm) %psub) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maximumnum__known_nnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maximumnum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maximumnum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_psub__maximumnum__known_nnorm__mode_ieee_preservesign(float nofpclass(nan inf zero nsub norm) %psub) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maximumnum__known_nnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maximumnum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maximumnum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+; A subnormal result may be flushed to zero in a non-IEEE output denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_psub__maximumnum__known_nnorm__mode_positivezero_ieee(float nofpclass(nan inf zero nsub norm) %psub) #4 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maximumnum__known_nnorm__mode_positivezero_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maximumnum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maximumnum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+; The operand fold remains valid when neither operand can be subnormal.
+define nofpclass(snan) float @known_pnorm__maximumnum__known_nnorm__mode_ieee_positivezero(float nofpclass(nan inf zero sub nnorm) %pnorm) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_pnorm__maximumnum__known_nnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[PNORM:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    ret float [[PNORM]]
+;
+  %result = call float @llvm.maximumnum.f32(float %pnorm, float -1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_pnorm__maximumnum__known_nnorm__mode_ieee_preservesign(float nofpclass(nan inf zero sub nnorm) %pnorm) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_pnorm__maximumnum__known_nnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[PNORM:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    ret float [[PNORM]]
+;
+  %result = call float @llvm.maximumnum.f32(float %pnorm, float -1.000000e+00)
+  ret float %result
+}
+
 define nofpclass(snan) float @simplify_multiple_use_maximumnum(ptr %ptr) {
 ; CHECK-LABEL: define nofpclass(snan) float @simplify_multiple_use_maximumnum(
 ; CHECK-SAME: ptr [[PTR:%.*]]) {
@@ -2197,3 +2250,6 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 attributes #0 = { denormal_fpenv(preservesign) }
 attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|preservesign) }
+attributes #4 = { denormal_fpenv(positivezero|ieee) }
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll
index 52767c45c7e28..596cb5034279b 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-maxnum.ll
@@ -2071,6 +2071,59 @@ define nofpclass(snan) float @known_nsub__maxnum__known_ninf() {
   ret float %result
 }
 
+; A subnormal input is treated as zero in a non-IEEE input denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_psub__maxnum__known_nnorm__mode_ieee_positivezero(float nofpclass(nan inf zero nsub norm) %psub) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maxnum__known_nnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maxnum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maxnum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_psub__maxnum__known_nnorm__mode_ieee_preservesign(float nofpclass(nan inf zero nsub norm) %psub) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maxnum__known_nnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maxnum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maxnum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+; A subnormal result may be flushed to zero in a non-IEEE output denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_psub__maxnum__known_nnorm__mode_positivezero_ieee(float nofpclass(nan inf zero nsub norm) %psub) #4 {
+; CHECK-LABEL: define nofpclass(snan) float @known_psub__maxnum__known_nnorm__mode_positivezero_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero nsub norm) [[PSUB:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.maxnum.f32(float [[PSUB]], float -1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.maxnum.f32(float %psub, float -1.000000e+00)
+  ret float %result
+}
+
+; The operand fold remains valid when neither operand can be subnormal.
+define nofpclass(snan) float @known_pnorm__maxnum__known_nnorm__mode_ieee_positivezero(float nofpclass(nan inf zero sub nnorm) %pnorm) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_pnorm__maxnum__known_nnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[PNORM:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    ret float [[PNORM]]
+;
+  %result = call float @llvm.maxnum.f32(float %pnorm, float -1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_pnorm__maxnum__known_nnorm__mode_ieee_preservesign(float nofpclass(nan inf zero sub nnorm) %pnorm) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_pnorm__maxnum__known_nnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[PNORM:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    ret float [[PNORM]]
+;
+  %result = call float @llvm.maxnum.f32(float %pnorm, float -1.000000e+00)
+  ret float %result
+}
+
 define nofpclass(snan) float @simplify_multiple_use_maxnum(ptr %ptr) {
 ; CHECK-LABEL: define nofpclass(snan) float @simplify_multiple_use_maxnum(
 ; CHECK-SAME: ptr [[PTR:%.*]]) {
@@ -2199,3 +2252,6 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 attributes #0 = { denormal_fpenv(preservesign) }
 attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|preservesign) }
+attributes #4 = { denormal_fpenv(positivezero|ieee) }
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll
index dabbcb08aea2e..3159228283421 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimum.ll
@@ -2052,6 +2052,59 @@ define nofpclass(snan) float @known_nsub__minimum__known_ninf() {
   ret float %result
 }
 
+; A subnormal input is treated as zero in a non-IEEE input denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_nsub__minimum__known_pnorm__mode_ieee_positivezero(float nofpclass(nan inf zero psub norm) %nsub) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minimum__known_pnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minimum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minimum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_nsub__minimum__known_pnorm__mode_ieee_preservesign(float nofpclass(nan inf zero psub norm) %nsub) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minimum__known_pnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minimum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minimum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+; A subnormal result may be flushed to zero in a non-IEEE output denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_nsub__minimum__known_pnorm__mode_positivezero_ieee(float nofpclass(nan inf zero psub norm) %nsub) #4 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minimum__known_pnorm__mode_positivezero_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minimum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minimum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+; The operand fold remains valid when neither operand can be subnormal.
+define nofpclass(snan) float @known_nnorm__minimum__known_pnorm__mode_ieee_positivezero(float nofpclass(nan inf zero sub pnorm) %nnorm) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nnorm__minimum__known_pnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[NNORM:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    ret float [[NNORM]]
+;
+  %result = call float @llvm.minimum.f32(float %nnorm, float 1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_nnorm__minimum__known_pnorm__mode_ieee_preservesign(float nofpclass(nan inf zero sub pnorm) %nnorm) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nnorm__minimum__known_pnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[NNORM:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    ret float [[NNORM]]
+;
+  %result = call float @llvm.minimum.f32(float %nnorm, float 1.000000e+00)
+  ret float %result
+}
+
 define nofpclass(snan) float @simplify_multiple_use_minimum(ptr %ptr) {
 ; CHECK-LABEL: define nofpclass(snan) float @simplify_multiple_use_minimum(
 ; CHECK-SAME: ptr [[PTR:%.*]]) {
@@ -2181,3 +2234,6 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 attributes #0 = { denormal_fpenv(preservesign) }
 attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|preservesign) }
+attributes #4 = { denormal_fpenv(positivezero|ieee) }
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll
index 39351308a296d..0ccd36b990642 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minimumnum.ll
@@ -2060,6 +2060,59 @@ define nofpclass(snan) float @known_nsub__minimumnum__known_ninf() {
   ret float %result
 }
 
+; A subnormal input is treated as zero in a non-IEEE input denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_nsub__minimumnum__known_pnorm__mode_ieee_positivezero(float nofpclass(nan inf zero psub norm) %nsub) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minimumnum__known_pnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minimumnum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minimumnum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_nsub__minimumnum__known_pnorm__mode_ieee_preservesign(float nofpclass(nan inf zero psub norm) %nsub) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minimumnum__known_pnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minimumnum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minimumnum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+; A subnormal result may be flushed to zero in a non-IEEE output denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_nsub__minimumnum__known_pnorm__mode_positivezero_ieee(float nofpclass(nan inf zero psub norm) %nsub) #4 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minimumnum__known_pnorm__mode_positivezero_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minimumnum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minimumnum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+; The operand fold remains valid when neither operand can be subnormal.
+define nofpclass(snan) float @known_nnorm__minimumnum__known_pnorm__mode_ieee_positivezero(float nofpclass(nan inf zero sub pnorm) %nnorm) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nnorm__minimumnum__known_pnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[NNORM:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    ret float [[NNORM]]
+;
+  %result = call float @llvm.minimumnum.f32(float %nnorm, float 1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_nnorm__minimumnum__known_pnorm__mode_ieee_preservesign(float nofpclass(nan inf zero sub pnorm) %nnorm) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nnorm__minimumnum__known_pnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[NNORM:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    ret float [[NNORM]]
+;
+  %result = call float @llvm.minimumnum.f32(float %nnorm, float 1.000000e+00)
+  ret float %result
+}
+
 define nofpclass(snan) float @simplify_multiple_use_minimumnum(ptr %ptr) {
 ; CHECK-LABEL: define nofpclass(snan) float @simplify_multiple_use_minimumnum(
 ; CHECK-SAME: ptr [[PTR:%.*]]) {
@@ -2188,3 +2241,6 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 attributes #0 = { denormal_fpenv(preservesign) }
 attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|preservesign) }
+attributes #4 = { denormal_fpenv(positivezero|ieee) }
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll
index b0ea63794c9a9..e9514fee8e873 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-minnum.ll
@@ -2061,6 +2061,59 @@ define nofpclass(snan) float @known_nsub__minnum__known_ninf() {
   ret float %result
 }
 
+; A subnormal input is treated as zero in a non-IEEE input denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_nsub__minnum__known_pnorm__mode_ieee_positivezero(float nofpclass(nan inf zero psub norm) %nsub) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minnum__known_pnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minnum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minnum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_nsub__minnum__known_pnorm__mode_ieee_preservesign(float nofpclass(nan inf zero psub norm) %nsub) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minnum__known_pnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minnum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minnum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+; A subnormal result may be flushed to zero in a non-IEEE output denormal mode,
+; so the intrinsic cannot be replaced with the original subnormal operand.
+define nofpclass(snan) float @known_nsub__minnum__known_pnorm__mode_positivezero_ieee(float nofpclass(nan inf zero psub norm) %nsub) #4 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nsub__minnum__known_pnorm__mode_positivezero_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[NSUB:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[RESULT:%.*]] = call nnan nsz float @llvm.minnum.f32(float [[NSUB]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[RESULT]]
+;
+  %result = call float @llvm.minnum.f32(float %nsub, float 1.000000e+00)
+  ret float %result
+}
+
+; The operand fold remains valid when neither operand can be subnormal.
+define nofpclass(snan) float @known_nnorm__minnum__known_pnorm__mode_ieee_positivezero(float nofpclass(nan inf zero sub pnorm) %nnorm) #2 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nnorm__minnum__known_pnorm__mode_ieee_positivezero(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[NNORM:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    ret float [[NNORM]]
+;
+  %result = call float @llvm.minnum.f32(float %nnorm, float 1.000000e+00)
+  ret float %result
+}
+
+define nofpclass(snan) float @known_nnorm__minnum__known_pnorm__mode_ieee_preservesign(float nofpclass(nan inf zero sub pnorm) %nnorm) #3 {
+; CHECK-LABEL: define nofpclass(snan) float @known_nnorm__minnum__known_pnorm__mode_ieee_preservesign(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[NNORM:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    ret float [[NNORM]]
+;
+  %result = call float @llvm.minnum.f32(float %nnorm, float 1.000000e+00)
+  ret float %result
+}
+
 define nofpclass(snan) float @simplify_multiple_use_minnum(ptr %ptr) {
 ; CHECK-LABEL: define nofpclass(snan) float @simplify_multiple_use_minnum(
 ; CHECK-SAME: ptr [[PTR:%.*]]) {
@@ -2189,3 +2242,6 @@ define nofpclass(snan) float @qnan_result_demands_snan_rhs(i1 %cond, float %unkn
 
 attributes #0 = { denormal_fpenv(preservesign) }
 attributes #1 = { denormal_fpenv(dynamic) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|preservesign) }
+attributes #4 = { denormal_fpenv(positivezero|ieee) }



More information about the llvm-commits mailing list