Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp: InstCombinerImpl::visitSelectInst
Issue title: [InstCombine] `(fcmp une T, F) ? T : F --> T` should not rely on the nsz flag on fcmp
Labels: miscompilation
llvm:instcombine
floating-point
Reported by: dtcxzyw

Reproducer: https://alive2.llvm.org/ce/z/_FgncC
```
define double @src(double %x, double %y) {
  %cmp = fcmp nsz une double %x, %y
  %cond = select i1 %cmp, double %x, double %y
  ret double %cond
}

define double @tgt(double returned %x, double %y) {
  ret double %x
}
```
```
Transformation doesn't verify!

ERROR: Value mismatch

Example:
double %x = #x8000000000000000 (-0.0)
double %y = #x0000000000000000 (+0.0)

Source:
i1 %cmp = #x0 (0)
double %cond = #x0000000000000000 (+0.0)

Target:
Source value: #x0000000000000000 (+0.0)
Target value: #x8000000000000000 (-0.0)
```
`nsz` flag should not be propagated from fcmp into select.



Fix patch:
commit 3ec0c5c7fef03985b43432c6b914c289d8a5435e
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sat May 31 16:25:10 2025 +0800

    [InstCombine] Propagate FMF from select instead of fcmp (#141010)
    
    Previously,
    https://github.com/llvm/llvm-project/commit/3d6b53980ce4ee855484fc8ae6ac3f99c85e48c3
    propagates FMF from fcmp to avoid performance regressions. With the help
    of https://github.com/llvm/llvm-project/pull/139861,
    https://github.com/llvm/llvm-project/pull/141015, and
    https://github.com/llvm/llvm-project/pull/141914, we can still convert
    SPF into fabs/minnum/maxnum intrinsics even if some flags are missing.
    This patch propagates FMF from select to address the long-standing
    issue.
    
    Closes https://github.com/llvm/llvm-project/issues/140994.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index a791fc5db669..cca5705f6b58 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -25,6 +25,7 @@
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/FMF.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/Instruction.h"
@@ -3879,11 +3880,16 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
       // (X ugt Y) ? X : Y -> (X ole Y) ? Y : X
       if (FCmp->hasOneUse() && FCmpInst::isUnordered(Pred)) {
         FCmpInst::Predicate InvPred = FCmp->getInversePredicate();
-        // FIXME: The FMF should propagate from the select, not the fcmp.
         Value *NewCond = Builder.CreateFCmpFMF(InvPred, Cmp0, Cmp1, FCmp,
                                                FCmp->getName() + ".inv");
+        // Propagate ninf/nnan from fcmp to select.
+        FastMathFlags FMF = SI.getFastMathFlags();
+        if (FCmp->hasNoNaNs())
+          FMF.setNoNaNs(true);
+        if (FCmp->hasNoInfs())
+          FMF.setNoInfs(true);
         Value *NewSel =
-            Builder.CreateSelectFMF(NewCond, FalseVal, TrueVal, FCmp);
+            Builder.CreateSelectFMF(NewCond, FalseVal, TrueVal, FMF);
         return replaceInstUsesWith(SI, NewSel);
       }
     }


Regression tests:
file: llvm/test/Transforms/InstCombine/clamp-to-minmax.ll
commands: opt < %s -passes=instcombine -S
opt < %s -passes=instcombine -use-constant-fp-for-fixed-length-splat -use-constant-int-for-fixed-length-splat -S
tests: test_name: clamp_negative_same_op
test_body: define half @clamp_negative_same_op(half %x) {
  %inner_cmp = fcmp nnan ninf nsz ult half %x, 2.550000e+02
  %inner_sel = select i1 %inner_cmp, half %x, half 2.550000e+02
  %outer_cmp = fcmp nnan ninf nsz ult half %x, 1.000000e+00
  %r = select i1 %outer_cmp, half %inner_sel, half 1.000000e+00
  ret half %r
}

test_name: clamp_float_fast_unordered_nonstrict_maxmin
test_body: define half @clamp_float_fast_unordered_nonstrict_maxmin(half %x) {
  %cmp2 = fcmp nnan ninf nsz ult half %x, 2.550000e+02
  %min = select i1 %cmp2, half %x, half 2.550000e+02
  %cmp1 = fcmp nnan ninf nsz ule half %x, 1.000000e+00
  %r = select i1 %cmp1, half 1.000000e+00, half %min
  ret half %r
}

