[llvm] [GISel] Use GISelValueTracking in isKnownNeverNaN (PR #190542)

Tim Gymnich via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 5 15:23:46 PDT 2026


https://github.com/tgymnich updated https://github.com/llvm/llvm-project/pull/190542

>From 283b2c9ade8b7b496c1dffa3882ae3b236df1fcd Mon Sep 17 00:00:00 2001
From: Tim Gymnich <tim at gymni.ch>
Date: Sat, 4 Apr 2026 19:14:37 -0500
Subject: [PATCH 1/5] [Support] KnownFPClass: add static helpers for
 trig/inverse-trig intrinsics

Add KnownFPClass::tan, sinh, cosh, tanh, asin, acos, atan, and atan2
static helper methods, following the same pattern as the existing sin/cos
helpers. These encode IEEE 754 class semantics for the respective
mathematical functions and can be used by any FP class analysis pass
(e.g., GISelValueTracking) to avoid duplicating the same logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
 llvm/include/llvm/Support/KnownFPClass.h      |  25 ++++
 llvm/lib/Support/KnownFPClass.cpp             | 111 ++++++++++++++++++
 .../CodeGen/GlobalISel/KnownFPClassTest.cpp   |  85 +++++++++-----
 3 files changed, 193 insertions(+), 28 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index c48e0f8b7b65b..b18bfb55eacd1 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -299,6 +299,31 @@ struct KnownFPClass {
   /// Report known values for cos
   LLVM_ABI static KnownFPClass cos(const KnownFPClass &Src);
 
+  /// Report known values for tan
+  LLVM_ABI static KnownFPClass tan(const KnownFPClass &Src);
+
+  /// Report known values for sinh
+  LLVM_ABI static KnownFPClass sinh(const KnownFPClass &Src);
+
+  /// Report known values for cosh
+  LLVM_ABI static KnownFPClass cosh(const KnownFPClass &Src);
+
+  /// Report known values for tanh
+  LLVM_ABI static KnownFPClass tanh(const KnownFPClass &Src);
+
+  /// Report known values for asin
+  LLVM_ABI static KnownFPClass asin(const KnownFPClass &Src);
+
+  /// Report known values for acos
+  LLVM_ABI static KnownFPClass acos(const KnownFPClass &Src);
+
+  /// Report known values for atan
+  LLVM_ABI static KnownFPClass atan(const KnownFPClass &Src);
+
+  /// Report known values for atan2
+  LLVM_ABI static KnownFPClass atan2(const KnownFPClass &LHS,
+                                     const KnownFPClass &RHS);
+
   /// Return true if the sign bit must be 0, ignoring the sign of nans.
   bool signBitIsZeroOrNaN() const { return isKnownNever(fcNegative); }
 
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 4f704eabfc6ec..3d361c80ff154 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -612,6 +612,117 @@ KnownFPClass KnownFPClass::cos(const KnownFPClass &KnownSrc) {
   return sin(KnownSrc);
 }
 
+KnownFPClass KnownFPClass::tan(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  // tan never returns Inf (tan(±Inf) = NaN; tan(finite) = finite).
+  Known.knownNot(fcInf);
+
+  // NaN propagates. tan(±Inf) is NaN.
+  if (KnownSrc.isKnownNeverNaN() && KnownSrc.isKnownNeverInfinity())
+    Known.knownNot(fcNan);
+
+  return Known;
+}
+
+KnownFPClass KnownFPClass::sinh(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  // sinh is sign-preserving: sinh(x) < 0 iff x < 0.
+  if (KnownSrc.isKnownNever(fcNegative))
+    Known.knownNot(fcNegative);
+
+  Known.propagateNaN(KnownSrc);
+
+  return Known;
+}
+
+KnownFPClass KnownFPClass::cosh(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  // cosh(x) >= 1 for all real x; cosh(±Inf) = +Inf. Never negative.
+  Known.knownNot(fcNegative);
+
+  Known.propagateNaN(KnownSrc);
+
+  return Known;
+}
+
+KnownFPClass KnownFPClass::tanh(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  // tanh is bounded to (-1, 1), never Inf.
+  Known.knownNot(fcInf);
+
+  // tanh is sign-preserving: tanh(x) < 0 iff x < 0.
+  if (KnownSrc.isKnownNever(fcNegative))
+    Known.knownNot(fcNegative);
+
+  Known.propagateNaN(KnownSrc);
+
+  return Known;
+}
+
+KnownFPClass KnownFPClass::asin(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  // asin is bounded to [-π/2, π/2], never Inf.
+  Known.knownNot(fcInf);
+
+  // asin is sign-preserving.
+  if (KnownSrc.isKnownNever(fcNegative))
+    Known.knownNot(fcNegative);
+
+  // NaN propagates. asin(x) is also NaN for |x| > 1, so we cannot rule
+  // out NaN without knowing the source is in [-1, 1].
+  Known.propagateNaN(KnownSrc);
+
+  return Known;
+}
+
+KnownFPClass KnownFPClass::acos(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  // acos is bounded to [0, π], never Inf or negative.
+  Known.knownNot(fcInf);
+  Known.knownNot(fcNegative);
+
+  // NaN propagates. acos(x) is also NaN for |x| > 1, so we cannot rule
+  // out NaN without knowing the source is in [-1, 1].
+  Known.propagateNaN(KnownSrc);
+
+  return Known;
+}
+
+KnownFPClass KnownFPClass::atan(const KnownFPClass &KnownSrc) {
+  KnownFPClass Known;
+
+  // atan is bounded to (-π/2, π/2), never Inf. atan(±Inf) = ±π/2 (finite).
+  Known.knownNot(fcInf);
+
+  // atan is sign-preserving: atan(x) < 0 iff x < 0.
+  if (KnownSrc.isKnownNever(fcNegative))
+    Known.knownNot(fcNegative);
+
+  Known.propagateNaN(KnownSrc);
+
+  return Known;
+}
+
+KnownFPClass KnownFPClass::atan2(const KnownFPClass &KnownLHS,
+                                 const KnownFPClass &KnownRHS) {
+  KnownFPClass Known;
+
+  // atan2 result is in (-π, π], never Inf.
+  Known.knownNot(fcInf);
+
+  // NaN if either operand is NaN.
+  if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN())
+    Known.knownNot(fcNan);
+
+  return Known;
+}
+
 KnownFPClass KnownFPClass::fpext(const KnownFPClass &KnownSrc,
                                  const fltSemantics &DstTy,
                                  const fltSemantics &SrcTy) {
diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
index e47e53968b090..c0a1d81952773 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -115,8 +115,8 @@ TEST_F(AArch64GISelMITest, TestFPClassCstZeroFPExt) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcZero | fcNormal, Known.KnownFPClasses);
-  EXPECT_EQ(std::nullopt, Known.SignBit);
+  EXPECT_EQ(fcPosZero, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassCstVecZeroFPExt) {
@@ -141,8 +141,8 @@ TEST_F(AArch64GISelMITest, TestFPClassCstVecZeroFPExt) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcZero | fcNormal, Known.KnownFPClasses);
-  EXPECT_EQ(std::nullopt, Known.SignBit);
+  EXPECT_EQ(fcPosZero, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassCstZeroFPTrunc) {
@@ -164,7 +164,7 @@ TEST_F(AArch64GISelMITest, TestFPClassCstZeroFPTrunc) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcZero | fcPosSubnormal | fcPosNormal, Known.KnownFPClasses);
+  EXPECT_EQ(fcPosFinite | fcNegZero, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
 
@@ -190,7 +190,7 @@ TEST_F(AArch64GISelMITest, TestFPClassCstVecZeroFPTrunc) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcZero | fcPosSubnormal | fcPosNormal, Known.KnownFPClasses);
+  EXPECT_EQ(fcPosFinite | fcNegZero, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
 
@@ -623,7 +623,7 @@ TEST_F(AArch64GISelMITest, TestFPClassCopySignNInfSrc0_NegSign) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcNegFinite | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcQNan | fcNegZero | fcNegNormal, Known.KnownFPClasses);
   EXPECT_EQ(true, Known.SignBit);
 }
 
@@ -649,7 +649,7 @@ TEST_F(AArch64GISelMITest, TestFPClassCopySignNInfSrc0_PosSign) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPosFinite | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcQNan | fcPosZero | fcPosNormal, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
 
@@ -722,7 +722,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAdd) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcAllFlags, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags & ~fcSNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -747,7 +747,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAdd_Zero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcAllFlags & ~fcNegZero, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags & ~fcSNan & ~fcNegZero, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -772,7 +772,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAdd_NegZero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcAllFlags, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags & ~fcSNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -797,14 +797,15 @@ TEST_F(AArch64GISelMITest, TestFPClassFstrictAdd_Zero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcAllFlags & ~fcNegZero, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags & ~fcSNan & ~fcNegZero, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFMul) {
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
-    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %load:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %val:_(s32) = G_FREEZE %load
     %fmul:_(s32) = G_FMUL %val, %val
     %copy_fadd:_(s32) = COPY %fmul
 )";
@@ -821,7 +822,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFMul) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcPositive | fcQNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -847,7 +848,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFMulZero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive, Known.KnownFPClasses);
+  EXPECT_EQ(fcPosZero, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
 
@@ -873,7 +874,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFLogNeg) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcFinite | fcNan | fcNegInf, Known.KnownFPClasses);
+  EXPECT_EQ(fcQNan | fcNegInf | fcPosZero | fcNormal, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -896,7 +897,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFLogPosZero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcFinite | fcNegInf, Known.KnownFPClasses);
+  EXPECT_EQ(fcNegInf | fcPosZero | fcNormal, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -919,7 +920,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFLogNegZero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcFinite | fcNegInf, Known.KnownFPClasses);
+  EXPECT_EQ(fcNegInf | fcPosZero | fcNormal, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -944,7 +945,7 @@ TEST_F(AArch64GISelMITest, TestFPClassCopy) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(~fcNegative, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
 
@@ -996,7 +997,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFLDExp) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcPositive | fcQNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -1021,7 +1022,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFPowIEvenExp) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcPositive | fcQNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -1047,14 +1048,15 @@ TEST_F(AArch64GISelMITest, TestFPClassFPowIPos) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
-  EXPECT_EQ(std::nullopt, Known.SignBit);
+  EXPECT_EQ(fcPositive, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFDiv) {
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
-    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %load:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %val:_(s32) = G_FREEZE %load
     %fdiv:_(s32) = G_FDIV %val, %val
     %copy_fdiv:_(s32) = COPY %fdiv
 )";
@@ -1071,7 +1073,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFDiv) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPosNormal | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcPosNormal | fcQNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -1095,14 +1097,15 @@ TEST_F(AArch64GISelMITest, TestFPClassFDiv_Inf) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive, Known.KnownFPClasses);
+  EXPECT_EQ(fcPosInf, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFRem) {
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
-    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %load:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %val:_(s32) = G_FREEZE %load
     %frem:_(s32) = G_FREM %val, %val
     %copy_frem:_(s32) = COPY %frem
 )";
