[llvm] [ValueTracking] Fix computeKnownFPClass handling of nsz (PR #186315)

Yunbo Ni via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 11 23:13:06 PDT 2026


https://github.com/cardigan1008 updated https://github.com/llvm/llvm-project/pull/186315

>From d99350053e15bd8be83c52fcb1049351aacf0779 Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Fri, 13 Mar 2026 12:49:21 +0800
Subject: [PATCH 01/12] [ValueTracking] Improve nofpclass inference for nsz
 fadd

---
 llvm/lib/Analysis/ValueTracking.cpp          |  7 +++++--
 llvm/test/Transforms/Attributor/nofpclass.ll | 11 +++++++++++
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 0672dc889b640..eab514892fc1a 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5587,8 +5587,11 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
           Op->getType()->getScalarType()->getFltSemantics();
       DenormalMode Mode =
           F ? F->getDenormalMode(FltSem) : DenormalMode::getDynamic();
-
-      if (Self && Opc == Instruction::FAdd) {
+      
+      const FPMathOperator *FPop = cast<FPMathOperator>(Op); 
+      bool HasNSZ = FPop->hasNoSignedZeros();
+      
+      if (!HasNSZ && Self && Opc == Instruction::FAdd) {
         Known = KnownFPClass::fadd_self(KnownLHS, Mode);
       } else {
         // RHS is canonically cheaper to compute. Skip inspecting the LHS if
diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index 507d0f3ff98f3..3946c9f88e41d 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -3326,6 +3326,17 @@ define float @fadd_double_known_negative_nonsub_dynamic(float noundef nofpclass(
   ret float %add
 }
 
+define float @fadd_double_known_negative_zero_nsz(float noundef nofpclass(ninf pzero sub nnorm) %arg) {
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
+; CHECK-LABEL: define noundef nofpclass(ninf nsub nnorm) float @fadd_double_known_negative_zero_nsz
+; CHECK-SAME: (float noundef nofpclass(ninf pzero sub nnorm) [[ARG:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[ADD:%.*]] = fadd nsz float [[ARG]], [[ARG]]
+; CHECK-NEXT:    ret float [[ADD]]
+;
+  %add = fadd nsz float %arg, %arg
+  ret float %add
+}
+
 define float @fsub_self(float noundef %arg) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef float @fsub_self

>From db4d943ca5ae1fbc757cad71bb43f3b6f6dbb011 Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Fri, 13 Mar 2026 12:50:10 +0800
Subject: [PATCH 02/12] [ValueTracking] Fix format issues

---
 llvm/lib/Analysis/ValueTracking.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index eab514892fc1a..25a8a8166d3af 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5587,10 +5587,10 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
           Op->getType()->getScalarType()->getFltSemantics();
       DenormalMode Mode =
           F ? F->getDenormalMode(FltSem) : DenormalMode::getDynamic();
-      
-      const FPMathOperator *FPop = cast<FPMathOperator>(Op); 
+
+      const FPMathOperator *FPop = cast<FPMathOperator>(Op);
       bool HasNSZ = FPop->hasNoSignedZeros();
-      
+
       if (!HasNSZ && Self && Opc == Instruction::FAdd) {
         Known = KnownFPClass::fadd_self(KnownLHS, Mode);
       } else {

>From 5cf73a400c422bcfeefcca7f15e67a26c5580c37 Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Fri, 10 Apr 2026 16:48:55 +0800
Subject: [PATCH 03/12] [ValueTracking] Handle nsz more generically

---
 llvm/lib/Analysis/ValueTracking.cpp | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 25a8a8166d3af..edd96ac264614 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5588,10 +5588,7 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       DenormalMode Mode =
           F ? F->getDenormalMode(FltSem) : DenormalMode::getDynamic();
 
-      const FPMathOperator *FPop = cast<FPMathOperator>(Op);
-      bool HasNSZ = FPop->hasNoSignedZeros();
-
-      if (!HasNSZ && Self && Opc == Instruction::FAdd) {
+      if (Self && Opc == Instruction::FAdd) {
         Known = KnownFPClass::fadd_self(KnownLHS, Mode);
       } else {
         // RHS is canonically cheaper to compute. Skip inspecting the LHS if
@@ -6070,6 +6067,20 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
   default:
     break;
   }
+
+  // With no-signed-zeros semantics, +0 and -0 are interchangeable. 
+  // If the operation has nsz and only one sign of zero is possible in the result,
+  // the other must also be considered possible. 
+  if (const auto *FPOp = dyn_cast_or_null<FPMathOperator>(Op)) {
+    if (FPOp->hasNoSignedZeros()) {
+      FPClassTest KnownZero = Known.KnownFPClasses & fcZero;
+      if (KnownZero && KnownZero != fcZero) {
+        Known.KnownFPClasses |= fcZero;
+        // The sign of zero is now indeterminate since nsz allows either sign.
+        Known.SignBit = std::nullopt;
+      }
+    }
+  }
 }
 
 KnownFPClass llvm::computeKnownFPClass(const Value *V,

>From f28f81f9fdf62b2a2c061436233d1941439664aa Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Fri, 10 Apr 2026 16:49:28 +0800
Subject: [PATCH 04/12] [ValueTracking] Fix format issues

---
 llvm/lib/Analysis/ValueTracking.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index edd96ac264614..c530230a8d840 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6068,9 +6068,9 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
     break;
   }
 
-  // With no-signed-zeros semantics, +0 and -0 are interchangeable. 
-  // If the operation has nsz and only one sign of zero is possible in the result,
-  // the other must also be considered possible. 
+  // With no-signed-zeros semantics, +0 and -0 are interchangeable.
+  // If the operation has nsz and only one sign of zero is possible in the
+  // result, the other must also be considered possible.
   if (const auto *FPOp = dyn_cast_or_null<FPMathOperator>(Op)) {
     if (FPOp->hasNoSignedZeros()) {
       FPClassTest KnownZero = Known.KnownFPClasses & fcZero;

>From 2a2fa1cd47c022d1d0c245077cea405e728d5f57 Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Sat, 11 Apr 2026 18:47:57 +0800
Subject: [PATCH 05/12] [ValueTracking] Handle DAZ and exclude ops not
 appliable to nsz

---
 llvm/lib/Analysis/ValueTracking.cpp | 68 +++++++++++++++++++++++------
 1 file changed, 54 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index c530230a8d840..1623df5d108b1 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4982,6 +4982,31 @@ static bool isAbsoluteValueLessEqualOne(const Value *V) {
   return match(V, m_Intrinsic<Intrinsic::amdgcn_trig_preop>(m_Value()));
 }
 
+static bool shouldApplyNSZToResult(const Operator *Op) {
+  switch (Op->getOpcode()) {
+  case Instruction::FNeg:
+  case Instruction::Select:
+  case Instruction::PHI:
+    return false;
+  case Instruction::Call:
+    if (const auto *II = dyn_cast<IntrinsicInst>(Op)) {
+      switch (II->getIntrinsicID()) {
+      case Intrinsic::fabs:
+      case Intrinsic::copysign:
+      case Intrinsic::ssa_copy:
+        return false;
+      default:
+        break;
+      }
+    }
+    break;
+  default:
+    break;
+  }
+
+  return true;
+}
+
 void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
                          FPClassTest InterestedClasses, KnownFPClass &Known,
                          const SimplifyQuery &Q, unsigned Depth) {
@@ -5081,11 +5106,24 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
     KnownNotFromFlags |= Arg->getNoFPClass();
 
   const Operator *Op = dyn_cast<Operator>(V);
+  bool HasNoSignedZeros = false;
+  DenormalMode Mode = DenormalMode::getDynamic();
   if (const FPMathOperator *FPOp = dyn_cast_or_null<FPMathOperator>(Op)) {
     if (FPOp->hasNoNaNs())
       KnownNotFromFlags |= fcNan;
     if (FPOp->hasNoInfs())
       KnownNotFromFlags |= fcInf;
+    HasNoSignedZeros =
+        FPOp->hasNoSignedZeros() && shouldApplyNSZToResult(Op);
+    if (HasNoSignedZeros) {
+      if (const auto *I = dyn_cast<Instruction>(Op)) {
+        const Function *F = I->getFunction();
+        const fltSemantics &FltSem =
+            Op->getType()->getScalarType()->getFltSemantics();
+        Mode =
+            F ? F->getDenormalMode(FltSem) : DenormalMode::getDynamic();
+      }
+    }
   }
 
   KnownFPClass AssumedClasses = computeKnownFPClassFromContext(V, Q);
@@ -5096,6 +5134,22 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
   InterestedClasses &= ~KnownNotFromFlags;
 
   llvm::scope_exit ClearClassesFromFlags([=, &Known] {
+    // With no-signed-zeros semantics, +0 and -0 are interchangeable.
+    // If the operation has nsz and only one sign of zero is possible in the
+    // result, the other must also be considered possible.
+    if (HasNoSignedZeros) {
+      bool NeverPosZero = Known.isKnownNeverLogicalPosZero(Mode);
+      bool NeverNegZero = Known.isKnownNeverLogicalNegZero(Mode);
+      if (NeverPosZero != NeverNegZero) {
+        if (!NeverPosZero)
+          Known.KnownFPClasses |= fcPosZero | fcPosSubnormal;
+        if (!NeverNegZero)
+          Known.KnownFPClasses |= fcNegZero | fcNegSubnormal;
+        // The sign of zero is now indeterminate since nsz allows either sign.
+        Known.SignBit = std::nullopt;
+      }
+    }
+
     Known.knownNot(KnownNotFromFlags);
     if (!Known.SignBit && AssumedClasses.SignBit) {
       if (*AssumedClasses.SignBit)
@@ -6067,20 +6121,6 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
   default:
     break;
   }
-
-  // With no-signed-zeros semantics, +0 and -0 are interchangeable.
-  // If the operation has nsz and only one sign of zero is possible in the
-  // result, the other must also be considered possible.
-  if (const auto *FPOp = dyn_cast_or_null<FPMathOperator>(Op)) {
-    if (FPOp->hasNoSignedZeros()) {
-      FPClassTest KnownZero = Known.KnownFPClasses & fcZero;
-      if (KnownZero && KnownZero != fcZero) {
-        Known.KnownFPClasses |= fcZero;
-        // The sign of zero is now indeterminate since nsz allows either sign.
-        Known.SignBit = std::nullopt;
-      }
-    }
-  }
 }
 
 KnownFPClass llvm::computeKnownFPClass(const Value *V,

>From 49f3e5e3285dd4771d7cb9f4dcccd3ab12286dfe Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Sat, 11 Apr 2026 20:20:44 +0800
Subject: [PATCH 06/12] [ValueTracking] Move check after inference and add
 tests

---
 llvm/lib/Analysis/ValueTracking.cpp          | 72 +++++++++-----------
 llvm/test/Transforms/Attributor/nofpclass.ll | 11 +++
 2 files changed, 42 insertions(+), 41 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1623df5d108b1..86021caec9ca0 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4982,24 +4982,19 @@ static bool isAbsoluteValueLessEqualOne(const Value *V) {
   return match(V, m_Intrinsic<Intrinsic::amdgcn_trig_preop>(m_Value()));
 }
 
+/// \return true if result-side NSZ relaxation should be applied to this
+/// opcode.
+///
+/// With the stricter NSZ interpretation, NSZ only relaxes input zero sign.
+/// Do not relax opcodes whose output zero sign is fixed or sign-preserving
+/// (for example, fabs, copysign, and fneg).
 static bool shouldApplyNSZToResult(const Operator *Op) {
   switch (Op->getOpcode()) {
   case Instruction::FNeg:
   case Instruction::Select:
   case Instruction::PHI:
-    return false;
   case Instruction::Call:
-    if (const auto *II = dyn_cast<IntrinsicInst>(Op)) {
-      switch (II->getIntrinsicID()) {
-      case Intrinsic::fabs:
-      case Intrinsic::copysign:
-      case Intrinsic::ssa_copy:
-        return false;
-      default:
-        break;
-      }
-    }
-    break;
+    return false;
   default:
     break;
   }
@@ -5106,24 +5101,11 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
     KnownNotFromFlags |= Arg->getNoFPClass();
 
   const Operator *Op = dyn_cast<Operator>(V);
-  bool HasNoSignedZeros = false;
-  DenormalMode Mode = DenormalMode::getDynamic();
   if (const FPMathOperator *FPOp = dyn_cast_or_null<FPMathOperator>(Op)) {
     if (FPOp->hasNoNaNs())
       KnownNotFromFlags |= fcNan;
     if (FPOp->hasNoInfs())
       KnownNotFromFlags |= fcInf;
-    HasNoSignedZeros =
-        FPOp->hasNoSignedZeros() && shouldApplyNSZToResult(Op);
-    if (HasNoSignedZeros) {
-      if (const auto *I = dyn_cast<Instruction>(Op)) {
-        const Function *F = I->getFunction();
-        const fltSemantics &FltSem =
-            Op->getType()->getScalarType()->getFltSemantics();
-        Mode =
-            F ? F->getDenormalMode(FltSem) : DenormalMode::getDynamic();
-      }
-    }
   }
 
   KnownFPClass AssumedClasses = computeKnownFPClassFromContext(V, Q);
@@ -5134,22 +5116,6 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
   InterestedClasses &= ~KnownNotFromFlags;
 
   llvm::scope_exit ClearClassesFromFlags([=, &Known] {
-    // With no-signed-zeros semantics, +0 and -0 are interchangeable.
-    // If the operation has nsz and only one sign of zero is possible in the
-    // result, the other must also be considered possible.
-    if (HasNoSignedZeros) {
-      bool NeverPosZero = Known.isKnownNeverLogicalPosZero(Mode);
-      bool NeverNegZero = Known.isKnownNeverLogicalNegZero(Mode);
-      if (NeverPosZero != NeverNegZero) {
-        if (!NeverPosZero)
-          Known.KnownFPClasses |= fcPosZero | fcPosSubnormal;
-        if (!NeverNegZero)
-          Known.KnownFPClasses |= fcNegZero | fcNegSubnormal;
-        // The sign of zero is now indeterminate since nsz allows either sign.
-        Known.SignBit = std::nullopt;
-      }
-    }
-
     Known.knownNot(KnownNotFromFlags);
     if (!Known.SignBit && AssumedClasses.SignBit) {
       if (*AssumedClasses.SignBit)
@@ -6121,6 +6087,30 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
   default:
     break;
   }
+
+  // With no-signed-zeros semantics, +0 and -0 are interchangeable.
+  // If only one sign of logical zero is possible in the result, the other
+  // sign must also be considered possible. Apply this selectively because
+  // some ops preserve or explicitly determine the zero sign.
+  if (const auto *FPOp = dyn_cast_or_null<FPMathOperator>(Op)) {
+    if (FPOp->hasNoSignedZeros() && shouldApplyNSZToResult(Op)) {
+      const auto *I = dyn_cast<Instruction>(Op);
+      const Function *F = I ? I->getFunction() : nullptr;
+      const fltSemantics &FltSem =
+          Op->getType()->getScalarType()->getFltSemantics();
+      DenormalMode Mode =
+          F ? F->getDenormalMode(FltSem) : DenormalMode::getDynamic();
+      bool NeverPosZero = Known.isKnownNeverLogicalPosZero(Mode);
+      bool NeverNegZero = Known.isKnownNeverLogicalNegZero(Mode);
+      if (NeverPosZero != NeverNegZero) {
+        if (NeverPosZero)
+          Known.KnownFPClasses |= fcPosZero | fcPosSubnormal;
+        if (NeverNegZero)
+          Known.KnownFPClasses |= fcNegZero | fcNegSubnormal;
+        Known.SignBit = std::nullopt;
+      }
+    }
+  }
 }
 
 KnownFPClass llvm::computeKnownFPClass(const Value *V,
diff --git a/llvm/test/Transforms/Attributor/nofpclass.ll b/llvm/test/Transforms/Attributor/nofpclass.ll
index 3946c9f88e41d..a2c82146b3daf 100644
--- a/llvm/test/Transforms/Attributor/nofpclass.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass.ll
@@ -3337,6 +3337,17 @@ define float @fadd_double_known_negative_zero_nsz(float noundef nofpclass(ninf p
   ret float %add
 }
 
+define float @fadd_double_known_negative_zero_nsz_daz(float noundef nofpclass(ninf pzero sub nnorm) %arg) #0 {
+; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn denormal_fpenv(preservesign) memory(none)
+; CHECK-LABEL: define noundef nofpclass(ninf pzero nsub nnorm) float @fadd_double_known_negative_zero_nsz_daz
+; CHECK-SAME: (float noundef nofpclass(ninf pzero sub nnorm) [[ARG:%.*]]) #[[ATTR10]] {
+; CHECK-NEXT:    [[ADD:%.*]] = fadd nsz float [[ARG]], [[ARG]]
+; CHECK-NEXT:    ret float [[ADD]]
+;
+  %add = fadd nsz float %arg, %arg
+  ret float %add
+}
+
 define float @fsub_self(float noundef %arg) {
 ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
 ; CHECK-LABEL: define noundef float @fsub_self

>From 6712cbdf15994dbb9460f541ccf62e71a1205cfc Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Sat, 11 Apr 2026 20:52:48 +0800
Subject: [PATCH 07/12] [ValueTracking] Polish up exlusion of applying nsz

---
 llvm/lib/Analysis/ValueTracking.cpp | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index b00bb25e18354..5d721e111d2cd 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4989,22 +4989,29 @@ static bool isAbsoluteValueULEOne(const Value *V) {
 }
 
 /// \return true if result-side NSZ relaxation should be applied to this
-/// opcode.
+/// operation.
 ///
 /// With the stricter NSZ interpretation, NSZ only relaxes input zero sign.
-/// Do not relax opcodes whose output zero sign is fixed or sign-preserving
-/// (for example, fabs, copysign, and fneg).
+/// Do not relax ops whose output zero sign is fixed or sign-preserving:
+/// fabs always produces +0, copysign copies the sign of its second operand,
+/// and fneg(fabs) always produces -0.
 static bool shouldApplyNSZToResult(const Operator *Op) {
-  switch (Op->getOpcode()) {
-  case Instruction::FNeg:
-  case Instruction::Select:
-  case Instruction::PHI:
-  case Instruction::Call:
-    return false;
-  default:
-    break;
+  if (const auto *II = dyn_cast<IntrinsicInst>(Op)) {
+    switch (II->getIntrinsicID()) {
+    case Intrinsic::fabs:
+    case Intrinsic::copysign:
+      return false;
+    default:
+      break;
+    }
   }
 
+  // fneg(fabs(...)) always produces -0 for zero inputs.
+  if (Op->getOpcode() == Instruction::FNeg)
+    if (match(Op->getOperand(0),
+              m_Intrinsic<Intrinsic::fabs>(m_Value())))
+      return false;
+
   return true;
 }
 

>From be3eafe972854b9c2f2ee7eda583b890bfcf2c0a Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Sat, 11 Apr 2026 20:54:06 +0800
Subject: [PATCH 08/12] [ValueTracking] Fix format issues

---
 llvm/lib/Analysis/ValueTracking.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 5d721e111d2cd..3fcd89a2aa810 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5008,8 +5008,7 @@ static bool shouldApplyNSZToResult(const Operator *Op) {
 
   // fneg(fabs(...)) always produces -0 for zero inputs.
   if (Op->getOpcode() == Instruction::FNeg)
-    if (match(Op->getOperand(0),
-              m_Intrinsic<Intrinsic::fabs>(m_Value())))
+    if (match(Op->getOperand(0), m_Intrinsic<Intrinsic::fabs>(m_Value())))
       return false;
 
   return true;

>From 90fd36d220af4b09aee5cc4f5b097fc9741fa737 Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Sat, 11 Apr 2026 20:54:45 +0800
Subject: [PATCH 09/12] [ValueTracking] Update regressions

---
 llvm/test/Transforms/InstCombine/fast-math.ll             | 3 ++-
 .../InstSimplify/floating-point-arithmetic-strictfp.ll    | 3 ++-
 .../Transforms/InstSimplify/floating-point-arithmetic.ll  | 8 ++++++--
 3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/fast-math.ll b/llvm/test/Transforms/InstCombine/fast-math.ll
index 7b5f5cf477de9..d6bc5ea6b9b29 100644
--- a/llvm/test/Transforms/InstCombine/fast-math.ll
+++ b/llvm/test/Transforms/InstCombine/fast-math.ll
@@ -742,7 +742,8 @@ define double @sqrt_intrinsic_not_so_fast(double %x, double %y) {
 define double @sqrt_intrinsic_arg_4th(double noundef %x) {
 ; CHECK-LABEL: @sqrt_intrinsic_arg_4th(
 ; CHECK-NEXT:    [[MUL:%.*]] = fmul fast double [[X:%.*]], [[X]]
-; CHECK-NEXT:    ret double [[MUL]]
+; CHECK-NEXT:    [[FABS:%.*]] = call fast double @llvm.fabs.f64(double [[MUL]])
+; CHECK-NEXT:    ret double [[FABS]]
 ;
   %mul = fmul fast double %x, %x
   %mul2 = fmul fast double %mul, %mul
diff --git a/llvm/test/Transforms/InstSimplify/floating-point-arithmetic-strictfp.ll b/llvm/test/Transforms/InstSimplify/floating-point-arithmetic-strictfp.ll
index 9a078a8f569da..2f9efa91f8c89 100644
--- a/llvm/test/Transforms/InstSimplify/floating-point-arithmetic-strictfp.ll
+++ b/llvm/test/Transforms/InstSimplify/floating-point-arithmetic-strictfp.ll
@@ -252,7 +252,8 @@ define float @fabs_sqrt_nsz(float %a) #0 {
 define float @fabs_sqrt_nnan_nsz(float %a) #0 {
 ; CHECK-LABEL: @fabs_sqrt_nnan_nsz(
 ; CHECK-NEXT:    [[SQRT:%.*]] = call nnan nsz float @llvm.experimental.constrained.sqrt.f32(float [[A:%.*]], metadata !"round.tonearest", metadata !"fpexcept.ignore")
-; CHECK-NEXT:    ret float [[SQRT]]
+; CHECK-NEXT:    [[FABS:%.*]] = call float @llvm.fabs.f32(float [[SQRT]]) #[[ATTR0]]
+; CHECK-NEXT:    ret float [[FABS]]
 ;
   %sqrt = call nnan nsz float @llvm.experimental.constrained.sqrt.f32(float %a, metadata !"round.tonearest", metadata !"fpexcept.ignore")
   %fabs = call float @llvm.fabs.f32(float %sqrt) #0
diff --git a/llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll b/llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll
index 0312e8ed7d9ba..f9058891701c7 100644
--- a/llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll
+++ b/llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll
@@ -644,7 +644,8 @@ define float @fabs_sqrt_nsz(float %a) {
 define float @fabs_sqrt_nnan_nsz(float %a) {
 ; CHECK-LABEL: @fabs_sqrt_nnan_nsz(
 ; CHECK-NEXT:    [[SQRT:%.*]] = call nnan nsz float @llvm.sqrt.f32(float [[A:%.*]])
-; CHECK-NEXT:    ret float [[SQRT]]
+; CHECK-NEXT:    [[FABS:%.*]] = call float @llvm.fabs.f32(float [[SQRT]])
+; CHECK-NEXT:    ret float [[FABS]]
 ;
   %sqrt = call nnan nsz float @llvm.sqrt.f32(float %a)
   %fabs = call float @llvm.fabs.f32(float %sqrt)
@@ -1061,7 +1062,10 @@ define i1 @copysign_known_positive_maybe_neg0(float %unknown, float %sign) {
 
 define i1 @copysign_known_positive(float %unknown, float %sign) {
 ; CHECK-LABEL: @copysign_known_positive(
-; CHECK-NEXT:    ret i1 true
+; CHECK-NEXT:    [[SQRT:%.*]] = call nnan ninf nsz float @llvm.sqrt.f32(float [[SIGN:%.*]])
+; CHECK-NEXT:    [[COPYSIGN:%.*]] = call float @llvm.copysign.f32(float [[UNKNOWN:%.*]], float [[SQRT]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp nnan oge float [[COPYSIGN]], 0.000000e+00
+; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sqrt = call ninf nnan nsz float @llvm.sqrt.f32(float %sign)
   %copysign = call float @llvm.copysign.f32(float %unknown, float %sqrt)

>From 5c1e91215c3ea1430e5df2f12a1336682e68693c Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Sun, 12 Apr 2026 13:12:59 +0800
Subject: [PATCH 10/12] [ValueTracking] Only modify fc*Zero not subnormal

---
 llvm/lib/Analysis/ValueTracking.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 3fcd89a2aa810..459b14d0206a9 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6165,9 +6165,9 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       bool NeverNegZero = Known.isKnownNeverLogicalNegZero(Mode);
       if (NeverPosZero != NeverNegZero) {
         if (NeverPosZero)
-          Known.KnownFPClasses |= fcPosZero | fcPosSubnormal;
+          Known.KnownFPClasses |= fcPosZero;
         if (NeverNegZero)
-          Known.KnownFPClasses |= fcNegZero | fcNegSubnormal;
+          Known.KnownFPClasses |= fcNegZero;
         Known.SignBit = std::nullopt;
       }
     }

>From fd774dd56514e43c1ecda69570ad713a535fefcf Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Sun, 12 Apr 2026 13:35:23 +0800
Subject: [PATCH 11/12] [ValueTracking] Update tests

---
 .../Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll  | 8 ++++----
 llvm/unittests/Analysis/ValueTrackingTest.cpp             | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll
index 9f1e7fb49c73d..7cb2700d8b2c1 100644
--- a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll
+++ b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-rsq.ll
@@ -107,9 +107,9 @@ define float @ret_rsq_f32__no_snan_input(float nofpclass(snan) %arg) {
 }
 
 define float @ret_rsq_f32_nsz(float %arg) {
-; CHECK-LABEL: define nofpclass(nzero sub nnorm) float @ret_rsq_f32_nsz(
+; CHECK-LABEL: define nofpclass(sub nnorm) float @ret_rsq_f32_nsz(
 ; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nsz nofpclass(nzero sub nnorm) float @llvm.amdgcn.rsq.f32(float [[ARG]]) #[[ATTR4]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nsz nofpclass(sub nnorm) float @llvm.amdgcn.rsq.f32(float [[ARG]]) #[[ATTR4]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call nsz float @llvm.amdgcn.rsq.f32(float %arg)
@@ -147,9 +147,9 @@ define double @ret_rsq_f64_known_zero(double nofpclass(zero) %arg) {
 }
 
 define float @ret_rsq_f32_known_no_nan(float nofpclass(nan) %arg) {
-; CHECK-LABEL: define nofpclass(snan nzero sub nnorm) float @ret_rsq_f32_known_no_nan(
+; CHECK-LABEL: define nofpclass(snan sub nnorm) float @ret_rsq_f32_known_no_nan(
 ; CHECK-SAME: float nofpclass(nan) [[ARG:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nsz nofpclass(snan nzero sub nnorm) float @llvm.amdgcn.rsq.f32(float nofpclass(nan) [[ARG]]) #[[ATTR4]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nsz nofpclass(snan sub nnorm) float @llvm.amdgcn.rsq.f32(float nofpclass(nan) [[ARG]]) #[[ATTR4]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call nsz float @llvm.amdgcn.rsq.f32(float %arg)
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index de481e39307cb..8db90bcc1940f 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -2250,7 +2250,7 @@ TEST_F(ComputeKnownFPClassTest, SqrtNszSignBit) {
       "}\n");
 
   const FPClassTest SqrtMask = fcPosInf | fcPosNormal | fcZero | fcNan;
-  const FPClassTest NszSqrtMask = fcPosInf | fcPosNormal | fcPosZero | fcNan;
+  const FPClassTest NszSqrtMask = fcPosInf | fcPosNormal | fcZero | fcNan;
 
   {
     KnownFPClass UseInstrInfo =
@@ -2300,7 +2300,7 @@ TEST_F(ComputeKnownFPClassTest, SqrtNszSignBit) {
     KnownFPClass UseInstrInfoNSZNoNan =
         computeKnownFPClass(A4, M->getDataLayout(), fcAllFlags, nullptr,
                             nullptr, nullptr, nullptr, /*UseInstrInfo=*/true);
-    EXPECT_EQ(fcPosInf | fcPosNormal | fcPosZero | fcQNan,
+    EXPECT_EQ(fcPosInf | fcPosNormal | fcZero | fcQNan,
               UseInstrInfoNSZNoNan.KnownFPClasses);
     EXPECT_EQ(std::nullopt, UseInstrInfoNSZNoNan.SignBit);
 

>From c0fe963d1622c925a649cd830e4e5f0a682b3892 Mon Sep 17 00:00:00 2001
From: cardigan1008 <ybni at cse.cuhk.edu.hk>
Date: Sun, 12 Apr 2026 14:12:45 +0800
Subject: [PATCH 12/12] [ValueTracking] Update tests

---
 llvm/test/Transforms/InstCombine/fabs.ll      | 16 ++++++++++++++++
 llvm/test/Transforms/InstCombine/fast-math.ll |  3 +--
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/fabs.ll b/llvm/test/Transforms/InstCombine/fabs.ll
index 0c3ed56a8347a..4cd21db33ac15 100644
--- a/llvm/test/Transforms/InstCombine/fabs.ll
+++ b/llvm/test/Transforms/InstCombine/fabs.ll
@@ -1826,3 +1826,19 @@ define i1 @test_fabs_used_is_fpclass_pzero(float %x) {
   %is_fpclass = call i1 @llvm.is.fpclass.f32(float %sel, i32 64)
   ret i1 %is_fpclass
 }
+
+define float @fabs_fneg_nsz_assume_neg(float %a) {
+; CHECK-LABEL: @fabs_fneg_nsz_assume_neg(
+; CHECK-NEXT:    [[I32:%.*]] = bitcast float [[A:%.*]] to i32
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[I32]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[C:%.*]] = call float @llvm.fabs.f32(float [[A]])
+; CHECK-NEXT:    ret float [[C]]
+;
+  %i32 = bitcast float %a to i32
+  %cmp = icmp slt i32 %i32, 0
+  call void @llvm.assume(i1 %cmp)
+  %b = fneg nsz float %a
+  %c = call float @llvm.fabs.f32(float %b)
+  ret float %c
+}
diff --git a/llvm/test/Transforms/InstCombine/fast-math.ll b/llvm/test/Transforms/InstCombine/fast-math.ll
index d6bc5ea6b9b29..7b5f5cf477de9 100644
--- a/llvm/test/Transforms/InstCombine/fast-math.ll
+++ b/llvm/test/Transforms/InstCombine/fast-math.ll
@@ -742,8 +742,7 @@ define double @sqrt_intrinsic_not_so_fast(double %x, double %y) {
 define double @sqrt_intrinsic_arg_4th(double noundef %x) {
 ; CHECK-LABEL: @sqrt_intrinsic_arg_4th(
 ; CHECK-NEXT:    [[MUL:%.*]] = fmul fast double [[X:%.*]], [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = call fast double @llvm.fabs.f64(double [[MUL]])
-; CHECK-NEXT:    ret double [[FABS]]
+; CHECK-NEXT:    ret double [[MUL]]
 ;
   %mul = fmul fast double %x, %x
   %mul2 = fmul fast double %mul, %mul



More information about the llvm-commits mailing list