Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp: foldFNegIntoConstant
Issue title: foldFNegIntoConstant incorrectly preserves fast math flags for 0 constants
Labels: miscompilation
llvm:instcombine
floating-point
Reported by: arsenm

> https://alive2.llvm.org/ce/z/MHShQY longer version

_Originally posted by @arsenm in https://github.com/llvm/llvm-project/pull/92512#discussion_r1619895371_


Some of the combines in foldFNegIntoConstant are unsafely taking the flags directly from the original fneg instruction, when the preservation logic requires dropping ninf in some cases. 

Comments: author: SahilPatidar
body: @arsenm, could we use the same logic you shared in #92512 for dropping `ninf`?
```cpp
      FastMathFlags FMF = match(C, m_AnyZeroFP())? I.getFastMathFlags() &
                          cast<FPMathOperator>(I.getOperand(0))->getFastMathFlags():
                          I.getFastMathFlags();
```
However, this logic will also drop `nnan` and other flags. Should we check specifically for ninf instead?
author: arsenm
body: > However, this logic will also drop `nnan` and other flags. Should we check specifically for ninf instead?

That will be conservatively correct, but I think this can do better. For example, nnan on the fneg should be sufficient to preserve nnan on the final result, same with nsz 

Fix patch:
commit 971c49fbf361c22ccf20913f61a58c28b26c4e27
Author: SahilPatidar <patidarsahil2001@gmail.com>
Date:   Thu Jun 12 16:01:43 2025 +0530

    [InstCombine] Ensure Safe Handling of Flags in foldFNegIntoConstant (#94148)
    
    Fix #93769
    
    alive2: https://alive2.llvm.org/ce/z/MHShQY

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index fc7dd302b27a..f0f709bb16d8 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2857,8 +2857,14 @@ static Instruction *foldFNegIntoConstant(Instruction &I, const DataLayout &DL) {
   // Fold negation into constant operand.
   // -(X * C) --> X * (-C)
   if (match(FNegOp, m_FMul(m_Value(X), m_Constant(C))))
-    if (Constant *NegC = ConstantFoldUnaryOpOperand(Instruction::FNeg, C, DL))
-      return BinaryOperator::CreateFMulFMF(X, NegC, &I);
+    if (Constant *NegC = ConstantFoldUnaryOpOperand(Instruction::FNeg, C, DL)) {
+      FastMathFlags FNegF = I.getFastMathFlags();
+      FastMathFlags OpF = FNegOp->getFastMathFlags();
+      FastMathFlags FMF = FastMathFlags::unionValue(FNegF, OpF) |
+                          FastMathFlags::intersectRewrite(FNegF, OpF);
+      FMF.setNoInfs(FNegF.noInfs() && OpF.noInfs());
+      return BinaryOperator::CreateFMulFMF(X, NegC, FMF);
+    }
   // -(X / C) --> X / (-C)
   if (match(FNegOp, m_FDiv(m_Value(X), m_Constant(C))))
     if (Constant *NegC = ConstantFoldUnaryOpOperand(Instruction::FNeg, C, DL))


Regression tests:
file: llvm/test/Transforms/InstCombine/fneg.ll
commands: opt < %s -passes=instcombine -S
opt < %s -passes=instcombine -use-constant-fp-for-fixed-length-splat -S
tests: test_name: test_fneg_mul_combine_nsz_ninf_with_vec_const
test_body: define <2 x float> @test_fneg_mul_combine_nsz_ninf_with_vec_const(<2 x float> %a) {
  %f1 = fmul nsz <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: test_fneg_mul_combine_reassoc_ninf_with_vec_const1
test_body: define <2 x float> @test_fneg_mul_combine_reassoc_ninf_with_vec_const1(<2 x float> %a) {
  %f1 = fmul <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: test_fneg_ninf_mul_with_anyzero
test_body: define float @test_fneg_ninf_mul_with_anyzero(float %a) {
  %mul = fmul float %a, 0.000000e+00
  %f = fneg ninf float %mul
  ret float %f
}

test_name: test_fneg_mul_combine_contract_ninf_with_vec_const2
test_body: define <2 x float> @test_fneg_mul_combine_contract_ninf_with_vec_const2(<2 x float> %a) {
  %f1 = fmul ninf <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: fmul_fneg
test_body: define float @fmul_fneg(float %x) {
  %m = fmul float %x, 4.200000e+01
  %r = fneg float %m
  ret float %r
}

test_name: fmul_fsub_fmf
test_body: define float @fmul_fsub_fmf(float %x) {
  %m = fmul float %x, 4.200000e+01
  %r = fsub nsz float -0.000000e+00, %m
  ret float %r
}

test_name: test_fneg_nnan_mul_with_anyzero
test_body: define float @test_fneg_nnan_mul_with_anyzero(float %a) {
  %f1 = fmul ninf float %a, 0.000000e+00
  %f2 = fneg nnan float %f1
  ret float %f2
}

test_name: test_fneg_mul_combine_nnan_ninf_with_vec_const2
test_body: define <2 x float> @test_fneg_mul_combine_nnan_ninf_with_vec_const2(<2 x float> %a) {
  %f1 = fmul ninf <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg nnan ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: test_fsub_ninf_mul_with_anyzero
test_body: define float @test_fsub_ninf_mul_with_anyzero(float %a) {
  %f1 = fmul nsz float %a, 0.000000e+00
  %f2 = fsub ninf float -0.000000e+00, %f1
  ret float %f2
}

test_name: test_fneg_nsz_mul_with_anyzero
test_body: define float @test_fneg_nsz_mul_with_anyzero(float %a) {
  %f1 = fmul ninf float %a, 0.000000e+00
  %f2 = fneg nsz float %f1
  ret float %f2
}

test_name: test_fneg_ninf_mul_nnan_with_const
test_body: define float @test_fneg_ninf_mul_nnan_with_const(float %a) {
  %f1 = fmul nnan float %a, 0.000000e+00
  %f2 = fneg ninf float %f1
  ret float %f2
}

test_name: test_fneg_ninf_nnan_mul_with_vec_const
test_body: define <2 x float> @test_fneg_ninf_nnan_mul_with_vec_const(<2 x float> %a) {
  %f1 = fmul <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg nnan ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: test_fneg_mul_combine_reassoc_ninf_with_vec_const3
test_body: define <2 x float> @test_fneg_mul_combine_reassoc_ninf_with_vec_const3(<2 x float> %a) {
  %f1 = fmul <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: test_fneg_mul_combine_contract_ninf_with_vec_const1
test_body: define <2 x float> @test_fneg_mul_combine_contract_ninf_with_vec_const1(<2 x float> %a) {
  %f1 = fmul <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: fmul_fneg_fmf
test_body: define float @fmul_fneg_fmf(float %x) {
  %m = fmul float %x, 4.200000e+01
  %r = fneg nsz float %m
  ret float %r
}

test_name: test_fneg_mul_combine_nnan_ninf_with_vec_const
test_body: define <2 x float> @test_fneg_mul_combine_nnan_ninf_with_vec_const(<2 x float> %a) {
  %f1 = fmul nnan <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: test_fneg_mul_combine_reassoc_ninf_with_vec_const2
test_body: define <2 x float> @test_fneg_mul_combine_reassoc_ninf_with_vec_const2(<2 x float> %a) {
  %f1 = fmul ninf <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: test_fneg_mul_combine_contract_ninf_with_vec_const3
test_body: define <2 x float> @test_fneg_mul_combine_contract_ninf_with_vec_const3(<2 x float> %a) {
  %f1 = fmul <2 x float> %a, <float 0.000000e+00, float -0.000000e+00>
  %f2 = fneg ninf <2 x float> %f1
  ret <2 x float> %f2
}

test_name: test_fneg_ninf_mul_nsz_with_const
test_body: define float @test_fneg_ninf_mul_nsz_with_const(float %a) {
  %f1 = fmul nsz float %a, 0.000000e+00
  %f2 = fneg ninf float %f1
  ret float %f2
}