[llvm-branch-commits] [llvm] InstCombine: Handle fsub in SimplifyDemandedFPClass (PR #175852)

Matt Arsenault via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jan 14 08:11:48 PST 2026


https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/175852

>From d96ba8b2e5a122703515cb499974cb82d9828214 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Mon, 5 Jan 2026 14:20:21 +0100
Subject: [PATCH 1/2] InstCombine: Handle fsub in SimplifyDemandedFPClass

alive2 fails on some of the tests, but this is due to existing
folds in instsimplify and https://github.com/AliveToolkit/alive2/issues/1273
---
 llvm/include/llvm/Support/KnownFPClass.h      |   5 +
 llvm/lib/Analysis/ValueTracking.cpp           |  81 +++--------
 llvm/lib/Support/KnownFPClass.cpp             |  21 +++
 .../InstCombineSimplifyDemanded.cpp           |  15 +-
 .../simplify-demanded-fpclass-fsub.ll         | 128 ++++++++----------
 5 files changed, 112 insertions(+), 138 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index e2b0d20c790a9..010ca6a3e84ec 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -222,6 +222,11 @@ struct KnownFPClass {
   fadd_self(const KnownFPClass &Src,
             DenormalMode Mode = DenormalMode::getDynamic());
 
+  /// Report known values for fsub
+  LLVM_ABI static KnownFPClass
+  fsub(const KnownFPClass &LHS, const KnownFPClass &RHS,
+       DenormalMode Mode = DenormalMode::getDynamic());
+
   /// Report known values for fmul
   LLVM_ABI static KnownFPClass
   fmul(const KnownFPClass &LHS, const KnownFPClass &RHS,
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index ece8425aef698..51fbffd6fd371 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5524,78 +5524,37 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
                         KnownRHS, Q, Depth + 1);
 
     // Special case fadd x, x, which is the canonical form of fmul x, 2.
-    bool SelfAdd = Op->getOperand(0) == Op->getOperand(1) &&
-                   isGuaranteedNotToBeUndef(Op->getOperand(0), Q.AC, Q.CxtI,
-                                            Q.DT, Depth + 1);
-    if (SelfAdd)
+    bool Self = Op->getOperand(0) == Op->getOperand(1) &&
+                isGuaranteedNotToBeUndef(Op->getOperand(0), Q.AC, Q.CxtI, Q.DT,
+                                         Depth + 1);
+    if (Self)
       KnownLHS = KnownRHS;
 
     if ((WantNaN && KnownRHS.isKnownNeverNaN()) ||
         (WantNegative && KnownRHS.cannotBeOrderedLessThanZero()) ||
         WantNegZero || Opc == Instruction::FSub) {
 
-      if (!SelfAdd) {
-        // RHS is canonically cheaper to compute. Skip inspecting the LHS if
-        // there's no point.
-        computeKnownFPClass(Op->getOperand(0), DemandedElts, InterestedSrcs,
-                            KnownLHS, Q, 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);
-
       // FIXME: Context function should always be passed in separately
       const Function *F = cast<Instruction>(Op)->getFunction();
+      const fltSemantics &FltSem =
+          Op->getType()->getScalarType()->getFltSemantics();
+      DenormalMode Mode =
+          F ? F->getDenormalMode(FltSem) : DenormalMode::getDynamic();
 
-      if (Op->getOpcode() == Instruction::FAdd) {
-        if (KnownLHS.cannotBeOrderedLessThanZero() &&
-            KnownRHS.cannotBeOrderedLessThanZero())
-          Known.knownNot(KnownFPClass::OrderedLessThanZeroMask);
-        if (KnownLHS.cannotBeOrderedGreaterThanZero() &&
-            KnownRHS.cannotBeOrderedGreaterThanZero())
-          Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask);
-
-        if (!F)
-          break;
-
-        const fltSemantics &FltSem =
-            Op->getType()->getScalarType()->getFltSemantics();
-        DenormalMode Mode = F->getDenormalMode(FltSem);
-
-        // Doubling 0 will give the same 0.
-        if (SelfAdd && KnownRHS.isKnownNeverLogicalPosZero(Mode) &&
-            (Mode.Output == DenormalMode::IEEE ||
-             (Mode.Output == DenormalMode::PreserveSign &&
-              KnownRHS.isKnownNeverPosSubnormal()) ||
-             (Mode.Output == DenormalMode::PositiveZero &&
-              KnownRHS.isKnownNeverSubnormal())))
-          Known.knownNot(fcPosZero);
-
-        // (fadd x, 0.0) is guaranteed to return +0.0, not -0.0.
-        if ((KnownLHS.isKnownNeverLogicalNegZero(Mode) ||
-             KnownRHS.isKnownNeverLogicalNegZero(Mode)) &&
-            // Make sure output negative denormal can't flush to -0
-            (Mode.Output == DenormalMode::IEEE ||
-             Mode.Output == DenormalMode::PositiveZero))
-          Known.knownNot(fcNegZero);
+      if (Self && Opc == Instruction::FAdd) {
+        Known = KnownFPClass::fadd_self(KnownLHS, Mode);
       } else {
-        if (!F)
-          break;
+        // RHS is canonically cheaper to compute. Skip inspecting the LHS if
+        // there's no point.
 
-        const fltSemantics &FltSem =
-            Op->getType()->getScalarType()->getFltSemantics();
-        DenormalMode Mode = F->getDenormalMode(FltSem);
-
-        // Only fsub -0, +0 can return -0
-        if ((KnownLHS.isKnownNeverLogicalNegZero(Mode) ||
-             KnownRHS.isKnownNeverLogicalPosZero(Mode)) &&
-            // Make sure output negative denormal can't flush to -0
-            (Mode.Output == DenormalMode::IEEE ||
-             Mode.Output == DenormalMode::PositiveZero))
-          Known.knownNot(fcNegZero);
+        if (!Self) {
+          computeKnownFPClass(Op->getOperand(0), DemandedElts, InterestedSrcs,
+                              KnownLHS, Q, Depth + 1);
+        }
+
+        Known = Opc == Instruction::FAdd
+                    ? KnownFPClass::fadd(KnownLHS, KnownRHS, Mode)
+                    : KnownFPClass::fsub(KnownLHS, KnownRHS, Mode);
       }
     }
 
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index ae8c4a9133897..dbd5707873f5d 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -279,6 +279,27 @@ KnownFPClass KnownFPClass::fadd_self(const KnownFPClass &KnownSrc,
   return Known;
 }
 
+KnownFPClass KnownFPClass::fsub(const KnownFPClass &KnownLHS,
+                                const KnownFPClass &KnownRHS,
+                                DenormalMode Mode) {
+  KnownFPClass Known;
+  // Adding positive and negative infinity produces NaN.
+  // TODO: Check sign of infinities.
+  if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() &&
+      (KnownLHS.isKnownNeverInfinity() || KnownRHS.isKnownNeverInfinity()))
+    Known.knownNot(fcNan);
+
+  // Only fsub -0, +0 can return -0
+  if ((KnownLHS.isKnownNeverLogicalNegZero(Mode) ||
+       KnownRHS.isKnownNeverLogicalPosZero(Mode)) &&
+      // Make sure output negative denormal can't flush to -0
+      (Mode.Output == DenormalMode::IEEE ||
+       Mode.Output == DenormalMode::PositiveZero))
+    Known.knownNot(fcNegZero);
+
+  return Known;
+}
+
 KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS,
                                 const KnownFPClass &KnownRHS,
                                 DenormalMode Mode) {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 05ed123b8be75..a483f44c01ab8 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2081,13 +2081,15 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
     Known.fneg();
     break;
   }
-  case Instruction::FAdd: {
+  case Instruction::FAdd:
+  case Instruction::FSub: {
     KnownFPClass KnownLHS, KnownRHS;
 
     const SimplifyQuery &SQ = getSimplifyQuery();
 
     // fadd x, x can be handled more aggressively.
     if (I->getOperand(0) == I->getOperand(1) &&
+        I->getOpcode() == Instruction::FAdd &&
         isGuaranteedNotToBeUndef(I->getOperand(0), SQ.AC, CxtI, SQ.DT,
                                  Depth + 1)) {
       Type *EltTy = VTy->getScalarType();
@@ -2141,7 +2143,10 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
 
       Type *EltTy = VTy->getScalarType();
       DenormalMode Mode = F.getDenormalMode(EltTy->getFltSemantics());
-      Known = KnownFPClass::fadd(KnownLHS, KnownRHS, Mode);
+
+      Known = I->getOpcode() == Instruction::FAdd
+                  ? KnownFPClass::fadd(KnownLHS, KnownRHS, Mode)
+                  : KnownFPClass::fsub(KnownLHS, KnownRHS, Mode);
     }
 
     FPClassTest ValidResults = DemandedMask & Known.KnownFPClasses;
@@ -2157,11 +2162,13 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
     }
 
     // With nnan: X + {+/-}Inf --> {+/-}Inf
-    if (KnownRHS.isKnownAlways(fcInf | fcNan) &&
-        KnownLHS.isKnownNever(fcNan))
+    if (I->getOpcode() == Instruction::FAdd &&
+        KnownRHS.isKnownAlways(fcInf | fcNan) && KnownLHS.isKnownNever(fcNan)) {
       return I->getOperand(1);
+    }
 
     // With nnan: {+/-}Inf + X --> {+/-}Inf
+    // With nnan: {+/-}Inf - X --> {+/-}Inf
     if (KnownLHS.isKnownAlways(fcInf | fcNan) &&
         KnownRHS.isKnownNever(fcNan))
       return I->getOperand(0);
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll
index 53c2817612662..b4a98bd59c71c 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-fsub.ll
@@ -34,8 +34,7 @@ declare void @use(half)
 define nofpclass(inf zero sub norm) half @ret_only_nan(half %x, half %y) {
 ; CHECK-LABEL: define nofpclass(inf zero sub norm) half @ret_only_nan(
 ; CHECK-SAME: half [[X:%.*]], half [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half 0xH7E00
 ;
   %add = fsub half %x, %y
   ret half %add
@@ -64,7 +63,7 @@ define nofpclass(qnan inf zero sub norm) half @ret_only_snan(half %x, half %y) {
 define nofpclass(nan inf sub norm) half @ret_only_zero(half %x, half %y) {
 ; CHECK-LABEL: define nofpclass(nan inf sub norm) half @ret_only_zero(
 ; CHECK-SAME: half [[X:%.*]], half [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -92,7 +91,7 @@ define nofpclass(nan inf pzero sub norm) half @ret_only_nzero(half %x, half %y)
 define nofpclass(nan zero sub norm) half @ret_only_inf(half %x, half %y) {
 ; CHECK-LABEL: define nofpclass(nan zero sub norm) half @ret_only_inf(
 ; CHECK-SAME: half [[X:%.*]], half [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -218,9 +217,7 @@ define nofpclass(nan inf) half @ret_nofpclass_inf_nan__fsub_select_unknown_or_in
 ; CHECK-SAME: i1 [[COND:%.*]], half [[X:%.*]], half [[Y:%.*]]) {
 ; CHECK-NEXT:    [[INF0:%.*]] = call half @returns_inf()
 ; CHECK-NEXT:    [[INF1:%.*]] = call half @returns_inf()
-; CHECK-NEXT:    [[X_OR_INF:%.*]] = select i1 [[COND]], half [[X]], half [[INF0]]
-; CHECK-NEXT:    [[Y_OR_INF:%.*]] = select i1 [[COND]], half [[Y]], half [[INF1]]
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X_OR_INF]], [[Y_OR_INF]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %inf0 = call half @returns_inf()
@@ -236,7 +233,7 @@ define nofpclass(inf sub norm) half @nan_result_demands_inf_input_lhs(i1 %cond,
 ; CHECK-LABEL: define nofpclass(inf sub norm) half @nan_result_demands_inf_input_lhs(
 ; CHECK-SAME: i1 [[COND:%.*]], half nofpclass(zero) [[X:%.*]], half nofpclass(nan inf zero) [[Y:%.*]]) {
 ; CHECK-NEXT:    [[PINF:%.*]] = call half @returns_pinf()
-; CHECK-NEXT:    [[X_OR_INF:%.*]] = select i1 [[COND]], half [[X]], half [[PINF]]
+; CHECK-NEXT:    [[X_OR_INF:%.*]] = select i1 [[COND]], half [[X]], half 0xH7C00
 ; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X_OR_INF]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
@@ -251,7 +248,7 @@ define nofpclass(inf sub norm) half @nan_result_demands_inf_input_rhs(i1 %cond,
 ; CHECK-LABEL: define nofpclass(inf sub norm) half @nan_result_demands_inf_input_rhs(
 ; CHECK-SAME: i1 [[COND:%.*]], half nofpclass(zero) [[X:%.*]], half [[Y:%.*]]) {
 ; CHECK-NEXT:    [[PINF:%.*]] = call half @returns_pinf()
-; CHECK-NEXT:    [[Y_OR_INF:%.*]] = select i1 [[COND]], half [[Y]], half [[PINF]]
+; CHECK-NEXT:    [[Y_OR_INF:%.*]] = select i1 [[COND]], half [[Y]], half 0xH7C00
 ; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y_OR_INF]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
@@ -267,7 +264,7 @@ define nofpclass(nan sub norm zero) half @inf_result_demands_inf_input_lhs(i1 %c
 ; CHECK-SAME: i1 [[COND:%.*]], half nofpclass(zero) [[X:%.*]], half nofpclass(zero) [[Y:%.*]]) {
 ; CHECK-NEXT:    [[INF:%.*]] = call half @returns_inf()
 ; CHECK-NEXT:    [[X_OR_INF:%.*]] = select i1 [[COND]], half [[X]], half [[INF]]
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X_OR_INF]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X_OR_INF]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %inf = call half @returns_inf()
@@ -282,7 +279,7 @@ define nofpclass(nan sub norm zero) half @inf_result_demands_inf_input_rhs(i1 %c
 ; CHECK-SAME: i1 [[COND:%.*]], half nofpclass(zero) [[X:%.*]], half nofpclass(zero) [[Y:%.*]]) {
 ; CHECK-NEXT:    [[INF:%.*]] = call half @returns_inf()
 ; CHECK-NEXT:    [[Y_OR_INF:%.*]] = select i1 [[COND]], half [[Y]], half [[INF]]
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y_OR_INF]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y_OR_INF]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %inf = call half @returns_inf()
@@ -296,8 +293,8 @@ define nofpclass(nan sub norm zero) half @inf_result_demands_pinf_input_lhs(i1 %
 ; CHECK-LABEL: define nofpclass(nan zero sub norm) half @inf_result_demands_pinf_input_lhs(
 ; CHECK-SAME: i1 [[COND:%.*]], half nofpclass(zero) [[X:%.*]], half nofpclass(zero) [[Y:%.*]]) {
 ; CHECK-NEXT:    [[PINF:%.*]] = call half @returns_pinf()
-; CHECK-NEXT:    [[X_OR_INF:%.*]] = select i1 [[COND]], half [[X]], half [[PINF]]
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X_OR_INF]], [[Y]]
+; CHECK-NEXT:    [[X_OR_INF:%.*]] = select i1 [[COND]], half [[X]], half 0xH7C00
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X_OR_INF]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pinf = call half @returns_pinf()
@@ -311,8 +308,8 @@ define nofpclass(nan sub norm zero) half @inf_result_demands_pinf_input_rhs(i1 %
 ; CHECK-LABEL: define nofpclass(nan zero sub norm) half @inf_result_demands_pinf_input_rhs(
 ; CHECK-SAME: i1 [[COND:%.*]], half nofpclass(zero) [[X:%.*]], half nofpclass(zero) [[Y:%.*]]) {
 ; CHECK-NEXT:    [[PINF:%.*]] = call half @returns_pinf()
-; CHECK-NEXT:    [[Y_OR_INF:%.*]] = select i1 [[COND]], half [[Y]], half [[PINF]]
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y_OR_INF]]
+; CHECK-NEXT:    [[Y_OR_INF:%.*]] = select i1 [[COND]], half [[Y]], half 0xH7C00
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y_OR_INF]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pinf = call half @returns_pinf()
@@ -326,8 +323,8 @@ define nofpclass(nan sub norm zero) half @inf_result_demands_ninf_input_lhs(i1 %
 ; CHECK-LABEL: define nofpclass(nan zero sub norm) half @inf_result_demands_ninf_input_lhs(
 ; CHECK-SAME: i1 [[COND:%.*]], half nofpclass(zero) [[X:%.*]], half nofpclass(zero) [[Y:%.*]]) {
 ; CHECK-NEXT:    [[NINF:%.*]] = call half @returns_ninf()
-; CHECK-NEXT:    [[X_OR_INF:%.*]] = select i1 [[COND]], half [[X]], half [[NINF]]
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X_OR_INF]], [[Y]]
+; CHECK-NEXT:    [[X_OR_INF:%.*]] = select i1 [[COND]], half [[X]], half 0xHFC00
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X_OR_INF]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %ninf = call half @returns_ninf()
@@ -341,8 +338,8 @@ define nofpclass(nan sub norm zero) half @inf_result_demands_ninf_input_rhs(i1 %
 ; CHECK-LABEL: define nofpclass(nan zero sub norm) half @inf_result_demands_ninf_input_rhs(
 ; CHECK-SAME: i1 [[COND:%.*]], half nofpclass(zero) [[X:%.*]], half nofpclass(zero) [[Y:%.*]]) {
 ; CHECK-NEXT:    [[NINF:%.*]] = call half @returns_ninf()
-; CHECK-NEXT:    [[Y_OR_INF:%.*]] = select i1 [[COND]], half [[Y]], half [[NINF]]
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y_OR_INF]]
+; CHECK-NEXT:    [[Y_OR_INF:%.*]] = select i1 [[COND]], half [[Y]], half 0xHFC00
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y_OR_INF]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %ninf = call half @returns_ninf()
@@ -367,7 +364,7 @@ define nofpclass(snan) half @no_nans_inputs(half nofpclass(nan) %x, half nofpcla
 define nofpclass(nan) half @no_nans_input_and_output(half nofpclass(nan) %x, half nofpclass(nan) %y) {
 ; CHECK-LABEL: define nofpclass(nan) half @no_nans_input_and_output(
 ; CHECK-SAME: half nofpclass(nan) [[X:%.*]], half nofpclass(nan) [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -390,7 +387,7 @@ define nofpclass(snan) half @no_infs_inputs(half nofpclass(inf) %x, half nofpcla
 define nofpclass(inf) half @no_infs_inputs_and_outputs(half nofpclass(inf) %x, half nofpclass(inf) %y) {
 ; CHECK-LABEL: define nofpclass(inf) half @no_infs_inputs_and_outputs(
 ; CHECK-SAME: half nofpclass(inf) [[X:%.*]], half nofpclass(inf) [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub ninf half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -401,7 +398,7 @@ define nofpclass(inf) half @no_infs_inputs_and_outputs(half nofpclass(inf) %x, h
 define nofpclass(snan) half @no_nans_infs_inputs(half nofpclass(nan inf) %x, half nofpclass(nan inf) %y) {
 ; CHECK-LABEL: define nofpclass(snan) half @no_nans_infs_inputs(
 ; CHECK-SAME: half nofpclass(nan inf) [[X:%.*]], half nofpclass(nan inf) [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -412,7 +409,7 @@ define nofpclass(snan) half @no_nans_infs_inputs(half nofpclass(nan inf) %x, hal
 define nofpclass(inf) half @no_nans_infs_inputs__noinfs_output(half nofpclass(nan inf) %x, half nofpclass(nan inf) %y) {
 ; CHECK-LABEL: define nofpclass(inf) half @no_nans_infs_inputs__noinfs_output(
 ; CHECK-SAME: half nofpclass(nan inf) [[X:%.*]], half nofpclass(nan inf) [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan ninf half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -423,7 +420,7 @@ define nofpclass(inf) half @no_nans_infs_inputs__noinfs_output(half nofpclass(na
 define nofpclass(nan) half @no_nans_infs_inputs__nonans_output(half nofpclass(nan inf) %x, half nofpclass(nan inf) %y) {
 ; CHECK-LABEL: define nofpclass(nan) half @no_nans_infs_inputs__nonans_output(
 ; CHECK-SAME: half nofpclass(nan inf) [[X:%.*]], half nofpclass(nan inf) [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -434,7 +431,7 @@ define nofpclass(nan) half @no_nans_infs_inputs__nonans_output(half nofpclass(na
 define nofpclass(nzero) half @inferred_nan_output__noinfs_only_lhs(half nofpclass(nan inf) %x, half nofpclass(nan) %y) {
 ; CHECK-LABEL: define nofpclass(nzero) half @inferred_nan_output__noinfs_only_lhs(
 ; CHECK-SAME: half nofpclass(nan inf) [[X:%.*]], half nofpclass(nan) [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -445,7 +442,7 @@ define nofpclass(nzero) half @inferred_nan_output__noinfs_only_lhs(half nofpclas
 define nofpclass(nzero) half @inferred_nan_output__noinfs_only_rhs(half nofpclass(nan) %x, half nofpclass(nan inf) %y) {
 ; CHECK-LABEL: define nofpclass(nzero) half @inferred_nan_output__noinfs_only_rhs(
 ; CHECK-SAME: half nofpclass(nan) [[X:%.*]], half nofpclass(nan inf) [[Y:%.*]]) {
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[X]], [[Y]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %add = fsub half %x, %y
@@ -516,7 +513,7 @@ define nofpclass(nan) half @ret_nonan_fabs_fsub_known_positive_or_nan_sources()
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan_fabs_fsub_known_positive_or_nan_sources() {
 ; CHECK-NEXT:    [[POSITIVE_OR_NAN0:%.*]] = call half @returns_positive_or_nan()
 ; CHECK-NEXT:    [[POSITIVE_OR_NAN1:%.*]] = call half @returns_positive_or_nan()
-; CHECK-NEXT:    [[KNOWN_POSITIVE_ADD:%.*]] = fsub half [[POSITIVE_OR_NAN0]], [[POSITIVE_OR_NAN1]]
+; CHECK-NEXT:    [[KNOWN_POSITIVE_ADD:%.*]] = fsub nnan half [[POSITIVE_OR_NAN0]], [[POSITIVE_OR_NAN1]]
 ; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[KNOWN_POSITIVE_ADD]])
 ; CHECK-NEXT:    ret half [[FABS]]
 ;
@@ -532,9 +529,7 @@ define nofpclass(nan) half @ret_nonan_fabs_fsub_known_positive_sources() {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan_fabs_fsub_known_positive_sources() {
 ; CHECK-NEXT:    [[POSITIVE0:%.*]] = call half @returns_positive()
 ; CHECK-NEXT:    [[POSITIVE1:%.*]] = call half @returns_positive()
-; CHECK-NEXT:    [[KNOWN_POSITIVE_ADD:%.*]] = fsub half [[POSITIVE1]], [[POSITIVE1]]
-; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[KNOWN_POSITIVE_ADD]])
-; CHECK-NEXT:    ret half [[FABS]]
+; CHECK-NEXT:    ret half 0xH0000
 ;
   %positive0 = call half @returns_positive()
   %positive1 = call half @returns_positive()
@@ -595,7 +590,7 @@ define nofpclass(nan) half @ret_nonan__unknown__fsub__inf(half %unknown) {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__unknown__fsub__inf(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[INF:%.*]] = call half @returns_inf()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[UNKNOWN]], [[INF]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[UNKNOWN]], [[INF]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %inf = call half @returns_inf()
@@ -607,8 +602,7 @@ define nofpclass(nan) half @ret_nonan__inf__fsub__unknown(half %unknown) {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__inf__fsub__unknown(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[INF:%.*]] = call half @returns_inf()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[INF]], [[UNKNOWN]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[INF]]
 ;
   %inf = call half @returns_inf()
   %add = fsub half %inf, %unknown
@@ -619,7 +613,7 @@ define nofpclass(nan) half @ret_nonan__unknown__fsub__inf_or_nan(half %unknown)
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__unknown__fsub__inf_or_nan(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[INF_OR_NAN:%.*]] = call half @returns_inf_or_nan()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[UNKNOWN]], [[INF_OR_NAN]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[UNKNOWN]], [[INF_OR_NAN]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %inf.or.nan = call half @returns_inf_or_nan()
@@ -631,8 +625,7 @@ define nofpclass(nan) half @ret_nonan__inf_or_nan__fsub__unknown(half %unknown)
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__inf_or_nan__fsub__unknown(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[INF_OR_NAN:%.*]] = call half @returns_inf_or_nan()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[INF_OR_NAN]], [[UNKNOWN]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[INF_OR_NAN]]
 ;
   %inf.or.nan = call half @returns_inf_or_nan()
   %add = fsub half %inf.or.nan, %unknown
@@ -667,7 +660,7 @@ define nofpclass(nan) half @ret_nonan__unknown__fsub__zero(half %unknown) {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__unknown__fsub__zero(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[ZERO:%.*]] = call half @returns_zero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[UNKNOWN]], [[ZERO]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[UNKNOWN]], [[ZERO]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %zero = call half @returns_zero()
@@ -679,7 +672,7 @@ define nofpclass(nan) half @ret_nonan__zero__fsub__unknown(half %unknown) {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__zero__fsub__unknown(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[ZERO:%.*]] = call half @returns_zero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[ZERO]], [[UNKNOWN]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half [[ZERO]], [[UNKNOWN]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %zero = call half @returns_zero()
@@ -691,8 +684,7 @@ define nofpclass(snan) half @unknown__fsub__pzero(half %unknown) {
 ; CHECK-LABEL: define nofpclass(snan) half @unknown__fsub__pzero(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[UNKNOWN]], [[PZERO]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[UNKNOWN]]
 ;
   %pzero = call half @returns_pzero()
   %add = fsub half %unknown, %pzero
@@ -703,7 +695,7 @@ define nofpclass(snan) half @pzero__fsub__unknown(half %unknown) {
 ; CHECK-LABEL: define nofpclass(snan) half @pzero__fsub__unknown(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[PZERO]], [[UNKNOWN]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub half 0xH0000, [[UNKNOWN]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pzero = call half @returns_pzero()
@@ -715,8 +707,7 @@ define nofpclass(nan) half @ret_nonan__unknown__fsub__pzero(half %unknown) {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__unknown__fsub__pzero(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[UNKNOWN]], [[PZERO]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[UNKNOWN]]
 ;
   %pzero = call half @returns_pzero()
   %add = fsub half %unknown, %pzero
@@ -727,7 +718,7 @@ define nofpclass(nan) half @ret_nonan__pzero__fsub__unknown(half %unknown) {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__pzero__fsub__unknown(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[PZERO]], [[UNKNOWN]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub nnan half 0xH0000, [[UNKNOWN]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pzero = call half @returns_pzero()
@@ -739,7 +730,7 @@ define nofpclass(snan) half @unknown__fsub__nzero(half %unknown) {
 ; CHECK-LABEL: define nofpclass(snan) half @unknown__fsub__nzero(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[NZERO:%.*]] = call half @returns_nzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[UNKNOWN]], [[NZERO]]
+; CHECK-NEXT:    [[ADD:%.*]] = fadd half [[UNKNOWN]], 0xH0000
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %nzero = call half @returns_nzero()
@@ -751,7 +742,7 @@ define nofpclass(snan) half @nzero__fsub__unknown(half %unknown) {
 ; CHECK-LABEL: define nofpclass(snan) half @nzero__fsub__unknown(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[NZERO:%.*]] = call half @returns_nzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[NZERO]], [[UNKNOWN]]
+; CHECK-NEXT:    [[ADD:%.*]] = fneg half [[UNKNOWN]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %nzero = call half @returns_nzero()
@@ -787,7 +778,7 @@ define nofpclass(nan) half @ret_nonan__unknown__fsub__nzero(half %unknown) {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__unknown__fsub__nzero(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[NZERO:%.*]] = call half @returns_nzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[UNKNOWN]], [[NZERO]]
+; CHECK-NEXT:    [[ADD:%.*]] = fadd nnan half [[UNKNOWN]], 0xH0000
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %nzero = call half @returns_nzero()
@@ -799,7 +790,7 @@ define nofpclass(nan) half @ret_nonan__nzero__fsub__unknown(half %unknown) {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__nzero__fsub__unknown(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[NZERO:%.*]] = call half @returns_nzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[NZERO]], [[UNKNOWN]]
+; CHECK-NEXT:    [[ADD:%.*]] = fneg half [[UNKNOWN]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %nzero = call half @returns_nzero()
@@ -835,8 +826,7 @@ define nofpclass(snan) half @not_nzero__fsub__pzero(half nofpclass(nzero) %not.n
 ; CHECK-LABEL: define nofpclass(snan) half @not_nzero__fsub__pzero(
 ; CHECK-SAME: half nofpclass(nzero) [[NOT_NZERO:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[NOT_NZERO]], [[PZERO]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[NOT_NZERO]]
 ;
   %pzero = call half @returns_pzero()
   %add = fsub half %not.nzero, %pzero
@@ -847,7 +837,7 @@ define nofpclass(snan) half @pzero__fsub__not_nzero(half nofpclass(nzero) %not.n
 ; CHECK-LABEL: define nofpclass(snan) half @pzero__fsub__not_nzero(
 ; CHECK-SAME: half nofpclass(nzero) [[NOT_NZERO:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[PZERO]], [[NOT_NZERO]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub half 0xH0000, [[NOT_NZERO]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pzero = call half @returns_pzero()
@@ -907,8 +897,7 @@ define nofpclass(snan) half @unknown__fsub_nsz__pzero(half %unknown) {
 ; CHECK-LABEL: define nofpclass(snan) half @unknown__fsub_nsz__pzero(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub nsz half [[UNKNOWN]], [[PZERO]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[UNKNOWN]]
 ;
   %pzero = call half @returns_pzero()
   %add = fsub nsz half %unknown, %pzero
@@ -919,7 +908,7 @@ define nofpclass(snan) half @pzero__fsub_nsz__unknown(half %unknown) {
 ; CHECK-LABEL: define nofpclass(snan) half @pzero__fsub_nsz__unknown(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub nsz half [[PZERO]], [[UNKNOWN]]
+; CHECK-NEXT:    [[ADD:%.*]] = fneg nsz half [[UNKNOWN]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pzero = call half @returns_pzero()
@@ -931,8 +920,7 @@ define nofpclass(snan) half @unknown__fsub_nsz__pzero_or_nan(half %unknown) {
 ; CHECK-LABEL: define nofpclass(snan) half @unknown__fsub_nsz__pzero_or_nan(
 ; CHECK-SAME: half [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[PZERO_OR_NAN:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub nsz half [[UNKNOWN]], [[PZERO_OR_NAN]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[UNKNOWN]]
 ;
   %pzero.or.nan = call half @returns_pzero()
   %add = fsub nsz half %unknown, %pzero.or.nan
@@ -1031,8 +1019,7 @@ define nofpclass(snan) half @not_nzero__fsub__pzero__daz(half nofpclass(nzero) %
 ; CHECK-LABEL: define nofpclass(snan) half @not_nzero__fsub__pzero__daz(
 ; CHECK-SAME: half nofpclass(nzero) [[NOT_NZERO:%.*]]) #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[NOT_NZERO]], [[PZERO]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[NOT_NZERO]]
 ;
   %pzero = call half @returns_pzero()
   %add = fsub half %not.nzero, %pzero
@@ -1043,7 +1030,7 @@ define nofpclass(snan) half @pzero__fsub__not_nzero__daz(half nofpclass(nzero) %
 ; CHECK-LABEL: define nofpclass(snan) half @pzero__fsub__not_nzero__daz(
 ; CHECK-SAME: half nofpclass(nzero) [[NOT_NZERO:%.*]]) #[[ATTR0]] {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[PZERO]], [[NOT_NZERO]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub half 0xH0000, [[NOT_NZERO]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pzero = call half @returns_pzero()
@@ -1055,8 +1042,7 @@ define nofpclass(snan) half @not_nzero__fsub__pzero__dynamic(half nofpclass(nzer
 ; CHECK-LABEL: define nofpclass(snan) half @not_nzero__fsub__pzero__dynamic(
 ; CHECK-SAME: half nofpclass(nzero) [[NOT_NZERO:%.*]]) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[NOT_NZERO]], [[PZERO]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[NOT_NZERO]]
 ;
   %pzero = call half @returns_pzero()
   %add = fsub half %not.nzero, %pzero
@@ -1067,7 +1053,7 @@ define nofpclass(snan) half @pzero__fsub__not_nzero__dynamic(half nofpclass(nzer
 ; CHECK-LABEL: define nofpclass(snan) half @pzero__fsub__not_nzero__dynamic(
 ; CHECK-SAME: half nofpclass(nzero) [[NOT_NZERO:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[PZERO]], [[NOT_NZERO]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub half 0xH0000, [[NOT_NZERO]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pzero = call half @returns_pzero()
@@ -1079,8 +1065,7 @@ define nofpclass(snan) half @not_nzero_nsub__fsub__pzero__daz(half nofpclass(nze
 ; CHECK-LABEL: define nofpclass(snan) half @not_nzero_nsub__fsub__pzero__daz(
 ; CHECK-SAME: half nofpclass(nzero nsub) [[NOT_NZERO_NSUB:%.*]]) #[[ATTR0]] {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[NOT_NZERO_NSUB]], [[PZERO]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[NOT_NZERO_NSUB]]
 ;
   %pzero = call half @returns_pzero()
   %add = fsub half %not.nzero.nsub, %pzero
@@ -1091,7 +1076,7 @@ define nofpclass(snan) half @pzero__fsub__not_nzero_nsub__daz(half nofpclass(nze
 ; CHECK-LABEL: define nofpclass(snan) half @pzero__fsub__not_nzero_nsub__daz(
 ; CHECK-SAME: half nofpclass(nzero nsub) [[NOT_NZERO_NSUB:%.*]]) #[[ATTR0]] {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[PZERO]], [[NOT_NZERO_NSUB]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub half 0xH0000, [[NOT_NZERO_NSUB]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pzero = call half @returns_pzero()
@@ -1103,8 +1088,7 @@ define nofpclass(snan) half @not_nzero_nsub__fsub__pzero__dynamic(half nofpclass
 ; CHECK-LABEL: define nofpclass(snan) half @not_nzero_nsub__fsub__pzero__dynamic(
 ; CHECK-SAME: half nofpclass(nzero nsub) [[NOT_NZERO_NSUB:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[NOT_NZERO_NSUB]], [[PZERO]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half [[NOT_NZERO_NSUB]]
 ;
   %pzero = call half @returns_pzero()
   %add = fsub half %not.nzero.nsub, %pzero
@@ -1115,7 +1099,7 @@ define nofpclass(snan) half @pzero__fsub__not_nzero_nsub__dynamic(half nofpclass
 ; CHECK-LABEL: define nofpclass(snan) half @pzero__fsub__not_nzero_nsub__dynamic(
 ; CHECK-SAME: half nofpclass(nzero nsub) [[NOT_NZERO_NSUB:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[PZERO:%.*]] = call half @returns_pzero()
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[PZERO]], [[NOT_NZERO_NSUB]]
+; CHECK-NEXT:    [[ADD:%.*]] = fsub half 0xH0000, [[NOT_NZERO_NSUB]]
 ; CHECK-NEXT:    ret half [[ADD]]
 ;
   %pzero = call half @returns_pzero()
@@ -1127,9 +1111,7 @@ define nofpclass(nan) half @ret_nonan__fsub_self__nonan(i1 %cond, half noundef %
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan__fsub_self__nonan(
 ; CHECK-SAME: i1 [[COND:%.*]], half noundef [[UNKNOWN:%.*]]) {
 ; CHECK-NEXT:    [[NAN:%.*]] = call noundef half @returns_nan()
-; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], half [[NAN]], half [[UNKNOWN]]
-; CHECK-NEXT:    [[ADD:%.*]] = fsub half [[SELECT]], [[SELECT]]
-; CHECK-NEXT:    ret half [[ADD]]
+; CHECK-NEXT:    ret half 0xH0000
 ;
   %nan = call noundef half @returns_nan()
   %select = select i1 %cond, half %nan, half %unknown
@@ -1141,7 +1123,7 @@ define nofpclass(nan) half @ret_nonan_fneg_fabs_fsub_known_negative_or_nan_sourc
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan_fneg_fabs_fsub_known_negative_or_nan_sources() {
 ; CHECK-NEXT:    [[NEGATIVE_OR_NAN0:%.*]] = call half @returns_negative_or_nan()
 ; CHECK-NEXT:    [[NEGATIVE_OR_NAN1:%.*]] = call half @returns_negative_or_nan()
-; CHECK-NEXT:    [[KNOWN_NEGATIVE_SUB:%.*]] = fsub half [[NEGATIVE_OR_NAN0]], [[NEGATIVE_OR_NAN1]]
+; CHECK-NEXT:    [[KNOWN_NEGATIVE_SUB:%.*]] = fsub nnan half [[NEGATIVE_OR_NAN0]], [[NEGATIVE_OR_NAN1]]
 ; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[KNOWN_NEGATIVE_SUB]])
 ; CHECK-NEXT:    [[NEG_FABS:%.*]] = fneg half [[FABS]]
 ; CHECK-NEXT:    ret half [[NEG_FABS]]
@@ -1158,7 +1140,7 @@ define nofpclass(nan) half @ret_nonan_fneg_fabs_fsub_known_negative_sources() {
 ; CHECK-LABEL: define nofpclass(nan) half @ret_nonan_fneg_fabs_fsub_known_negative_sources() {
 ; CHECK-NEXT:    [[NEGATIVE0:%.*]] = call half @returns_negative()
 ; CHECK-NEXT:    [[NEGATIVE1:%.*]] = call half @returns_negative()
-; CHECK-NEXT:    [[KNOWN_NEGATIVE_OP:%.*]] = fsub half [[NEGATIVE0]], [[NEGATIVE1]]
+; CHECK-NEXT:    [[KNOWN_NEGATIVE_OP:%.*]] = fsub nnan half [[NEGATIVE0]], [[NEGATIVE1]]
 ; CHECK-NEXT:    [[FABS:%.*]] = call half @llvm.fabs.f16(half [[KNOWN_NEGATIVE_OP]])
 ; CHECK-NEXT:    [[NEG_FABS:%.*]] = fneg half [[FABS]]
 ; CHECK-NEXT:    ret half [[NEG_FABS]]

>From d670efc4765cfd7746c6fa1cec41f3b19c554c17 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Wed, 14 Jan 2026 17:02:20 +0100
Subject: [PATCH 2/2] Implement as fadd+fneg

---
 llvm/include/llvm/Support/KnownFPClass.h |  6 ++++++
 llvm/lib/Support/KnownFPClass.cpp        | 17 +----------------
 2 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 010ca6a3e84ec..358804c932891 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -176,6 +176,12 @@ struct KnownFPClass {
       SignBit = !*SignBit;
   }
 
+  static KnownFPClass fneg(const KnownFPClass &Src) {
+    KnownFPClass Known = Src;
+    Known.fneg();
+    return Known;
+  }
+
   void fabs() {
     if (KnownFPClasses & fcNegZero)
       KnownFPClasses |= fcPosZero;
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index dbd5707873f5d..2946c597aa286 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -282,22 +282,7 @@ KnownFPClass KnownFPClass::fadd_self(const KnownFPClass &KnownSrc,
 KnownFPClass KnownFPClass::fsub(const KnownFPClass &KnownLHS,
                                 const KnownFPClass &KnownRHS,
                                 DenormalMode Mode) {
-  KnownFPClass Known;
-  // Adding positive and negative infinity produces NaN.
-  // TODO: Check sign of infinities.
-  if (KnownLHS.isKnownNeverNaN() && KnownRHS.isKnownNeverNaN() &&
-      (KnownLHS.isKnownNeverInfinity() || KnownRHS.isKnownNeverInfinity()))
-    Known.knownNot(fcNan);
-
-  // Only fsub -0, +0 can return -0
-  if ((KnownLHS.isKnownNeverLogicalNegZero(Mode) ||
-       KnownRHS.isKnownNeverLogicalPosZero(Mode)) &&
-      // Make sure output negative denormal can't flush to -0
-      (Mode.Output == DenormalMode::IEEE ||
-       Mode.Output == DenormalMode::PositiveZero))
-    Known.knownNot(fcNegZero);
-
-  return Known;
+  return fadd(KnownLHS, fneg(KnownRHS), Mode);
 }
 
 KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS,



More information about the llvm-branch-commits mailing list