[llvm] [AMDGPU] Improve codegen for copysign(x, fneg(y)) (PR #207178)

Madhur Kumar via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 2 06:02:29 PDT 2026


https://github.com/MadhurKumar004 created https://github.com/llvm/llvm-project/pull/207178

Fixes: https://github.com/llvm/llvm-project/issues/174009

>From 6857547aa0ecb6b66525d62c6b409359dbd7ed95 Mon Sep 17 00:00:00 2001
From: Madhur Kumar <madhurkumar004 at gmail.com>
Date: Thu, 2 Jul 2026 18:30:32 +0530
Subject: [PATCH] [AMDGPU] Improve codegen for copysign(x, fneg(y))

---
 llvm/lib/Target/AMDGPU/SIISelLowering.cpp  | 16 ++++++++++++-
 llvm/test/CodeGen/AMDGPU/fcopysign-fneg.ll | 26 ++++++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/fcopysign-fneg.ll

diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index e0b0fe46d44a6..7227ce73989e8 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -14159,11 +14159,25 @@ SDValue SITargetLowering::performFCopySignCombine(SDNode *N,
   SelectionDAG &DAG = DCI.DAG;
   SDLoc DL(N);
   EVT SignVT = SignOp.getValueType();
+  EVT MagVT = MagnitudeOp.getValueType();
+
+  // copysign(0.0, fneg(x)) for f32: fold to and(signmask, not(bitcast(x)))
+  // which AMDGPU ISel matches as V_BFI_B32(x, 0, signmask)
+  if (SignOp.getOpcode() == ISD::FNEG && MagVT == MVT::f32) {
+    SDValue StrippedSign = SignOp.getOperand(0);
+    ConstantFPSDNode *C = dyn_cast<ConstantFPSDNode>(MagnitudeOp);
+    if (StrippedSign.getValueType() == MVT::f32 && C && C->isZero()) {
+      SDValue SignAsInt = DAG.getBitcast(MVT::i32, StrippedSign);
+      SDValue NotSign = DAG.getNOT(DL, SignAsInt, MVT::i32);
+      SDValue MaskC = DAG.getConstant(0x80000000, DL, MVT::i32);
+      SDValue And = DAG.getNode(ISD::AND, DL, MVT::i32, MaskC, NotSign);
+      return DAG.getBitcast(MVT::f32, And);
+    }
+  }
 
   // f64 fcopysign is really an f32 copysign on the high bits, so replace the
   // lower half with a copy.
   // fcopysign f64:x, _:y -> x.lo32, (fcopysign (f32 x.hi32), _:y)
-  EVT MagVT = MagnitudeOp.getValueType();
 
   unsigned NumElts = MagVT.isVector() ? MagVT.getVectorNumElements() : 1;
 
diff --git a/llvm/test/CodeGen/AMDGPU/fcopysign-fneg.ll b/llvm/test/CodeGen/AMDGPU/fcopysign-fneg.ll
new file mode 100644
index 0000000000000..bcea2ff05af50
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/fcopysign-fneg.ll
@@ -0,0 +1,26 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=amdgcn -mcpu=gfx900 < %s | FileCheck -check-prefix=GFX9 %s
+
+define float @copysign_fneg_pos_zero(float %y) {
+; GFX9-LABEL: copysign_fneg_pos_zero:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    s_brev_b32 s4, 1
+; GFX9-NEXT:    v_bfi_b32 v0, v0, 0, s4
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+  %fneg = fneg float %y
+  %b = call float @llvm.copysign.f32(float 0.0, float %fneg)
+  ret float %b
+}
+
+define float @copysign_fneg_neg_zero(float %y) {
+; GFX9-LABEL: copysign_fneg_neg_zero:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    s_brev_b32 s4, 1
+; GFX9-NEXT:    v_bfi_b32 v0, v0, 0, s4
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+  %fneg = fneg float %y
+  %b = call float @llvm.copysign.f32(float -0.0, float %fneg)
+  ret float %b
+}



More information about the llvm-commits mailing list