Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp: InstCombinerImpl::foldSelectIntoOp
Issue title: [InstCombine] ninf should not be preserved
Labels: miscompilation
llvm:instcombine
floating-point
generated by fuzzer
Reported by: dtcxzyw

Reproducer: https://alive2.llvm.org/ce/z/GNeZKU
```
; bin/opt -passes=instcombine test.ll -S
define float @select_fpclass_fadd(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fadd float %A, %B
  %D = select ninf i1 %cond, float %C, float %A
  ret float %D
}
```
```
=>
define float @select_fpclass_fadd(i1 %cond, float nofpclass(3) %A, float %B) {
#0:
  %C = select ninf i1 %cond, float %B, float -0.000000
  %D = fadd float nofpclass(3) %A, %C
  ret float %D
}
Transformation doesn't verify!

ERROR: Target is more poisonous than source

Example:
i1 %cond = #x1 (1)
float nofpclass(3) %A = #xff800000 (-oo)
float %B = #x7f800000 (+oo)

Source:
float %C = #xffc00000 (QNaN)
float %D = #xffc00000 (QNaN)

Target:
float %C = poison
float %D = poison
Source value: #xffc00000 (QNaN)
Target value: poison
```

Comments: author: dtcxzyw
body: https://github.com/llvm/llvm-project/blob/6ee362e1b5eb52421e0e700074c40ff9e7e0205e/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp#L545-L546

Fix patch:
commit 9e63b7ae4c6f6a06882ade921795c42108355c71
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sat Oct 11 10:04:55 2025 +0800

    [InstCombine] Fix flag propagation in `foldSelectIntoOp` (#162003)
    
    Consider the following transform:
    ```
    C = binop float A, nnan OOp
    D = select ninf, i1 cond, float C, float A
    ->
    E = select ninf, i1 cond, float OOp, float Identity
    F = binop float A, E
    ```
    We cannot propagate ninf from the original select, because OOp may be
    inf, and the flag only guarantees that FalseVal (op OOp) is never
    infinity.
    Examples: -inf + +inf = NaN, -inf - -inf = NaN, 0 * inf = NaN
    Specifically, if the original select has both ninf and nnan, we can
    safely propagate the flag.
    
    Alive2:
    + fadd: https://alive2.llvm.org/ce/z/TWfktv
    + fsub: https://alive2.llvm.org/ce/z/RAsjJb
    + fmul: https://alive2.llvm.org/ce/z/8eg4ND
    
    Closes https://github.com/llvm/llvm-project/issues/161634.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 8c8fc69e27ee..6b67b48a138b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -544,8 +544,18 @@ Instruction *InstCombinerImpl::foldSelectIntoOp(SelectInst &SI, Value *TrueVal,
 
     Value *NewSel = Builder.CreateSelect(SI.getCondition(), Swapped ? C : OOp,
                                          Swapped ? OOp : C, "", &SI);
-    if (isa<FPMathOperator>(&SI))
-      cast<Instruction>(NewSel)->setFastMathFlags(FMF);
+    if (isa<FPMathOperator>(&SI)) {
+      FastMathFlags NewSelFMF = FMF;
+      // We cannot propagate ninf from the original select, because OOp may be
+      // inf and the flag only guarantees that FalseVal (op OOp) is never
+      // infinity.
+      // Examples: -inf + +inf = NaN, -inf - -inf = NaN, 0 * inf = NaN
+      // Specifically, if the original select has both ninf and nnan, we can
+      // safely propagate the flag.
+      NewSelFMF.setNoInfs(TVI->hasNoInfs() ||
+                          (NewSelFMF.noInfs() && NewSelFMF.noNaNs()));
+      cast<Instruction>(NewSel)->setFastMathFlags(NewSelFMF);
+    }
     NewSel->takeName(TVI);
     BinaryOperator *BO =
         BinaryOperator::Create(TVI->getOpcode(), FalseVal, NewSel);


Regression tests:
file: llvm/test/Transforms/InstCombine/select-binop-foldable-floating-point.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: select_fpclass_fadd_nnan_ninf
test_body: define float @select_fpclass_fadd_nnan_ninf(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fadd float %A, %B
  %D = select nnan ninf i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fmul_ninf2
test_body: define float @select_fpclass_fmul_ninf2(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fmul float %A, %B
  %D = select ninf i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fadd_ninf2
test_body: define float @select_fpclass_fadd_ninf2(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fadd float %A, %B
  %D = select ninf i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fmul_ninf3
test_body: define float @select_fpclass_fmul_ninf3(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fmul ninf float %A, %B
  %D = select ninf i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fsub_ninf2
test_body: define float @select_fpclass_fsub_ninf2(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fsub float %A, %B
  %D = select ninf i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fadd
test_body: define float @select_fpclass_fadd(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fadd float %A, %B
  %D = select i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fadd_ninf1
test_body: define float @select_fpclass_fadd_ninf1(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fadd ninf float %A, %B
  %D = select i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fadd_ninf3
test_body: define float @select_fpclass_fadd_ninf3(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fadd ninf float %A, %B
  %D = select ninf i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fsub_nnan_ninf
test_body: define float @select_fpclass_fsub_nnan_ninf(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fsub float %A, %B
  %D = select nnan ninf i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fmul_ninf1
test_body: define float @select_fpclass_fmul_ninf1(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fmul ninf float %A, %B
  %D = select i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fsub_ninf1
test_body: define float @select_fpclass_fsub_ninf1(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fsub ninf float %A, %B
  %D = select i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fmul_nnan_ninf
test_body: define float @select_fpclass_fmul_nnan_ninf(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fmul float %A, %B
  %D = select nnan ninf i1 %cond, float %C, float %A
  ret float %D
}

test_name: select_fpclass_fsub_ninf3
test_body: define float @select_fpclass_fsub_ninf3(i1 %cond, float nofpclass(nan) %A, float %B) {
  %C = fsub ninf float %A, %B
  %D = select ninf i1 %cond, float %C, float %A
  ret float %D
}