test_name: clamp_float_fast_ordered_nonstrict_minmax
test_body: define half @clamp_float_fast_ordered_nonstrict_minmax(half %x) {
  %cmp2 = fcmp nnan ninf nsz ogt half %x, 1.000000e+00
  %max = select i1 %cmp2, half %x, half 1.000000e+00
  %cmp1 = fcmp nnan ninf nsz oge half %x, 2.550000e+02
  %r = select i1 %cmp1, half 2.550000e+02, half %max
  ret half %r
}

test_name: clamp_float_fast_ordered_strict_maxmin
test_body: define half @clamp_float_fast_ordered_strict_maxmin(half %x) {
  %cmp2 = fcmp nnan ninf nsz olt half %x, 2.550000e+02
  %min = select i1 %cmp2, half %x, half 2.550000e+02
  %cmp1 = fcmp nnan ninf nsz olt half %x, 1.000000e+00
  %r = select i1 %cmp1, half 1.000000e+00, half %min
  ret half %r
}

test_name: clamp_float_fast_ordered_strict_minmax
test_body: define half @clamp_float_fast_ordered_strict_minmax(half %x) {
  %cmp2 = fcmp nnan ninf nsz ogt half %x, 1.000000e+00
  %max = select i1 %cmp2, half %x, half 1.000000e+00
  %cmp1 = fcmp nnan ninf nsz ogt half %x, 2.550000e+02
  %r = select i1 %cmp1, half 2.550000e+02, half %max
  ret half %r
}

test_name: clamp_float_fast_ordered_nonstrict_maxmin
test_body: define half @clamp_float_fast_ordered_nonstrict_maxmin(half %x) {
  %cmp2 = fcmp nnan ninf nsz olt half %x, 2.550000e+02
  %min = select i1 %cmp2, half %x, half 2.550000e+02
  %cmp1 = fcmp nnan ninf nsz ole half %x, 1.000000e+00
  %r = select i1 %cmp1, half 1.000000e+00, half %min
  ret half %r
}

test_name: clamp_float_fast_unordered_nonstrict_minmax
test_body: define half @clamp_float_fast_unordered_nonstrict_minmax(half %x) {
  %cmp2 = fcmp nnan ninf nsz ugt half %x, 1.000000e+00
  %max = select i1 %cmp2, half %x, half 1.000000e+00
  %cmp1 = fcmp nnan ninf nsz uge half %x, 2.550000e+02
  %r = select i1 %cmp1, half 2.550000e+02, half %max
  ret half %r
}

test_name: clamp_test_1
test_body: define half @clamp_test_1(half %x) {
  %inner_cmp = fcmp nnan ninf nsz ult half %x, 2.550000e+02
  %inner_sel = select i1 %inner_cmp, half %x, half 2.550000e+02
  %outer_cmp = fcmp nnan ninf nsz ugt half %x, 1.000000e+00
  %r = select i1 %outer_cmp, half %inner_sel, half 1.000000e+00
  ret half %r
}

test_name: clamp_float_fast_unordered_strict_maxmin
test_body: define half @clamp_float_fast_unordered_strict_maxmin(half %x) {
  %cmp2 = fcmp nnan ninf nsz ult half %x, 2.550000e+02
  %min = select i1 %cmp2, half %x, half 2.550000e+02
  %cmp1 = fcmp nnan ninf nsz ult half %x, 1.000000e+00
  %r = select i1 %cmp1, half 1.000000e+00, half %min
  ret half %r
}

test_name: clamp_negative_wrong_const
test_body: define half @clamp_negative_wrong_const(half %x) {
  %inner_cmp = fcmp nnan ninf nsz ult half %x, 2.550000e+02
  %inner_sel = select i1 %inner_cmp, half %x, half 2.550000e+02
  %outer_cmp = fcmp nnan ninf nsz ugt half %x, 5.120000e+02
  %r = select i1 %outer_cmp, half %inner_sel, half 5.120000e+02
  ret half %r
}

test_name: clamp_float_fast_unordered_strict_minmax
test_body: define half @clamp_float_fast_unordered_strict_minmax(half %x) {
  %cmp2 = fcmp nnan ninf nsz ugt half %x, 1.000000e+00
  %max = select i1 %cmp2, half %x, half 1.000000e+00
  %cmp1 = fcmp nnan ninf nsz ugt half %x, 2.550000e+02
  %r = select i1 %cmp1, half 2.550000e+02, half %max
  ret half %r
}

