[llvm] [AMDGPU] Handle negated f16 and fp conversion DAG combines (PR #213202)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 2 18:05:06 PDT 2026
https://github.com/HalfBloodPrince010 updated https://github.com/llvm/llvm-project/pull/213202
>From 1fc14bb3e5b4c86ffa52e29b274e0ce66796926d Mon Sep 17 00:00:00 2001
From: Prashanth Srikanth Pujar <prashanth.pujar.usc at gmail.com>
Date: Sun, 2 Aug 2026 17:29:15 -0700
Subject: [PATCH 1/2] [AMDGPU] Add baseline tests for negated f16 conversions
---
.../CodeGen/AMDGPU/fneg-f16-conversions.ll | 195 ++++++++++++++++++
1 file changed, 195 insertions(+)
create mode 100644 llvm/test/CodeGen/AMDGPU/fneg-f16-conversions.ll
diff --git a/llvm/test/CodeGen/AMDGPU/fneg-f16-conversions.ll b/llvm/test/CodeGen/AMDGPU/fneg-f16-conversions.ll
new file mode 100644
index 0000000000000..b66dec66876a8
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/fneg-f16-conversions.ll
@@ -0,0 +1,195 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=amdgpu7-amd-amdhsa < %s | FileCheck %s
+
+define amdgpu_kernel void @fneg_fdiv_fpext_half(ptr addrspace(1) %out, ptr addrspace(1) %in, float %x) {
+; CHECK-LABEL: fneg_fdiv_fpext_half:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: s_load_dwordx4 s[0:3], s[8:9], 0x0
+; CHECK-NEXT: s_load_dword s4, s[8:9], 0x4
+; CHECK-NEXT: s_add_i32 s12, s12, s17
+; CHECK-NEXT: s_mov_b32 flat_scratch_lo, s13
+; CHECK-NEXT: s_lshr_b32 flat_scratch_hi, s12, 8
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_mov_b32_e32 v0, s2
+; CHECK-NEXT: v_mov_b32_e32 v1, s3
+; CHECK-NEXT: flat_load_ushort v0, v[0:1]
+; CHECK-NEXT: s_waitcnt vmcnt(0)
+; CHECK-NEXT: v_cvt_f32_f16_e32 v2, v0
+; CHECK-NEXT: v_div_scale_f32 v0, s[2:3], v2, v2, s4
+; CHECK-NEXT: v_div_scale_f32 v1, vcc, s4, v2, s4
+; CHECK-NEXT: v_rcp_f32_e32 v3, v0
+; CHECK-NEXT: v_fma_f32 v4, -v0, v3, 1.0
+; CHECK-NEXT: v_fma_f32 v3, v4, v3, v3
+; CHECK-NEXT: v_mul_f32_e32 v4, v1, v3
+; CHECK-NEXT: v_fma_f32 v5, -v0, v4, v1
+; CHECK-NEXT: v_fma_f32 v4, v5, v3, v4
+; CHECK-NEXT: v_fma_f32 v0, -v0, v4, v1
+; CHECK-NEXT: v_div_fmas_f32 v3, v0, v3, v4
+; CHECK-NEXT: v_mov_b32_e32 v0, s0
+; CHECK-NEXT: v_mov_b32_e32 v1, s1
+; CHECK-NEXT: v_div_fixup_f32 v2, v3, v2, s4
+; CHECK-NEXT: v_xor_b32_e32 v2, 0x80000000, v2
+; CHECK-NEXT: flat_store_dword v[0:1], v2
+; CHECK-NEXT: s_endpgm
+entry:
+ %h = load half, ptr addrspace(1) %in, align 2
+ %f = fpext half %h to float
+ %div = fdiv float %x, %f
+ %neg = fneg float %div
+ store float %neg, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+define amdgpu_kernel void @fneg_fpext_half(ptr addrspace(1) %out, ptr addrspace(1) %in) {
+; CHECK-LABEL: fneg_fpext_half:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: s_load_dwordx4 s[0:3], s[8:9], 0x0
+; CHECK-NEXT: s_add_i32 s12, s12, s17
+; CHECK-NEXT: s_mov_b32 flat_scratch_lo, s13
+; CHECK-NEXT: s_lshr_b32 flat_scratch_hi, s12, 8
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_mov_b32_e32 v0, s2
+; CHECK-NEXT: v_mov_b32_e32 v1, s3
+; CHECK-NEXT: flat_load_ushort v0, v[0:1]
+; CHECK-NEXT: v_mov_b32_e32 v1, s1
+; CHECK-NEXT: s_waitcnt vmcnt(0)
+; CHECK-NEXT: v_cvt_f32_f16_e64 v2, -v0
+; CHECK-NEXT: v_mov_b32_e32 v0, s0
+; CHECK-NEXT: flat_store_dword v[0:1], v2
+; CHECK-NEXT: s_endpgm
+entry:
+ %h = load half, ptr addrspace(1) %in, align 2
+ %f = fpext half %h to float
+ %neg = fneg float %f
+ store float %neg, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+define amdgpu_kernel void @fadd_fptrunc_half_bitcast_user(ptr addrspace(1) %out, ptr addrspace(1) %a, ptr addrspace(1) %b) {
+; CHECK-LABEL: fadd_fptrunc_half_bitcast_user:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: s_load_dwordx2 s[0:1], s[8:9], 0x4
+; CHECK-NEXT: s_add_i32 s12, s12, s17
+; CHECK-NEXT: s_mov_b32 flat_scratch_lo, s13
+; CHECK-NEXT: s_lshr_b32 flat_scratch_hi, s12, 8
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_mov_b32_e32 v0, s0
+; CHECK-NEXT: v_mov_b32_e32 v1, s1
+; CHECK-NEXT: flat_load_ushort v0, v[0:1]
+; CHECK-NEXT: s_load_dwordx4 s[0:3], s[8:9], 0x0
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: s_load_dword s2, s[2:3], 0x0
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_cvt_f16_f32_e32 v1, s2
+; CHECK-NEXT: v_cvt_f32_f16_e32 v1, v1
+; CHECK-NEXT: s_waitcnt vmcnt(0)
+; CHECK-NEXT: v_cvt_f32_f16_e32 v0, v0
+; CHECK-NEXT: v_sub_f32_e32 v0, v0, v1
+; CHECK-NEXT: v_cvt_f16_f32_e32 v2, v0
+; CHECK-NEXT: v_mov_b32_e32 v0, s0
+; CHECK-NEXT: v_mov_b32_e32 v1, s1
+; CHECK-NEXT: flat_store_short v[0:1], v2
+; CHECK-NEXT: s_endpgm
+entry:
+ %av = load float, ptr addrspace(1) %a, align 4
+ %bv = load half, ptr addrspace(1) %b, align 2
+ %h = fptrunc float %av to half
+ %neg = fneg half %h
+ %r = fadd half %neg, %bv
+ store half %r, ptr addrspace(1) %out, align 2
+ ret void
+}
+
+define amdgpu_kernel void @fmul_fneg_fptrunc_half(ptr addrspace(1) %out, ptr addrspace(1) %a, ptr addrspace(1) %b) {
+; CHECK-LABEL: fmul_fneg_fptrunc_half:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: s_load_dwordx2 s[0:1], s[8:9], 0x4
+; CHECK-NEXT: s_add_i32 s12, s12, s17
+; CHECK-NEXT: s_mov_b32 flat_scratch_lo, s13
+; CHECK-NEXT: s_lshr_b32 flat_scratch_hi, s12, 8
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_mov_b32_e32 v0, s0
+; CHECK-NEXT: v_mov_b32_e32 v1, s1
+; CHECK-NEXT: flat_load_ushort v0, v[0:1]
+; CHECK-NEXT: s_load_dwordx4 s[0:3], s[8:9], 0x0
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: s_load_dword s2, s[2:3], 0x0
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_cvt_f16_f32_e64 v1, -s2
+; CHECK-NEXT: v_cvt_f32_f16_e32 v1, v1
+; CHECK-NEXT: s_waitcnt vmcnt(0)
+; CHECK-NEXT: v_cvt_f32_f16_e32 v0, v0
+; CHECK-NEXT: v_mul_f32_e32 v0, v1, v0
+; CHECK-NEXT: v_cvt_f16_f32_e32 v2, v0
+; CHECK-NEXT: v_mov_b32_e32 v0, s0
+; CHECK-NEXT: v_mov_b32_e32 v1, s1
+; CHECK-NEXT: flat_store_short v[0:1], v2
+; CHECK-NEXT: s_endpgm
+entry:
+ %av = load float, ptr addrspace(1) %a, align 4
+ %bv = load half, ptr addrspace(1) %b, align 2
+ %h = fptrunc float %av to half
+ %neg = fneg half %h
+ %r = fmul half %neg, %bv
+ store half %r, ptr addrspace(1) %out, align 2
+ ret void
+}
+
+define amdgpu_kernel void @fneg_fneg_fpext_half(ptr addrspace(1) %out, ptr addrspace(1) %in) {
+; CHECK-LABEL: fneg_fneg_fpext_half:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: s_load_dwordx4 s[0:3], s[8:9], 0x0
+; CHECK-NEXT: s_add_i32 s12, s12, s17
+; CHECK-NEXT: s_mov_b32 flat_scratch_lo, s13
+; CHECK-NEXT: s_lshr_b32 flat_scratch_hi, s12, 8
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_mov_b32_e32 v0, s2
+; CHECK-NEXT: v_mov_b32_e32 v1, s3
+; CHECK-NEXT: flat_load_ushort v0, v[0:1]
+; CHECK-NEXT: v_mov_b32_e32 v1, s1
+; CHECK-NEXT: s_waitcnt vmcnt(0)
+; CHECK-NEXT: v_cvt_f32_f16_e32 v2, v0
+; CHECK-NEXT: v_mov_b32_e32 v0, s0
+; CHECK-NEXT: flat_store_dword v[0:1], v2
+; CHECK-NEXT: s_endpgm
+entry:
+ %h = load half, ptr addrspace(1) %in, align 2
+ %f = fpext half %h to float
+ %neg0 = fneg float %f
+ %neg1 = fneg float %neg0
+ store float %neg1, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+define amdgpu_kernel void @fsub_fptrunc_half_rhs(ptr addrspace(1) %out, ptr addrspace(1) %a, ptr addrspace(1) %b) {
+; CHECK-LABEL: fsub_fptrunc_half_rhs:
+; CHECK: ; %bb.0: ; %entry
+; CHECK-NEXT: s_load_dwordx4 s[0:3], s[8:9], 0x0
+; CHECK-NEXT: s_load_dwordx2 s[4:5], s[8:9], 0x4
+; CHECK-NEXT: s_add_i32 s12, s12, s17
+; CHECK-NEXT: s_mov_b32 flat_scratch_lo, s13
+; CHECK-NEXT: s_lshr_b32 flat_scratch_hi, s12, 8
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_mov_b32_e32 v0, s2
+; CHECK-NEXT: v_mov_b32_e32 v1, s3
+; CHECK-NEXT: flat_load_ushort v0, v[0:1]
+; CHECK-NEXT: s_load_dword s2, s[4:5], 0x0
+; CHECK-NEXT: s_waitcnt lgkmcnt(0)
+; CHECK-NEXT: v_cvt_f16_f32_e32 v1, s2
+; CHECK-NEXT: v_cvt_f32_f16_e32 v1, v1
+; CHECK-NEXT: s_waitcnt vmcnt(0)
+; CHECK-NEXT: v_cvt_f32_f16_e32 v0, v0
+; CHECK-NEXT: v_sub_f32_e32 v0, v0, v1
+; CHECK-NEXT: v_cvt_f16_f32_e32 v2, v0
+; CHECK-NEXT: v_mov_b32_e32 v0, s0
+; CHECK-NEXT: v_mov_b32_e32 v1, s1
+; CHECK-NEXT: flat_store_short v[0:1], v2
+; CHECK-NEXT: s_endpgm
+entry:
+ %av = load half, ptr addrspace(1) %a, align 2
+ %bv = load float, ptr addrspace(1) %b, align 4
+ %h = fptrunc float %bv to half
+ %r = fsub half %av, %h
+ store half %r, ptr addrspace(1) %out, align 2
+ ret void
+}
>From edbb76ed7f5974860e6f1ca05d1ae001688315cf Mon Sep 17 00:00:00 2001
From: Prashanth Srikanth Pujar <prashanth.pujar.usc at gmail.com>
Date: Sun, 2 Aug 2026 18:02:34 -0700
Subject: [PATCH 2/2] [AMDGPU] Handle negated f16 conversion DAG combines
Add changes to teach AMDGPU getNegatedExpressions to handle f16
conversion nodes while avoiding the source modifier regressions
for existing users. In particular preserve profitable subtract
forms instead of pushing negation into the f16 conversion.
---
llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp | 36 +++++++++++++++++++
.../CodeGen/AMDGPU/fneg-f16-conversions.ll | 3 +-
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index 962988ff97e39..0e9d2857f27e2 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -21,7 +21,9 @@
#include "SIMachineFunctionInfo.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
+#include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/SelectionDAGNodes.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/IntrinsicsAMDGPU.h"
#include "llvm/Support/CommandLine.h"
@@ -940,6 +942,40 @@ SDValue AMDGPUTargetLowering::getNegatedExpression(
return SDValue();
break;
}
+ case ISD::FP16_TO_FP: {
+ // If the Users of the Op can handle the negation, defer it for that Op
+ // and do not handle the conversion
+ if (allUsesHaveSourceMods(Op.getNode()))
+ return SDValue();
+
+ SDValue Src = Op.getOperand(0);
+ EVT VT = Op.getValueType();
+ EVT SrcVT = Src.getValueType();
+ SDLoc SL(Op);
+ Cost = NegatibleCost::Neutral;
+ SDValue Negated = DAG.getNode(ISD::XOR, SL, SrcVT, Src,
+ DAG.getConstant(0x8000, SL, SrcVT));
+ return DAG.getNode(ISD::FP16_TO_FP, SL, VT, Negated);
+ }
+ case ISD::FP_TO_FP16: {
+ if (allUsesHaveSourceMods(Op.getNode()))
+ return SDValue();
+
+ SDValue Src = Op.getOperand(0);
+ EVT VT = Op.getValueType();
+ EVT SrcVT = Src.getValueType();
+ SDLoc SL(Op);
+
+ if (Src->getOpcode() == ISD::FNEG) {
+ Cost = NegatibleCost::Cheaper;
+ // Negative of Negative is the Source Operand itself
+ // -(FP_TO_FP16(FNEG(X)) = FP_TO_FP16(X)
+ return DAG.getNode(ISD::FP_TO_FP16, SL, VT, Src->getOperand(0));
+ }
+ Cost = NegatibleCost::Neutral;
+ SDValue Negated = DAG.getNode(ISD::FNEG, SL, SrcVT, Src);
+ return DAG.getNode(ISD::FP_TO_FP16, SL, VT, Negated);
+ }
case AMDGPUISD::RCP: {
SDValue Src = Op.getOperand(0);
EVT VT = Op.getValueType();
diff --git a/llvm/test/CodeGen/AMDGPU/fneg-f16-conversions.ll b/llvm/test/CodeGen/AMDGPU/fneg-f16-conversions.ll
index b66dec66876a8..fc89d8b57bede 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg-f16-conversions.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg-f16-conversions.ll
@@ -14,7 +14,7 @@ define amdgpu_kernel void @fneg_fdiv_fpext_half(ptr addrspace(1) %out, ptr addrs
; CHECK-NEXT: v_mov_b32_e32 v1, s3
; CHECK-NEXT: flat_load_ushort v0, v[0:1]
; CHECK-NEXT: s_waitcnt vmcnt(0)
-; CHECK-NEXT: v_cvt_f32_f16_e32 v2, v0
+; CHECK-NEXT: v_cvt_f32_f16_e64 v2, -v0
; CHECK-NEXT: v_div_scale_f32 v0, s[2:3], v2, v2, s4
; CHECK-NEXT: v_div_scale_f32 v1, vcc, s4, v2, s4
; CHECK-NEXT: v_rcp_f32_e32 v3, v0
@@ -28,7 +28,6 @@ define amdgpu_kernel void @fneg_fdiv_fpext_half(ptr addrspace(1) %out, ptr addrs
; CHECK-NEXT: v_mov_b32_e32 v0, s0
; CHECK-NEXT: v_mov_b32_e32 v1, s1
; CHECK-NEXT: v_div_fixup_f32 v2, v3, v2, s4
-; CHECK-NEXT: v_xor_b32_e32 v2, 0x80000000, v2
; CHECK-NEXT: flat_store_dword v[0:1], v2
; CHECK-NEXT: s_endpgm
entry:
More information about the llvm-commits
mailing list