@@ -1119,10 +1122,35 @@ TEST_F(AArch64GISelMITest, TestFPClassFRem) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcZero | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcZero | fcQNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
+TEST_F(AArch64GISelMITest, TestFPClassFRemSelf_KnownFiniteNonZero) {
+  // X % X where X is a known-finite, known-nonzero value should produce
+  // exactly [+-]0.0 (no NaN possible).
+  StringRef MIRString = R"(
+    %val:_(s32) = G_FCONSTANT float 2.0
+    %frem:_(s32) = G_FREM %val, %val
+    %copy_frem:_(s32) = COPY %frem
+)";
+
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+
+  GISelValueTracking Info(*MF);
+
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+
+  // 2.0 % 2.0 = 0.0 exactly — NaN is impossible since 2.0 is finite and nonzero.
+  EXPECT_EQ(fcZero, Known.KnownFPClasses);
+}
+
 TEST_F(AArch64GISelMITest, TestFPClassShuffleVec) {
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
@@ -1257,3 +1285,4 @@ TEST_F(AArch64GISelMITest, TestFPClassVecInsertElem) {
   EXPECT_EQ(fcPosFinite, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
+

>From 6eb541f71754d448371e044cc626648caee2fd14 Mon Sep 17 00:00:00 2001
From: Tim Gymnich <tim at gymni.ch>
Date: Sat, 4 Apr 2026 19:14:56 -0500
Subject: [PATCH 2/5] [GISel] isKnownNeverNaN: thread GISelValueTracking for
 sharper analysis

Pass GISelValueTracking* through isKnownNeverNaN and isKnownNeverSNaN so
that the implementation can call computeKnownFPClass to derive NaN
information from value tracking, rather than only looking at flags and
direct constant definitions. Update all callers.

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
 llvm/include/llvm/CodeGen/GlobalISel/Utils.h  |  6 +-
 .../lib/CodeGen/GlobalISel/CombinerHelper.cpp |  4 +-
 .../CodeGen/GlobalISel/LegalizerHelper.cpp    |  6 +-
 llvm/lib/CodeGen/GlobalISel/Utils.cpp         | 77 ++-----------------
 llvm/lib/Target/AMDGPU/AMDGPUInstructions.td  |  2 +-
 .../Target/AMDGPU/AMDGPURegBankCombiner.cpp   | 10 +--
 llvm/test/CodeGen/AArch64/known-never-nan.ll  | 39 ++++++----
 .../GlobalISel/clamp-minmax-const-combine.ll  | 31 ++------
 .../GlobalISel/fmed3-min-max-const-combine.ll | 26 +++----
 .../regbankcombiner-clamp-minmax-const.mir    | 35 ++-------
 .../regbankcombiner-fmed3-minmax-const.mir    | 15 ++--
 11 files changed, 75 insertions(+), 176 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
index de6606e7ed14a..0ffec025b7e36 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
@@ -340,12 +340,12 @@ isKnownToBeAPowerOfTwo(Register Val, const MachineRegisterInfo &MRI,
 
 /// Returns true if \p Val can be assumed to never be a NaN. If \p SNaN is true,
 /// this returns if \p Val can be assumed to never be a signaling NaN.
-LLVM_ABI bool isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI,
+LLVM_ABI bool isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI, GISelValueTracking *VT,
                               bool SNaN = false);
 
 /// Returns true if \p Val can be assumed to never be a signaling NaN.
-inline bool isKnownNeverSNaN(Register Val, const MachineRegisterInfo &MRI) {
-  return isKnownNeverNaN(Val, MRI, true);
+inline bool isKnownNeverSNaN(Register Val, const MachineRegisterInfo &MRI, GISelValueTracking *VT) {
+  return isKnownNeverNaN(Val, MRI, VT, true);
 }
 
 LLVM_ABI Align inferAlignFromPtrInfo(MachineFunction &MF,
diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index 177170575fe07..cfeefdb906f71 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -7093,8 +7093,8 @@ unsigned CombinerHelper::getFPMinMaxOpcForSelect(
 CombinerHelper::SelectPatternNaNBehaviour
 CombinerHelper::computeRetValAgainstNaN(Register LHS, Register RHS,
                                         bool IsOrderedComparison) const {
-  bool LHSSafe = isKnownNeverNaN(LHS, MRI);
-  bool RHSSafe = isKnownNeverNaN(RHS, MRI);
+  bool LHSSafe = isKnownNeverNaN(LHS, MRI, VT);
+  bool RHSSafe = isKnownNeverNaN(RHS, MRI, VT);
   // Completely unsafe.
   if (!LHSSafe && !RHSSafe)
     return SelectPatternNaNBehaviour::NOT_APPLICABLE;
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index ac3d1f62ce9cc..b865bcad92444 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -8968,10 +8968,10 @@ LegalizerHelper::lowerFMinNumMaxNum(MachineInstr &MI) {
     // Note this must be done here, and not as an optimization combine in the
     // absence of a dedicate quiet-snan instruction as we're using an
     // omni-purpose G_FCANONICALIZE.
-    if (!isKnownNeverSNaN(Src0, MRI))
+    if (!isKnownNeverSNaN(Src0, MRI, VT))
       Src0 = MIRBuilder.buildFCanonicalize(Ty, Src0, MI.getFlags()).getReg(0);
 
-    if (!isKnownNeverSNaN(Src1, MRI))
+    if (!isKnownNeverSNaN(Src1, MRI, VT))
       Src1 = MIRBuilder.buildFCanonicalize(Ty, Src1, MI.getFlags()).getReg(0);
   }
 
@@ -9011,7 +9011,7 @@ LegalizerHelper::lowerFMinimumMaximum(MachineInstr &MI) {
 
   // Propagate any NaN of both operands
   if (!MI.getFlag(MachineInstr::FmNoNans) &&
-      (!isKnownNeverNaN(Src0, MRI) || isKnownNeverNaN(Src1, MRI))) {
+      (!isKnownNeverNaN(Src0, MRI, VT) || isKnownNeverNaN(Src1, MRI, VT))) {
     auto IsOrdered = MIRBuilder.buildFCmp(CmpInst::FCMP_ORD, CmpTy, Src0, Src1);
 
     LLT ElementTy = Ty.isScalar() ? Ty : Ty.getElementType();
diff --git a/llvm/lib/CodeGen/GlobalISel/Utils.cpp b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
index d019633369163..96e2c9bbbf002 100644
--- a/llvm/lib/CodeGen/GlobalISel/Utils.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
@@ -833,7 +833,7 @@ llvm::ConstantFoldVectorBinop(unsigned Opcode, const Register Op1,
   return FoldedElements;
 }
 
-bool llvm::isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI,
+bool llvm::isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI, GISelValueTracking *VT,
                            bool SNaN) {
   const MachineInstr *DefMI = MRI.getVRegDef(Val);
   if (!DefMI)
@@ -842,78 +842,11 @@ bool llvm::isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI,
   if (DefMI->getFlag(MachineInstr::FmNoNans))
     return true;
 
-  // If the value is a constant, we can obviously see if it is a NaN or not.
-  if (const ConstantFP *FPVal = getConstantFPVRegVal(Val, MRI)) {
-    return !FPVal->getValueAPF().isNaN() ||
-           (SNaN && !FPVal->getValueAPF().isSignaling());
-  }
-
-  if (DefMI->getOpcode() == TargetOpcode::G_BUILD_VECTOR) {
-    for (const auto &Op : DefMI->uses())
-      if (!isKnownNeverNaN(Op.getReg(), MRI, SNaN))
-        return false;
-    return true;
-  }
+  KnownFPClass FPClass = VT->computeKnownFPClass(Val, fcNan);
+  if (SNaN)
+    return FPClass.isKnownNever(fcSNan);
 
-  switch (DefMI->getOpcode()) {
-  default:
-    break;
-  case TargetOpcode::G_FADD:
-  case TargetOpcode::G_FSUB:
-  case TargetOpcode::G_FMUL:
-  case TargetOpcode::G_FDIV:
-  case TargetOpcode::G_FREM:
-  case TargetOpcode::G_FSIN:
-  case TargetOpcode::G_FCOS:
-  case TargetOpcode::G_FTAN:
-  case TargetOpcode::G_FACOS:
-  case TargetOpcode::G_FASIN:
-  case TargetOpcode::G_FATAN:
-  case TargetOpcode::G_FATAN2:
-  case TargetOpcode::G_FCOSH:
-  case TargetOpcode::G_FSINH:
-  case TargetOpcode::G_FTANH:
-  case TargetOpcode::G_FMA:
-  case TargetOpcode::G_FMAD:
-    if (SNaN)
-      return true;
-
-    // TODO: Need isKnownNeverInfinity
-    return false;
-  case TargetOpcode::G_FMINNUM_IEEE:
-  case TargetOpcode::G_FMAXNUM_IEEE: {
-    if (SNaN)
-      return true;
-    // This can return a NaN if either operand is an sNaN, or if both operands
-    // are NaN.
-    return (isKnownNeverNaN(DefMI->getOperand(1).getReg(), MRI) &&
-            isKnownNeverSNaN(DefMI->getOperand(2).getReg(), MRI)) ||
-           (isKnownNeverSNaN(DefMI->getOperand(1).getReg(), MRI) &&
-            isKnownNeverNaN(DefMI->getOperand(2).getReg(), MRI));
-  }
-  case TargetOpcode::G_FMINNUM:
-  case TargetOpcode::G_FMAXNUM: {
-    // Only one needs to be known not-nan, since it will be returned if the
-    // other ends up being one.
-    return isKnownNeverNaN(DefMI->getOperand(1).getReg(), MRI, SNaN) ||
-           isKnownNeverNaN(DefMI->getOperand(2).getReg(), MRI, SNaN);
-  }
-  }
-
-  if (SNaN) {
-    // FP operations quiet. For now, just handle the ones inserted during
-    // legalization.
-    switch (DefMI->getOpcode()) {
-    case TargetOpcode::G_FPEXT:
-    case TargetOpcode::G_FPTRUNC:
-    case TargetOpcode::G_FCANONICALIZE:
-      return true;
-    default:
-      return false;
-    }
-  }
-
-  return false;
+  return FPClass.isKnownNeverNaN();
 }
 
 Align llvm::inferAlignFromPtrInfo(MachineFunction &MF,
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUInstructions.td b/llvm/lib/Target/AMDGPU/AMDGPUInstructions.td
index 9b6443a99ac55..3bea58ce95f0c 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUInstructions.td
+++ b/llvm/lib/Target/AMDGPU/AMDGPUInstructions.td
@@ -844,7 +844,7 @@ class NeverNaNPats<dag ops, list<dag> frags> : PatFrags<ops, frags> {
     return CurDAG->isKnownNeverNaN(SDValue(N,0));
   }];
   let GISelPredicateCode = [{
-    return isKnownNeverNaN(MI.getOperand(0).getReg(), MRI);
+    return isKnownNeverNaN(MI.getOperand(0).getReg(), MRI, VT);
   }];
 }
 
diff --git a/llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp b/llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp
index 0e1d4075ed92e..413d6cb4c1e4a 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp
@@ -265,7 +265,7 @@ bool AMDGPURegBankCombinerImpl::matchFPMinMaxToMed3(
   // nodes(max/min) have same behavior when one input is NaN and other isn't.
   // Don't consider max(min(SNaN, K1), K0) since there is no isKnownNeverQNaN,
   // also post-legalizer inputs to min/max are fcanonicalized (never SNaN).
-  if ((getIEEE() && isFminnumIeee(MI)) || isKnownNeverNaN(Dst, MRI)) {
+  if ((getIEEE() && isFminnumIeee(MI)) || isKnownNeverNaN(Dst, MRI, VT)) {
     // Don't fold single use constant that can't be inlined.
     if ((!MRI.hasOneNonDBGUse(K0->VReg) || TII.isInlineConstant(K0->Value)) &&
         (!MRI.hasOneNonDBGUse(K1->VReg) || TII.isInlineConstant(K1->Value))) {
@@ -295,8 +295,8 @@ bool AMDGPURegBankCombinerImpl::matchFPMinMaxToClamp(MachineInstr &MI,
   // For IEEE=true consider NaN inputs. Only min(max(QNaN, 0.0), 1.0) evaluates
   // to 0.0 requires dx10_clamp = true.
   if ((getIEEE() && getDX10Clamp() && isFminnumIeee(MI) &&
-       isKnownNeverSNaN(Val, MRI)) ||
-      isKnownNeverNaN(MI.getOperand(0).getReg(), MRI)) {
+       isKnownNeverSNaN(Val, MRI, VT)) ||
+      isKnownNeverNaN(MI.getOperand(0).getReg(), MRI, VT)) {
     Reg = Val;
     return true;
   }
@@ -342,9 +342,9 @@ bool AMDGPURegBankCombinerImpl::matchFPMed3ToClamp(MachineInstr &MI,
   // no NaN inputs. Most often MI is marked with nnan fast math flag.
   // For IEEE=true consider NaN inputs. Requires dx10_clamp = true. Safe to fold
   // when Val could be QNaN. If Val can also be SNaN third input should be 0.0.
-  if (isKnownNeverNaN(MI.getOperand(0).getReg(), MRI) ||
+  if (isKnownNeverNaN(MI.getOperand(0).getReg(), MRI, VT) ||
       (getIEEE() && getDX10Clamp() &&
-       (isKnownNeverSNaN(Val, MRI) || isOp3Zero()))) {
+       (isKnownNeverSNaN(Val, MRI, VT) || isOp3Zero()))) {
     Reg = Val;
     return true;
   }
diff --git a/llvm/test/CodeGen/AArch64/known-never-nan.ll b/llvm/test/CodeGen/AArch64/known-never-nan.ll
index bd080e29890e2..13f14dda19e84 100644
--- a/llvm/test/CodeGen/AArch64/known-never-nan.ll
+++ b/llvm/test/CodeGen/AArch64/known-never-nan.ll
@@ -27,18 +27,30 @@ define float @fmaxnm(i32 %i1, i32 %i2) #0 {
 ; If f1 is 0, fmul is NaN because 0.0 * -INF = NaN
 ; Therefore, this is not fmaxnm.
 define float @not_fmaxnm_maybe_nan(i32 %i1, i32 %i2) #0 {
-; CHECK-LABEL: not_fmaxnm_maybe_nan:
-; CHECK:       // %bb.0:
-; CHECK-NEXT:    ucvtf s0, w0
-; CHECK-NEXT:    ucvtf s1, w1
-; CHECK-NEXT:    mov w8, #-8388608 // =0xff800000
-; CHECK-NEXT:    fmov s2, #17.00000000
-; CHECK-NEXT:    fmov s3, w8
-; CHECK-NEXT:    fmul s0, s0, s3
-; CHECK-NEXT:    fadd s1, s1, s2
-; CHECK-NEXT:    fcmp s0, s1
-; CHECK-NEXT:    fcsel s0, s0, s1, pl
-; CHECK-NEXT:    ret
+; CHECK-SD-LABEL: not_fmaxnm_maybe_nan:
+; CHECK-SD:       // %bb.0:
+; CHECK-SD-NEXT:    ucvtf s0, w0
+; CHECK-SD-NEXT:    ucvtf s1, w1
+; CHECK-SD-NEXT:    mov w8, #-8388608 // =0xff800000
+; CHECK-SD-NEXT:    fmov s2, #17.00000000
+; CHECK-SD-NEXT:    fmov s3, w8
+; CHECK-SD-NEXT:    fmul s0, s0, s3
+; CHECK-SD-NEXT:    fadd s1, s1, s2
+; CHECK-SD-NEXT:    fcmp s0, s1
+; CHECK-SD-NEXT:    fcsel s0, s0, s1, pl
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: not_fmaxnm_maybe_nan:
+; CHECK-GI:       // %bb.0:
+; CHECK-GI-NEXT:    ucvtf s0, w0
+; CHECK-GI-NEXT:    ucvtf s1, w1
+; CHECK-GI-NEXT:    mov w8, #-8388608 // =0xff800000
+; CHECK-GI-NEXT:    fmov s2, #17.00000000
+; CHECK-GI-NEXT:    fmov s3, w8
+; CHECK-GI-NEXT:    fmul s0, s0, s3
+; CHECK-GI-NEXT:    fadd s1, s1, s2
+; CHECK-GI-NEXT:    fmax s0, s0, s1
+; CHECK-GI-NEXT:    ret
   %f1 = uitofp i32 %i1 to float
   %fmul = fmul float %f1, 0xfff0000000000000 ; -INFINITY as 64-bit hex
   %f2 = uitofp i32 %i2 to float
@@ -50,6 +62,3 @@ define float @not_fmaxnm_maybe_nan(i32 %i1, i32 %i2) #0 {
 
 attributes #0 = { "no-signed-zeros-fp-math"="true" }
 
-;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
-; CHECK-GI: {{.*}}
-; CHECK-SD: {{.*}}
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/clamp-minmax-const-combine.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/clamp-minmax-const-combine.ll
index 8fd41241b7ae4..9e708c4b0a7a3 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/clamp-minmax-const-combine.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/clamp-minmax-const-combine.ll
@@ -384,9 +384,7 @@ define float @test_min_max_maybe_NaN_input_ieee_false(float %a) #1 {
 ; GFX10-LABEL: test_min_max_maybe_NaN_input_ieee_false:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_mul_f32_e32 v0, 2.0, v0
-; GFX10-NEXT:    v_max_f32_e32 v0, 0, v0
-; GFX10-NEXT:    v_min_f32_e32 v0, 1.0, v0
+; GFX10-NEXT:    v_mul_f32_e64 v0, v0, 2.0 clamp
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX1170-LABEL: test_min_max_maybe_NaN_input_ieee_false:
@@ -415,8 +413,7 @@ define float @test_min_max_maybe_NaN_input_ieee_true_dx10clamp_false(float %a) #
 ; GFX10-LABEL: test_min_max_maybe_NaN_input_ieee_true_dx10clamp_false:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_mul_f32_e32 v0, 2.0, v0
-; GFX10-NEXT:    v_med3_f32 v0, v0, 0, 1.0
+; GFX10-NEXT:    v_mul_f32_e64 v0, v0, 2.0 clamp
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX1170-LABEL: test_min_max_maybe_NaN_input_ieee_true_dx10clamp_false:
@@ -446,17 +443,13 @@ define float @test_max_min_maybe_NaN_input_ieee_true(float %a) #0 {
 ; GFX10-LABEL: test_max_min_maybe_NaN_input_ieee_true:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_mul_f32_e32 v0, 2.0, v0
-; GFX10-NEXT:    v_min_f32_e32 v0, 1.0, v0
-; GFX10-NEXT:    v_max_f32_e32 v0, 0, v0
+; GFX10-NEXT:    v_mul_f32_e64 v0, v0, 2.0 clamp
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX1170-LABEL: test_max_min_maybe_NaN_input_ieee_true:
 ; GFX1170:       ; %bb.0:
 ; GFX1170-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX1170-NEXT:    v_mul_f32_e32 v0, 2.0, v0
-; GFX1170-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX1170-NEXT:    v_minmax_num_f32 v0, v0, 1.0, 0
+; GFX1170-NEXT:    v_mul_f32_e64 v0, v0, 2.0 clamp
 ; GFX1170-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-LABEL: test_max_min_maybe_NaN_input_ieee_true:
@@ -466,9 +459,7 @@ define float @test_max_min_maybe_NaN_input_ieee_true(float %a) #0 {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_mul_f32_e32 v0, 2.0, v0
-; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX12-NEXT:    v_minmax_num_f32 v0, v0, 1.0, 0
+; GFX12-NEXT:    v_mul_f32_e64 v0, v0, 2.0 clamp
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
   %fmul = fmul float %a, 2.0
   %minnum = call float @llvm.minnum.f32(float %fmul, float 1.0)
@@ -480,17 +471,13 @@ define float @test_max_min_maybe_NaN_input_ieee_false(float %a) #1 {
 ; GFX10-LABEL: test_max_min_maybe_NaN_input_ieee_false:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_mul_f32_e32 v0, 2.0, v0
-; GFX10-NEXT:    v_min_f32_e32 v0, 1.0, v0
-; GFX10-NEXT:    v_max_f32_e32 v0, 0, v0
+; GFX10-NEXT:    v_mul_f32_e64 v0, v0, 2.0 clamp
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX1170-LABEL: test_max_min_maybe_NaN_input_ieee_false:
 ; GFX1170:       ; %bb.0:
 ; GFX1170-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX1170-NEXT:    v_mul_f32_e32 v0, 2.0, v0
-; GFX1170-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX1170-NEXT:    v_minmax_num_f32 v0, v0, 1.0, 0
+; GFX1170-NEXT:    v_mul_f32_e64 v0, v0, 2.0 clamp
 ; GFX1170-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-LABEL: test_max_min_maybe_NaN_input_ieee_false:
@@ -500,9 +487,7 @@ define float @test_max_min_maybe_NaN_input_ieee_false(float %a) #1 {
 ; GFX12-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_mul_f32_e32 v0, 2.0, v0
-; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX12-NEXT:    v_minmax_num_f32 v0, v0, 1.0, 0
+; GFX12-NEXT:    v_mul_f32_e64 v0, v0, 2.0 clamp
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
   %fmul = fmul float %a, 2.0
   %minnum = call float @llvm.minnum.f32(float %fmul, float 1.0)
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/fmed3-min-max-const-combine.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/fmed3-min-max-const-combine.ll
index dbcd4eaa2d71d..2677e2e8005d7 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/fmed3-min-max-const-combine.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/fmed3-min-max-const-combine.ll
@@ -553,15 +553,13 @@ define float @test_min_max_maybe_NaN_input_ieee_false(float %a) #1 {
 ; GFX8-LABEL: test_min_max_maybe_NaN_input_ieee_false:
 ; GFX8:       ; %bb.0:
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_max_f32_e32 v0, 2.0, v0
-; GFX8-NEXT:    v_min_f32_e32 v0, 4.0, v0
+; GFX8-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: test_min_max_maybe_NaN_input_ieee_false:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_max_f32_e32 v0, 2.0, v0
-; GFX10-NEXT:    v_min_f32_e32 v0, 4.0, v0
+; GFX10-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX1170-LABEL: test_min_max_maybe_NaN_input_ieee_false:
@@ -594,15 +592,13 @@ define float @test_max_min_maybe_NaN_input_ieee_false(float %a) #1 {
 ; GFX8-LABEL: test_max_min_maybe_NaN_input_ieee_false:
 ; GFX8:       ; %bb.0:
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX8-NEXT:    v_min_f32_e32 v0, 4.0, v0
-; GFX8-NEXT:    v_max_f32_e32 v0, 2.0, v0
+; GFX8-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: test_max_min_maybe_NaN_input_ieee_false:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_min_f32_e32 v0, 4.0, v0
-; GFX10-NEXT:    v_max_f32_e32 v0, 2.0, v0
+; GFX10-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX1170-LABEL: test_max_min_maybe_NaN_input_ieee_false:
@@ -610,7 +606,7 @@ define float @test_max_min_maybe_NaN_input_ieee_false(float %a) #1 {
 ; GFX1170-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX1170-NEXT:    v_max_num_f32_e32 v0, v0, v0
 ; GFX1170-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX1170-NEXT:    v_minmax_num_f32 v0, v0, 4.0, 2.0
+; GFX1170-NEXT:    v_med3_num_f32 v0, v0, 2.0, 4.0
 ; GFX1170-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-LABEL: test_max_min_maybe_NaN_input_ieee_false:
@@ -622,7 +618,7 @@ define float @test_max_min_maybe_NaN_input_ieee_false(float %a) #1 {
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-NEXT:    v_max_num_f32_e32 v0, v0, v0
 ; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX12-NEXT:    v_minmax_num_f32 v0, v0, 4.0, 2.0
+; GFX12-NEXT:    v_med3_num_f32 v0, v0, 2.0, 4.0
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
   %minnum = call float @llvm.minnum.f32(float %a, float 4.0)
   %fmed = call float @llvm.maxnum.f32(float %minnum, float 2.0)
@@ -635,16 +631,14 @@ define float @test_max_min_maybe_NaN_input_ieee_true(float %a) #0 {
 ; GFX8:       ; %bb.0:
 ; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX8-NEXT:    v_mul_f32_e32 v0, 1.0, v0
-; GFX8-NEXT:    v_min_f32_e32 v0, 4.0, v0
-; GFX8-NEXT:    v_max_f32_e32 v0, 2.0, v0
+; GFX8-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
 ; GFX8-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: test_max_min_maybe_NaN_input_ieee_true:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX10-NEXT:    v_max_f32_e32 v0, v0, v0
-; GFX10-NEXT:    v_min_f32_e32 v0, 4.0, v0
-; GFX10-NEXT:    v_max_f32_e32 v0, 2.0, v0
+; GFX10-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX1170-LABEL: test_max_min_maybe_NaN_input_ieee_true:
@@ -652,7 +646,7 @@ define float @test_max_min_maybe_NaN_input_ieee_true(float %a) #0 {
 ; GFX1170-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX1170-NEXT:    v_max_num_f32_e32 v0, v0, v0
 ; GFX1170-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX1170-NEXT:    v_minmax_num_f32 v0, v0, 4.0, 2.0
+; GFX1170-NEXT:    v_med3_num_f32 v0, v0, 2.0, 4.0
 ; GFX1170-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-LABEL: test_max_min_maybe_NaN_input_ieee_true:
@@ -664,7 +658,7 @@ define float @test_max_min_maybe_NaN_input_ieee_true(float %a) #0 {
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-NEXT:    v_max_num_f32_e32 v0, v0, v0
 ; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX12-NEXT:    v_minmax_num_f32 v0, v0, 4.0, 2.0
+; GFX12-NEXT:    v_med3_num_f32 v0, v0, 2.0, 4.0
 ; GFX12-NEXT:    s_setpc_b64 s[30:31]
   %minnum = call float @llvm.minnum.f32(float %a, float 4.0)
   %fmed = call float @llvm.maxnum.f32(float %minnum, float 2.0)
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/regbankcombiner-clamp-minmax-const.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/regbankcombiner-clamp-minmax-const.mir
index 70fd67363648d..c3eed50092056 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/regbankcombiner-clamp-minmax-const.mir
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/regbankcombiner-clamp-minmax-const.mir
@@ -441,13 +441,8 @@ body: |
     ; CHECK-NEXT: [[C:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     ; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
     ; CHECK-NEXT: [[FMUL:%[0-9]+]]:vgpr(s32) = G_FMUL [[COPY]], [[COPY1]]
-    ; CHECK-NEXT: [[C1:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 0.000000e+00
-    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:vgpr(s32) = COPY [[C1]](s32)
-    ; CHECK-NEXT: [[FMAXNUM:%[0-9]+]]:vgpr(s32) = G_FMAXNUM [[FMUL]], [[COPY2]]
-    ; CHECK-NEXT: [[C2:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 1.000000e+00
-    ; CHECK-NEXT: [[COPY3:%[0-9]+]]:vgpr(s32) = COPY [[C2]](s32)
-    ; CHECK-NEXT: [[FMINNUM:%[0-9]+]]:vgpr(s32) = G_FMINNUM [[FMAXNUM]], [[COPY3]]
-    ; CHECK-NEXT: $vgpr0 = COPY [[FMINNUM]](s32)
+    ; CHECK-NEXT: [[AMDGPU_CLAMP:%[0-9]+]]:vgpr(s32) = G_AMDGPU_CLAMP [[FMUL]]
+    ; CHECK-NEXT: $vgpr0 = COPY [[AMDGPU_CLAMP]](s32)
     %0:vgpr(s32) = COPY $vgpr0
     %2:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     %9:vgpr(s32) = COPY %2(s32)
@@ -481,13 +476,9 @@ body: |
     ; CHECK-NEXT: [[C:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     ; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
     ; CHECK-NEXT: [[FMUL:%[0-9]+]]:vgpr(s32) = G_FMUL [[COPY]], [[COPY1]]
-    ; CHECK-NEXT: [[C1:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 0.000000e+00
     ; CHECK-NEXT: [[FCANONICALIZE:%[0-9]+]]:vgpr(s32) = G_FCANONICALIZE [[FMUL]]
-    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:vgpr(s32) = COPY [[C1]](s32)
-    ; CHECK-NEXT: [[C2:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 1.000000e+00
-    ; CHECK-NEXT: [[COPY3:%[0-9]+]]:vgpr(s32) = COPY [[C2]](s32)
-    ; CHECK-NEXT: [[AMDGPU_FMED3_:%[0-9]+]]:vgpr(s32) = G_AMDGPU_FMED3 [[FCANONICALIZE]], [[COPY2]], [[COPY3]]
-    ; CHECK-NEXT: $vgpr0 = COPY [[AMDGPU_FMED3_]](s32)
+    ; CHECK-NEXT: [[AMDGPU_CLAMP:%[0-9]+]]:vgpr(s32) = G_AMDGPU_CLAMP [[FCANONICALIZE]]
+    ; CHECK-NEXT: $vgpr0 = COPY [[AMDGPU_CLAMP]](s32)
     %0:vgpr(s32) = COPY $vgpr0
     %2:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     %10:vgpr(s32) = COPY %2(s32)
@@ -522,14 +513,9 @@ body: |
     ; CHECK-NEXT: [[C:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     ; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
     ; CHECK-NEXT: [[FMUL:%[0-9]+]]:vgpr(s32) = G_FMUL [[COPY]], [[COPY1]]
-    ; CHECK-NEXT: [[C1:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 1.000000e+00
     ; CHECK-NEXT: [[FCANONICALIZE:%[0-9]+]]:vgpr(s32) = G_FCANONICALIZE [[FMUL]]
-    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:vgpr(s32) = COPY [[C1]](s32)
-    ; CHECK-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:vgpr(s32) = G_FMINNUM_IEEE [[FCANONICALIZE]], [[COPY2]]
-    ; CHECK-NEXT: [[C2:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 0.000000e+00
-    ; CHECK-NEXT: [[COPY3:%[0-9]+]]:vgpr(s32) = COPY [[C2]](s32)
-    ; CHECK-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:vgpr(s32) = G_FMAXNUM_IEEE [[FMINNUM_IEEE]], [[COPY3]]
-    ; CHECK-NEXT: $vgpr0 = COPY [[FMAXNUM_IEEE]](s32)
+    ; CHECK-NEXT: [[AMDGPU_CLAMP:%[0-9]+]]:vgpr(s32) = G_AMDGPU_CLAMP [[FCANONICALIZE]]
+    ; CHECK-NEXT: $vgpr0 = COPY [[AMDGPU_CLAMP]](s32)
     %0:vgpr(s32) = COPY $vgpr0
     %2:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     %10:vgpr(s32) = COPY %2(s32)
@@ -564,13 +550,8 @@ body: |
     ; CHECK-NEXT: [[C:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     ; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
     ; CHECK-NEXT: [[FMUL:%[0-9]+]]:vgpr(s32) = G_FMUL [[COPY]], [[COPY1]]
-    ; CHECK-NEXT: [[C1:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 1.000000e+00
-    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:vgpr(s32) = COPY [[C1]](s32)
-    ; CHECK-NEXT: [[FMINNUM:%[0-9]+]]:vgpr(s32) = G_FMINNUM [[FMUL]], [[COPY2]]
-    ; CHECK-NEXT: [[C2:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 0.000000e+00
-    ; CHECK-NEXT: [[COPY3:%[0-9]+]]:vgpr(s32) = COPY [[C2]](s32)
-    ; CHECK-NEXT: [[FMAXNUM:%[0-9]+]]:vgpr(s32) = G_FMAXNUM [[FMINNUM]], [[COPY3]]
-    ; CHECK-NEXT: $vgpr0 = COPY [[FMAXNUM]](s32)
+    ; CHECK-NEXT: [[AMDGPU_CLAMP:%[0-9]+]]:vgpr(s32) = G_AMDGPU_CLAMP [[FMUL]]
+    ; CHECK-NEXT: $vgpr0 = COPY [[AMDGPU_CLAMP]](s32)
     %0:vgpr(s32) = COPY $vgpr0
     %2:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     %9:vgpr(s32) = COPY %2(s32)
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/regbankcombiner-fmed3-minmax-const.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/regbankcombiner-fmed3-minmax-const.mir
index 2f41d86100040..a53e97af0d028 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/regbankcombiner-fmed3-minmax-const.mir
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/regbankcombiner-fmed3-minmax-const.mir
@@ -469,11 +469,10 @@ body: |
     ; CHECK-NEXT: [[COPY:%[0-9]+]]:vgpr(s32) = COPY $vgpr0
     ; CHECK-NEXT: [[C:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     ; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
-    ; CHECK-NEXT: [[FMAXNUM:%[0-9]+]]:vgpr(s32) = G_FMAXNUM [[COPY]], [[COPY1]]
     ; CHECK-NEXT: [[C1:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 4.000000e+00
     ; CHECK-NEXT: [[COPY2:%[0-9]+]]:vgpr(s32) = COPY [[C1]](s32)
-    ; CHECK-NEXT: [[FMINNUM:%[0-9]+]]:vgpr(s32) = G_FMINNUM [[FMAXNUM]], [[COPY2]]
-    ; CHECK-NEXT: $vgpr0 = COPY [[FMINNUM]](s32)
+    ; CHECK-NEXT: [[AMDGPU_FMED3_:%[0-9]+]]:vgpr(s32) = G_AMDGPU_FMED3 [[COPY]], [[COPY1]], [[COPY2]]
+    ; CHECK-NEXT: $vgpr0 = COPY [[AMDGPU_FMED3_]](s32)
     %0:vgpr(s32) = COPY $vgpr0
     %2:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     %7:vgpr(s32) = COPY %2(s32)
@@ -502,11 +501,10 @@ body: |
     ; CHECK-NEXT: [[COPY:%[0-9]+]]:vgpr(s32) = COPY $vgpr0
     ; CHECK-NEXT: [[C:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 4.000000e+00
     ; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
-    ; CHECK-NEXT: [[FMINNUM:%[0-9]+]]:vgpr(s32) = G_FMINNUM [[COPY]], [[COPY1]]
     ; CHECK-NEXT: [[C1:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     ; CHECK-NEXT: [[COPY2:%[0-9]+]]:vgpr(s32) = COPY [[C1]](s32)
-    ; CHECK-NEXT: [[FMAXNUM:%[0-9]+]]:vgpr(s32) = G_FMAXNUM [[FMINNUM]], [[COPY2]]
-    ; CHECK-NEXT: $vgpr0 = COPY [[FMAXNUM]](s32)
+    ; CHECK-NEXT: [[AMDGPU_FMED3_:%[0-9]+]]:vgpr(s32) = G_AMDGPU_FMED3 [[COPY]], [[COPY2]], [[COPY1]]
+    ; CHECK-NEXT: $vgpr0 = COPY [[AMDGPU_FMED3_]](s32)
     %0:vgpr(s32) = COPY $vgpr0
     %2:sgpr(s32) = G_FCONSTANT float 4.000000e+00
     %7:vgpr(s32) = COPY %2(s32)
@@ -536,11 +534,10 @@ body: |
     ; CHECK-NEXT: [[C:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 4.000000e+00
     ; CHECK-NEXT: [[FCANONICALIZE:%[0-9]+]]:vgpr(s32) = G_FCANONICALIZE [[COPY]]
     ; CHECK-NEXT: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
-    ; CHECK-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:vgpr(s32) = G_FMINNUM_IEEE [[FCANONICALIZE]], [[COPY1]]
     ; CHECK-NEXT: [[C1:%[0-9]+]]:sgpr(s32) = G_FCONSTANT float 2.000000e+00
     ; CHECK-NEXT: [[COPY2:%[0-9]+]]:vgpr(s32) = COPY [[C1]](s32)
-    ; CHECK-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:vgpr(s32) = G_FMAXNUM_IEEE [[FMINNUM_IEEE]], [[COPY2]]
-    ; CHECK-NEXT: $vgpr0 = COPY [[FMAXNUM_IEEE]](s32)
+    ; CHECK-NEXT: [[AMDGPU_FMED3_:%[0-9]+]]:vgpr(s32) = G_AMDGPU_FMED3 [[FCANONICALIZE]], [[COPY2]], [[COPY1]]
+    ; CHECK-NEXT: $vgpr0 = COPY [[AMDGPU_FMED3_]](s32)
     %0:vgpr(s32) = COPY $vgpr0
     %2:sgpr(s32) = G_FCONSTANT float 4.000000e+00
     %7:vgpr(s32) = G_FCANONICALIZE %0

>From 31c66e2de3f61d95e030a6812886214019b51f6d Mon Sep 17 00:00:00 2001
From: Tim Gymnich <tim at gymni.ch>
Date: Sat, 4 Apr 2026 19:15:19 -0500
Subject: [PATCH 3/5] [GISel] computeKnownFPClass: backport improvements from
 ValueTracking
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Bring GISelValueTracking::computeKnownFPClass closer to parity with the
IR-level ValueTracking::computeKnownFPClass by delegating to the
centralized KnownFPClass static helpers instead of hand-rolling logic:

- G_FSQRT: apply nsz flag to rule out -0.0 result
- G_FSIN/G_FCOS: use KnownFPClass::sin/cos helpers
- G_FTAN/G_FSINH/G_FCOSH/G_FTANH/G_FASIN/G_FACOS/G_FATAN/G_FATAN2:
  use the new trig helpers added to KnownFPClass
- G_FEXP/G_FEXP2/G_FEXP10: use KnownFPClass::exp helper
- G_FLOG/G_FLOG2/G_FLOG10: use KnownFPClass::log helper
- G_FPOWI: use KnownFPClass::powi helper
- G_FLDEXP: use KnownFPClass::ldexp helper
- G_FADD/G_FSUB: use KnownFPClass::fadd/fadd_self/fsub helpers
- G_FMUL: use KnownFPClass::square/fmul helpers; add constant-RHS
  specialization via the APFloat overload; detect x-floor(x) pattern
  (values in [0,1)) to rule out overflow when the other operand is finite
- G_FDIV: use KnownFPClass::fdiv/fdiv_self helpers
- G_FREM: use KnownFPClass::frem_self for X%X; add early-exit when NaN
  information is not needed (matching G_FDIV)
- G_FMA: use KnownFPClass::fma_square/fma helpers; pre-apply FMF flags
  to source classes before passing to helpers for tighter results
- G_FMAXNUM/G_FMINNUM/etc: use KnownFPClass::minMaxLike helper
- G_FCANONICALIZE: use KnownFPClass::canonicalize helper (removes
  redundant explicit knownNot(fcSNan) — the helper already does it)
- G_FPEXT: fix bug where the source register was passed as the result
  register, causing infinite recursion on non-constant inputs
- G_FPTRUNC: delegate to KnownFPClass::fptrunc helper
- G_SITOFP/G_UITOFP: use KnownBits analysis to derive sign and
  non-zero information
- RoundToIntegral: fix InterestedSrcs cross-widening (was a no-op)
- All self-operand paths (fdiv, frem, fmul, fadd) guarded with
  isGuaranteedNotToBeUndef to avoid wrong precision on undef inputs
- Replace scope_exit-based sNaN clearing with explicit knownNot(fcSNan)
  at each control-flow exit for clarity

Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
 .../llvm/CodeGen/GlobalISel/MIPatternMatch.h  |   6 +
 .../CodeGen/GlobalISel/GISelValueTracking.cpp | 772 +++++++++---------
 .../CodeGen/GlobalISel/KnownFPClassTest.cpp   | 312 +++++++
 3 files changed, 708 insertions(+), 382 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h b/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h
index 8db99ba4ed883..db1f4a89d3469 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h
@@ -761,6 +761,12 @@ inline UnaryOp_match<SrcTy, TargetOpcode::G_FSQRT> m_GFSqrt(const SrcTy &Src) {
   return UnaryOp_match<SrcTy, TargetOpcode::G_FSQRT>(Src);
 }
 
+template <typename SrcTy>
+inline UnaryOp_match<SrcTy, TargetOpcode::G_FFLOOR>
+m_GFFloor(const SrcTy &Src) {
+  return UnaryOp_match<SrcTy, TargetOpcode::G_FFLOOR>(Src);
+}
+
 // General helper for generic MI compares, i.e. G_ICMP and G_FCMP
 // TODO: Allow checking a specific predicate.
 template <typename Pred_P, typename LHS_P, typename RHS_P, unsigned Opcode,
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index f0b455fbdc7d0..073bae76f6da3 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -868,13 +868,6 @@ void GISelValueTracking::computeKnownBitsImpl(Register R, KnownBits &Known,
   LLVM_DEBUG(dumpResult(MI, Known, Depth));
 }
 
-static bool outputDenormalIsIEEEOrPosZero(const MachineFunction &MF, LLT Ty) {
-  Ty = Ty.getScalarType();
-  DenormalMode Mode = MF.getDenormalMode(getFltSemanticForLLT(Ty));
-  return Mode.Output == DenormalMode::IEEE ||
-         Mode.Output == DenormalMode::PositiveZero;
-}
-
 void GISelValueTracking::computeKnownFPClass(Register R, KnownFPClass &Known,
                                              FPClassTest InterestedClasses,
                                              unsigned Depth) {
@@ -884,6 +877,16 @@ void GISelValueTracking::computeKnownFPClass(Register R, KnownFPClass &Known,
   computeKnownFPClass(R, DemandedElts, InterestedClasses, Known, Depth);
 }
 
+/// Return true if this value is known to be the fractional part x - floor(x),
+/// which lies in [0, 1). This implies the value cannot introduce overflow in a
+/// fmul when the other operand is known finite.
+static bool isAbsoluteValueULEOne(Register R, const MachineRegisterInfo &MRI) {
+  using namespace MIPatternMatch;
+  Register SubX;
+  return mi_match(R, MRI,
+                  m_GFSub(m_Reg(SubX), m_GFFloor(m_DeferredReg(SubX))));
+}
+
 void GISelValueTracking::computeKnownFPClassForFPTrunc(
     const MachineInstr &MI, const APInt &DemandedElts,
     FPClassTest InterestedClasses, KnownFPClass &Known, unsigned Depth) {
@@ -895,15 +898,7 @@ void GISelValueTracking::computeKnownFPClassForFPTrunc(
   KnownFPClass KnownSrc;
   computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                       Depth + 1);
-
-  // Sign should be preserved
-  // TODO: Handle cannot be ordered greater than zero
-  if (KnownSrc.cannotBeOrderedLessThanZero())
-    Known.knownNot(KnownFPClass::OrderedLessThanZeroMask);
-
-  Known.propagateNaN(KnownSrc, true);
-
-  // Infinity needs a range check.
+  Known = KnownFPClass::fptrunc(KnownSrc);
 }
 
 void GISelValueTracking::computeKnownFPClass(Register R,
@@ -1065,6 +1060,7 @@ void GISelValueTracking::computeKnownFPClass(Register R,
   case TargetOpcode::G_FMA:
   case TargetOpcode::G_STRICT_FMA:
   case TargetOpcode::G_FMAD: {
+    Known.knownNot(fcSNan);
     if ((InterestedClasses & fcNegative) == fcNone)
       break;
 
@@ -1072,19 +1068,43 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     Register B = MI.getOperand(2).getReg();
     Register C = MI.getOperand(3).getReg();
 
-    if (A != B)
-      break;
-
-    // The multiply cannot be -0 and therefore the add can't be -0
-    Known.knownNot(fcNegZero);
-
-    // x * x + y is non-negative if y is non-negative.
-    KnownFPClass KnownAddend;
-    computeKnownFPClass(C, DemandedElts, InterestedClasses, KnownAddend,
-                        Depth + 1);
+    DenormalMode Mode =
+        MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
 
-    if (KnownAddend.cannotBeOrderedLessThanZero())
-      Known.knownNot(fcNegative);
+    if (A == B && isGuaranteedNotToBeUndef(A, MRI, Depth + 1)) {
+      // x * x + y
+      KnownFPClass KnownSrc, KnownAddend;
+      computeKnownFPClass(C, DemandedElts, InterestedClasses, KnownAddend,
+                          Depth + 1);
+      computeKnownFPClass(A, DemandedElts, InterestedClasses, KnownSrc,
+                          Depth + 1);
+      if (KnownNotFromFlags) {
+        KnownSrc.knownNot(KnownNotFromFlags);
+        KnownAddend.knownNot(KnownNotFromFlags);
+      }
+      Known = KnownFPClass::fma_square(KnownSrc, KnownAddend, Mode);
+    } else {
+      KnownFPClass KnownSrc[3];
+      computeKnownFPClass(A, DemandedElts, InterestedClasses, KnownSrc[0],
+                          Depth + 1);
+      if (KnownSrc[0].isUnknown())
+        break;
+      computeKnownFPClass(B, DemandedElts, InterestedClasses, KnownSrc[1],
+                          Depth + 1);
+      if (KnownSrc[1].isUnknown())
+        break;
+      computeKnownFPClass(C, DemandedElts, InterestedClasses, KnownSrc[2],
+                          Depth + 1);
+      if (KnownSrc[2].isUnknown())
+        break;
+      if (KnownNotFromFlags) {
+        KnownSrc[0].knownNot(KnownNotFromFlags);
+        KnownSrc[1].knownNot(KnownNotFromFlags);
+        KnownSrc[2].knownNot(KnownNotFromFlags);
+      }
+      Known = KnownFPClass::fma(KnownSrc[0], KnownSrc[1], KnownSrc[2], Mode);
+    }
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FSQRT:
@@ -1095,20 +1115,14 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       InterestedSrcs |= KnownFPClass::OrderedLessThanZeroMask;
 
     Register Val = MI.getOperand(1).getReg();
-
     computeKnownFPClass(Val, DemandedElts, InterestedSrcs, KnownSrc, Depth + 1);
 
-    if (KnownSrc.isKnownNeverPosInfinity())
-      Known.knownNot(fcPosInf);
-    if (KnownSrc.isKnownNever(fcSNan))
-      Known.knownNot(fcSNan);
-
-    // Any negative value besides -0 returns a nan.
-    if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero())
-      Known.knownNot(fcNan);
-
-    // The only negative value that can be returned is -0 for -0 inputs.
-    Known.knownNot(fcNegInf | fcNegSubnormal | fcNegNormal);
+    DenormalMode Mode =
+        MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
+    Known = KnownFPClass::sqrt(KnownSrc, Mode);
+    if (MI.getFlag(MachineInstr::MIFlag::FmNsz))
+      Known.knownNot(fcNegZero);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FABS: {
@@ -1122,19 +1136,102 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     Known.fabs();
     break;
   }
+  case TargetOpcode::G_FATAN2: {
+    Register Y = MI.getOperand(1).getReg();
+    Register X = MI.getOperand(2).getReg();
+    KnownFPClass KnownY, KnownX;
+    computeKnownFPClass(Y, DemandedElts, InterestedClasses, KnownY, Depth + 1);
+    computeKnownFPClass(X, DemandedElts, InterestedClasses, KnownX, Depth + 1);
+    Known = KnownFPClass::atan2(KnownY, KnownX);
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FSINH: {
+    Register Val = MI.getOperand(1).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    Known = KnownFPClass::sinh(KnownSrc);
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FCOSH: {
+    Register Val = MI.getOperand(1).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    Known = KnownFPClass::cosh(KnownSrc);
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FTANH: {
+    Register Val = MI.getOperand(1).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    Known = KnownFPClass::tanh(KnownSrc);
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FASIN: {
+    Register Val = MI.getOperand(1).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    Known = KnownFPClass::asin(KnownSrc);
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FACOS: {
+    Register Val = MI.getOperand(1).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    Known = KnownFPClass::acos(KnownSrc);
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FATAN: {
+    Register Val = MI.getOperand(1).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    Known = KnownFPClass::atan(KnownSrc);
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FTAN: {
+    Register Val = MI.getOperand(1).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    Known = KnownFPClass::tan(KnownSrc);
+    Known.knownNot(fcSNan);
+    break;
+  }
   case TargetOpcode::G_FSIN:
-  case TargetOpcode::G_FCOS:
-  case TargetOpcode::G_FSINCOS: {
+  case TargetOpcode::G_FCOS: {
     // Return NaN on infinite inputs.
     Register Val = MI.getOperand(1).getReg();
     KnownFPClass KnownSrc;
-
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
-    Known.knownNot(fcInf);
-
-    if (KnownSrc.isKnownNeverNaN() && KnownSrc.isKnownNeverInfinity())
-      Known.knownNot(fcNan);
+    Known = Opcode == TargetOpcode::G_FCOS ? KnownFPClass::cos(KnownSrc)
+                                           : KnownFPClass::sin(KnownSrc);
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FSINCOS: {
+    // Operand layout: (sin_dst, cos_dst, src)
+    Register Src = MI.getOperand(2).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Src, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    if (R == MI.getOperand(0).getReg())
+      Known = KnownFPClass::sin(KnownSrc);
+    else
+      Known = KnownFPClass::cos(KnownSrc);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FMAXNUM:
@@ -1154,99 +1251,35 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(RHS, DemandedElts, InterestedClasses, KnownRHS,
                         Depth + 1);
 
-    bool NeverNaN = KnownLHS.isKnownNeverNaN() || KnownRHS.isKnownNeverNaN();
-    Known = KnownLHS | KnownRHS;
-
-    // If either operand is not NaN, the result is not NaN.
-    if (NeverNaN && (Opcode == TargetOpcode::G_FMINNUM ||
-                     Opcode == TargetOpcode::G_FMAXNUM ||
-                     Opcode == TargetOpcode::G_FMINIMUMNUM ||
-                     Opcode == TargetOpcode::G_FMAXIMUMNUM))
-      Known.knownNot(fcNan);
-
-    if (Opcode == TargetOpcode::G_FMAXNUM ||
-        Opcode == TargetOpcode::G_FMAXIMUMNUM ||
-        Opcode == TargetOpcode::G_FMAXNUM_IEEE) {
-      // If at least one operand is known to be positive, the result must be
-      // positive.
-      if ((KnownLHS.cannotBeOrderedLessThanZero() &&
-           KnownLHS.isKnownNeverNaN()) ||
-          (KnownRHS.cannotBeOrderedLessThanZero() &&
-           KnownRHS.isKnownNeverNaN()))
-        Known.knownNot(KnownFPClass::OrderedLessThanZeroMask);
-    } else if (Opcode == TargetOpcode::G_FMAXIMUM) {
-      // If at least one operand is known to be positive, the result must be
-      // positive.
-      if (KnownLHS.cannotBeOrderedLessThanZero() ||
-          KnownRHS.cannotBeOrderedLessThanZero())
-        Known.knownNot(KnownFPClass::OrderedLessThanZeroMask);
-    } else if (Opcode == TargetOpcode::G_FMINNUM ||
-               Opcode == TargetOpcode::G_FMINIMUMNUM ||
-               Opcode == TargetOpcode::G_FMINNUM_IEEE) {
-      // If at least one operand is known to be negative, the result must be
-      // negative.
-      if ((KnownLHS.cannotBeOrderedGreaterThanZero() &&
-           KnownLHS.isKnownNeverNaN()) ||
-          (KnownRHS.cannotBeOrderedGreaterThanZero() &&
-           KnownRHS.isKnownNeverNaN()))
-        Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask);
-    } else if (Opcode == TargetOpcode::G_FMINIMUM) {
-      // If at least one operand is known to be negative, the result must be
-      // negative.
-      if (KnownLHS.cannotBeOrderedGreaterThanZero() ||
-          KnownRHS.cannotBeOrderedGreaterThanZero())
-        Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask);
-    } 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.KnownFPClasses & fcZero) != fcNone &&
-        !Known.isKnownNeverSubnormal()) {
-      DenormalMode Mode =
-          MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
-      if (Mode != DenormalMode::getIEEE())
-        Known.KnownFPClasses |= fcZero;
+    KnownFPClass::MinMaxKind Kind;
+    switch (Opcode) {
+    case TargetOpcode::G_FMINIMUM:
+      Kind = KnownFPClass::MinMaxKind::minimum;
+      break;
+    case TargetOpcode::G_FMAXIMUM:
+      Kind = KnownFPClass::MinMaxKind::maximum;
+      break;
+    case TargetOpcode::G_FMINIMUMNUM:
+      Kind = KnownFPClass::MinMaxKind::minimumnum;
+      break;
+    case TargetOpcode::G_FMAXIMUMNUM:
+      Kind = KnownFPClass::MinMaxKind::maximumnum;
+      break;
+    case TargetOpcode::G_FMINNUM:
+    case TargetOpcode::G_FMINNUM_IEEE:
+      Kind = KnownFPClass::MinMaxKind::minnum;
+      break;
+    case TargetOpcode::G_FMAXNUM:
+    case TargetOpcode::G_FMAXNUM_IEEE:
+      Kind = KnownFPClass::MinMaxKind::maxnum;
+      break;
+    default:
+      llvm_unreachable("unhandled min/max opcode");
     }
 
-    if (Known.isKnownNeverNaN()) {
-      if (KnownLHS.SignBit && KnownRHS.SignBit &&
-          *KnownLHS.SignBit == *KnownRHS.SignBit) {
-        if (*KnownLHS.SignBit)
-          Known.signBitMustBeOne();
-        else
-          Known.signBitMustBeZero();
-      } else if ((Opcode == TargetOpcode::G_FMAXIMUM ||
-                  Opcode == TargetOpcode::G_FMINIMUM) ||
-                 Opcode == TargetOpcode::G_FMAXIMUMNUM ||
-                 Opcode == TargetOpcode::G_FMINIMUMNUM ||
-                 Opcode == TargetOpcode::G_FMAXNUM_IEEE ||
-                 Opcode == TargetOpcode::G_FMINNUM_IEEE ||
-                 // FIXME: Should be using logical zero versions
-                 ((KnownLHS.isKnownNeverNegZero() ||
-                   KnownRHS.isKnownNeverPosZero()) &&
-                  (KnownLHS.isKnownNeverPosZero() ||
-                   KnownRHS.isKnownNeverNegZero()))) {
-        if ((Opcode == TargetOpcode::G_FMAXIMUM ||
-             Opcode == TargetOpcode::G_FMAXNUM ||
-             Opcode == TargetOpcode::G_FMAXIMUMNUM ||
-             Opcode == TargetOpcode::G_FMAXNUM_IEEE) &&
-            (KnownLHS.SignBit == false || KnownRHS.SignBit == false))
-          Known.signBitMustBeZero();
-        else if ((Opcode == TargetOpcode::G_FMINIMUM ||
-                  Opcode == TargetOpcode::G_FMINNUM ||
-                  Opcode == TargetOpcode::G_FMINIMUMNUM ||
-                  Opcode == TargetOpcode::G_FMINNUM_IEEE) &&
-                 (KnownLHS.SignBit == true || KnownRHS.SignBit == true))
-          Known.signBitMustBeOne();
-      }
-    }
+    DenormalMode Mode =
+        MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
+    Known = KnownFPClass::minMaxLike(KnownLHS, KnownRHS, Kind, Mode);
     break;
   }
   case TargetOpcode::G_FCANONICALIZE: {
@@ -1255,42 +1288,10 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
 
-    // This is essentially a stronger form of
-    // propagateCanonicalizingSrc. Other "canonicalizing" operations don't
-    // actually have an IR canonicalization guarantee.
-
-    // Canonicalize may flush denormals to zero, so we have to consider the
-    // denormal mode to preserve known-not-0 knowledge.
-    Known.KnownFPClasses = KnownSrc.KnownFPClasses | fcZero | fcQNan;
-
-    // Stronger version of propagateNaN
-    // Canonicalize is guaranteed to quiet signaling nans.
-    if (KnownSrc.isKnownNeverNaN())
-      Known.knownNot(fcNan);
-    else
-      Known.knownNot(fcSNan);
-
-    // If the parent function flushes denormals, the canonical output cannot
-    // be a denormal.
     LLT Ty = MRI.getType(Val).getScalarType();
     const fltSemantics &FPType = getFltSemanticForLLT(Ty);
     DenormalMode DenormMode = MF->getDenormalMode(FPType);
-    if (DenormMode == DenormalMode::getIEEE()) {
-      if (KnownSrc.isKnownNever(fcPosZero))
-        Known.knownNot(fcPosZero);
-      if (KnownSrc.isKnownNever(fcNegZero))
-        Known.knownNot(fcNegZero);
-      break;
-    }
-
-    if (DenormMode.inputsAreZero() || DenormMode.outputsAreZero())
-      Known.knownNot(fcSubnormal);
-
-    if (DenormMode.Input == DenormalMode::PositiveZero ||
-        (DenormMode.Output == DenormalMode::PositiveZero &&
-         DenormMode.Input == DenormalMode::IEEE))
-      Known.knownNot(fcNegZero);
-
+    Known = KnownFPClass::canonicalize(KnownSrc, DenormMode);
     break;
   }
   case TargetOpcode::G_VECREDUCE_FMAX:
@@ -1308,53 +1309,39 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       Known.SignBit.reset();
     break;
   }
-  case TargetOpcode::G_TRUNC:
   case TargetOpcode::G_FFLOOR:
   case TargetOpcode::G_FCEIL:
   case TargetOpcode::G_FRINT:
   case TargetOpcode::G_FNEARBYINT:
   case TargetOpcode::G_INTRINSIC_FPTRUNC_ROUND:
-  case TargetOpcode::G_INTRINSIC_ROUND: {
+  case TargetOpcode::G_INTRINSIC_ROUND:
+  case TargetOpcode::G_INTRINSIC_ROUNDEVEN:
+  case TargetOpcode::G_INTRINSIC_TRUNC: {
     Register Val = MI.getOperand(1).getReg();
     KnownFPClass KnownSrc;
     FPClassTest InterestedSrcs = InterestedClasses;
     if (InterestedSrcs & fcPosFinite)
-      InterestedSrcs |= fcPosFinite;
-    if (InterestedSrcs & fcNegFinite)
       InterestedSrcs |= fcNegFinite;
+    if (InterestedSrcs & fcNegFinite)
+      InterestedSrcs |= fcPosFinite;
     computeKnownFPClass(Val, DemandedElts, InterestedSrcs, KnownSrc, Depth + 1);
 
-    // Integer results cannot be subnormal.
-    Known.knownNot(fcSubnormal);
-
-    Known.propagateNaN(KnownSrc, true);
-
     // TODO: handle multi unit FPTypes once LLT FPInfo lands
-
-    // Negative round ups to 0 produce -0
-    if (KnownSrc.isKnownNever(fcPosFinite))
-      Known.knownNot(fcPosFinite);
-    if (KnownSrc.isKnownNever(fcNegFinite))
-      Known.knownNot(fcNegFinite);
-
+    bool IsTrunc = Opcode == TargetOpcode::G_INTRINSIC_TRUNC;
+    Known = KnownFPClass::roundToIntegral(KnownSrc, IsTrunc,
+                                          /*IsMultiUnitFPType=*/false);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FEXP:
   case TargetOpcode::G_FEXP2:
   case TargetOpcode::G_FEXP10: {
-    Known.knownNot(fcNegative);
-    if ((InterestedClasses & fcNan) == fcNone)
-      break;
-
     Register Val = MI.getOperand(1).getReg();
     KnownFPClass KnownSrc;
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
-    if (KnownSrc.isKnownNeverNaN()) {
-      Known.knownNot(fcNan);
-      Known.signBitMustBeZero();
-    }
-
+    Known = KnownFPClass::exp(KnownSrc);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FLOG:
@@ -1364,6 +1351,7 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     // log([+-]0.0) -> -inf
     // log(-inf) -> nan
     // log(-x) -> nan
+    Known.knownNot(fcSNan);
     if ((InterestedClasses & (fcNan | fcInf)) == fcNone)
       break;
 
@@ -1371,28 +1359,21 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     if ((InterestedClasses & fcNegInf) != fcNone)
       InterestedSrcs |= fcZero | fcSubnormal;
     if ((InterestedClasses & fcNan) != fcNone)
-      InterestedSrcs |= fcNan | (fcNegative & ~fcNan);
+      InterestedSrcs |= fcNan | fcNegative;
 
     Register Val = MI.getOperand(1).getReg();
     KnownFPClass KnownSrc;
     computeKnownFPClass(Val, DemandedElts, InterestedSrcs, KnownSrc, Depth + 1);
 
-    if (KnownSrc.isKnownNeverPosInfinity())
-      Known.knownNot(fcPosInf);
-
-    if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero())
-      Known.knownNot(fcNan);
-
     LLT Ty = MRI.getType(Val).getScalarType();
     const fltSemantics &FltSem = getFltSemanticForLLT(Ty);
     DenormalMode Mode = MF->getDenormalMode(FltSem);
-
-    if (KnownSrc.isKnownNeverLogicalZero(Mode))
-      Known.knownNot(fcNegInf);
-
+    Known = KnownFPClass::log(KnownSrc, Mode);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FPOWI: {
+    Known.knownNot(fcSNan);
     if ((InterestedClasses & fcNegative) == fcNone)
       break;
 
@@ -1401,24 +1382,13 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     KnownBits ExponentKnownBits = getKnownBits(
         Exp, ExpTy.isVector() ? DemandedElts : APInt(1, 1), Depth + 1);
 
-    if (ExponentKnownBits.Zero[0]) { // Is even
-      Known.knownNot(fcNegative);
-      break;
-    }
-
-    // Given that exp is an integer, here are the
-    // ways that pow can return a negative value:
-    //
-    //   pow(-x, exp)   --> negative if exp is odd and x is negative.
-    //   pow(-0, exp)   --> -inf if exp is negative odd.
-    //   pow(-0, exp)   --> -0 if exp is positive odd.
-    //   pow(-inf, exp) --> -0 if exp is negative odd.
-    //   pow(-inf, exp) --> -inf if exp is positive odd.
     Register Val = MI.getOperand(1).getReg();
     KnownFPClass KnownSrc;
-    computeKnownFPClass(Val, DemandedElts, fcNegative, KnownSrc, Depth + 1);
-    if (KnownSrc.isKnownNever(fcNegative))
-      Known.knownNot(fcNegative);
+    if (ExponentKnownBits.isZero() || !ExponentKnownBits.isEven())
+      computeKnownFPClass(Val, DemandedElts, fcNegative, KnownSrc, Depth + 1);
+
+    Known = KnownFPClass::powi(KnownSrc, ExponentKnownBits);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FLDEXP:
@@ -1427,33 +1397,23 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     KnownFPClass KnownSrc;
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
-    Known.propagateNaN(KnownSrc, /*PropagateSign=*/true);
-
-    // Sign is preserved, but underflows may produce zeroes.
-    if (KnownSrc.isKnownNever(fcNegative))
-      Known.knownNot(fcNegative);
-    else if (KnownSrc.cannotBeOrderedLessThanZero())
-      Known.knownNot(KnownFPClass::OrderedLessThanZeroMask);
-
-    if (KnownSrc.isKnownNever(fcPositive))
-      Known.knownNot(fcPositive);
-    else if (KnownSrc.cannotBeOrderedGreaterThanZero())
-      Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask);
 
     // Can refine inf/zero handling based on the exponent operand.
     const FPClassTest ExpInfoMask = fcZero | fcSubnormal | fcInf;
-    if ((InterestedClasses & ExpInfoMask) == fcNone)
-      break;
-    if ((KnownSrc.KnownFPClasses & ExpInfoMask) == fcNone)
-      break;
-
-    // TODO: Handle constant range of Exp
+    KnownBits ExpBits;
+    if ((KnownSrc.KnownFPClasses & ExpInfoMask) != fcNone) {
+      Register ExpReg = MI.getOperand(2).getReg();
+      LLT ExpTy = MRI.getType(ExpReg);
+      ExpBits = getKnownBits(ExpReg,
+                             ExpTy.isVector() ? DemandedElts : APInt(1, 1),
+                             Depth + 1);
+    }
 
-    break;
-  }
-  case TargetOpcode::G_INTRINSIC_ROUNDEVEN: {
-    computeKnownFPClassForFPTrunc(MI, DemandedElts, InterestedClasses, Known,
-                                  Depth);
+    LLT ScalarTy = DstTy.getScalarType();
+    const fltSemantics &Flt = getFltSemanticForLLT(ScalarTy);
+    DenormalMode Mode = MF->getDenormalMode(Flt);
+    Known = KnownFPClass::ldexp(KnownSrc, ExpBits, Flt, Mode);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FADD:
@@ -1462,112 +1422,92 @@ void GISelValueTracking::computeKnownFPClass(Register R,
   case TargetOpcode::G_STRICT_FSUB: {
     Register LHS = MI.getOperand(1).getReg();
     Register RHS = MI.getOperand(2).getReg();
-    KnownFPClass KnownLHS, KnownRHS;
+    bool IsAdd = (Opcode == TargetOpcode::G_FADD ||
+                  Opcode == TargetOpcode::G_STRICT_FADD);
     bool WantNegative =
-        (Opcode == TargetOpcode::G_FADD ||
-         Opcode == TargetOpcode::G_STRICT_FADD) &&
+        IsAdd &&
         (InterestedClasses & KnownFPClass::OrderedLessThanZeroMask) != fcNone;
     bool WantNaN = (InterestedClasses & fcNan) != fcNone;
     bool WantNegZero = (InterestedClasses & fcNegZero) != fcNone;
 
-    if (!WantNaN && !WantNegative && !WantNegZero)
+    if (!WantNaN && !WantNegative && !WantNegZero) {
+      Known.knownNot(fcSNan);
       break;
+    }
+
+    DenormalMode Mode =
+        MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
 
     FPClassTest InterestedSrcs = InterestedClasses;
     if (WantNegative)
       InterestedSrcs |= KnownFPClass::OrderedLessThanZeroMask;
     if (InterestedClasses & fcNan)
       InterestedSrcs |= fcInf;
+
+    // Special case fadd x, x (canonical form of fmul x, 2).
+    if (IsAdd && LHS == RHS && isGuaranteedNotToBeUndef(LHS, MRI, Depth + 1)) {
+      KnownFPClass KnownSelf;
+      computeKnownFPClass(LHS, DemandedElts, InterestedSrcs, KnownSelf,
+                          Depth + 1);
+      Known = KnownFPClass::fadd_self(KnownSelf, Mode);
+      Known.knownNot(fcSNan);
+      break;
+    }
+
+    KnownFPClass KnownLHS, KnownRHS;
     computeKnownFPClass(RHS, DemandedElts, InterestedSrcs, KnownRHS, Depth + 1);
 
     if ((WantNaN && KnownRHS.isKnownNeverNaN()) ||
         (WantNegative && KnownRHS.cannotBeOrderedLessThanZero()) ||
-        WantNegZero ||
-        (Opcode == TargetOpcode::G_FSUB ||
-         Opcode == TargetOpcode::G_STRICT_FSUB)) {
-
+        WantNegZero || !IsAdd) {
       // RHS is canonically cheaper to compute. Skip inspecting the LHS if
       // there's no point.
       computeKnownFPClass(LHS, DemandedElts, InterestedSrcs, KnownLHS,
                           Depth + 1);
-      // Adding positive and negative infinity produces NaN.
-      // TODO: Check sign of infinities.
-      if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() &&
-          (KnownLHS.isKnownNeverInfinity() || KnownRHS.isKnownNeverInfinity()))
-        Known.knownNot(fcNan);
-
-      if (Opcode == TargetOpcode::G_FADD ||
-          Opcode == TargetOpcode::G_STRICT_FADD) {
-        if (KnownLHS.cannotBeOrderedLessThanZero() &&
-            KnownRHS.cannotBeOrderedLessThanZero())
-          Known.knownNot(KnownFPClass::OrderedLessThanZeroMask);
-
-        // (fadd x, 0.0) is guaranteed to return +0.0, not -0.0.
-        if ((KnownLHS.isKnownNeverLogicalNegZero(MF->getDenormalMode(
-                 getFltSemanticForLLT(DstTy.getScalarType()))) ||
-             KnownRHS.isKnownNeverLogicalNegZero(MF->getDenormalMode(
-                 getFltSemanticForLLT(DstTy.getScalarType())))) &&
-            // Make sure output negative denormal can't flush to -0
-            outputDenormalIsIEEEOrPosZero(*MF, DstTy))
-          Known.knownNot(fcNegZero);
-      } else {
-        // Only fsub -0, +0 can return -0
-        if ((KnownLHS.isKnownNeverLogicalNegZero(MF->getDenormalMode(
-                 getFltSemanticForLLT(DstTy.getScalarType()))) ||
-             KnownRHS.isKnownNeverLogicalPosZero(MF->getDenormalMode(
-                 getFltSemanticForLLT(DstTy.getScalarType())))) &&
-            // Make sure output negative denormal can't flush to -0
-            outputDenormalIsIEEEOrPosZero(*MF, DstTy))
-          Known.knownNot(fcNegZero);
-      }
     }
 
+    if (IsAdd)
+      Known = KnownFPClass::fadd(KnownLHS, KnownRHS, Mode);
+    else
+      Known = KnownFPClass::fsub(KnownLHS, KnownRHS, Mode);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FMUL:
   case TargetOpcode::G_STRICT_FMUL: {
     Register LHS = MI.getOperand(1).getReg();
     Register RHS = MI.getOperand(2).getReg();
-    // X * X is always non-negative or a NaN.
-    if (LHS == RHS)
-      Known.knownNot(fcNegative);
-
-    if ((InterestedClasses & fcNan) != fcNan)
-      break;
-
-    // fcSubnormal is only needed in case of DAZ.
-    const FPClassTest NeedForNan = fcNan | fcInf | fcZero | fcSubnormal;
-
-    KnownFPClass KnownLHS, KnownRHS;
-    computeKnownFPClass(RHS, DemandedElts, NeedForNan, KnownRHS, Depth + 1);
-    if (!KnownRHS.isKnownNeverNaN())
-      break;
-
-    computeKnownFPClass(LHS, DemandedElts, NeedForNan, KnownLHS, Depth + 1);
-    if (!KnownLHS.isKnownNeverNaN())
-      break;
-
-    if (KnownLHS.SignBit && KnownRHS.SignBit) {
-      if (*KnownLHS.SignBit == *KnownRHS.SignBit)
-        Known.signBitMustBeZero();
-      else
-        Known.signBitMustBeOne();
-    }
-
-    // If 0 * +/-inf produces NaN.
-    if (KnownLHS.isKnownNeverInfinity() && KnownRHS.isKnownNeverInfinity()) {
-      Known.knownNot(fcNan);
-      break;
+    DenormalMode Mode =
+        MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
+
+    // X * X is always non-negative or a NaN (use square() for precision).
+    if (LHS == RHS && isGuaranteedNotToBeUndef(LHS, MRI, Depth + 1)) {
+      KnownFPClass KnownSrc;
+      computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownSrc, Depth + 1);
+      Known = KnownFPClass::square(KnownSrc, Mode);
+    } else {
+      // If RHS is a scalar constant, use the more precise APFloat overload.
+      auto RHSCst = GFConstant::getConstant(RHS, MRI);
+      if (RHSCst && RHSCst->getKind() == GFConstant::GFConstantKind::Scalar) {
+        KnownFPClass KnownLHS;
+        computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownLHS, Depth + 1);
+        Known = KnownFPClass::fmul(KnownLHS, RHSCst->getScalarValue(), Mode);
+      } else {
+        KnownFPClass KnownLHS, KnownRHS;
+        computeKnownFPClass(RHS, DemandedElts, fcAllFlags, KnownRHS, Depth + 1);
+        computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownLHS, Depth + 1);
+        Known = KnownFPClass::fmul(KnownLHS, KnownRHS, Mode);
+
+        // If one operand is known |x| <= 1 and the other is finite, the
+        // product cannot overflow to infinity.
+        if (KnownLHS.isKnownNever(fcInf) && isAbsoluteValueULEOne(RHS, MRI))
+          Known.knownNot(fcInf);
+        else if (KnownRHS.isKnownNever(fcInf) &&
+                 isAbsoluteValueULEOne(LHS, MRI))
+          Known.knownNot(fcInf);
+      }
     }
-
-    if ((KnownRHS.isKnownNeverInfinity() ||
-         KnownLHS.isKnownNeverLogicalZero(MF->getDenormalMode(
-             getFltSemanticForLLT(DstTy.getScalarType())))) &&
-        (KnownLHS.isKnownNeverInfinity() ||
-         KnownRHS.isKnownNeverLogicalZero(
-             MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType())))))
-      Known.knownNot(fcNan);
-
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FDIV:
@@ -1575,16 +1515,39 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     Register LHS = MI.getOperand(1).getReg();
     Register RHS = MI.getOperand(2).getReg();
 
-    if (LHS == RHS) {
-      // TODO: Could filter out snan if we inspect the operand
+    if (Opcode == TargetOpcode::G_FREM)
+      Known.knownNot(fcInf);
+
+    DenormalMode Mode =
+        MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
+
+    if (LHS == RHS && isGuaranteedNotToBeUndef(LHS, MRI, Depth + 1)) {
       if (Opcode == TargetOpcode::G_FDIV) {
-        // X / X is always exactly 1.0 or a NaN.
-        Known.KnownFPClasses = fcNan | fcPosNormal;
+        const bool WantNan = (InterestedClasses & fcNan) != fcNone;
+        if (!WantNan) {
+          // X / X is always exactly 1.0 or a NaN.
+          Known.KnownFPClasses = fcPosNormal | fcQNan;
+          break;
+        }
+        KnownFPClass KnownSrc;
+        computeKnownFPClass(LHS, DemandedElts,
+                            fcNan | fcInf | fcZero | fcSubnormal, KnownSrc,
+                            Depth + 1);
+        Known = KnownFPClass::fdiv_self(KnownSrc, Mode);
       } else {
-        // X % X is always exactly [+-]0.0 or a NaN.
-        Known.KnownFPClasses = fcNan | fcZero;
+        const bool WantNan = (InterestedClasses & fcNan) != fcNone;
+        if (!WantNan) {
+          // X % X is always exactly [+-]0.0 or a NaN.
+          Known.KnownFPClasses = fcZero | fcQNan;
+          break;
+        }
+        KnownFPClass KnownSrc;
+        computeKnownFPClass(LHS, DemandedElts,
+                            fcNan | fcInf | fcZero | fcSubnormal, KnownSrc,
+                            Depth + 1);
+        Known = KnownFPClass::frem_self(KnownSrc, Mode);
       }
-
+      Known.knownNot(fcSNan);
       break;
     }
 
@@ -1592,8 +1555,10 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     const bool WantNegative = (InterestedClasses & fcNegative) != fcNone;
     const bool WantPositive = Opcode == TargetOpcode::G_FREM &&
                               (InterestedClasses & fcPositive) != fcNone;
-    if (!WantNan && !WantNegative && !WantPositive)
+    if (!WantNan && !WantNegative && !WantPositive) {
+      Known.knownNot(fcSNan);
       break;
+    }
 
     KnownFPClass KnownLHS, KnownRHS;
 
@@ -1601,40 +1566,20 @@ void GISelValueTracking::computeKnownFPClass(Register R,
                         KnownRHS, Depth + 1);
 
     bool KnowSomethingUseful =
-        KnownRHS.isKnownNeverNaN() || KnownRHS.isKnownNever(fcNegative);
+        KnownRHS.isKnownNeverNaN() || KnownRHS.isKnownNever(fcNegative) ||
+        KnownRHS.isKnownNever(fcPositive);
 
     if (KnowSomethingUseful || WantPositive) {
-      const FPClassTest InterestedLHS =
-          WantPositive ? fcAllFlags
-                       : fcNan | fcInf | fcZero | fcSubnormal | fcNegative;
-
-      computeKnownFPClass(LHS, DemandedElts, InterestedClasses & InterestedLHS,
-                          KnownLHS, Depth + 1);
+      computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownLHS, Depth + 1);
     }
 
     if (Opcode == TargetOpcode::G_FDIV) {
-      // Only 0/0, Inf/Inf produce NaN.
-      if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() &&
-          (KnownLHS.isKnownNeverInfinity() ||
-           KnownRHS.isKnownNeverInfinity()) &&
-          ((KnownLHS.isKnownNeverLogicalZero(MF->getDenormalMode(
-               getFltSemanticForLLT(DstTy.getScalarType())))) ||
-           (KnownRHS.isKnownNeverLogicalZero(MF->getDenormalMode(
-               getFltSemanticForLLT(DstTy.getScalarType())))))) {
-        Known.knownNot(fcNan);
-      }
-
-      // X / -0.0 is -Inf (or NaN).
-      // +X / +X is +X
-      if (KnownLHS.isKnownNever(fcNegative) &&
-          KnownRHS.isKnownNever(fcNegative))
-        Known.knownNot(fcNegative);
+      Known = KnownFPClass::fdiv(KnownLHS, KnownRHS, Mode);
     } else {
       // Inf REM x and x REM 0 produce NaN.
       if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() &&
           KnownLHS.isKnownNeverInfinity() &&
-          KnownRHS.isKnownNeverLogicalZero(MF->getDenormalMode(
-              getFltSemanticForLLT(DstTy.getScalarType())))) {
+          KnownRHS.isKnownNeverLogicalZero(Mode)) {
         Known.knownNot(fcNan);
       }
 
@@ -1650,37 +1595,44 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       if (KnownLHS.isKnownNever(fcPositive))
         Known.knownNot(fcPositive);
     }
-
+    Known.knownNot(fcSNan);
+    break;
+  }
+  case TargetOpcode::G_FFREXP: {
+    // Only handle the mantissa output (operand 0); the exponent is an integer.
+    if (R != MI.getOperand(0).getReg())
+      break;
+    Register Src = MI.getOperand(2).getReg();
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Src, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
+    DenormalMode Mode =
+        MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
+    Known = KnownFPClass::frexp_mant(KnownSrc, Mode);
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FPEXT: {
-    Register Dst = MI.getOperand(0).getReg();
     Register Src = MI.getOperand(1).getReg();
-    // Infinity, nan and zero propagate from source.
-    computeKnownFPClass(R, DemandedElts, InterestedClasses, Known, Depth + 1);
+    KnownFPClass KnownSrc;
+    computeKnownFPClass(Src, DemandedElts, InterestedClasses, KnownSrc,
+                        Depth + 1);
 
-    LLT DstTy = MRI.getType(Dst).getScalarType();
-    const fltSemantics &DstSem = getFltSemanticForLLT(DstTy);
+    LLT DstScalarTy = DstTy.getScalarType();
+    const fltSemantics &DstSem = getFltSemanticForLLT(DstScalarTy);
     LLT SrcTy = MRI.getType(Src).getScalarType();
     const fltSemantics &SrcSem = getFltSemanticForLLT(SrcTy);
 
-    // All subnormal inputs should be in the normal range in the result type.
-    if (APFloat::isRepresentableAsNormalIn(SrcSem, DstSem)) {
-      if (Known.KnownFPClasses & fcPosSubnormal)
-        Known.KnownFPClasses |= fcPosNormal;
-      if (Known.KnownFPClasses & fcNegSubnormal)
-        Known.KnownFPClasses |= fcNegNormal;
-      Known.knownNot(fcSubnormal);
-    }
-
-    // Sign bit of a nan isn't guaranteed.
-    if (!Known.isKnownNeverNaN())
-      Known.SignBit = std::nullopt;
+    Known = KnownFPClass::fpext(KnownSrc, DstSem, SrcSem);
+    // fpext quiets sNaN — an sNaN input produces a qNaN output.
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FPTRUNC: {
     computeKnownFPClassForFPTrunc(MI, DemandedElts, InterestedClasses, Known,
                                   Depth);
+    // fptrunc quiets sNaN — an sNaN input produces a qNaN output.
+    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_SITOFP:
@@ -1693,24 +1645,48 @@ void GISelValueTracking::computeKnownFPClass(Register R,
 
     // sitofp and uitofp turn into +0.0 for zero.
     Known.knownNot(fcNegZero);
+
+    // UIToFP is always non-negative regardless of known bits.
     if (Opcode == TargetOpcode::G_UITOFP)
       Known.signBitMustBeZero();
 
+    // Only compute known bits if we can learn something useful from them.
+    if (!(InterestedClasses & (fcPosZero | fcNormal | fcInf)))
+      break;
+
     Register Val = MI.getOperand(1).getReg();
     LLT Ty = MRI.getType(Val);
+    KnownBits IntKnown =
+        getKnownBits(Val, Ty.isVector() ? DemandedElts : APInt(1, 1), Depth + 1);
+
+    // If the integer is non-zero, the result cannot be +0.0.
+    if (IntKnown.isNonZero())
+      Known.knownNot(fcPosZero);
+
+    if (Opcode == TargetOpcode::G_SITOFP) {
+      // If the signed integer is known non-negative, the result is
+      // non-negative. If the signed integer is known negative, the result is
+      // negative.
+      if (IntKnown.isNonNegative())
+        Known.signBitMustBeZero();
+      else if (IntKnown.isNegative())
+        Known.signBitMustBeOne();
+    }
 
     if (InterestedClasses & fcInf) {
-      // Get width of largest magnitude integer (remove a bit if signed).
-      // This still works for a signed minimum value because the largest FP
-      // value is scaled by some fraction close to 2.0 (1.0 + 0.xxxx).;
-      int IntSize = Ty.getScalarSizeInBits();
-      if (Opcode == TargetOpcode::G_SITOFP)
-        --IntSize;
+      LLT FPTy = DstTy.getScalarType();
+      const fltSemantics &FltSem = getFltSemanticForLLT(FPTy);
+
+      // Compute the effective integer width after removing known-zero leading
+      // bits, to check if the result can overflow to infinity.
+      int IntSize = IntKnown.getBitWidth();
+      if (Opcode == TargetOpcode::G_UITOFP)
+        IntSize -= IntKnown.countMinLeadingZeros();
+      else
+        IntSize -= IntKnown.countMinSignBits();
 
       // If the exponent of the largest finite FP value can hold the largest
       // integer, the result of the cast must be finite.
-      LLT FPTy = DstTy.getScalarType();
-      const fltSemantics &FltSem = getFltSemanticForLLT(FPTy);
       if (ilogb(APFloat::getLargest(FltSem)) >= IntSize)
         Known.knownNot(fcInf);
     }
@@ -1853,6 +1829,38 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     }
     break;
   }
+  case TargetOpcode::G_PHI:
+  case TargetOpcode::PHI: {
+    // Cap PHI recursion below the global limit to avoid spending the entire
+    // budget chasing loop back-edges (matches ValueTracking's PhiRecursionLimit).
+    if (Depth + 2 > MaxAnalysisRecursionDepth)
+      break;
+    // PHI's operands are a mix of registers and basic blocks interleaved.
+    // We only care about the register ones.
+    bool First = true;
+    for (unsigned Idx = 1; Idx < MI.getNumOperands(); Idx += 2) {
+      const MachineOperand &Src = MI.getOperand(Idx);
+      Register SrcReg = Src.getReg();
+      LLT SrcTy = MRI.getType(SrcReg);
+      if (!SrcReg.isVirtual() || Src.getSubReg() != 0 || !SrcTy.isValid()) {
+        Known.resetAll();
+        break;
+      }
+      if (First) {
+        computeKnownFPClass(SrcReg, DemandedElts, InterestedClasses, Known,
+                            Depth + 1);
+        First = false;
+      } else {
+        KnownFPClass Known2;
+        computeKnownFPClass(SrcReg, DemandedElts, InterestedClasses, Known2,
+                            Depth + 1);
+        Known = Known.intersectWith(Known2);
+      }
+      if (Known.isUnknown())
+        break;
+    }
+    break;
+  }
   case TargetOpcode::COPY: {
     Register Src = MI.getOperand(1).getReg();
 
diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
index c0a1d81952773..791ed3ba743e2 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -1286,3 +1286,315 @@ TEST_F(AArch64GISelMITest, TestFPClassVecInsertElem) {
   EXPECT_EQ(false, Known.SignBit);
 }
 
+TEST_F(AArch64GISelMITest, TestFPClassFSinh) {
+  // sinh: sign-preserving, can produce Inf, no sNaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fsinh:_(s32) = G_FSINH %val
+    %copy:_(s32) = COPY %fsinh
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  // Unknown input: can be anything except sNaN
+  EXPECT_EQ(fcAllFlags & ~fcSNan, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFSinhPos) {
+  // sinh of a non-negative source is non-negative
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs:_(s32) = nnan ninf G_FABS %val
+    %fsinh:_(s32) = G_FSINH %fabs
+    %copy:_(s32) = COPY %fsinh
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFCosh) {
+  // cosh: always non-negative, NaN propagates, no sNaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fcosh:_(s32) = G_FCOSH %val
+    %copy:_(s32) = COPY %fcosh
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFCoshNNaN) {
+  // cosh of a non-NaN source is also non-NaN (and non-negative)
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs:_(s32) = nnan G_FABS %val
+    %fcosh:_(s32) = G_FCOSH %fabs
+    %copy:_(s32) = COPY %fcosh
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+  EXPECT_TRUE(Known.isKnownNeverNaN());
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFTanh) {
+  // tanh: bounded to (-1,1), never Inf, sign-preserving, no sNaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %ftanh:_(s32) = G_FTANH %val
+    %copy:_(s32) = COPY %ftanh
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFTanhPos) {
+  // tanh of a non-negative source is non-negative
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs:_(s32) = nnan ninf G_FABS %val
+    %ftanh:_(s32) = G_FTANH %fabs
+    %copy:_(s32) = COPY %ftanh
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFAsin) {
+  // asin: bounded to [-π/2, π/2], never Inf, no sNaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fasin:_(s32) = G_FASIN %val
+    %copy:_(s32) = COPY %fasin
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFAsinPos) {
+  // asin of a non-negative source is non-negative
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs:_(s32) = nnan ninf G_FABS %val
+    %fasin:_(s32) = G_FASIN %fabs
+    %copy:_(s32) = COPY %fasin
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFAcos) {
+  // acos: bounded to [0, π], never Inf, never negative, no sNaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %facos:_(s32) = G_FACOS %val
+    %copy:_(s32) = COPY %facos
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFAtan) {
+  // atan: bounded to (-π/2, π/2), never Inf, sign-preserving, no sNaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fatan:_(s32) = G_FATAN %val
+    %copy:_(s32) = COPY %fatan
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFAtanPos) {
+  // atan of a non-negative source is non-negative
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs:_(s32) = nnan ninf G_FABS %val
+    %fatan:_(s32) = G_FATAN %fabs
+    %copy:_(s32) = COPY %fatan
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFTan) {
+  // tan: never Inf (tan(±Inf) = NaN, tan(finite) = finite), no sNaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %ftan:_(s32) = G_FTAN %val
+    %copy:_(s32) = COPY %ftan
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFTanNNaN) {
+  // tan of a non-NaN, non-Inf source is non-NaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs:_(s32) = nnan ninf G_FABS %val
+    %ftan:_(s32) = G_FTAN %fabs
+    %copy:_(s32) = COPY %ftan
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNeverNaN());
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFAtan2) {
+  // atan2: result in (-π, π], never Inf, NaN if either operand is NaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %y:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %x:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fatan2:_(s32) = G_FATAN2 %y, %x
+    %copy:_(s32) = COPY %fatan2
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+}
+
+TEST_F(AArch64GISelMITest, TestFPClassFAtan2NNaN) {
+  // atan2 with two non-NaN inputs is non-NaN
+  StringRef MIRString = R"(
+    %ptr:_(p0) = G_IMPLICIT_DEF
+    %y:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %x:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
+    %fabs_y:_(s32) = nnan ninf G_FABS %y
+    %fabs_x:_(s32) = nnan ninf G_FABS %x
+    %fatan2:_(s32) = G_FATAN2 %fabs_y, %fabs_x
+    %copy:_(s32) = COPY %fatan2
+)";
+  setUp(MIRString);
+  if (!TM)
+    GTEST_SKIP();
+  Register CopyReg = Copies[Copies.size() - 1];
+  MachineInstr *FinalCopy = MRI->getVRegDef(CopyReg);
+  Register SrcReg = FinalCopy->getOperand(1).getReg();
+  GISelValueTracking Info(*MF);
+  KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
+  EXPECT_TRUE(Known.isKnownNeverInfinity());
+  EXPECT_TRUE(Known.isKnownNeverNaN());
+}

>From ece94eb7ca8d098ab25daec66fdc91c97500decd Mon Sep 17 00:00:00 2001
From: Tim Gymnich <tim at gymni.ch>
Date: Sun, 5 Apr 2026 17:21:31 -0500
Subject: [PATCH 4/5] refactor

Signed-off-by: Tim Gymnich <tim at gymni.ch>
---
 .../CodeGen/GlobalISel/GISelValueTracking.cpp |  36 +-----
 llvm/lib/CodeGen/GlobalISel/Utils.cpp         |  69 +++++++++-
 .../AMDGPU/GlobalISel/legalize-fmaxnum.mir    |  30 ++---
 .../AMDGPU/GlobalISel/legalize-fminnum.mir    |  30 ++---
 .../CodeGen/GlobalISel/KnownFPClassTest.cpp   | 120 +++++++++---------
 5 files changed, 152 insertions(+), 133 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index 073bae76f6da3..8473ffb4614ed 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -1060,7 +1060,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
   case TargetOpcode::G_FMA:
   case TargetOpcode::G_STRICT_FMA:
   case TargetOpcode::G_FMAD: {
-    Known.knownNot(fcSNan);
     if ((InterestedClasses & fcNegative) == fcNone)
       break;
 
@@ -1104,7 +1103,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       }
       Known = KnownFPClass::fma(KnownSrc[0], KnownSrc[1], KnownSrc[2], Mode);
     }
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FSQRT:
@@ -1122,7 +1120,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     Known = KnownFPClass::sqrt(KnownSrc, Mode);
     if (MI.getFlag(MachineInstr::MIFlag::FmNsz))
       Known.knownNot(fcNegZero);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FABS: {
@@ -1143,7 +1140,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Y, DemandedElts, InterestedClasses, KnownY, Depth + 1);
     computeKnownFPClass(X, DemandedElts, InterestedClasses, KnownX, Depth + 1);
     Known = KnownFPClass::atan2(KnownY, KnownX);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FSINH: {
@@ -1152,7 +1148,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
     Known = KnownFPClass::sinh(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FCOSH: {
@@ -1161,7 +1156,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
     Known = KnownFPClass::cosh(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FTANH: {
@@ -1170,7 +1164,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
     Known = KnownFPClass::tanh(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FASIN: {
@@ -1179,7 +1172,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
     Known = KnownFPClass::asin(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FACOS: {
@@ -1188,7 +1180,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
     Known = KnownFPClass::acos(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FATAN: {
@@ -1197,7 +1188,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
     Known = KnownFPClass::atan(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FTAN: {
@@ -1206,7 +1196,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
     Known = KnownFPClass::tan(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FSIN:
@@ -1218,7 +1207,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
                         Depth + 1);
     Known = Opcode == TargetOpcode::G_FCOS ? KnownFPClass::cos(KnownSrc)
                                            : KnownFPClass::sin(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FSINCOS: {
@@ -1231,7 +1219,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       Known = KnownFPClass::sin(KnownSrc);
     else
       Known = KnownFPClass::cos(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FMAXNUM:
@@ -1330,7 +1317,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     bool IsTrunc = Opcode == TargetOpcode::G_INTRINSIC_TRUNC;
     Known = KnownFPClass::roundToIntegral(KnownSrc, IsTrunc,
                                           /*IsMultiUnitFPType=*/false);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FEXP:
@@ -1341,7 +1327,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(Val, DemandedElts, InterestedClasses, KnownSrc,
                         Depth + 1);
     Known = KnownFPClass::exp(KnownSrc);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FLOG:
@@ -1351,7 +1336,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     // log([+-]0.0) -> -inf
     // log(-inf) -> nan
     // log(-x) -> nan
-    Known.knownNot(fcSNan);
     if ((InterestedClasses & (fcNan | fcInf)) == fcNone)
       break;
 
@@ -1369,11 +1353,9 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     const fltSemantics &FltSem = getFltSemanticForLLT(Ty);
     DenormalMode Mode = MF->getDenormalMode(FltSem);
     Known = KnownFPClass::log(KnownSrc, Mode);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FPOWI: {
-    Known.knownNot(fcSNan);
     if ((InterestedClasses & fcNegative) == fcNone)
       break;
 
@@ -1388,7 +1370,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       computeKnownFPClass(Val, DemandedElts, fcNegative, KnownSrc, Depth + 1);
 
     Known = KnownFPClass::powi(KnownSrc, ExponentKnownBits);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FLDEXP:
@@ -1413,7 +1394,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     const fltSemantics &Flt = getFltSemanticForLLT(ScalarTy);
     DenormalMode Mode = MF->getDenormalMode(Flt);
     Known = KnownFPClass::ldexp(KnownSrc, ExpBits, Flt, Mode);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FADD:
@@ -1431,7 +1411,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     bool WantNegZero = (InterestedClasses & fcNegZero) != fcNone;
 
     if (!WantNaN && !WantNegative && !WantNegZero) {
-      Known.knownNot(fcSNan);
       break;
     }
 
@@ -1450,7 +1429,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       computeKnownFPClass(LHS, DemandedElts, InterestedSrcs, KnownSelf,
                           Depth + 1);
       Known = KnownFPClass::fadd_self(KnownSelf, Mode);
-      Known.knownNot(fcSNan);
       break;
     }
 
@@ -1470,7 +1448,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       Known = KnownFPClass::fadd(KnownLHS, KnownRHS, Mode);
     else
       Known = KnownFPClass::fsub(KnownLHS, KnownRHS, Mode);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FMUL:
@@ -1507,7 +1484,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
           Known.knownNot(fcInf);
       }
     }
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FDIV:
@@ -1526,7 +1502,7 @@ void GISelValueTracking::computeKnownFPClass(Register R,
         const bool WantNan = (InterestedClasses & fcNan) != fcNone;
         if (!WantNan) {
           // X / X is always exactly 1.0 or a NaN.
-          Known.KnownFPClasses = fcPosNormal | fcQNan;
+          Known.KnownFPClasses = fcPosNormal | fcNan;
           break;
         }
         KnownFPClass KnownSrc;
@@ -1538,7 +1514,7 @@ void GISelValueTracking::computeKnownFPClass(Register R,
         const bool WantNan = (InterestedClasses & fcNan) != fcNone;
         if (!WantNan) {
           // X % X is always exactly [+-]0.0 or a NaN.
-          Known.KnownFPClasses = fcZero | fcQNan;
+          Known.KnownFPClasses = fcZero | fcNan;
           break;
         }
         KnownFPClass KnownSrc;
@@ -1547,7 +1523,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
                             Depth + 1);
         Known = KnownFPClass::frem_self(KnownSrc, Mode);
       }
-      Known.knownNot(fcSNan);
       break;
     }
 
@@ -1556,7 +1531,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     const bool WantPositive = Opcode == TargetOpcode::G_FREM &&
                               (InterestedClasses & fcPositive) != fcNone;
     if (!WantNan && !WantNegative && !WantPositive) {
-      Known.knownNot(fcSNan);
       break;
     }
 
@@ -1595,7 +1569,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
       if (KnownLHS.isKnownNever(fcPositive))
         Known.knownNot(fcPositive);
     }
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FFREXP: {
@@ -1609,7 +1582,6 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     DenormalMode Mode =
         MF->getDenormalMode(getFltSemanticForLLT(DstTy.getScalarType()));
     Known = KnownFPClass::frexp_mant(KnownSrc, Mode);
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FPEXT: {
@@ -1624,15 +1596,11 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     const fltSemantics &SrcSem = getFltSemanticForLLT(SrcTy);
 
     Known = KnownFPClass::fpext(KnownSrc, DstSem, SrcSem);
-    // fpext quiets sNaN — an sNaN input produces a qNaN output.
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_FPTRUNC: {
     computeKnownFPClassForFPTrunc(MI, DemandedElts, InterestedClasses, Known,
                                   Depth);
-    // fptrunc quiets sNaN — an sNaN input produces a qNaN output.
-    Known.knownNot(fcSNan);
     break;
   }
   case TargetOpcode::G_SITOFP:
diff --git a/llvm/lib/CodeGen/GlobalISel/Utils.cpp b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
index 96e2c9bbbf002..243a8419b177d 100644
--- a/llvm/lib/CodeGen/GlobalISel/Utils.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
@@ -842,7 +842,74 @@ bool llvm::isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI, GISelVa
   if (DefMI->getFlag(MachineInstr::FmNoNans))
     return true;
 
-  KnownFPClass FPClass = VT->computeKnownFPClass(Val, fcNan);
+  // IEEE 754 arithmetic operations always quiet signaling NaNs. Short-circuit
+  // the value-tracking analysis for the SNaN-only case: if the defining op is
+  // known to quiet sNaN, the output can never be an sNaN.
+  if (SNaN) {
+    switch (DefMI->getOpcode()) {
+    default:
+      break;
+    case TargetOpcode::G_FADD:
+    case TargetOpcode::G_STRICT_FADD:
+    case TargetOpcode::G_FSUB:
+    case TargetOpcode::G_STRICT_FSUB:
+    case TargetOpcode::G_FMUL:
+    case TargetOpcode::G_STRICT_FMUL:
+    case TargetOpcode::G_FDIV:
+    case TargetOpcode::G_FREM:
+    case TargetOpcode::G_FMA:
+    case TargetOpcode::G_STRICT_FMA:
+    case TargetOpcode::G_FMAD:
+    case TargetOpcode::G_FSQRT:
+    case TargetOpcode::G_STRICT_FSQRT:
+    // Note: G_FABS and G_FNEG are bit-manipulation ops that preserve sNaN
+    // exactly (LLVM LangRef: "never change anything except possibly the sign
+    // bit"). They must NOT be listed here.
+    case TargetOpcode::G_FSIN:
+    case TargetOpcode::G_FCOS:
+    case TargetOpcode::G_FSINCOS:
+    case TargetOpcode::G_FTAN:
+    case TargetOpcode::G_FASIN:
+    case TargetOpcode::G_FACOS:
+    case TargetOpcode::G_FATAN:
+    case TargetOpcode::G_FATAN2:
+    case TargetOpcode::G_FSINH:
+    case TargetOpcode::G_FCOSH:
+    case TargetOpcode::G_FTANH:
+    case TargetOpcode::G_FEXP:
+    case TargetOpcode::G_FEXP2:
+    case TargetOpcode::G_FEXP10:
+    case TargetOpcode::G_FLOG:
+    case TargetOpcode::G_FLOG2:
+    case TargetOpcode::G_FLOG10:
+    case TargetOpcode::G_FPOWI:
+    case TargetOpcode::G_FLDEXP:
+    case TargetOpcode::G_STRICT_FLDEXP:
+    case TargetOpcode::G_FFREXP:
+    case TargetOpcode::G_INTRINSIC_TRUNC:
+    case TargetOpcode::G_INTRINSIC_ROUND:
+    case TargetOpcode::G_INTRINSIC_ROUNDEVEN:
+    case TargetOpcode::G_FFLOOR:
+    case TargetOpcode::G_FCEIL:
+    case TargetOpcode::G_FRINT:
+    case TargetOpcode::G_FNEARBYINT:
+    case TargetOpcode::G_FPEXT:
+    case TargetOpcode::G_FPTRUNC:
+    case TargetOpcode::G_FCANONICALIZE:
+    case TargetOpcode::G_FMINNUM:
+    case TargetOpcode::G_FMAXNUM:
+    case TargetOpcode::G_FMINNUM_IEEE:
+    case TargetOpcode::G_FMAXNUM_IEEE:
+    case TargetOpcode::G_FMINIMUM:
+    case TargetOpcode::G_FMAXIMUM:
+    case TargetOpcode::G_FMINIMUMNUM:
+    case TargetOpcode::G_FMAXIMUMNUM:
+      return true;
+    }
+  }
+
+  KnownFPClass FPClass = VT->computeKnownFPClass(Val, SNaN ? fcSNan : fcNan);
+
   if (SNaN)
     return FPClass.isKnownNever(fcSNan);
 
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fmaxnum.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fmaxnum.mir
index 3fc2b1cd66c1c..ed6e554948611 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fmaxnum.mir
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fmaxnum.mir
@@ -746,9 +746,8 @@ body: |
     ; SI-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; SI-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; SI-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; SI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMAXNUM_IEEE]]
-    ; SI-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; SI-NEXT: [[FMAXNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; SI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; SI-NEXT: [[FMAXNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FMAXNUM_IEEE]], [[FCANONICALIZE2]]
     ; SI-NEXT: $vgpr0 = COPY [[FMAXNUM_IEEE1]](s32)
     ;
     ; VI-LABEL: name: test_fmaxnum_with_fmaxnum_argument_s32_ieee_mode_on
@@ -760,9 +759,8 @@ body: |
     ; VI-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; VI-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; VI-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; VI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMAXNUM_IEEE]]
-    ; VI-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; VI-NEXT: [[FMAXNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; VI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; VI-NEXT: [[FMAXNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FMAXNUM_IEEE]], [[FCANONICALIZE2]]
     ; VI-NEXT: $vgpr0 = COPY [[FMAXNUM_IEEE1]](s32)
     ;
     ; GFX9-LABEL: name: test_fmaxnum_with_fmaxnum_argument_s32_ieee_mode_on
@@ -774,9 +772,8 @@ body: |
     ; GFX9-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; GFX9-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; GFX9-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; GFX9-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMAXNUM_IEEE]]
-    ; GFX9-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; GFX9-NEXT: [[FMAXNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; GFX9-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; GFX9-NEXT: [[FMAXNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FMAXNUM_IEEE]], [[FCANONICALIZE2]]
     ; GFX9-NEXT: $vgpr0 = COPY [[FMAXNUM_IEEE1]](s32)
     %0:_(s32) = COPY $vgpr0
     %1:_(s32) = COPY $vgpr1
@@ -856,9 +853,8 @@ body: |
     ; SI-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; SI-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; SI-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; SI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMINNUM_IEEE]]
-    ; SI-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; SI-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; SI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; SI-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FMINNUM_IEEE]], [[FCANONICALIZE2]]
     ; SI-NEXT: $vgpr0 = COPY [[FMAXNUM_IEEE]](s32)
     ;
     ; VI-LABEL: name: test_fmaxnum_with_fminnum_argument_s32_ieee_mode_on
@@ -870,9 +866,8 @@ body: |
     ; VI-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; VI-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; VI-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; VI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMINNUM_IEEE]]
-    ; VI-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; VI-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; VI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; VI-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FMINNUM_IEEE]], [[FCANONICALIZE2]]
     ; VI-NEXT: $vgpr0 = COPY [[FMAXNUM_IEEE]](s32)
     ;
     ; GFX9-LABEL: name: test_fmaxnum_with_fminnum_argument_s32_ieee_mode_on
@@ -884,9 +879,8 @@ body: |
     ; GFX9-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; GFX9-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; GFX9-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; GFX9-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMINNUM_IEEE]]
-    ; GFX9-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; GFX9-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; GFX9-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; GFX9-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FMINNUM_IEEE]], [[FCANONICALIZE2]]
     ; GFX9-NEXT: $vgpr0 = COPY [[FMAXNUM_IEEE]](s32)
     %0:_(s32) = COPY $vgpr0
     %1:_(s32) = COPY $vgpr1
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fminnum.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fminnum.mir
index e2b3f3560acfc..f511df1976d0e 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fminnum.mir
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-fminnum.mir
@@ -746,9 +746,8 @@ body: |
     ; SI-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; SI-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; SI-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; SI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMINNUM_IEEE]]
-    ; SI-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; SI-NEXT: [[FMINNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; SI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; SI-NEXT: [[FMINNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FMINNUM_IEEE]], [[FCANONICALIZE2]]
     ; SI-NEXT: $vgpr0 = COPY [[FMINNUM_IEEE1]](s32)
     ;
     ; VI-LABEL: name: test_fminnum_with_fminnum_argument_s32_ieee_mode_on
@@ -760,9 +759,8 @@ body: |
     ; VI-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; VI-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; VI-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; VI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMINNUM_IEEE]]
-    ; VI-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; VI-NEXT: [[FMINNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; VI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; VI-NEXT: [[FMINNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FMINNUM_IEEE]], [[FCANONICALIZE2]]
     ; VI-NEXT: $vgpr0 = COPY [[FMINNUM_IEEE1]](s32)
     ;
     ; GFX9-LABEL: name: test_fminnum_with_fminnum_argument_s32_ieee_mode_on
@@ -774,9 +772,8 @@ body: |
     ; GFX9-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; GFX9-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; GFX9-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; GFX9-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMINNUM_IEEE]]
-    ; GFX9-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; GFX9-NEXT: [[FMINNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; GFX9-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; GFX9-NEXT: [[FMINNUM_IEEE1:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FMINNUM_IEEE]], [[FCANONICALIZE2]]
     ; GFX9-NEXT: $vgpr0 = COPY [[FMINNUM_IEEE1]](s32)
     %0:_(s32) = COPY $vgpr0
     %1:_(s32) = COPY $vgpr1
@@ -856,9 +853,8 @@ body: |
     ; SI-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; SI-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; SI-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; SI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMAXNUM_IEEE]]
-    ; SI-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; SI-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; SI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; SI-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FMAXNUM_IEEE]], [[FCANONICALIZE2]]
     ; SI-NEXT: $vgpr0 = COPY [[FMINNUM_IEEE]](s32)
     ;
     ; VI-LABEL: name: test_fminnum_with_fmaxnum_argument_s32_ieee_mode_on
@@ -870,9 +866,8 @@ body: |
     ; VI-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; VI-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; VI-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; VI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMAXNUM_IEEE]]
-    ; VI-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; VI-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; VI-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; VI-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FMAXNUM_IEEE]], [[FCANONICALIZE2]]
     ; VI-NEXT: $vgpr0 = COPY [[FMINNUM_IEEE]](s32)
     ;
     ; GFX9-LABEL: name: test_fminnum_with_fmaxnum_argument_s32_ieee_mode_on
@@ -884,9 +879,8 @@ body: |
     ; GFX9-NEXT: [[FCANONICALIZE1:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY1]]
     ; GFX9-NEXT: [[FMAXNUM_IEEE:%[0-9]+]]:_(s32) = G_FMAXNUM_IEEE [[FCANONICALIZE]], [[FCANONICALIZE1]]
     ; GFX9-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $vgpr2
-    ; GFX9-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[FMAXNUM_IEEE]]
-    ; GFX9-NEXT: [[FCANONICALIZE3:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
-    ; GFX9-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FCANONICALIZE2]], [[FCANONICALIZE3]]
+    ; GFX9-NEXT: [[FCANONICALIZE2:%[0-9]+]]:_(s32) = G_FCANONICALIZE [[COPY2]]
+    ; GFX9-NEXT: [[FMINNUM_IEEE:%[0-9]+]]:_(s32) = G_FMINNUM_IEEE [[FMAXNUM_IEEE]], [[FCANONICALIZE2]]
     ; GFX9-NEXT: $vgpr0 = COPY [[FMINNUM_IEEE]](s32)
     %0:_(s32) = COPY $vgpr0
     %1:_(s32) = COPY $vgpr1
diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
index 791ed3ba743e2..0760ab239c95e 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -546,7 +546,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFnegFabsNInf) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ((fcNegative & ~fcNegInf) | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcNegFinite | fcNan, Known.KnownFPClasses);
   EXPECT_EQ(true, Known.SignBit);
 }
 
@@ -623,7 +623,7 @@ TEST_F(AArch64GISelMITest, TestFPClassCopySignNInfSrc0_NegSign) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcQNan | fcNegZero | fcNegNormal, Known.KnownFPClasses);
+  EXPECT_EQ(fcNan | fcNegZero | fcNegNormal, Known.KnownFPClasses);
   EXPECT_EQ(true, Known.SignBit);
 }
 
@@ -649,7 +649,7 @@ TEST_F(AArch64GISelMITest, TestFPClassCopySignNInfSrc0_PosSign) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcQNan | fcPosZero | fcPosNormal, Known.KnownFPClasses);
+  EXPECT_EQ(fcNan | fcPosZero | fcPosNormal, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
 
@@ -673,7 +673,7 @@ TEST_F(AArch64GISelMITest, TestFPClassUIToFP) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPosFinite & ~fcSubnormal, Known.KnownFPClasses);
+  EXPECT_EQ(fcPosZero | fcPosNormal, Known.KnownFPClasses);
   EXPECT_EQ(false, Known.SignBit);
 }
 
@@ -697,7 +697,7 @@ TEST_F(AArch64GISelMITest, TestFPClassSIToFP) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcFinite & ~fcNegZero & ~fcSubnormal, Known.KnownFPClasses);
+  EXPECT_EQ(fcPosNormal | fcNegNormal | fcPosZero, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -722,7 +722,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAdd) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcAllFlags & ~fcSNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -747,7 +747,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAdd_Zero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcAllFlags & ~fcSNan & ~fcNegZero, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags & ~fcNegZero, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -772,7 +772,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAdd_NegZero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcAllFlags & ~fcSNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -797,7 +797,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFstrictAdd_Zero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcAllFlags & ~fcSNan & ~fcNegZero, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags & ~fcNegZero, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -822,7 +822,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFMul) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive | fcQNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -874,7 +874,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFLogNeg) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcQNan | fcNegInf | fcPosZero | fcNormal, Known.KnownFPClasses);
+  EXPECT_EQ(fcNan | fcNegInf | fcPosZero | fcNormal, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -997,7 +997,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFLDExp) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive | fcQNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -1022,7 +1022,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFPowIEvenExp) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPositive | fcQNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -1073,7 +1073,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFDiv) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcPosNormal | fcQNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcPosNormal | fcNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -1122,7 +1122,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFRem) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  EXPECT_EQ(fcZero | fcQNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcZero | fcNan, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
@@ -1287,7 +1287,6 @@ TEST_F(AArch64GISelMITest, TestFPClassVecInsertElem) {
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFSinh) {
-  // sinh: sign-preserving, can produce Inf, no sNaN
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1302,13 +1301,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFSinh) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  // Unknown input: can be anything except sNaN
-  EXPECT_EQ(fcAllFlags & ~fcSNan, Known.KnownFPClasses);
+  EXPECT_EQ(fcAllFlags, Known.KnownFPClasses);
   EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFSinhPos) {
-  // sinh of a non-negative source is non-negative
+  // sinh is sign-preserving: non-negative input → non-negative output.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1324,12 +1322,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFSinhPos) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNever(fcNegative));
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcPositive, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFCosh) {
-  // cosh: always non-negative, NaN propagates, no sNaN
+  // cosh(x) >= 1 for all real x; never negative.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1344,12 +1342,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFCosh) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNever(fcNegative));
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcPositive | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFCoshNNaN) {
-  // cosh of a non-NaN source is also non-NaN (and non-negative)
+  // cosh of a non-NaN source is non-NaN (and non-negative).
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1365,12 +1363,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFCoshNNaN) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNever(fcNegative));
-  EXPECT_TRUE(Known.isKnownNeverNaN());
+  EXPECT_EQ(fcPositive, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFTanh) {
-  // tanh: bounded to (-1,1), never Inf, sign-preserving, no sNaN
+  // tanh is bounded to (-1, 1): never Inf.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1385,12 +1383,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFTanh) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcAllFlags & ~fcInf, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFTanhPos) {
-  // tanh of a non-negative source is non-negative
+  // tanh is sign-preserving and bounded to (-1,1): non-negative finite output.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1406,13 +1404,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFTanhPos) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcNegative));
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcPosFinite, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFAsin) {
-  // asin: bounded to [-π/2, π/2], never Inf, no sNaN
+  // asin is bounded to [-π/2, π/2]: never Inf.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1427,12 +1424,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFAsin) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcAllFlags & ~fcInf, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFAsinPos) {
-  // asin of a non-negative source is non-negative
+  // asin is sign-preserving and bounded: non-negative finite output.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1448,12 +1445,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFAsinPos) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+  EXPECT_EQ(fcPosFinite, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFAcos) {
-  // acos: bounded to [0, π], never Inf, never negative, no sNaN
+  // acos is bounded to [0, π]: never Inf, never negative.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1468,13 +1465,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFAcos) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcNegative));
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcPosFinite | fcNan, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFAtan) {
-  // atan: bounded to (-π/2, π/2), never Inf, sign-preserving, no sNaN
+  // atan is bounded to (-π/2, π/2): never Inf (atan(±Inf) = ±π/2, finite).
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1489,12 +1485,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFAtan) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcAllFlags & ~fcInf, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFAtanPos) {
-  // atan of a non-negative source is non-negative
+  // atan is sign-preserving and bounded: non-negative finite output.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1510,12 +1506,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFAtanPos) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcNegative));
+  EXPECT_EQ(fcPosFinite, Known.KnownFPClasses);
+  EXPECT_EQ(false, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFTan) {
-  // tan: never Inf (tan(±Inf) = NaN, tan(finite) = finite), no sNaN
+  // tan(±Inf) = NaN, tan(finite) = finite: never Inf.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1530,12 +1526,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFTan) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcAllFlags & ~fcInf, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFTanNNaN) {
-  // tan of a non-NaN, non-Inf source is non-NaN
+  // tan of a non-NaN, non-Inf source is non-NaN and non-Inf.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %val:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1551,12 +1547,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFTanNNaN) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNeverNaN());
+  EXPECT_EQ(fcFinite, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFAtan2) {
-  // atan2: result in (-π, π], never Inf, NaN if either operand is NaN
+  // atan2 result is in (-π, π]: never Inf.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %y:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1572,12 +1568,12 @@ TEST_F(AArch64GISelMITest, TestFPClassFAtan2) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNever(fcSNan));
+  EXPECT_EQ(fcAllFlags & ~fcInf, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFAtan2NNaN) {
-  // atan2 with two non-NaN inputs is non-NaN
+  // atan2 with two non-NaN inputs is non-NaN and non-Inf.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %y:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1595,6 +1591,6 @@ TEST_F(AArch64GISelMITest, TestFPClassFAtan2NNaN) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_TRUE(Known.isKnownNeverInfinity());
-  EXPECT_TRUE(Known.isKnownNeverNaN());
+  EXPECT_EQ(fcFinite, Known.KnownFPClasses);
+  EXPECT_EQ(std::nullopt, Known.SignBit);
 }

>From a9b9057bb6845da390b0f57c635d63140defbefc Mon Sep 17 00:00:00 2001
From: Tim Gymnich <tim at gymni.ch>
Date: Mon, 6 Apr 2026 00:22:48 +0200
Subject: [PATCH 5/5] clang format

Signed-off-by: Tim Gymnich <tim at gymni.ch>
---
 llvm/include/llvm/CodeGen/GlobalISel/Utils.h  |  7 ++++---
 .../CodeGen/GlobalISel/GISelValueTracking.cpp | 21 +++++++++----------
 llvm/lib/CodeGen/GlobalISel/Utils.cpp         |  4 ++--
 .../CodeGen/GlobalISel/KnownFPClassTest.cpp   |  3 ++-
 4 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
index 0ffec025b7e36..125967faf845e 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
@@ -340,11 +340,12 @@ isKnownToBeAPowerOfTwo(Register Val, const MachineRegisterInfo &MRI,
 
 /// Returns true if \p Val can be assumed to never be a NaN. If \p SNaN is true,
 /// this returns if \p Val can be assumed to never be a signaling NaN.
-LLVM_ABI bool isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI, GISelValueTracking *VT,
-                              bool SNaN = false);
+LLVM_ABI bool isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI,
+                              GISelValueTracking *VT, bool SNaN = false);
 
 /// Returns true if \p Val can be assumed to never be a signaling NaN.
-inline bool isKnownNeverSNaN(Register Val, const MachineRegisterInfo &MRI, GISelValueTracking *VT) {
+inline bool isKnownNeverSNaN(Register Val, const MachineRegisterInfo &MRI,
+                             GISelValueTracking *VT) {
   return isKnownNeverNaN(Val, MRI, VT, true);
 }
 
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index 8473ffb4614ed..2c462bae4ad24 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -883,8 +883,7 @@ void GISelValueTracking::computeKnownFPClass(Register R, KnownFPClass &Known,
 static bool isAbsoluteValueULEOne(Register R, const MachineRegisterInfo &MRI) {
   using namespace MIPatternMatch;
   Register SubX;
-  return mi_match(R, MRI,
-                  m_GFSub(m_Reg(SubX), m_GFFloor(m_DeferredReg(SubX))));
+  return mi_match(R, MRI, m_GFSub(m_Reg(SubX), m_GFFloor(m_DeferredReg(SubX))));
 }
 
 void GISelValueTracking::computeKnownFPClassForFPTrunc(
@@ -1385,9 +1384,8 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     if ((KnownSrc.KnownFPClasses & ExpInfoMask) != fcNone) {
       Register ExpReg = MI.getOperand(2).getReg();
       LLT ExpTy = MRI.getType(ExpReg);
-      ExpBits = getKnownBits(ExpReg,
-                             ExpTy.isVector() ? DemandedElts : APInt(1, 1),
-                             Depth + 1);
+      ExpBits = getKnownBits(
+          ExpReg, ExpTy.isVector() ? DemandedElts : APInt(1, 1), Depth + 1);
     }
 
     LLT ScalarTy = DstTy.getScalarType();
@@ -1539,9 +1537,9 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     computeKnownFPClass(RHS, DemandedElts, fcNan | fcInf | fcZero | fcNegative,
                         KnownRHS, Depth + 1);
 
-    bool KnowSomethingUseful =
-        KnownRHS.isKnownNeverNaN() || KnownRHS.isKnownNever(fcNegative) ||
-        KnownRHS.isKnownNever(fcPositive);
+    bool KnowSomethingUseful = KnownRHS.isKnownNeverNaN() ||
+                               KnownRHS.isKnownNever(fcNegative) ||
+                               KnownRHS.isKnownNever(fcPositive);
 
     if (KnowSomethingUseful || WantPositive) {
       computeKnownFPClass(LHS, DemandedElts, fcAllFlags, KnownLHS, Depth + 1);
@@ -1624,8 +1622,8 @@ void GISelValueTracking::computeKnownFPClass(Register R,
 
     Register Val = MI.getOperand(1).getReg();
     LLT Ty = MRI.getType(Val);
-    KnownBits IntKnown =
-        getKnownBits(Val, Ty.isVector() ? DemandedElts : APInt(1, 1), Depth + 1);
+    KnownBits IntKnown = getKnownBits(
+        Val, Ty.isVector() ? DemandedElts : APInt(1, 1), Depth + 1);
 
     // If the integer is non-zero, the result cannot be +0.0.
     if (IntKnown.isNonZero())
@@ -1800,7 +1798,8 @@ void GISelValueTracking::computeKnownFPClass(Register R,
   case TargetOpcode::G_PHI:
   case TargetOpcode::PHI: {
     // Cap PHI recursion below the global limit to avoid spending the entire
-    // budget chasing loop back-edges (matches ValueTracking's PhiRecursionLimit).
+    // budget chasing loop back-edges (matches ValueTracking's
+    // PhiRecursionLimit).
     if (Depth + 2 > MaxAnalysisRecursionDepth)
       break;
     // PHI's operands are a mix of registers and basic blocks interleaved.
diff --git a/llvm/lib/CodeGen/GlobalISel/Utils.cpp b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
index 243a8419b177d..8810115ce4718 100644
--- a/llvm/lib/CodeGen/GlobalISel/Utils.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
@@ -833,8 +833,8 @@ llvm::ConstantFoldVectorBinop(unsigned Opcode, const Register Op1,
   return FoldedElements;
 }
 
-bool llvm::isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI, GISelValueTracking *VT,
-                           bool SNaN) {
+bool llvm::isKnownNeverNaN(Register Val, const MachineRegisterInfo &MRI,
+                           GISelValueTracking *VT, bool SNaN) {
   const MachineInstr *DefMI = MRI.getVRegDef(Val);
   if (!DefMI)
     return false;
diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
index 0760ab239c95e..7abcce533a26c 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -1147,7 +1147,8 @@ TEST_F(AArch64GISelMITest, TestFPClassFRemSelf_KnownFiniteNonZero) {
 
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
 
-  // 2.0 % 2.0 = 0.0 exactly — NaN is impossible since 2.0 is finite and nonzero.
+  // 2.0 % 2.0 = 0.0 exactly — NaN is impossible since 2.0 is finite and
+  // nonzero.
   EXPECT_EQ(fcZero, Known.KnownFPClasses);
 }
 



More information about the llvm-commits mailing list