file: llvm/test/Transforms/InstCombine/fcmp-fadd-select.ll
commands: opt < %s -passes=instcombine -S
tests: test_name: test_fcmp_ugt_fadd_select_fastmath_preserve
test_body: define half @test_fcmp_ugt_fadd_select_fastmath_preserve(half %in) {
  %cmp1 = fcmp ugt half %in, 0.000000e+00
  %add = fadd nnan half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_uge_fadd_select_fastmath_preserve
test_body: define half @test_fcmp_uge_fadd_select_fastmath_preserve(half %in) {
  %cmp1 = fcmp uge half %in, 0.000000e+00
  %add = fadd nnan half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ugt_fadd_select_neg_constant
test_body: define half @test_fcmp_ugt_fadd_select_neg_constant(half %in) {
  %cmp1 = fcmp ugt half %in, -0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ugt_fadd_select_constant_swapped
test_body: define half @test_fcmp_ugt_fadd_select_constant_swapped(half %in) {
  %cmp1 = fcmp ugt half %in, 0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half 1.000000e+00, half %add
  ret half %sel
}

test_name: test_fcmp_uge_fadd_select_constant_swapped
test_body: define half @test_fcmp_uge_fadd_select_constant_swapped(half %in) {
  %cmp1 = fcmp uge half %in, 0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half 1.000000e+00, half %add
  ret half %sel
}

test_name: test_fcmp_ult_fadd_select_fastmath_preserve
test_body: define half @test_fcmp_ult_fadd_select_fastmath_preserve(half %in) {
  %cmp1 = fcmp ult half %in, 0.000000e+00
  %add = fadd nnan half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ule_fadd_select_constant_swapped
test_body: define half @test_fcmp_ule_fadd_select_constant_swapped(half %in) {
  %cmp1 = fcmp ule half %in, 0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half 1.000000e+00, half %add
  ret half %sel
}

test_name: test_fcmp_uge_fadd_select_neg_constant
test_body: define half @test_fcmp_uge_fadd_select_neg_constant(half %in) {
  %cmp1 = fcmp uge half %in, -0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ule_fadd_select_neg_constant
test_body: define half @test_fcmp_ule_fadd_select_neg_constant(half %in) {
  %cmp1 = fcmp ule half %in, -0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ult_fadd_select_constant_swapped
test_body: define half @test_fcmp_ult_fadd_select_constant_swapped(half %in) {
  %cmp1 = fcmp ult half %in, 0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half 1.000000e+00, half %add
  ret half %sel
}

test_name: test_fcmp_uge_fadd_select_constant
test_body: define half @test_fcmp_uge_fadd_select_constant(half %in) {
  %cmp1 = fcmp uge half %in, 0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ult_fadd_select_neg_constant
test_body: define half @test_fcmp_ult_fadd_select_neg_constant(half %in) {
  %cmp1 = fcmp ult half %in, -0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ugt_fadd_select_constant
test_body: define half @test_fcmp_ugt_fadd_select_constant(half %in) {
  %cmp1 = fcmp ugt half %in, 0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ule_fadd_select_constant
test_body: define half @test_fcmp_ule_fadd_select_constant(half %in) {
  %cmp1 = fcmp ule half %in, 0.000000e+00
  %add = fadd half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

test_name: test_fcmp_ule_fadd_select_fastmath_preserve
test_body: define half @test_fcmp_ule_fadd_select_fastmath_preserve(half %in) {
  %cmp1 = fcmp ule half %in, 0.000000e+00
  %add = fadd nnan half %in, 1.000000e+00
  %sel = select nnan nsz i1 %cmp1, half %add, half 1.000000e+00
  ret half %sel
}

file: llvm/test/Transforms/InstCombine/minmax-fold.ll
commands: opt -S -passes=instcombine < %s
tests: test_name: common_factor_umax_extra_use_both
test_body: declare void @extra_use(i32)

define i32 @common_factor_umax_extra_use_both(i32 %a, i32 %b, i32 %c) {
  %cmp_bc = icmp ugt i32 %b, %c
  %max_bc = select i1 %cmp_bc, i32 %b, i32 %c
  %cmp_ba = icmp ugt i32 %b, %a
  %max_ba = select i1 %cmp_ba, i32 %b, i32 %a
  %cmp_bc_ba = icmp ugt i32 %max_bc, %max_ba
  %max_abc = select i1 %cmp_bc_ba, i32 %max_bc, i32 %max_ba
  call void @extra_use(i32 %max_bc)
  call void @extra_use(i32 %max_ba)
  ret i32 %max_abc
}

test_name: not_min_of_min
test_body: define half @not_min_of_min(i8 %i, half %x) {
  %cmp1 = fcmp nnan ninf nsz ult half %x, 1.000000e+00
  %min1 = select i1 %cmp1, half %x, half 1.000000e+00
  %cmp2 = fcmp nnan ninf nsz ult half %x, 2.000000e+00
  %min2 = select i1 %cmp2, half %x, half 2.000000e+00
  %cmp3 = icmp ult i8 %i, 16
  %r = select i1 %cmp3, half %min1, half %min2
  ret half %r
}

file: llvm/test/Transforms/InstCombine/minmax-fp.ll
commands: opt -S -passes=instcombine < %s
tests: test_name: t15
test_body: define i8 @t15(half %a) {
  %1 = fcmp nsz ule half %a, 0.000000e+00
  %2 = fptosi half %a to i8
  %3 = select i1 %1, i8 %2, i8 0
  ret i8 %3
}

test_name: fneg_fmax
test_body: define half @fneg_fmax(half %x, half %y) {
  %n1 = fneg half %x
  %n2 = fneg half %y
  %cond = fcmp nnan ogt half %n1, %n2
  %max = select i1 %cond, half %n1, half %n2
  ret half %max
}

test_name: t11
test_body: define i8 @t11(half %a, half %b) {
  %1 = fcmp nnan ninf nsz ult half %b, %a
  %2 = fptosi half %a to i8
  %3 = fptosi half %b to i8
  %4 = select i1 %1, i8 %3, i8 %2
  ret i8 %4
}

test_name: fneg_fmin
test_body: define double @fneg_fmin(double %x, double %y) {
  %n1 = fneg double %x
  %n2 = fneg double %y
  %cond = fcmp nnan nsz ule double %n1, %n2
  %max = select i1 %cond, double %n1, double %n2
  ret double %max
}

test_name: t14_commute
test_body: define i8 @t14_commute(half %a) {
  %1 = fcmp ule half %a, 0.000000e+00
  %2 = fptosi half %a to i8
  %3 = select i1 %1, i8 0, i8 %2
  ret i8 %3
}

test_name: t9
test_body: define i8 @t9(half %a) {
  %1 = fcmp ult half %a, 0.000000e+00
  %2 = fptosi half %a to i8
  %3 = select i1 %1, i8 %2, i8 0
  ret i8 %3
}

file: llvm/test/Transforms/InstCombine/unordered-fcmp-select.ll
commands: opt -S -passes=instcombine < %s
tests: test_name: select_min_ugt
test_body: define half @select_min_ugt(half %a, half %b) {
  %cmp = fcmp nnan ninf nsz ugt half %a, %b
  %sel = select i1 %cmp, half %b, half %a
  ret half %sel
}

test_name: select_fcmp_une
test_body: define half @select_fcmp_une(half %a, half %b) {
  %cmp = fcmp une half %a, %b
  %sel = select nnan i1 %cmp, half %a, half %b
  ret half %sel
}

test_name: select_min_uge
test_body: define half @select_min_uge(half %a, half %b) {
  %cmp = fcmp nsz uge half %a, %b
  %sel = select nnan ninf nsz i1 %cmp, half %b, half %a
  ret half %sel
}

test_name: select_max_uge
test_body: define half @select_max_uge(half %a, half %b) {
  %cmp = fcmp nnan uge half %a, %b
  %sel = select ninf i1 %cmp, half %a, half %b
  ret half %sel
}

test_name: select_fcmp_ueq
test_body: define half @select_fcmp_ueq(half %a, half %b) {
  %cmp = fcmp ueq half %a, %b
  %sel = select nnan i1 %cmp, half %a, half %b
  ret half %sel
}

test_name: select_min_ule
test_body: define half @select_min_ule(half %a, half %b) {
  %cmp = fcmp ule half %a, %b
  %sel = select ninf i1 %cmp, half %a, half %b
  ret half %sel
}

test_name: select_max_ugt
test_body: define half @select_max_ugt(half %a, half %b) {
  %cmp = fcmp ugt half %a, %b
  %sel = select i1 %cmp, half %a, half %b
  ret half %sel
}

test_name: select_max_ule
test_body: define half @select_max_ule(half %a, half %b) {
  %cmp = fcmp nnan ninf nsz ule half %a, %b
  %sel = select nsz i1 %cmp, half %b, half %a
  ret half %sel
}

test_name: select_min_ult
test_body: define half @select_min_ult(half %a, half %b) {
  %cmp = fcmp nsz ult half %a, %b
  %sel = select nnan ninf nsz i1 %cmp, half %a, half %b
  ret half %sel
}

test_name: select_max_ult
test_body: define half @select_max_ult(half %a, half %b) {
  %cmp = fcmp ult half %a, %b
  %sel = select nnan ninf i1 %cmp, half %b, half %a
  ret half %sel
}