Bug type: miscompilation
Component: InstCombine
Bug location: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp: InstCombinerImpl::visitCallInst
Issue title: [InstCombine] FMF flags should not be propagated
Labels: miscompilation
llvm:instcombine
floating-point
Reported by: dtcxzyw

Alive2: https://alive2.llvm.org/ce/z/ajCxTh
```
define float @src1(float noundef %x, float noundef %y, float noundef %z) {
  %s = call float @llvm.copysign.f32(float %y, float %z)
  %r = call nnan float @llvm.copysign.f32(float %x, float %s)
  ret float %r
}

define float @tgt1(float noundef %x, float noundef %y, float noundef %z) {
  %r = call nnan float @llvm.copysign.f32(float %x, float %z)
  ret float %r
}

define float @src2(float noundef %x, float noundef %y, float noundef %z) {
  %s = call float @llvm.copysign.f32(float %y, float %z)
  %r = call ninf float @llvm.copysign.f32(float %x, float %s)
  ret float %r
}

define float @tgt2(float noundef %x, float noundef %y, float noundef %z) {
  %r = call ninf float @llvm.copysign.f32(float %x, float %z)
  ret float %r
}

define float @src3(float noundef %x, float noundef %y, float noundef %z) {
  %s = call float @llvm.copysign.f32(float %y, float %z)
  %r = call nsz float @llvm.copysign.f32(float %x, float %s)
  ret float %r
}

define float @tgt3(float noundef %x, float noundef %y, float noundef %z) {
  %r = call nsz float @llvm.copysign.f32(float %x, float %z)
  ret float %r
}
```



Fix patch:
commit c05599966cd594c15834378f57669719d221c4d4
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Mon Jan 6 16:23:46 2025 +0800

    [InstCombine] Fix FMF propagation in `copysign Mag, (copysign ?, X) -> copysign Mag, X` (#121574)
    
    Closes https://github.com/llvm/llvm-project/issues/121432.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index cdb2c11ef046..5494c70b34b1 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -2673,8 +2673,12 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
     // Propagate sign argument through nested calls:
     // copysign Mag, (copysign ?, X) --> copysign Mag, X
     Value *X;
-    if (match(Sign, m_Intrinsic<Intrinsic::copysign>(m_Value(), m_Value(X))))
-      return replaceOperand(*II, 1, X);
+    if (match(Sign, m_Intrinsic<Intrinsic::copysign>(m_Value(), m_Value(X)))) {
+      Value *CopySign = Builder.CreateCopySign(
+          Mag, X,
+          II->getFastMathFlags() & cast<Instruction>(Sign)->getFastMathFlags());
+      return replaceInstUsesWith(*II, CopySign);
+    }
 
     // Clear sign-bit of constant magnitude:
     // copysign -MagC, X --> copysign MagC, X


Regression tests:
file: llvm/test/Transforms/InstCombine/copysign.ll
commands: opt -S -passes=instcombine < %s
tests: test_name: copysign_sign_arg_mixed
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.copysign.f32(float, float) #0

define float @copysign_sign_arg_mixed(float %x, float %y, float %z) {
  %s = call ninf nsz float @llvm.copysign.f32(float %y, float %z)
  %r = call nnan nsz float @llvm.copysign.f32(float %x, float %s)
  ret float %r
}

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

test_name: copysign_sign_arg
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.copysign.f32(float, float) #0

define float @copysign_sign_arg(float %x, float %y, float %z) {
  %s = call float @llvm.copysign.f32(float %y, float %z)
  %r = call ninf float @llvm.copysign.f32(float %x, float %s)
  ret float %r
}

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

test_name: fneg_mag
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.copysign.f32(float, float) #0

define float @fneg_mag(float %x, float %y) {
  %n = fneg float %x
  %r = call float @llvm.copysign.f32(float %n, float %y)
  ret float %r
}

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

test_name: copysign_sign_arg_nnan
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.copysign.f32(float, float) #0

define float @copysign_sign_arg_nnan(float %x, float %y, float %z) {
  %s = call nnan float @llvm.copysign.f32(float %y, float %z)
  %r = call nnan float @llvm.copysign.f32(float %x, float %s)
  ret float %r
}

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

test_name: not_known_positive_sign_arg
test_body: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.copysign.f32(float, float) #0

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.maxnum.f32(float, float) #0

define float @not_known_positive_sign_arg(float %x, float %y) {
  %max = call float @llvm.maxnum.f32(float %x, float 0.000000e+00)
  %r = call ninf float @llvm.copysign.f32(float %y, float %max)
  ret float %r
}

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