Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp: foldSelectWithFCmpToFabs
Issue title: [InstCombine] The sign bit of NaN is not preserved when folding `(X <= +/-0.0) ? (0.0 - X) : X to fabs(X)`
Labels: miscompilation
llvm:instcombine
Reported by: dtcxzyw

Reproducer: https://alive2.llvm.org/ce/z/2JaLU9
```

----------------------------------------
define half @src(half %x) {
#0:
  %gtzero = fcmp ugt half %x, 0x0000
  %negx = fsub half 0x0000, %x
  %fabs = select i1 %gtzero, half %x, half %negx
  ret half %fabs
}
=>
define half @src(half %x) nofree willreturn memory(none) {
#0:
  %fabs = fabs half %x
  ret half %fabs
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
half %x = #xfd00 (SNaN)

Source:
i1 %gtzero = #x1 (1)
half %negx = #xff00 (QNaN)
half %fabs = #xfd00 (SNaN)

Target:
half %fabs = #x7d00 (SNaN)
Source value: #xfd00 (SNaN)
Target value: #x7d00 (SNaN)

Summary:
  0 correct transformations
  1 incorrect transformations
  0 failed-to-prove transformations
  0 Alive2 errors
```
Related issues:
https://github.com/AliveToolkit/alive2/pull/1155
https://github.com/llvm/llvm-project/issues/59279



