Bug type: miscompilation
Bug location: llvm/lib/Support/KnownFPClass.cpp: KnownFPClass::fmul
Issue title: [InstCombine] Dropping `fabs` around `fmul (fabs nnan x)`, `+inf` changes `NaN` sign for `x=+0.0`
Labels: miscompilation
llvm:instcombine
llvm:support
floating-point
Reported by: ariskeirio

**Description**

I observed a transformation involving `fabs` and `fmul` with `+inf` that cannot pass the verification of Alive2 due to a **NaN payload mismatch**, even though both sides evaluate to NaN.https://alive2.llvm.org/ce/z/7yBXf4

**Input Program**

**Source**

```llvm
define float @src(float %x) {
  %fabs.x = call nnan float @llvm.fabs.f32(float %x)
  %mul.fabs.x = fmul float %fabs.x, 0x7FF0000000000000
  %x.is.zero = fcmp oeq float %x, 0.000000e+00
  %2 = call float @llvm.fabs.f32(float %mul.fabs.x)
  %select = select i1 %x.is.zero, float %2, float %fabs.x
  ret float %select
}
```

This is transformed to:
**Target**

```llvm
define float @tgt(float %x) {
  %fabs.x = call nnan float @llvm.fabs.f32(float %x)
  %mul.fabs.x = fmul float %fabs.x, 0x7FF0000000000000
  %x.is.zero = fcmp oeq float %x, 0.000000e+00
  %select = select i1 %x.is.zero, float %mul.fabs.x, float %fabs.x
  ret float %select
}
```



Comments: author: Nathiyaa-Sengodan
body: Hi, I would like to work on this issue. 

This is my first LLVM contribution, so any guidance or pointers would be greatly appreciated.
author: ActuallyaDeviloper
body: I think this is working as specified.

[LangRef](https://llvm.org/docs/LangRef.html#behavior-of-floating-point-nan-values) says:
> Unless specified otherwise, the following rules apply whenever the IEEE 754 semantics say that a NaN value is returned: the result has a non-deterministic sign, the quiet bit and payload are non-deterministically chosen from the following set of options: ...
author: arsenm
body: > I think this is working as specified.

The nan sign bit still needs to be well behaved through select and fabs (as well as the other sign bit operations). The sign bit and payload bits are only nondeterministic when that value passes through a canonicalizing operation. Since the select reads a fabs value for the second input, any value taken from there must have a 0 sign-bit, including nans 

Fix patch:
commit 5a389d382b242bcbf4276b19fe21ee50fcdc988c
Author: Nathiyaa Sengodan <133644025+Nathiyaa-Sengodan@users.noreply.github.com>
Date:   Thu Feb 19 17:04:45 2026 +0530

    [ValueTracking] Treat fmul NaN sign bit as unknown to prevent incorrect fabs folding (#180339)
    
    ### Summary
    
    ValueTracking currently allows InstSimplify to reason about the sign bit
    of an `fmul` result based on the sign information of its operands. This
    can lead to removing a subsequent `llvm.fabs` under the assumption that
    the multiplication result is already non-negative.
    
    However, `fmul` may produce NaNs whose sign bit is non-deterministic.
    Since `llvm.fabs` canonicalizes the sign bit of NaNs, eliminating
    llvm.fabs` in these cases can change observable behaviour.
    
    ### Change
    
    Update ValueTracking to treat the sign bit of `fmul` as unknown when
    NaNs are possible, preventing InstSimplify from incorrectly removing
    llvm.fabs ` after `fmul`.
    
    Fixes #179613

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 99d29119b8d8..080535a44c6c 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -342,13 +342,6 @@ KnownFPClass KnownFPClass::fmul(const KnownFPClass &KnownLHS,
   if (!KnownLHS.isKnownNeverNaN() || !KnownRHS.isKnownNeverNaN())
     return Known;
 
-  if (KnownLHS.SignBit && KnownRHS.SignBit) {
-    if (*KnownLHS.SignBit == *KnownRHS.SignBit)
-      Known.signBitMustBeZero();
-    else
-      Known.signBitMustBeOne();
-  }
-
   // If 0 * +/-inf produces NaN.
   if ((KnownRHS.isKnownNeverInfinity() ||
        KnownLHS.isKnownNeverLogicalZero(Mode)) &&


Regression tests:
file: llvm/test/Transforms/InstSimplify/floating-point-arithmetic.ll
commands: opt < %s -passes=instsimplify -S
tests: test_name: fabs_fmul_nan
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.fabs.f32(float) #0

define float @fabs_fmul_nan(float %x) {
  %abs = call nnan float @llvm.fabs.f32(float %x)
  %mul = fmul float %abs, 0x7FF0000000000000
  %abs2 = call float @llvm.fabs.f32(float %mul)
  ret float %abs2
}

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }

test_name: fabs_fmul_nan_vector
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare <2 x float> @llvm.fabs.v2f32(<2 x float>) #0

define <2 x float> @fabs_fmul_nan_vector(<2 x float> %x) {
  %abs = call nnan <2 x float> @llvm.fabs.v2f32(<2 x float> %x)
  %mul = fmul <2 x float> %abs, splat (float 0x7FF0000000000000)
  %abs2 = call <2 x float> @llvm.fabs.v2f32(<2 x float> %mul)
  ret <2 x float> %abs2
}

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }