[llvm] Title: [ValueTracking] Properly relax operand zero signs for instructions with nsz (PR #207623)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 5 16:05:11 PDT 2026


https://github.com/AZero13 created https://github.com/llvm/llvm-project/pull/207623

This patch fixes a bug where InstSimplify incorrectly eliminated fabs when applied to the output of an nsz instruction.

Root Cause: When computing the known properties of an operand, computeKnownFPClass was ignoring the nsz (No Signed Zeros) flag when evaluating the inputs. For example, in fabs(fneg nsz %a) where %a is bounded to be strictly negative (e.g., -0.0), computeKnownFPClass mathematically determined the output of fneg to be strictly +0.0 (sign bit = 0). Believing the sign bit was definitively 0, InstSimplify invalidly optimized away the fabs.

However, the LangRef explicitly defines nsz as allowing optimizations to non-deterministically flip the sign bit of input operands. Therefore, fneg nsz is allowed to treat an input of -0.0 as +0.0, resulting in an output of -0.0. The output's sign bit is no longer strictly 0.

When fetching the known classes of an operand, if the evaluating instruction has nsz, we symmetrically relax the zero sign of the operand (adding fcPosZero if it was strictly fcNegZero, and vice versa) and erase its strict SignBit confidence.

Fixes #151303

>From 048a944961b11b80c7f9da12ee172edec0263da9 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Sun, 5 Jul 2026 19:03:18 -0400
Subject: [PATCH] Title: [ValueTracking] Properly relax operand zero signs for
 instructions with 'nsz'

This patch fixes a bug where InstSimplify incorrectly eliminated fabs when applied to the output of an nsz instruction.

Root Cause: When computing the known properties of an operand, computeKnownFPClass was ignoring the nsz (No Signed Zeros) flag when evaluating the inputs. For example, in fabs(fneg nsz %a) where %a is bounded to be strictly negative (e.g., -0.0), computeKnownFPClass mathematically determined the output of fneg to be strictly +0.0 (sign bit = 0). Believing the sign bit was definitively 0, InstSimplify invalidly optimized away the fabs.

However, the LangRef explicitly defines nsz as allowing optimizations to non-deterministically flip the sign bit of input operands. Therefore, fneg nsz is allowed to treat an input of -0.0 as +0.0, resulting in an output of -0.0. The output's sign bit is no longer strictly 0.

When fetching the known classes of an operand, if the evaluating instruction has nsz, we symmetrically relax the zero sign of the operand (adding fcPosZero if it was strictly fcNegZero, and vice versa) and erase its strict SignBit confidence.

Fixes #151303
---
 llvm/lib/Analysis/ValueTracking.cpp           | 72 ++++++++++++++-----
 .../InstSimplify/floating-point-arithmetic.ll | 18 +++++
 2 files changed, 72 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 7dd23f24dfcc7..ca4d8f1a07c9c 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4941,6 +4941,40 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
                          FPClassTest InterestedClasses, KnownFPClass &Known,
                          const SimplifyQuery &Q, unsigned Depth);
 