Fix patch:
commit 3e1e4062e1e95031c32c0ed9786647ef1a4141aa
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sat Apr 26 14:03:12 2025 +0800

    [InstCombine] Preserve signbit semantics of NaN with fold to fabs (#136648)
    
    As per the LangRef and IEEE 754-2008 standard, the sign bit of NaN is
    preserved if there is no floating-point operation being performed.
    See also
    https://github.com/llvm/llvm-project/commit/862e35e25a68502433da0a8d0819448ff5745339
    for reference.
    
    Alive2: https://alive2.llvm.org/ce/z/QYtEGj
    Closes https://github.com/llvm/llvm-project/issues/136646

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 4bba2f406b4c..5a42a5c996da 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -2793,7 +2793,14 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
 
     // fold (X <= +/-0.0) ? (0.0 - X) : X to fabs(X), when 'Swap' is false
     // fold (X >  +/-0.0) ? X : (0.0 - X) to fabs(X), when 'Swap' is true
-    if (match(TrueVal, m_FSub(m_PosZeroFP(), m_Specific(X)))) {
+    // Note: We require "nnan" for this fold because fcmp ignores the signbit
+    //       of NAN, but IEEE-754 specifies the signbit of NAN values with
+    //       fneg/fabs operations.
+    if (match(TrueVal, m_FSub(m_PosZeroFP(), m_Specific(X))) &&
+        (cast<FPMathOperator>(CondVal)->hasNoNaNs() || SI.hasNoNaNs() ||
+         isKnownNeverNaN(X, /*Depth=*/0,
+                         IC.getSimplifyQuery().getWithInstruction(
+                             cast<Instruction>(CondVal))))) {
       if (!Swap && (Pred == FCmpInst::FCMP_OLE || Pred == FCmpInst::FCMP_ULE)) {
         Value *Fabs = IC.Builder.CreateUnaryIntrinsic(Intrinsic::fabs, X, &SI);
         return IC.replaceInstUsesWith(SI, Fabs);


Regression tests:
file: llvm/test/Transforms/InstCombine/fabs.ll
commands: opt -mtriple=x86_64-unknown-linux-gnu < %s -passes=instcombine -S
tests: test_name: select_fcmp_ogt_zero
test_body: define half @select_fcmp_ogt_zero(half %x) {
  %gtzero = fcmp nnan ogt half %x, 0.000000e+00
  %negx = fsub half 0.000000e+00, %x
  %fabs = select i1 %gtzero, half %x, half %negx
  ret half %fabs
}

test_name: select_fcmp_ole_zero_no_nnan
test_body: define half @select_fcmp_ole_zero_no_nnan(half %x) {
  %lezero = fcmp ole half %x, 0.000000e+00
  %negx = fsub half 0.000000e+00, %x
  %fabs = select i1 %lezero, half %negx, half %x
  ret half %fabs
}

test_name: select_fcmp_nnan_ule_zero
test_body: define half @select_fcmp_nnan_ule_zero(half %x) {
  %lezero = fcmp nnan ule half %x, 0.000000e+00
  %negx = fsub nnan half 0.000000e+00, %x
  %fabs = select i1 %lezero, half %negx, half %x
  ret half %fabs
}

test_name: select_fcmp_ole_zero_select_nnan
test_body: define half @select_fcmp_ole_zero_select_nnan(half %x) {
  %lezero = fcmp ole half %x, 0.000000e+00
  %negx = fsub half 0.000000e+00, %x
  %fabs = select nnan i1 %lezero, half %negx, half %x
  ret half %fabs
}

test_name: select_fcmp_ole_zero
test_body: define half @select_fcmp_ole_zero(half %x) {
  %lezero = fcmp nnan ole half %x, 0.000000e+00
  %negx = fsub half 0.000000e+00, %x
  %fabs = select i1 %lezero, half %negx, half %x
  ret half %fabs
}

test_name: select_fcmp_nnan_ogt_zero
test_body: define half @select_fcmp_nnan_ogt_zero(half %x) {
  %gtzero = fcmp nnan ogt half %x, 0.000000e+00
  %negx = fsub nnan half 0.000000e+00, %x
  %fabs = select i1 %gtzero, half %x, half %negx
  ret half %fabs
}

test_name: select_nnan_fcmp_nnan_ole_zero
test_body: define half @select_nnan_fcmp_nnan_ole_zero(half %x) {
  %lezero = fcmp nnan ole half %x, 0.000000e+00
  %negx = fsub nnan half 0.000000e+00, %x
  %fabs = select nnan i1 %lezero, half %negx, half %x
  ret half %fabs
}

test_name: select_fcmp_nnan_ogt_negzero
test_body: define half @select_fcmp_nnan_ogt_negzero(half %x) {
  %gtzero = fcmp nnan ogt half %x, 0xH8000
  %negx = fsub nnan half 0xH0000, %x
  %fabs = select i1 %gtzero, half %x, half %negx
  ret half %fabs
}

test_name: select_fcmp_nnan_ugt_negzero
test_body: define half @select_fcmp_nnan_ugt_negzero(half %x) {
  %gtzero = fcmp nnan ugt half %x, 0xH8000
  %negx = fsub nnan half 0xH0000, %x
  %fabs = select i1 %gtzero, half %x, half %negx
  ret half %fabs
}

test_name: select_nnan_fcmp_nnan_ole_negzero
test_body: define <2 x float> @select_nnan_fcmp_nnan_ole_negzero(<2 x float> %x) {
  %lezero = fcmp nnan ole <2 x float> %x, splat (float -0.000000e+00)
  %negx = fsub nnan <2 x float> <float 0.000000e+00, float poison>, %x
  %fabs = select nnan <2 x i1> %lezero, <2 x float> %negx, <2 x float> %x
  ret <2 x float> %fabs
}

test_name: select_nnan_fcmp_nnan_ogt_negzero
test_body: define half @select_nnan_fcmp_nnan_ogt_negzero(half %x) {
  %gtzero = fcmp nnan ogt half %x, 0xH8000
  %negx = fsub nnan half 0xH0000, %x
  %fabs = select nnan i1 %gtzero, half %x, half %negx
  ret half %fabs
}

test_name: select_fcmp_nnan_ole_zero
test_body: define half @select_fcmp_nnan_ole_zero(half %x) {
  %lezero = fcmp nnan ole half %x, 0.000000e+00
  %negx = fsub nnan half 0.000000e+00, %x
  %fabs = select i1 %lezero, half %negx, half %x
  ret half %fabs
}

test_name: select_fcmp_nnan_ole_negzero
test_body: define <2 x float> @select_fcmp_nnan_ole_negzero(<2 x float> %x) {
  %lezero = fcmp nnan ole <2 x float> %x, splat (float -0.000000e+00)
  %negx = fsub nnan <2 x float> <float 0.000000e+00, float poison>, %x
  %fabs = select <2 x i1> %lezero, <2 x float> %negx, <2 x float> %x
  ret <2 x float> %fabs
}

test_name: select_nnan_fcmp_nnan_ogt_zero
test_body: define half @select_nnan_fcmp_nnan_ogt_zero(half %x) {
  %gtzero = fcmp nnan ogt half %x, 0.000000e+00
  %negx = fsub nnan half 0.000000e+00, %x
  %fabs = select nnan i1 %gtzero, half %x, half %negx
  ret half %fabs
}

test_name: select_fcmp_ole_zero_no_nnan_input_nofpclass_nan
test_body: define half @select_fcmp_ole_zero_no_nnan_input_nofpclass_nan(half nofpclass(nan) %x) {
  %lezero = fcmp ole half %x, 0.000000e+00
  %negx = fsub half 0.000000e+00, %x
  %fabs = select i1 %lezero, half %negx, half %x
  ret half %fabs
}