[llvm] [KnownFPClass] Rule out non-zero finite results for fmul and fdiv (PR #231052)
Eric Ross via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 11 17:37:56 PDT 2026
https://github.com/ZERICO2005 updated https://github.com/llvm/llvm-project/pull/231052
>From e8bc2f46443c5e2758d2b3af4ffb022049ab6803 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Sun, 11 Oct 2026 18:29:05 -0600
Subject: [PATCH] [KnownFPClass] Rule out non-zero finite results for fmul and
fdiv
---
llvm/lib/Analysis/ValueTracking.cpp | 14 +--
.../CodeGen/GlobalISel/GISelValueTracking.cpp | 12 +-
llvm/lib/Support/KnownFPClass.cpp | 49 +++++---
.../AArch64/GlobalISel/knownfpclass-ops.mir | 44 +++++++
.../Transforms/Attributor/nofpclass-fdiv.ll | 114 ++++++++++++++++--
.../Transforms/Attributor/nofpclass-fmul.ll | 106 +++++++++++++++-
6 files changed, 291 insertions(+), 48 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1cb2f2eb80a13..bd1a726f12b03 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6143,18 +6143,16 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
if (!WantNan && !WantNegative && !WantPositive)
break;
- KnownFPClass KnownLHS, KnownRHS;
+ KnownFPClass KnownRHS;
computeKnownFPClass(Op->getOperand(1), DemandedElts, fcAllFlags, KnownRHS,
Q, Depth + 1);
- bool KnowSomethingUseful =
- KnownRHS.isKnownNeverNaN() ||
- KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal) ||
- KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal);
+ if (KnownRHS.isUnknown())
+ break;
- if (KnowSomethingUseful)
- computeKnownFPClass(Op->getOperand(0), DemandedElts, fcAllFlags, KnownLHS,
- Q, Depth + 1);
+ KnownFPClass KnownLHS;
+ computeKnownFPClass(Op->getOperand(0), DemandedElts, fcAllFlags, KnownLHS,
+ Q, Depth + 1);
Known = KnownFPClass::fdiv(KnownLHS, KnownRHS, Mode);
break;
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index 3bd31d8227862..2652d11b8a38f 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -2027,16 +2027,14 @@ void GISelValueTracking::computeKnownFPClass(Register R,
if (!WantNan && !WantNegative && !WantPositive)
break;
- KnownFPClass KnownLHS, KnownRHS;
+ KnownFPClass KnownRHS;
computeKnownFPClass(RHS, DemandedElts, fcAllFlags, KnownRHS, Depth + 1);
- bool KnowSomethingUseful =
- KnownRHS.isKnownNeverNaN() ||
- KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal) ||
- KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal);
+ if (KnownRHS.isUnknown())
+ break;
- if (KnowSomethingUseful)
- computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownLHS, Depth + 1);
+ KnownFPClass KnownLHS;
+ computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownLHS, Depth + 1);
Known = KnownFPClass::fdiv(KnownLHS, KnownRHS, Mode);
break;
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 4a756af775930..40fd9999e213f 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -552,21 +552,33 @@ KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS_,
if ((KnownLHS.isKnownNever(fcNegative) &&
KnownRHS.isKnownNever(fcNegative)) ||
(KnownLHS.isKnownNever(fcPositive) && KnownRHS.isKnownNever(fcPositive)))
- Known.knownNot(fcNegative);
+ Known.knownNot(fcNegInf | fcNegZero);
if ((KnownLHS.isKnownNever(fcPositive) &&
KnownRHS.isKnownNever(fcNegative)) ||
(KnownLHS.isKnownNever(fcNegative) && KnownRHS.isKnownNever(fcPositive)))
- Known.knownNot(fcPositive);
+ Known.knownNot(fcPosInf | fcPosZero);
+
+ // Normal and subnormal results require two non-zero finite operands.
+ if ((KnownLHS.isKnownNever(fcPosSubnormal | fcPosNormal) ||
+ KnownRHS.isKnownNever(fcPosSubnormal | fcPosNormal)) &&
+ (KnownLHS.isKnownNever(fcNegSubnormal | fcNegNormal) ||
+ KnownRHS.isKnownNever(fcNegSubnormal | fcNegNormal)))
+ Known.knownNot(fcPosSubnormal | fcPosNormal);
+ if ((KnownLHS.isKnownNever(fcPosSubnormal | fcPosNormal) ||
+ KnownRHS.isKnownNever(fcNegSubnormal | fcNegNormal)) &&
+ (KnownLHS.isKnownNever(fcNegSubnormal | fcNegNormal) ||
+ KnownRHS.isKnownNever(fcPosSubnormal | fcPosNormal)))
+ Known.knownNot(fcNegSubnormal | fcNegNormal);
// Inf * Y => Inf or NaN
if (KnownLHS.isKnownAlways(fcInf | fcNan) ||
KnownRHS.isKnownAlways(fcInf | fcNan))
- Known.knownNot(fcNormal | fcSubnormal | fcZero);
+ Known.knownNot(fcZero);
// 0 * Y => 0 or NaN
if (KnownRHS.isKnownAlways(fcZero | fcNan) ||
KnownLHS.isKnownAlways(fcZero | fcNan))
- Known.knownNot(fcNormal | fcSubnormal | fcInf);
+ Known.knownNot(fcInf);
// 0 * +/-inf => NaN
if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() &&
@@ -623,37 +635,36 @@ KnownFPClass KnownFPClass::fdiv(const KnownFPClass &KnownLHS_,
Known.knownNot(fcNan);
}
- // X / -0.0 => -Inf (or NaN)
// +X / +Y or -X / -Y => +Q
// +X / -Y or -X / +Y => -Q
if ((KnownLHS.isKnownNever(fcNegative) &&
KnownRHS.isKnownNever(fcNegative)) ||
(KnownLHS.isKnownNever(fcPositive) && KnownRHS.isKnownNever(fcPositive)))
- Known.knownNot(fcNegative);
+ Known.knownNot(fcNegInf | fcNegZero);
if ((KnownLHS.isKnownNever(fcPositive) &&
KnownRHS.isKnownNever(fcNegative)) ||
(KnownLHS.isKnownNever(fcNegative) && KnownRHS.isKnownNever(fcPositive)))
- Known.knownNot(fcPositive);
+ Known.knownNot(fcPosInf | fcPosZero);
// Normal and subnormal results require two non-zero finite operands.
- if ((KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal) &&
- KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal)) ||
- (KnownLHS.isKnownNever(fcPosNormal | fcPosSubnormal) &&
- KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal)))
- Known.knownNot(fcNegNormal | fcNegSubnormal);
- if ((KnownLHS.isKnownNever(fcNegNormal | fcNegSubnormal) &&
- KnownRHS.isKnownNever(fcPosNormal | fcPosSubnormal)) ||
- (KnownLHS.isKnownNever(fcPosNormal | fcPosSubnormal) &&
- KnownRHS.isKnownNever(fcNegNormal | fcNegSubnormal)))
- Known.knownNot(fcPosNormal | fcPosSubnormal);
+ if ((KnownLHS.isKnownNever(fcPosSubnormal | fcPosNormal) ||
+ KnownRHS.isKnownNever(fcPosSubnormal | fcPosNormal)) &&
+ (KnownLHS.isKnownNever(fcNegSubnormal | fcNegNormal) ||
+ KnownRHS.isKnownNever(fcNegSubnormal | fcNegNormal)))
+ Known.knownNot(fcPosSubnormal | fcPosNormal);
+ if ((KnownLHS.isKnownNever(fcPosSubnormal | fcPosNormal) ||
+ KnownRHS.isKnownNever(fcNegSubnormal | fcNegNormal)) &&
+ (KnownLHS.isKnownNever(fcNegSubnormal | fcNegNormal) ||
+ KnownRHS.isKnownNever(fcPosSubnormal | fcPosNormal)))
+ Known.knownNot(fcNegSubnormal | fcNegNormal);
// 0 / X => 0 or NaN
if (KnownLHS.isKnownAlways(fcZero))
- Known.knownNot(fcSubnormal | fcNormal | fcInf);
+ Known.knownNot(fcInf);
// X / 0 => NaN or Inf
if (KnownRHS.isKnownAlways(fcZero))
- Known.knownNot(fcFinite);
+ Known.knownNot(fcZero);
return applyOutputDenormalMode(Known, Mode);
}
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownfpclass-ops.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownfpclass-ops.mir
index c3ad8c3881e0b..ebaf21f3126d2 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownfpclass-ops.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownfpclass-ops.mir
@@ -572,6 +572,28 @@ body: |
%copy:_(f32) = COPY %fmul
...
---
+# G_FCANONICALIZE turns sNaN into qNaN. Here we are testing to make sure that
+# both the LHS and RHS are demanded/queried to rule out sNaN.
+name: FMulNoSNaN
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: @FMulNoSNaN
+ ; CHECK-NEXT: %ptr:_ FPClasses:(all) SignBitKnown:?
+ ; CHECK-NEXT: %lhs:_ FPClasses:(all) SignBitKnown:?
+ ; CHECK-NEXT: %rhs:_ FPClasses:(all) SignBitKnown:?
+ ; CHECK-NEXT: %lhs_no_snan:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+ ; CHECK-NEXT: %rhs_no_snan:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+ ; CHECK-NEXT: %fmul:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+ ; CHECK-NEXT: %copy:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+ %ptr:_(p0) = G_IMPLICIT_DEF
+ %lhs:_(f32) = G_LOAD %ptr(p0) :: (load (f32))
+ %rhs:_(f32) = G_LOAD %ptr(p0) :: (load (f32))
+ %lhs_no_snan:_(f32) = G_FCANONICALIZE %lhs
+ %rhs_no_snan:_(f32) = G_FCANONICALIZE %rhs
+ %fmul:_(f32) = G_FMUL %lhs_no_snan, %rhs_no_snan
+ %copy:_(f32) = COPY %fmul
+...
+---
name: FLog
body: |
bb.0:
@@ -889,6 +911,28 @@ body: |
%copy:_(f32) = COPY %fdiv
...
---
+# G_FCANONICALIZE turns sNaN into qNaN. Here we are testing to make sure that
+# both the LHS and RHS are demanded/queried to rule out sNaN.
+name: FDivNoSNaN
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: @FDivNoSNaN
+ ; CHECK-NEXT: %ptr:_ FPClasses:(all) SignBitKnown:?
+ ; CHECK-NEXT: %lhs:_ FPClasses:(all) SignBitKnown:?
+ ; CHECK-NEXT: %rhs:_ FPClasses:(all) SignBitKnown:?
+ ; CHECK-NEXT: %lhs_no_snan:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+ ; CHECK-NEXT: %rhs_no_snan:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+ ; CHECK-NEXT: %fdiv:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+ ; CHECK-NEXT: %copy:_ FPClasses:(qnan inf zero sub norm) SignBitKnown:?
+ %ptr:_(p0) = G_IMPLICIT_DEF
+ %lhs:_(f32) = G_LOAD %ptr(p0) :: (load (f32))
+ %rhs:_(f32) = G_LOAD %ptr(p0) :: (load (f32))
+ %lhs_no_snan:_(f32) = G_FCANONICALIZE %lhs
+ %rhs_no_snan:_(f32) = G_FCANONICALIZE %rhs
+ %fdiv:_(f32) = G_FDIV %lhs_no_snan, %rhs_no_snan
+ %copy:_(f32) = COPY %fdiv
+...
+---
name: FDivSqrt
body: |
bb.0:
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
index 506d082682a0e..12e43495b5426 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fdiv.ll
@@ -589,7 +589,7 @@ define float @ret_fdiv_f32_known_zero_or_nan_lhs(float nofpclass(inf norm sub) %
}
define float @ret_fdiv_f32_known_zero_or_nan_rhs(float %arg0, float nofpclass(inf norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_zero_or_nan_rhs
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_f32_known_zero_or_nan_rhs
; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(inf sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -629,7 +629,7 @@ define float @ret_fdiv_f32_known_pzero_or_nan_lhs(float nofpclass(inf norm sub n
}
define float @ret_fdiv_f32_known_pzero_or_nan_rhs(float %arg0, float nofpclass(inf norm sub nzero) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_pzero_or_nan_rhs
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_f32_known_pzero_or_nan_rhs
; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(inf nzero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -669,7 +669,7 @@ define float @ret_fdiv_f32_known_nzero_or_nan_lhs(float nofpclass(inf norm sub p
}
define float @ret_fdiv_f32_known_nzero_or_nan_rhs(float %arg0, float nofpclass(inf norm sub pzero) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_nzero_or_nan_rhs
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_f32_known_nzero_or_nan_rhs
; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(inf pzero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -709,7 +709,7 @@ define float @ret_fdiv_f32_known_inf_or_nan_lhs(float nofpclass(zero norm sub) %
}
define float @ret_fdiv_f32_known_inf_or_nan_rhs(float %arg0, float nofpclass(zero norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_inf_or_nan_rhs
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_f32_known_inf_or_nan_rhs
; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -729,7 +729,7 @@ define float @ret_fdiv_f32_known_inf_lhs(float nofpclass(nan zero norm sub) %arg
}
define float @ret_fdiv_f32_known_inf_rhs(float %arg0, float nofpclass(nan zero norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_inf_rhs
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_f32_known_inf_rhs
; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(nan zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -749,7 +749,7 @@ define float @ret_fdiv_f32_known_pinf_or_nan_lhs(float nofpclass(ninf zero norm
}
define float @ret_fdiv_f32_known_pinf_or_nan_rhs(float %arg0, float nofpclass(ninf zero norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_pinf_or_nan_rhs
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_f32_known_pinf_or_nan_rhs
; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(ninf zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -769,7 +769,7 @@ define float @ret_fdiv_f32_known_ninf_or_nan_lhs(float nofpclass(pinf zero norm
}
define float @ret_fdiv_f32_known_ninf_or_nan_rhs(float %arg0, float nofpclass(pinf zero norm sub) %arg1) {
-; CHECK-LABEL: define float @ret_fdiv_f32_known_ninf_or_nan_rhs
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv_f32_known_ninf_or_nan_rhs
; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(pinf zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -1037,7 +1037,7 @@ define float @ret_known_inf_or_nan_fdiv_unknown(float nofpclass(norm sub zero) %
}
define float @ret_unknown_fdiv_known_inf_or_nan(float %arg0, float nofpclass(norm sub zero) %arg1) {
-; CHECK-LABEL: define float @ret_unknown_fdiv_known_inf_or_nan
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_unknown_fdiv_known_inf_or_nan
; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(zero sub norm) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -1067,7 +1067,7 @@ define float @fdiv_no_nan(float nofpclass(nan) %arg0, float nofpclass(nan) %arg1
}
define float @fdiv_no_snan(float nofpclass(snan) %arg0, float nofpclass(snan) %arg1) {
-; CHECK-LABEL: define float @fdiv_no_snan
+; CHECK-LABEL: define nofpclass(snan) float @fdiv_no_snan
; CHECK-SAME: (float nofpclass(snan) [[ARG0:%.*]], float nofpclass(snan) [[ARG1:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FDIV]]
@@ -1392,6 +1392,102 @@ define float @ret_fdiv_neginf_posnormal__mode_dynamic_dynamic(float nofpclass(na
ret float %fdiv
}
+; A non-zero finite result requires two non-zero finite operands, so
+; ruling out one sign class on either operand rules out the matching
+; sign of the result.
+
+define float @ret_fdiv__lhs_no_psub_pnorm__rhs_no_psub_pnorm(float nofpclass(psub pnorm) %lhs, float nofpclass(psub pnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fdiv__lhs_no_psub_pnorm__rhs_no_psub_pnorm
+; CHECK-SAME: (float nofpclass(psub pnorm) [[LHS:%.*]], float nofpclass(psub pnorm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_psub_pnorm__rhs_no_nsub_nnorm(float nofpclass(psub pnorm) %lhs, float nofpclass(nsub nnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fdiv__lhs_no_psub_pnorm__rhs_no_nsub_nnorm
+; CHECK-SAME: (float nofpclass(psub pnorm) [[LHS:%.*]], float nofpclass(nsub nnorm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_nsub_nnorm__rhs_no_psub_pnorm(float nofpclass(nsub nnorm) %lhs, float nofpclass(psub pnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fdiv__lhs_no_nsub_nnorm__rhs_no_psub_pnorm
+; CHECK-SAME: (float nofpclass(nsub nnorm) [[LHS:%.*]], float nofpclass(psub pnorm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_nsub_nnorm__rhs_no_nsub_nnorm(float nofpclass(nsub nnorm) %lhs, float nofpclass(nsub nnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fdiv__lhs_no_nsub_nnorm__rhs_no_nsub_nnorm
+; CHECK-SAME: (float nofpclass(nsub nnorm) [[LHS:%.*]], float nofpclass(nsub nnorm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_norm(float nofpclass(norm) %lhs, float %rhs) {
+; CHECK-LABEL: define float @ret_fdiv__lhs_no_norm
+; CHECK-SAME: (float nofpclass(norm) [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv__rhs_no_norm(float %lhs, float nofpclass(norm) %rhs) {
+; CHECK-LABEL: define float @ret_fdiv__rhs_no_norm
+; CHECK-SAME: (float [[LHS:%.*]], float nofpclass(norm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv__lhs_no_sub_norm(float nofpclass(sub norm) %lhs, float %rhs) {
+; CHECK-LABEL: define float @ret_fdiv__lhs_no_sub_norm
+; CHECK-SAME: (float nofpclass(sub norm) [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+define float @ret_fdiv__rhs_no_sub_norm(float %lhs, float nofpclass(sub norm) %rhs) {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fdiv__rhs_no_sub_norm
+; CHECK-SAME: (float [[LHS:%.*]], float nofpclass(sub norm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
+; subnormal / subnormal is always normal, so a non-zero finite result is
+; reachable here.
+define float @ret_fdiv__lhs_no_norm__rhs_no_norm(float nofpclass(norm) %lhs, float nofpclass(norm) %rhs) {
+; CHECK-LABEL: define float @ret_fdiv__lhs_no_norm__rhs_no_norm
+; CHECK-SAME: (float nofpclass(norm) [[LHS:%.*]], float nofpclass(norm) [[RHS:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT: [[FDIV:%.*]] = fdiv float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FDIV]]
+;
+ %fdiv = fdiv float %lhs, %rhs
+ ret float %fdiv
+}
+
attributes #0 = { denormal_fpenv(ieee|ieee) }
attributes #1 = { denormal_fpenv(ieee|preservesign) }
attributes #2 = { denormal_fpenv(ieee|positivezero) }
diff --git a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
index 312bfc76b8874..94f6bdcffacb7 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll
@@ -1014,7 +1014,7 @@ define float @ret_fmul_daz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm)
}
define float @ret_fmul_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #2 {
-; CHECK-LABEL: define float @ret_fmul_dapz_no_pos_no_neg(
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul_dapz_no_pos_no_neg(
; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR3:[0-9]+]] {
; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FMUL]]
@@ -1024,7 +1024,7 @@ define float @ret_fmul_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm)
}
define float @ret_fmul_dynamic_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #3 {
-; CHECK-LABEL: define float @ret_fmul_dynamic_no_pos_no_neg(
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul_dynamic_no_pos_no_neg(
; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR4:[0-9]+]] {
; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FMUL]]
@@ -1074,7 +1074,7 @@ define float @ret_fmul_dynamic_no_pos_nonsub_no_neg(float nofpclass(pinf pzero p
}
define float @ret_fmul_ftpz_dapz_no_pos_no_neg(float nofpclass(pinf pzero psub pnorm) %arg0, float nofpclass(ninf nzero nsub nnorm) %arg1) #4 {
-; CHECK-LABEL: define float @ret_fmul_ftpz_dapz_no_pos_no_neg(
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul_ftpz_dapz_no_pos_no_neg(
; CHECK-SAME: float nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ARG1:%.*]]) #[[ATTR5:[0-9]+]] {
; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]]
; CHECK-NEXT: ret float [[FMUL]]
@@ -1149,7 +1149,7 @@ define float @ret_fmul_negnormal_posnormal_mode_dynamic_dynamic(float nofpclass(
}
define float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #5 {
-; CHECK-LABEL: define nofpclass(nan) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(
+; CHECK-LABEL: define nofpclass(nan nsub nnorm) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(
; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero psub pnorm) [[RHS:%.*]]) #[[ATTR6]] {
; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
; CHECK-NEXT: ret float [[FMUL]]
@@ -1159,7 +1159,7 @@ define float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_dynamic_dynamic(
}
define float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %lhs, float nofpclass(nan inf zero psub pnorm) %rhs) #4 {
-; CHECK-LABEL: define nofpclass(nan) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(
+; CHECK-LABEL: define nofpclass(nan nsub nnorm) float @ret_fmul_negnormal_negsubnormal_both_lhs_rhs_mode_ftpz_dapz(
; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[LHS:%.*]], float nofpclass(nan inf zero psub pnorm) [[RHS:%.*]]) #[[ATTR5]] {
; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
; CHECK-NEXT: ret float [[FMUL]]
@@ -1255,6 +1255,102 @@ define float @ret_fmul_exact_negzero__mode_dynamic_dynamic(float nofpclass(nan i
ret float %fmul
}
+; A non-zero finite result requires two non-zero finite operands, so
+; ruling out one sign class on either operand rules out the matching
+; sign of the result.
+
+define float @ret_fmul__lhs_no_psub_pnorm__rhs_no_psub_pnorm(float nofpclass(psub pnorm) %lhs, float nofpclass(psub pnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fmul__lhs_no_psub_pnorm__rhs_no_psub_pnorm(
+; CHECK-SAME: float nofpclass(psub pnorm) [[LHS:%.*]], float nofpclass(psub pnorm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_psub_pnorm__rhs_no_nsub_nnorm(float nofpclass(psub pnorm) %lhs, float nofpclass(nsub nnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul__lhs_no_psub_pnorm__rhs_no_nsub_nnorm(
+; CHECK-SAME: float nofpclass(psub pnorm) [[LHS:%.*]], float nofpclass(nsub nnorm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_nsub_nnorm__rhs_no_psub_pnorm(float nofpclass(nsub nnorm) %lhs, float nofpclass(psub pnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(psub pnorm) float @ret_fmul__lhs_no_nsub_nnorm__rhs_no_psub_pnorm(
+; CHECK-SAME: float nofpclass(nsub nnorm) [[LHS:%.*]], float nofpclass(psub pnorm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_nsub_nnorm__rhs_no_nsub_nnorm(float nofpclass(nsub nnorm) %lhs, float nofpclass(nsub nnorm) %rhs) {
+; CHECK-LABEL: define nofpclass(nsub nnorm) float @ret_fmul__lhs_no_nsub_nnorm__rhs_no_nsub_nnorm(
+; CHECK-SAME: float nofpclass(nsub nnorm) [[LHS:%.*]], float nofpclass(nsub nnorm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_norm(float nofpclass(norm) %lhs, float %rhs) {
+; CHECK-LABEL: define float @ret_fmul__lhs_no_norm(
+; CHECK-SAME: float nofpclass(norm) [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul__rhs_no_norm(float %lhs, float nofpclass(norm) %rhs) {
+; CHECK-LABEL: define float @ret_fmul__rhs_no_norm(
+; CHECK-SAME: float [[LHS:%.*]], float nofpclass(norm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul__lhs_no_sub_norm(float nofpclass(sub norm) %lhs, float %rhs) {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fmul__lhs_no_sub_norm(
+; CHECK-SAME: float nofpclass(sub norm) [[LHS:%.*]], float [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+define float @ret_fmul__rhs_no_sub_norm(float %lhs, float nofpclass(sub norm) %rhs) {
+; CHECK-LABEL: define nofpclass(sub norm) float @ret_fmul__rhs_no_sub_norm(
+; CHECK-SAME: float [[LHS:%.*]], float nofpclass(sub norm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
+; The only finite non-zero result is subnormal * subnormal = min-subnormal
+; depending on the rounding mode.
+define float @ret_fmul__lhs_no_norm__rhs_no_norm(float nofpclass(norm) %lhs, float nofpclass(norm) %rhs) {
+; CHECK-LABEL: define float @ret_fmul__lhs_no_norm__rhs_no_norm(
+; CHECK-SAME: float nofpclass(norm) [[LHS:%.*]], float nofpclass(norm) [[RHS:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[LHS]], [[RHS]]
+; CHECK-NEXT: ret float [[FMUL]]
+;
+ %fmul = fmul float %lhs, %rhs
+ ret float %fmul
+}
+
attributes #0 = { denormal_fpenv(ieee|ieee) }
attributes #1 = { denormal_fpenv(ieee|preservesign) }
attributes #2 = { denormal_fpenv(ieee|positivezero) }
More information about the llvm-commits
mailing list