+/// Evaluates the known FP classes for an operand of an instruction.
+/// If the instruction has the 'nsz' flag, it non-deterministically relaxes
+/// the zero sign in the known classes.
+static void
+computeKnownFPClassForOperand(const Value *V, const APInt &DemandedElts,
+                              FPClassTest InterestedClasses,
+                              KnownFPClass &Known, const SimplifyQuery &Q,
+                              unsigned Depth, const Operator *CxtI) {
+  computeKnownFPClass(V, DemandedElts, InterestedClasses, Known, Q, Depth);
+
+  auto *I = dyn_cast_or_null<Instruction>(CxtI);
+  if (!I || !Q.IIQ.hasNoSignedZeros(I))
+    return;
+
+  Type *Ty = V->getType()->getScalarType();
+  if (!Ty->isFloatingPointTy())
+    return;
+
+  const Function *F = I->getFunction();
+  DenormalMode Mode = F ? F->getDenormalMode(Ty->getFltSemantics())
+                        : DenormalMode::getDynamic();
+
+  bool NeverPosZero = Known.isKnownNeverLogicalPosZero(Mode);
+  bool NeverNegZero = Known.isKnownNeverLogicalNegZero(Mode);
+
+  if (NeverPosZero != NeverNegZero) {
+    if (NeverPosZero)
+      Known.KnownFPClasses |= fcPosZero;
+    else
+      Known.KnownFPClasses |= fcNegZero;
+    Known.SignBit = std::nullopt;
+  }
+}
+
 static void computeKnownFPClass(const Value *V, KnownFPClass &Known,
                                 FPClassTest InterestedClasses,
                                 const SimplifyQuery &Q, unsigned Depth) {
@@ -4961,8 +4995,8 @@ static void computeKnownFPClassForFPTrunc(const Operator *Op,
     return;
 
   KnownFPClass KnownSrc;
-  computeKnownFPClass(Op->getOperand(0), DemandedElts, InterestedClasses,
-                      KnownSrc, Q, Depth + 1);
+  computeKnownFPClassForOperand(Op->getOperand(0), DemandedElts,
+                                InterestedClasses, KnownSrc, Q, Depth + 1, Op);
   Known = KnownFPClass::fptrunc(KnownSrc);
 }
 
@@ -5134,8 +5168,8 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
   const unsigned Opc = Op->getOpcode();
   switch (Opc) {
   case Instruction::FNeg: {
-    computeKnownFPClass(Op->getOperand(0), DemandedElts, InterestedClasses,
-                        Known, Q, Depth + 1);
+    computeKnownFPClassForOperand(Op->getOperand(0), DemandedElts,
+                                  InterestedClasses, Known, Q, Depth + 1, Op);
     Known.fneg();
     break;
   }
@@ -5657,8 +5691,8 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       InterestedSrcs |= KnownFPClass::OrderedLessThanZeroMask;
     if (InterestedClasses & fcNan)
       InterestedSrcs |= fcInf;
-    computeKnownFPClass(Op->getOperand(1), DemandedElts, InterestedSrcs,
-                        KnownRHS, Q, Depth + 1);
+    computeKnownFPClassForOperand(Op->getOperand(1), DemandedElts,
+                                  InterestedSrcs, KnownRHS, Q, Depth + 1, Op);
 
     // Special case fadd x, x, which is the canonical form of fmul x, 2.
     bool Self = Op->getOperand(0) == Op->getOperand(1) &&
@@ -5685,8 +5719,9 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
         // there's no point.
 
         if (!Self) {
-          computeKnownFPClass(Op->getOperand(0), DemandedElts, InterestedSrcs,
-                              KnownLHS, Q, Depth + 1);
+          computeKnownFPClassForOperand(Op->getOperand(0), DemandedElts,
+                                        InterestedSrcs, KnownLHS, Q, Depth + 1,
+                                        Op);
         }
 
         Known = Opc == Instruction::FAdd
@@ -5765,9 +5800,9 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
         break;
 
       KnownFPClass KnownSrc;
-      computeKnownFPClass(Op->getOperand(0), DemandedElts,
-                          fcNan | fcInf | fcZero | fcSubnormal, KnownSrc, Q,
-                          Depth + 1);
+      computeKnownFPClassForOperand(Op->getOperand(0), DemandedElts,
+                                    fcNan | fcInf | fcZero | fcSubnormal,
+                                    KnownSrc, Q, Depth + 1, Op);
       const Function *F = cast<Instruction>(Op)->getFunction();
       const fltSemantics &FltSem =
           Op->getType()->getScalarType()->getFltSemantics();
@@ -5789,17 +5824,17 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
 
     KnownFPClass KnownLHS, KnownRHS;
 
-    computeKnownFPClass(Op->getOperand(1), DemandedElts,
-                        fcNan | fcInf | fcZero | fcNegative, KnownRHS, Q,
-                        Depth + 1);
+    computeKnownFPClassForOperand(Op->getOperand(1), DemandedElts,
+                                  fcNan | fcInf | fcZero | fcNegative, KnownRHS,
+                                  Q, Depth + 1, Op);
 
     bool KnowSomethingUseful = KnownRHS.isKnownNeverNaN() ||
                                KnownRHS.isKnownNever(fcNegative) ||
                                KnownRHS.isKnownNever(fcPositive);
 
     if (KnowSomethingUseful || WantPositive) {
-      computeKnownFPClass(Op->getOperand(0), DemandedElts, fcAllFlags, KnownLHS,
-                          Q, Depth + 1);
+      computeKnownFPClassForOperand(Op->getOperand(0), DemandedElts, fcAllFlags,
+                                    KnownLHS, Q, Depth + 1, Op);
     }
 
     const Function *F = cast<Instruction>(Op)->getFunction();
@@ -5835,8 +5870,9 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
   }
   case Instruction::FPExt: {
     KnownFPClass KnownSrc;
-    computeKnownFPClass(Op->getOperand(0), DemandedElts, InterestedClasses,
-                        KnownSrc, Q, Depth + 1);
+    computeKnownFPClassForOperand(Op->getOperand(0), DemandedElts,
+                                  InterestedClasses, KnownSrc, Q, Depth + 1,
+                                  Op);
 
     const fltSemantics &DstTy =
         Op->getType()->getScalarType()->getFltSemantics();
diff --git a/llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll b/llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll
index 25a46c65b3c56..ffa42c2608aa2 100644
--- a/llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll
+++ b/llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll
@@ -1299,3 +1299,21 @@ define <2 x float> @fabs_fmul_nan_vector(<2 x float> %x) {
   %abs2 = call <2 x float> @llvm.fabs.v2f32(<2 x float> %mul)
   ret <2 x float> %abs2
 }
+
+; The fabs cannot be eliminated because fneg nsz may return -0.0.
+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:    [[B:%.*]] = fneg nsz float [[A]]
+; CHECK-NEXT:    [[C:%.*]] = call float @llvm.fabs.f32(float [[B]])
+; 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
+}



More information about the llvm-commits mailing list