[llvm] [KnownFPClass] Fix input/output subnormal handling for `propagateXorSign` (PR #219837)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 30 12:04:49 PDT 2026
https://github.com/ZERICO2005 created https://github.com/llvm/llvm-project/pull/219837
Fixes the input/output subnormal handling in `KnownFPClass::fmul` and `KnownFPClass::fdiv` as noted here https://github.com/llvm/llvm-project/pull/215014#discussion_r3744309938
I also added `cannotHavePositiveValue(Mode)` and `cannotHaveNegativeValue()` as safer functions for querying if a value can be positive/negative. For example, consider a sign preserving function like `sinh(x)`:
```c++
// sinh(x) can only be positive if x is positive.
if (KnownSrc.isKnownNever(fcPositive))
Known.knownNot(fcPositive); // Unsafe: negative subnormals may flush to +0.0.
// sinh(x) can only be positive if x is positive.
if (KnownSrc.cannotHavePositiveValue(Mode))
Known.knownNot(fcPositive); // Safe: negative subnormals are correctly accounted for
```
`cannotHaveNegativeValue()` is an alias for `isKnownNever(fcNegative)` at the moment, but it may also take the denormal mode in the future. If negative subnormals are guaranteed to flush to zero, it could be changed to `return isKnownNever(fcNegInf | fcNegNormal | fcNegZero) && (isKnownNever(fcNegSubnormal) || Mode.inputsMustBePositiveZero())`
`propagateXorSign` can be optimized further, but my main goal was to ensure that we have a correct baseline to work off of first.
>From 1ccb4434e36e14f395c1d3503193ac81d7b2f868 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Sun, 30 Aug 2026 12:41:00 -0600
Subject: [PATCH] [KnownFPClass] Fix input/output subnormal handling for
propagateXorSign
---
llvm/include/llvm/Support/KnownFPClass.h | 31 ++-
llvm/lib/Support/KnownFPClass.cpp | 4 +-
.../Transforms/Attributor/nofpclass-fdiv.ll | 218 ++++++++++++++++-
.../Transforms/Attributor/nofpclass-fmul.ll | 228 +++++++++++++++++-
4 files changed, 449 insertions(+), 32 deletions(-)
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 32a6515987d87..c0d3b0b8017e0 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -139,7 +139,7 @@ struct KnownFPClass {
return isKnownNever(fcNegative) && isKnownNeverLogicalPosZero(Mode);
}
- /// Return true if it's know this can never be a negative value or a logical
+ /// Return true if it's known this can never be a negative value or a logical
/// 0.
///
/// NaN --> true
@@ -150,6 +150,18 @@ struct KnownFPClass {
return isKnownNever(fcPositive) && isKnownNeverLogicalNegZero(Mode);
}
+ /// Return true if this cannot have a non-NaN positive value when interpreted
+ /// as an input under \p Mode.
+ bool cannotHavePositiveValue(DenormalMode Mode) const {
+ return isKnownNever(fcPositive) &&
+ (isKnownNever(fcNegSubnormal) || !Mode.inputsMayBePositiveZero());
+ }
+
+ /// Return true if this cannot have a non-NaN negative value.
+ ///
+ // TODO: Account for negative subnormals under positive-zero input mode.
+ bool cannotHaveNegativeValue() const { return isKnownNever(fcNegative); }
+
KnownFPClass intersectWith(const KnownFPClass &RHS) const {
return KnownFPClass(KnownFPClasses | RHS.KnownFPClasses,
SignBit == RHS.SignBit ? SignBit : std::nullopt);
@@ -430,14 +442,19 @@ struct KnownFPClass {
// Propagate knowledge for operations whose result sign is the xor of the
// operand signs, such as multiply and divide. This only rules out possible
// non-NaN sign classes. NaNs do not have a constrained sign class here.
- void propagateXorSign(const KnownFPClass &LHS, const KnownFPClass &RHS) {
- if ((LHS.isKnownNever(fcNegative) && RHS.isKnownNever(fcNegative)) ||
- (LHS.isKnownNever(fcPositive) && RHS.isKnownNever(fcPositive)))
+ void propagateXorSign(const KnownFPClass &LHS, const KnownFPClass &RHS,
+ DenormalMode Mode) {
+ if ((LHS.cannotHaveNegativeValue() && RHS.cannotHaveNegativeValue()) ||
+ (LHS.cannotHavePositiveValue(Mode) &&
+ RHS.cannotHavePositiveValue(Mode)))
knownNot(fcNegative);
- if ((LHS.isKnownNever(fcPositive) && RHS.isKnownNever(fcNegative)) ||
- (LHS.isKnownNever(fcNegative) && RHS.isKnownNever(fcPositive)))
- knownNot(fcPositive);
+ if ((LHS.cannotHavePositiveValue(Mode) && RHS.cannotHaveNegativeValue()) ||
+ (LHS.cannotHaveNegativeValue() && RHS.cannotHavePositiveValue(Mode))) {
+ knownNot(fcPosInf | fcPosNormal | fcPosSubnormal);
+ if (!Mode.outputsMayBePositiveZero())
+ knownNot(fcPosZero);
+ }
}
/// Propagate knowledge from a source value that could be a denormal or
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 09bb9c5538a34..8a00c97bd64c9 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -395,7 +395,7 @@ KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS,
// +X * +Y or -X * -Y => +Q
// +X * -Y or -X * +Y => -Q
- Known.propagateXorSign(KnownLHS, KnownRHS);
+ Known.propagateXorSign(KnownLHS, KnownRHS, Mode);
// Inf * Y => Inf or NaN
if (KnownLHS.isKnownAlways(fcInf | fcNan) ||
@@ -465,7 +465,7 @@ KnownFPClass KnownFPClass::fdiv(const KnownFPClass &KnownLHS,
// X / -0.0 => -Inf (or NaN)
// +X / +Y or -X / -Y => +Q
// +X / -Y or -X / +Y => -Q
- Known.propagateXorSign(KnownLHS, KnownRHS);
+ Known.propagateXorSign(KnownLHS, KnownRHS, Mode);
// Normal and subnormal results require two non-zero finite operands.
if ((KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal) &&
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
index f49b13d7b3f78..8f47c4f36b550 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
@@ -870,7 +870,7 @@ define float @ret_known_zero_or_nan_fdiv_known_inf(float nofpclass(inf norm sub)
define float @ret_fdiv_lhs_known_positive_or_nan(float %lhs, float %rhs) {
; CHECK-LABEL: define float @ret_fdiv_lhs_known_positive_or_nan
; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6:[0-9]+]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8:[0-9]+]]
; CHECK-NEXT: [[MUL:%.*]] = fdiv float [[LHS_FABS]], [[RHS]]
; CHECK-NEXT: ret float [[MUL]]
;
@@ -883,7 +883,7 @@ define float @ret_fdiv_lhs_known_positive_or_nan(float %lhs, float %rhs) {
define float @ret_fdiv_rhs_known_positive_or_nan(float %lhs, float %rhs) {
; CHECK-LABEL: define float @ret_fdiv_rhs_known_positive_or_nan
; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[MUL:%.*]] = fdiv float [[LHS]], [[RHS_FABS]]
; CHECK-NEXT: ret float [[MUL]]
;
@@ -896,8 +896,8 @@ define float @ret_fdiv_rhs_known_positive_or_nan(float %lhs, float %rhs) {
define float @ret_fdiv_both_signs_positive_or_nan(float %lhs, float %rhs) {
; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_fdiv_both_signs_positive_or_nan
; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6]]
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[MUL:%.*]] = fdiv float [[LHS_FABS]], [[RHS_FABS]]
; CHECK-NEXT: ret float [[MUL]]
;
@@ -911,8 +911,8 @@ define float @ret_fdiv_both_signs_positive_or_nan(float %lhs, float %rhs) {
define float @ret_fdiv_both_signs_negative_or_nan(float %lhs, float %rhs) {
; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_fdiv_both_signs_negative_or_nan
; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6]]
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[LHS_NEG_FABS:%.*]] = fneg float [[LHS_FABS]]
; CHECK-NEXT: [[RHS_NEG_FABS:%.*]] = fneg float [[RHS_FABS]]
; CHECK-NEXT: [[MUL:%.*]] = fdiv float [[LHS_NEG_FABS]], [[RHS_NEG_FABS]]
@@ -930,8 +930,8 @@ define float @ret_fdiv_both_signs_negative_or_nan(float %lhs, float %rhs) {
define float @ret_fdiv_lhs_negative_rhs_positive(float %lhs, float %rhs) {
; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_lhs_negative_rhs_positive
; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6]]
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[LHS_NEG_FABS:%.*]] = fneg float [[LHS_FABS]]
; CHECK-NEXT: [[MUL:%.*]] = fdiv float [[LHS_NEG_FABS]], [[RHS_FABS]]
; CHECK-NEXT: ret float [[MUL]]
@@ -947,8 +947,8 @@ define float @ret_fdiv_lhs_negative_rhs_positive(float %lhs, float %rhs) {
define float @ret_fdiv_rhs_negative_lhs_positive(float %lhs, float %rhs) {
; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_rhs_negative_lhs_positive
; CHECK-SAME: (float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR6]]
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR6]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[RHS_NEG_FABS:%.*]] = fneg float [[RHS_FABS]]
; CHECK-NEXT: [[MUL:%.*]] = fdiv float [[LHS_FABS]], [[RHS_NEG_FABS]]
; CHECK-NEXT: ret float [[MUL]]
@@ -1056,9 +1056,207 @@ define float @ret_known_inf_or_nan_fdiv_known_inf_or_nan(float nofpclass(norm su
ret float %fdiv
}
+define float @ret_fdiv_ieee_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_ieee_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_daz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_daz_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #2 {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fdiv_dapz_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_dynamic_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #3 {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fdiv_dynamic_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_ieee_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_ieee_no_pos_nonsub_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_daz_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_daz_no_pos_nonsub_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_dapz_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #2 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_dapz_no_pos_nonsub_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_dynamic_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #3 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fdiv_dynamic_no_pos_nonsub_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_ftpz_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #4 {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fdiv_ftpz_dapz_no_pos_no_neg
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR5:[0-9]+]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+; Miscompile regression. A maybe negative-subnormal operand is not known
+; positive-acting even under a positive-zero input mode, an unflushed
+; negative-subnormal / negative-normal can round to +0.0.
+define float @ret_fdiv_ftpz_dapz_lhs_positive_or_nsub_rhs_negative(float nofpclass(ninf nnorm nzero) %arg0, float nofpclass(pinf pzero psub pnorm nsub) %arg1) #4 {
+; CHECK-LABEL: define float @ret_fdiv_ftpz_dapz_lhs_positive_or_nsub_rhs_negative
+; CHECK-SAME: (float nofpclass(ninf nzero nnorm) [[ARG0:%.*]], float nofpclass(pinf pzero sub pnorm) [[ARG1:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+define float @ret_fdiv_ftpz_dapz_lhs_negative_rhs_positive_or_nsub(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nnorm nzero) %arg1) #4 {
+; CHECK-LABEL: define float @ret_fdiv_ftpz_dapz_lhs_negative_rhs_positive_or_nsub
+; CHECK-SAME: (float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nnorm) [[ARG1:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg0, %arg1
+ ret float %fdiv
+}
+
+; A negative subnormal result may be flushed to +0.0, but other positive
+; classes remain impossible.
+define float @ret_fdiv_posnormal_negnormal_mode_ftpz_dapz(float nofpclass(nan inf zero sub nnorm) %lhs, float nofpclass(nan inf zero sub pnorm) %rhs) #4 {
+; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_fdiv_posnormal_negnormal_mode_ftpz_dapz
+; CHECK-SAME: (float nofpclass(nan inf zero sub nnorm) [[LHS:%.*]], float nofpclass(nan inf zero sub pnorm) [[RHS:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv_negnormal_posnormal_mode_ftpz_dapz(float nofpclass(nan inf zero sub pnorm) %lhs, float nofpclass(nan inf zero sub nnorm) %rhs) #4 {
+; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_fdiv_negnormal_posnormal_mode_ftpz_dapz
+; CHECK-SAME: (float nofpclass(nan inf zero sub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero sub nnorm) [[RHS:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv_posnormal_negnormal_mode_dynamic_dynamic(float nofpclass(nan inf zero sub nnorm) %lhs, float nofpclass(nan inf zero sub pnorm) %rhs) #5 {
+; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_fdiv_posnormal_negnormal_mode_dynamic_dynamic
+; CHECK-SAME: (float nofpclass(nan inf zero sub nnorm) [[LHS:%.*]], float nofpclass(nan inf zero sub pnorm) [[RHS:%.*]]) #[[ATTR6:[0-9]+]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv_negnormal_posnormal_mode_dynamic_dynamic(float nofpclass(nan inf zero sub pnorm) %lhs, float nofpclass(nan inf zero sub nnorm) %rhs) #5 {
+; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_fdiv_negnormal_posnormal_mode_dynamic_dynamic
+; CHECK-SAME: (float nofpclass(nan inf zero sub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero sub nnorm) [[RHS:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #5 {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fdiv_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic
+; CHECK-SAME: (float nofpclass(nan inf zero psub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero psub pnorm) [[RHS:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #4 {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fdiv_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz
+; CHECK-SAME: (float nofpclass(nan inf zero psub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero psub pnorm) [[RHS:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv_self_negnormal_negsubnormal_mode_dynamic_dynamic(float noundef nofpclass(nan inf zero psub pnorm) %arg) #5 {
+; CHECK-LABEL: define noundef nofpclass(snan inf zero sub nnorm) float @ret_fdiv_self_negnormal_negsubnormal_mode_dynamic_dynamic
+; CHECK-SAME: (float noundef nofpclass(nan inf zero psub pnorm) [[ARG:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg, %arg
+ ret float %fdiv
+}
+
+define float @ret_fdiv_self_negnormal_negsubnormal_mode_ftpz_dapz(float noundef nofpclass(nan inf zero psub pnorm) %arg) #4 {
+; CHECK-LABEL: define noundef nofpclass(snan inf zero sub nnorm) float @ret_fdiv_self_negnormal_negsubnormal_mode_ftpz_dapz
+; CHECK-SAME: (float noundef nofpclass(nan inf zero psub pnorm) [[ARG:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG]], [[ARG]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %arg, %arg
+ ret float %fdiv
+}
+
attributes #0 = { denormal_fpenv(ieee|ieee) }
attributes #1 = { denormal_fpenv(ieee|preservesign) }
attributes #2 = { denormal_fpenv(ieee|positivezero) }
attributes #3 = { denormal_fpenv(ieee|dynamic) }
+attributes #4 = { denormal_fpenv(positivezero|positivezero) }
+attributes #5 = { denormal_fpenv(dynamic|dynamic) }
+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; TUNIT: {{.*}}
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
index ba98a804afcb4..4e0ab399362e9 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
@@ -618,7 +618,7 @@ define float @ret_known_zero_or_nan_mul_known_inf(float nofpclass(inf norm sub)
define float @ret_fmul_lhs_known_positive_or_nan(float %lhs, float %rhs) {
; CHECK-LABEL: define float @ret_fmul_lhs_known_positive_or_nan(
; CHECK-SAME: float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR2:[0-9]+]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8:[0-9]+]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[LHS_FABS]], [[RHS]]
; CHECK-NEXT: ret float [[MUL]]
;
@@ -631,7 +631,7 @@ define float @ret_fmul_lhs_known_positive_or_nan(float %lhs, float %rhs) {
define float @ret_fmul_rhs_known_positive_or_nan(float %lhs, float %rhs) {
; CHECK-LABEL: define float @ret_fmul_rhs_known_positive_or_nan(
; CHECK-SAME: float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR2]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[LHS]], [[RHS_FABS]]
; CHECK-NEXT: ret float [[MUL]]
;
@@ -644,8 +644,8 @@ define float @ret_fmul_rhs_known_positive_or_nan(float %lhs, float %rhs) {
define float @ret_fmul_both_signs_positive_or_nan(float %lhs, float %rhs) {
; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_fmul_both_signs_positive_or_nan(
; CHECK-SAME: float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR2]]
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR2]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[LHS_FABS]], [[RHS_FABS]]
; CHECK-NEXT: ret float [[MUL]]
;
@@ -659,8 +659,8 @@ define float @ret_fmul_both_signs_positive_or_nan(float %lhs, float %rhs) {
define float @ret_fmul_both_signs_negative_or_nan(float %lhs, float %rhs) {
; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_fmul_both_signs_negative_or_nan(
; CHECK-SAME: float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR2]]
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR2]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[LHS_NEG_FABS:%.*]] = fneg float [[LHS_FABS]]
; CHECK-NEXT: [[RHS_NEG_FABS:%.*]] = fneg float [[RHS_FABS]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[LHS_NEG_FABS]], [[RHS_NEG_FABS]]
@@ -678,8 +678,8 @@ define float @ret_fmul_both_signs_negative_or_nan(float %lhs, float %rhs) {
define float @ret_fmul_lhs_negative_rhs_positive(float %lhs, float %rhs) {
; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fmul_lhs_negative_rhs_positive(
; CHECK-SAME: float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR2]]
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR2]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[LHS_NEG_FABS:%.*]] = fneg float [[LHS_FABS]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[LHS_NEG_FABS]], [[RHS_FABS]]
; CHECK-NEXT: ret float [[MUL]]
@@ -695,8 +695,8 @@ define float @ret_fmul_lhs_negative_rhs_positive(float %lhs, float %rhs) {
define float @ret_fmul_rhs_negative_lhs_positive(float %lhs, float %rhs) {
; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fmul_rhs_negative_lhs_positive(
; CHECK-SAME: float [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR2]]
-; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR2]]
+; CHECK-NEXT: [[LHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[LHS]]) #[[ATTR8]]
+; CHECK-NEXT: [[RHS_FABS:%.*]] = call float @llvm.fabs.f32(float [[RHS]]) #[[ATTR8]]
; CHECK-NEXT: [[RHS_NEG_FABS:%.*]] = fneg float [[RHS_FABS]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[LHS_FABS]], [[RHS_NEG_FABS]]
; CHECK-NEXT: ret float [[MUL]]
@@ -914,7 +914,7 @@ define float @ret_fmul__not_inf__neg1.5(float nofpclass(inf) %x) {
define float @ret_fmul__not_inf__fsub_floor_pat(float nofpclass(inf) %x, float %y) {
; CHECK-LABEL: define nofpclass(inf) float @ret_fmul__not_inf__fsub_floor_pat(
; CHECK-SAME: float nofpclass(inf) [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Y]]) #[[ATTR2]]
+; CHECK-NEXT: [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Y]]) #[[ATTR8]]
; CHECK-NEXT: [[Y_SUB_FLOOR_Y:%.*]] = fsub float [[Y]], [[FLOOR_Y]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[X]], [[Y_SUB_FLOOR_Y]]
; CHECK-NEXT: ret float [[MUL]]
@@ -928,7 +928,7 @@ define float @ret_fmul__not_inf__fsub_floor_pat(float nofpclass(inf) %x, float %
define float @ret_fmul__not_inf__fsub_floor_pat_commute(float nofpclass(inf) %x, float %y) {
; CHECK-LABEL: define nofpclass(inf) float @ret_fmul__not_inf__fsub_floor_pat_commute(
; CHECK-SAME: float nofpclass(inf) [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Y]]) #[[ATTR2]]
+; CHECK-NEXT: [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Y]]) #[[ATTR8]]
; CHECK-NEXT: [[Y_SUB_FLOOR_Y:%.*]] = fsub float [[Y]], [[FLOOR_Y]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[Y_SUB_FLOOR_Y]], [[X]]
; CHECK-NEXT: ret float [[MUL]]
@@ -942,7 +942,7 @@ define float @ret_fmul__not_inf__fsub_floor_pat_commute(float nofpclass(inf) %x,
define float @ret_fmul__not_inf__fsub_floor_pat_wrong_floor_val(float nofpclass(inf) %x, float %y, float %z) {
; CHECK-LABEL: define float @ret_fmul__not_inf__fsub_floor_pat_wrong_floor_val(
; CHECK-SAME: float nofpclass(inf) [[X:%.*]], float [[Y:%.*]], float [[Z:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT: [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Z]]) #[[ATTR2]]
+; CHECK-NEXT: [[FLOOR_Y:%.*]] = call float @llvm.floor.f32(float [[Z]]) #[[ATTR8]]
; CHECK-NEXT: [[Y_SUB_FLOOR_Y:%.*]] = fsub float [[Y]], [[FLOOR_Y]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[X]], [[Y_SUB_FLOOR_Y]]
; CHECK-NEXT: ret float [[MUL]]
@@ -952,3 +952,205 @@ define float @ret_fmul__not_inf__fsub_floor_pat_wrong_floor_val(float nofpclass(
%mul = fmul float %x, %y.sub.floor.y
ret float %mul
}
+
+define float @ret_fmul_ieee_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fmul_ieee_no_pos_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_daz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fmul_daz_no_pos_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #2 {
+; CHECK-LABEL: define float @ret_fmul_dapz_no_pos_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_dynamic_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #3 {
+; CHECK-LABEL: define float @ret_fmul_dynamic_no_pos_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_ieee_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #0 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fmul_ieee_no_pos_nonsub_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_daz_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #1 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fmul_daz_no_pos_nonsub_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_dapz_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #2 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fmul_dapz_no_pos_nonsub_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_dynamic_no_pos_nonsub_no_neg(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #3 {
+; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_fmul_dynamic_no_pos_nonsub_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_ftpz_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #4 {
+; CHECK-LABEL: define float @ret_fmul_ftpz_dapz_no_pos_no_neg(
+; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR5:[0-9]+]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+; Miscompile regression. A maybe negative-subnormal operand is not known
+; positive-acting even under a positive-zero input mode, an unflushed
+; negative-subnormal * negative-normal can round to +0.0.
+define float @ret_fmul_ftpz_dapz_lhs_positive_or_nsub_rhs_negative(float nofpclass(ninf nnorm nzero) %arg0, float nofpclass(pinf pzero psub pnorm nsub) %arg1) #4 {
+; CHECK-LABEL: define float @ret_fmul_ftpz_dapz_lhs_positive_or_nsub_rhs_negative(
+; CHECK-SAME: float nofpclass(ninf nzero nnorm) [[ARG0:%.*]], float nofpclass(pinf pzero sub pnorm) [[ARG1:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+define float @ret_fmul_ftpz_dapz_lhs_negative_rhs_positive_or_nsub(float nofpclass(pinf pzero psub pnorm nsub) %arg0, float nofpclass(ninf nnorm nzero) %arg1) #4 {
+; CHECK-LABEL: define float @ret_fmul_ftpz_dapz_lhs_negative_rhs_positive_or_nsub(
+; CHECK-SAME: float nofpclass(pinf pzero sub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nnorm) [[ARG1:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg0, %arg1
+ ret float %fmul
+}
+
+; A negative subnormal result may be flushed to +0.0, but other positive
+; classes remain impossible.
+define float @ret_fmul_posnormal_negnormal_mode_ftpz_dapz(float nofpclass(nan inf zero sub nnorm) %lhs, float nofpclass(nan inf zero sub pnorm) %rhs) #4 {
+; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_fmul_posnormal_negnormal_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[LHS:%.*]], float nofpclass(nan inf zero sub pnorm) [[RHS:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul_negnormal_posnormal_mode_ftpz_dapz(float nofpclass(nan inf zero sub pnorm) %lhs, float nofpclass(nan inf zero sub nnorm) %rhs) #4 {
+; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_fmul_negnormal_posnormal_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero sub nnorm) [[RHS:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul_posnormal_negnormal_mode_dynamic_dynamic(float nofpclass(nan inf zero sub nnorm) %lhs, float nofpclass(nan inf zero sub pnorm) %rhs) #5 {
+; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_fmul_posnormal_negnormal_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[LHS:%.*]], float nofpclass(nan inf zero sub pnorm) [[RHS:%.*]]) #[[ATTR6:[0-9]+]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul_negnormal_posnormal_mode_dynamic_dynamic(float nofpclass(nan inf zero sub pnorm) %lhs, float nofpclass(nan inf zero sub nnorm) %rhs) #5 {
+; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_fmul_negnormal_posnormal_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero sub nnorm) [[RHS:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #5 {
+; CHECK-LABEL: define nofpclass(nan) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero psub pnorm) [[RHS:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #4 {
+; CHECK-LABEL: define nofpclass(nan) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero psub pnorm) [[RHS:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul_self_negnormal_negsubnormal_mode_dynamic_dynamic(float noundef nofpclass(nan inf zero psub pnorm) %arg) #5 {
+; CHECK-LABEL: define noundef nofpclass(nan ninf nzero nsub nnorm) float @ret_fmul_self_negnormal_negsubnormal_mode_dynamic_dynamic(
+; CHECK-SAME: float noundef nofpclass(nan inf zero psub pnorm) [[ARG:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG]], [[ARG]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg, %arg
+ ret float %fmul
+}
+
+define float @ret_fmul_self_negnormal_negsubnormal_mode_ftpz_dapz(float noundef nofpclass(nan inf zero psub pnorm) %arg) #4 {
+; CHECK-LABEL: define noundef nofpclass(nan ninf nzero nsub nnorm) float @ret_fmul_self_negnormal_negsubnormal_mode_ftpz_dapz(
+; CHECK-SAME: float noundef nofpclass(nan inf zero psub pnorm) [[ARG:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG]], [[ARG]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %arg, %arg
+ ret float %fmul
+}
+
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
+attributes #4 = { denormal_fpenv(positivezero|positivezero) }
+attributes #5 = { denormal_fpenv(dynamic|dynamic) }
More information about the llvm-commits
mailing list