[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