[llvm] [AMDGPU] Fix isKnownNeverNaN for FMIN_LEGACY/FMAX_LEGACY (PR #216338)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 16 22:50:58 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/216338

>From 1b0fc4bb8786f08cbc274bc836006f5467e3a67e Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 14 Aug 2026 17:12:39 +0200
Subject: [PATCH 1/3] [AMDGPU] Fix isKnownNeverNaN for FMIN_LEGACY/FMAX_LEGACY

These compare-selects return one of the operands bit-for-bit, so a signaling NaN operand passes through unquieted

Recurse into both operands instead of assuming never-sNaN
---
 llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp |  11 +-
 llvm/test/CodeGen/AMDGPU/fmed3.ll             | 409 ++++++++++++++++++
 2 files changed, 412 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index 962988ff97e39..b1c1e4bd7deb1 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -6284,14 +6284,9 @@ bool AMDGPUTargetLowering::isKnownNeverNaNForTargetNode(
   unsigned Opcode = Op.getOpcode();
   switch (Opcode) {
   case AMDGPUISD::FMIN_LEGACY:
-  case AMDGPUISD::FMAX_LEGACY: {
-    if (SNaN)
-      return true;
-
-    // TODO: Can check no nans on one of the operands for each one, but which
-    // one?
-    return false;
-  }
+  case AMDGPUISD::FMAX_LEGACY:
+    return DAG.isKnownNeverNaN(Op.getOperand(0), SNaN, Depth + 1) &&
+           DAG.isKnownNeverNaN(Op.getOperand(1), SNaN, Depth + 1);
   case AMDGPUISD::FMUL_LEGACY:
   case AMDGPUISD::CVT_PKRTZ_F16_F32: {
     if (SNaN)
diff --git a/llvm/test/CodeGen/AMDGPU/fmed3.ll b/llvm/test/CodeGen/AMDGPU/fmed3.ll
index c4960317f1a7d..f73d1b2eec328 100644
--- a/llvm/test/CodeGen/AMDGPU/fmed3.ll
+++ b/llvm/test/CodeGen/AMDGPU/fmed3.ll
@@ -9248,6 +9248,413 @@ define double @v_test_nnan_input_fmed3_r_i_i_f64_maximum_minimum(double %a) {
   ret double %med
 }
 
+; FMIN_LEGACY/FMAX_LEGACY are compare-selects that return one of the operands
+; bit-for-bit, so a signaling NaN operand passes through unquieted. Their
+; result must not be treated as known-never-sNaN.
+
+; The canonicalize of the min_legacy result must be kept to quiet a
+; passed-through sNaN.
+define amdgpu_kernel void @canonicalize_fmin_legacy(ptr addrspace(1) %out, float %a, float %b) #3 {
+; SI-SDAG-LABEL: canonicalize_fmin_legacy:
+; SI-SDAG:       ; %bb.0:
+; SI-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x9
+; SI-SDAG-NEXT:    s_mov_b32 s7, 0xf000
+; SI-SDAG-NEXT:    s_mov_b32 s6, -1
+; SI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; SI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
+; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
+; SI-SDAG-NEXT:    s_mov_b32 s4, s0
+; SI-SDAG-NEXT:    s_mov_b32 s5, s1
+; SI-SDAG-NEXT:    v_mul_f32_e32 v0, 1.0, v0
+; SI-SDAG-NEXT:    buffer_store_dword v0, off, s[4:7], 0
+; SI-SDAG-NEXT:    s_endpgm
+;
+; SI-GISEL-LABEL: canonicalize_fmin_legacy:
+; SI-GISEL:       ; %bb.0:
+; SI-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x9
+; SI-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; SI-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; SI-GISEL-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
+; SI-GISEL-NEXT:    s_mov_b32 s2, -1
+; SI-GISEL-NEXT:    v_mul_f32_e32 v0, 1.0, v0
+; SI-GISEL-NEXT:    s_mov_b32 s3, 0xf000
+; SI-GISEL-NEXT:    buffer_store_dword v0, off, s[0:3], 0
+; SI-GISEL-NEXT:    s_endpgm
+;
+; VI-SDAG-LABEL: canonicalize_fmin_legacy:
+; VI-SDAG:       ; %bb.0:
+; VI-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; VI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; VI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
+; VI-SDAG-NEXT:    v_mov_b32_e32 v1, s2
+; VI-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; VI-SDAG-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
+; VI-SDAG-NEXT:    v_mul_f32_e32 v2, 1.0, v0
+; VI-SDAG-NEXT:    v_mov_b32_e32 v0, s0
+; VI-SDAG-NEXT:    v_mov_b32_e32 v1, s1
+; VI-SDAG-NEXT:    flat_store_dword v[0:1], v2
+; VI-SDAG-NEXT:    s_endpgm
+;
+; VI-GISEL-LABEL: canonicalize_fmin_legacy:
+; VI-GISEL:       ; %bb.0:
+; VI-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; VI-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; VI-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; VI-GISEL-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; VI-GISEL-NEXT:    s_cmp_lg_u64 vcc, 0
+; VI-GISEL-NEXT:    s_cselect_b32 s2, s2, s3
+; VI-GISEL-NEXT:    v_mul_f32_e64 v2, 1.0, s2
+; VI-GISEL-NEXT:    v_mov_b32_e32 v0, s0
+; VI-GISEL-NEXT:    v_mov_b32_e32 v1, s1
+; VI-GISEL-NEXT:    flat_store_dword v[0:1], v2
+; VI-GISEL-NEXT:    s_endpgm
+;
+; GFX9-SDAG-LABEL: canonicalize_fmin_legacy:
+; GFX9-SDAG:       ; %bb.0:
+; GFX9-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v0, 0
+; GFX9-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v1, s3
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v2, s2
+; GFX9-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v1
+; GFX9-SDAG-NEXT:    v_cndmask_b32_e32 v1, v1, v2, vcc
+; GFX9-SDAG-NEXT:    v_max_f32_e32 v1, v1, v1
+; GFX9-SDAG-NEXT:    global_store_dword v0, v1, s[0:1]
+; GFX9-SDAG-NEXT:    s_endpgm
+;
+; GFX9-GISEL-LABEL: canonicalize_fmin_legacy:
+; GFX9-GISEL:       ; %bb.0:
+; GFX9-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; GFX9-GISEL-NEXT:    v_mov_b32_e32 v1, 0
+; GFX9-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX9-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; GFX9-GISEL-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; GFX9-GISEL-NEXT:    s_cmp_lg_u64 vcc, 0
+; GFX9-GISEL-NEXT:    s_cselect_b32 s2, s2, s3
+; GFX9-GISEL-NEXT:    v_max_f32_e64 v0, s2, s2
+; GFX9-GISEL-NEXT:    global_store_dword v1, v0, s[0:1]
+; GFX9-GISEL-NEXT:    s_endpgm
+;
+; GFX11-SDAG-LABEL: canonicalize_fmin_legacy:
+; GFX11-SDAG:       ; %bb.0:
+; GFX11-SDAG-NEXT:    s_load_b128 s[0:3], s[4:5], 0x24
+; GFX11-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX11-SDAG-NEXT:    v_dual_mov_b32 v1, 0 :: v_dual_mov_b32 v0, s2
+; GFX11-SDAG-NEXT:    v_cmp_lt_f32_e64 vcc_lo, s2, s3
+; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-SDAG-NEXT:    v_cndmask_b32_e32 v0, s3, v0, vcc_lo
+; GFX11-SDAG-NEXT:    v_max_f32_e32 v0, v0, v0
+; GFX11-SDAG-NEXT:    global_store_b32 v1, v0, s[0:1]
+; GFX11-SDAG-NEXT:    s_endpgm
+;
+; GFX11-GISEL-LABEL: canonicalize_fmin_legacy:
+; GFX11-GISEL:       ; %bb.0:
+; GFX11-GISEL-NEXT:    s_load_b128 s[0:3], s[4:5], 0x24
+; GFX11-GISEL-NEXT:    v_mov_b32_e32 v1, 0
+; GFX11-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX11-GISEL-NEXT:    v_cmp_lt_f32_e64 s4, s2, s3
+; GFX11-GISEL-NEXT:    s_cmp_lg_u32 s4, 0
+; GFX11-GISEL-NEXT:    s_cselect_b32 s2, s2, s3
+; GFX11-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
+; GFX11-GISEL-NEXT:    v_max_f32_e64 v0, s2, s2
+; GFX11-GISEL-NEXT:    global_store_b32 v1, v0, s[0:1]
+; GFX11-GISEL-NEXT:    s_endpgm
+  %cmp = fcmp olt float %a, %b
+  %min = select i1 %cmp, float %a, float %b
+  %canon = call float @llvm.canonicalize.f32(float %min)
+  store float %canon, ptr addrspace(1) %out
+  ret void
+}
+
+; The min_legacy result feeding the clamp chain may be an sNaN; med3 must
+; not be formed on it directly (med3 with an sNaN first operand under
+; IEEE=1 yields 4.0, the select chain yields 2.0).
+define amdgpu_kernel void @med3_fmin_legacy(ptr addrspace(1) %out, float %a, float %b) #3 {
+; SI-SDAG-LABEL: med3_fmin_legacy:
+; SI-SDAG:       ; %bb.0:
+; SI-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x9
+; SI-SDAG-NEXT:    s_mov_b32 s7, 0xf000
+; SI-SDAG-NEXT:    s_mov_b32 s6, -1
+; SI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; SI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
+; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
+; SI-SDAG-NEXT:    v_mul_f32_e32 v0, 1.0, v0
+; SI-SDAG-NEXT:    s_mov_b32 s4, s0
+; SI-SDAG-NEXT:    s_mov_b32 s5, s1
+; SI-SDAG-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
+; SI-SDAG-NEXT:    buffer_store_dword v0, off, s[4:7], 0
+; SI-SDAG-NEXT:    s_endpgm
+;
+; SI-GISEL-LABEL: med3_fmin_legacy:
+; SI-GISEL:       ; %bb.0:
+; SI-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x9
+; SI-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; SI-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; SI-GISEL-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
+; SI-GISEL-NEXT:    v_max_legacy_f32_e64 v0, v0, 2.0
+; SI-GISEL-NEXT:    s_mov_b32 s2, -1
+; SI-GISEL-NEXT:    v_min_legacy_f32_e64 v0, v0, 4.0
+; SI-GISEL-NEXT:    s_mov_b32 s3, 0xf000
+; SI-GISEL-NEXT:    buffer_store_dword v0, off, s[0:3], 0
+; SI-GISEL-NEXT:    s_endpgm
+;
+; VI-SDAG-LABEL: med3_fmin_legacy:
+; VI-SDAG:       ; %bb.0:
+; VI-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; VI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; VI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
+; VI-SDAG-NEXT:    v_mov_b32_e32 v1, s2
+; VI-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; VI-SDAG-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
+; VI-SDAG-NEXT:    v_mul_f32_e32 v0, 1.0, v0
+; VI-SDAG-NEXT:    v_med3_f32 v2, v0, 2.0, 4.0
+; VI-SDAG-NEXT:    v_mov_b32_e32 v0, s0
+; VI-SDAG-NEXT:    v_mov_b32_e32 v1, s1
+; VI-SDAG-NEXT:    flat_store_dword v[0:1], v2
+; VI-SDAG-NEXT:    s_endpgm
+;
+; VI-GISEL-LABEL: med3_fmin_legacy:
+; VI-GISEL:       ; %bb.0:
+; VI-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; VI-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; VI-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; VI-GISEL-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; VI-GISEL-NEXT:    s_cmp_lg_u64 vcc, 0
+; VI-GISEL-NEXT:    s_cselect_b32 s4, s2, s3
+; VI-GISEL-NEXT:    v_cmp_gt_f32_e64 s[2:3], s4, 2.0
+; VI-GISEL-NEXT:    s_cmp_lg_u64 s[2:3], 0
+; VI-GISEL-NEXT:    s_cselect_b32 s4, s4, 2.0
+; VI-GISEL-NEXT:    v_cmp_lt_f32_e64 s[2:3], s4, 4.0
+; VI-GISEL-NEXT:    s_cmp_lg_u64 s[2:3], 0
+; VI-GISEL-NEXT:    s_cselect_b32 s2, s4, 4.0
+; VI-GISEL-NEXT:    v_mov_b32_e32 v2, s2
+; VI-GISEL-NEXT:    v_mov_b32_e32 v0, s0
+; VI-GISEL-NEXT:    v_mov_b32_e32 v1, s1
+; VI-GISEL-NEXT:    flat_store_dword v[0:1], v2
+; VI-GISEL-NEXT:    s_endpgm
+;
+; GFX9-SDAG-LABEL: med3_fmin_legacy:
+; GFX9-SDAG:       ; %bb.0:
+; GFX9-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v0, 0
+; GFX9-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v1, s3
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v2, s2
+; GFX9-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v1
+; GFX9-SDAG-NEXT:    v_cndmask_b32_e32 v1, v1, v2, vcc
+; GFX9-SDAG-NEXT:    v_max_f32_e32 v1, v1, v1
+; GFX9-SDAG-NEXT:    v_med3_f32 v1, v1, 2.0, 4.0
+; GFX9-SDAG-NEXT:    global_store_dword v0, v1, s[0:1]
+; GFX9-SDAG-NEXT:    s_endpgm
+;
+; GFX9-GISEL-LABEL: med3_fmin_legacy:
+; GFX9-GISEL:       ; %bb.0:
+; GFX9-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; GFX9-GISEL-NEXT:    v_mov_b32_e32 v1, 0
+; GFX9-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX9-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; GFX9-GISEL-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; GFX9-GISEL-NEXT:    s_cmp_lg_u64 vcc, 0
+; GFX9-GISEL-NEXT:    s_cselect_b32 s4, s2, s3
+; GFX9-GISEL-NEXT:    v_cmp_gt_f32_e64 s[2:3], s4, 2.0
+; GFX9-GISEL-NEXT:    s_cmp_lg_u64 s[2:3], 0
+; GFX9-GISEL-NEXT:    s_cselect_b32 s4, s4, 2.0
+; GFX9-GISEL-NEXT:    v_cmp_lt_f32_e64 s[2:3], s4, 4.0
+; GFX9-GISEL-NEXT:    s_cmp_lg_u64 s[2:3], 0
+; GFX9-GISEL-NEXT:    s_cselect_b32 s2, s4, 4.0
+; GFX9-GISEL-NEXT:    v_mov_b32_e32 v0, s2
+; GFX9-GISEL-NEXT:    global_store_dword v1, v0, s[0:1]
+; GFX9-GISEL-NEXT:    s_endpgm
+;
+; GFX11-SDAG-LABEL: med3_fmin_legacy:
+; GFX11-SDAG:       ; %bb.0:
+; GFX11-SDAG-NEXT:    s_load_b128 s[0:3], s[4:5], 0x24
+; GFX11-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX11-SDAG-NEXT:    v_dual_mov_b32 v1, 0 :: v_dual_mov_b32 v0, s2
+; GFX11-SDAG-NEXT:    v_cmp_lt_f32_e64 vcc_lo, s2, s3
+; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-SDAG-NEXT:    v_cndmask_b32_e32 v0, s3, v0, vcc_lo
+; GFX11-SDAG-NEXT:    v_max_f32_e32 v0, v0, v0
+; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-SDAG-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
+; GFX11-SDAG-NEXT:    global_store_b32 v1, v0, s[0:1]
+; GFX11-SDAG-NEXT:    s_endpgm
+;
+; GFX11-GISEL-LABEL: med3_fmin_legacy:
+; GFX11-GISEL:       ; %bb.0:
+; GFX11-GISEL-NEXT:    s_load_b128 s[0:3], s[4:5], 0x24
+; GFX11-GISEL-NEXT:    v_mov_b32_e32 v1, 0
+; GFX11-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX11-GISEL-NEXT:    v_cmp_lt_f32_e64 s4, s2, s3
+; GFX11-GISEL-NEXT:    s_cmp_lg_u32 s4, 0
+; GFX11-GISEL-NEXT:    s_cselect_b32 s2, s2, s3
+; GFX11-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_2) | instid1(SALU_CYCLE_1)
+; GFX11-GISEL-NEXT:    v_cmp_gt_f32_e64 s3, s2, 2.0
+; GFX11-GISEL-NEXT:    s_cmp_lg_u32 s3, 0
+; GFX11-GISEL-NEXT:    s_cselect_b32 s2, s2, 2.0
+; GFX11-GISEL-NEXT:    v_cmp_lt_f32_e64 s3, s2, 4.0
+; GFX11-GISEL-NEXT:    s_cmp_lg_u32 s3, 0
+; GFX11-GISEL-NEXT:    s_cselect_b32 s2, s2, 4.0
+; GFX11-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
+; GFX11-GISEL-NEXT:    v_mov_b32_e32 v0, s2
+; GFX11-GISEL-NEXT:    global_store_b32 v1, v0, s[0:1]
+; GFX11-GISEL-NEXT:    s_endpgm
+  %cmp0 = fcmp olt float %a, %b
+  %inner = select i1 %cmp0, float %a, float %b
+  %cmp1 = fcmp ogt float %inner, 2.0
+  %max = select i1 %cmp1, float %inner, float 2.0
+  %cmp2 = fcmp olt float %max, 4.0
+  %med = select i1 %cmp2, float %max, float 4.0
+  store float %med, ptr addrspace(1) %out
+  ret void
+}
+
+; With no-NaN inputs med3 still forms directly.
+define amdgpu_kernel void @med3_fmin_legacy_nnan(ptr addrspace(1) %out, float %a, float %b) #3 {
+; SI-SDAG-LABEL: med3_fmin_legacy_nnan:
+; SI-SDAG:       ; %bb.0:
+; SI-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x9
+; SI-SDAG-NEXT:    s_mov_b32 s7, 0xf000
+; SI-SDAG-NEXT:    s_mov_b32 s6, -1
+; SI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; SI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
+; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
+; SI-SDAG-NEXT:    s_mov_b32 s4, s0
+; SI-SDAG-NEXT:    s_mov_b32 s5, s1
+; SI-SDAG-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0
+; SI-SDAG-NEXT:    buffer_store_dword v0, off, s[4:7], 0
+; SI-SDAG-NEXT:    s_endpgm
+;
+; SI-GISEL-LABEL: med3_fmin_legacy_nnan:
+; SI-GISEL:       ; %bb.0:
+; SI-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x9
+; SI-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; SI-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; SI-GISEL-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
+; SI-GISEL-NEXT:    v_max_legacy_f32_e64 v0, v0, 2.0
+; SI-GISEL-NEXT:    s_mov_b32 s2, -1
+; SI-GISEL-NEXT:    v_min_legacy_f32_e64 v0, v0, 4.0
+; SI-GISEL-NEXT:    s_mov_b32 s3, 0xf000
+; SI-GISEL-NEXT:    buffer_store_dword v0, off, s[0:3], 0
+; SI-GISEL-NEXT:    s_endpgm
+;
+; VI-SDAG-LABEL: med3_fmin_legacy_nnan:
+; VI-SDAG:       ; %bb.0:
+; VI-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; VI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; VI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
+; VI-SDAG-NEXT:    v_mov_b32_e32 v1, s2
+; VI-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; VI-SDAG-NEXT:    v_cndmask_b32_e32 v0, v0, v1, vcc
+; VI-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc, 2.0, v0
+; VI-SDAG-NEXT:    v_cndmask_b32_e32 v0, 2.0, v0, vcc
+; VI-SDAG-NEXT:    v_cmp_gt_f32_e32 vcc, 4.0, v0
+; VI-SDAG-NEXT:    v_cndmask_b32_e32 v2, 4.0, v0, vcc
+; VI-SDAG-NEXT:    v_mov_b32_e32 v0, s0
+; VI-SDAG-NEXT:    v_mov_b32_e32 v1, s1
+; VI-SDAG-NEXT:    flat_store_dword v[0:1], v2
+; VI-SDAG-NEXT:    s_endpgm
+;
+; VI-GISEL-LABEL: med3_fmin_legacy_nnan:
+; VI-GISEL:       ; %bb.0:
+; VI-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; VI-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; VI-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; VI-GISEL-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; VI-GISEL-NEXT:    s_cmp_lg_u64 vcc, 0
+; VI-GISEL-NEXT:    s_cselect_b32 s4, s2, s3
+; VI-GISEL-NEXT:    v_cmp_gt_f32_e64 s[2:3], s4, 2.0
+; VI-GISEL-NEXT:    s_cmp_lg_u64 s[2:3], 0
+; VI-GISEL-NEXT:    s_cselect_b32 s4, s4, 2.0
+; VI-GISEL-NEXT:    v_cmp_lt_f32_e64 s[2:3], s4, 4.0
+; VI-GISEL-NEXT:    s_cmp_lg_u64 s[2:3], 0
+; VI-GISEL-NEXT:    s_cselect_b32 s2, s4, 4.0
+; VI-GISEL-NEXT:    v_mov_b32_e32 v2, s2
+; VI-GISEL-NEXT:    v_mov_b32_e32 v0, s0
+; VI-GISEL-NEXT:    v_mov_b32_e32 v1, s1
+; VI-GISEL-NEXT:    flat_store_dword v[0:1], v2
+; VI-GISEL-NEXT:    s_endpgm
+;
+; GFX9-SDAG-LABEL: med3_fmin_legacy_nnan:
+; GFX9-SDAG:       ; %bb.0:
+; GFX9-SDAG-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v0, 0
+; GFX9-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v1, s3
+; GFX9-SDAG-NEXT:    v_mov_b32_e32 v2, s2
+; GFX9-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v1
+; GFX9-SDAG-NEXT:    v_cndmask_b32_e32 v1, v1, v2, vcc
+; GFX9-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc, 2.0, v1
+; GFX9-SDAG-NEXT:    v_cndmask_b32_e32 v1, 2.0, v1, vcc
+; GFX9-SDAG-NEXT:    v_cmp_gt_f32_e32 vcc, 4.0, v1
+; GFX9-SDAG-NEXT:    v_cndmask_b32_e32 v1, 4.0, v1, vcc
+; GFX9-SDAG-NEXT:    global_store_dword v0, v1, s[0:1]
+; GFX9-SDAG-NEXT:    s_endpgm
+;
+; GFX9-GISEL-LABEL: med3_fmin_legacy_nnan:
+; GFX9-GISEL:       ; %bb.0:
+; GFX9-GISEL-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; GFX9-GISEL-NEXT:    v_mov_b32_e32 v1, 0
+; GFX9-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX9-GISEL-NEXT:    v_mov_b32_e32 v0, s3
+; GFX9-GISEL-NEXT:    v_cmp_lt_f32_e32 vcc, s2, v0
+; GFX9-GISEL-NEXT:    s_cmp_lg_u64 vcc, 0
+; GFX9-GISEL-NEXT:    s_cselect_b32 s4, s2, s3
+; GFX9-GISEL-NEXT:    v_cmp_gt_f32_e64 s[2:3], s4, 2.0
+; GFX9-GISEL-NEXT:    s_cmp_lg_u64 s[2:3], 0
+; GFX9-GISEL-NEXT:    s_cselect_b32 s4, s4, 2.0
+; GFX9-GISEL-NEXT:    v_cmp_lt_f32_e64 s[2:3], s4, 4.0
+; GFX9-GISEL-NEXT:    s_cmp_lg_u64 s[2:3], 0
+; GFX9-GISEL-NEXT:    s_cselect_b32 s2, s4, 4.0
+; GFX9-GISEL-NEXT:    v_mov_b32_e32 v0, s2
+; GFX9-GISEL-NEXT:    global_store_dword v1, v0, s[0:1]
+; GFX9-GISEL-NEXT:    s_endpgm
+;
+; GFX11-SDAG-LABEL: med3_fmin_legacy_nnan:
+; GFX11-SDAG:       ; %bb.0:
+; GFX11-SDAG-NEXT:    s_load_b128 s[0:3], s[4:5], 0x24
+; GFX11-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX11-SDAG-NEXT:    v_dual_mov_b32 v1, 0 :: v_dual_mov_b32 v0, s2
+; GFX11-SDAG-NEXT:    v_cmp_lt_f32_e64 vcc_lo, s2, s3
+; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-SDAG-NEXT:    v_cndmask_b32_e32 v0, s3, v0, vcc_lo
+; GFX11-SDAG-NEXT:    v_cmp_lt_f32_e32 vcc_lo, 2.0, v0
+; GFX11-SDAG-NEXT:    v_cndmask_b32_e32 v0, 2.0, v0, vcc_lo
+; GFX11-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX11-SDAG-NEXT:    v_cmp_gt_f32_e32 vcc_lo, 4.0, v0
+; GFX11-SDAG-NEXT:    v_cndmask_b32_e32 v0, 4.0, v0, vcc_lo
+; GFX11-SDAG-NEXT:    global_store_b32 v1, v0, s[0:1]
+; GFX11-SDAG-NEXT:    s_endpgm
+;
+; GFX11-GISEL-LABEL: med3_fmin_legacy_nnan:
+; GFX11-GISEL:       ; %bb.0:
+; GFX11-GISEL-NEXT:    s_load_b128 s[0:3], s[4:5], 0x24
+; GFX11-GISEL-NEXT:    v_mov_b32_e32 v1, 0
+; GFX11-GISEL-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX11-GISEL-NEXT:    v_cmp_lt_f32_e64 s4, s2, s3
+; GFX11-GISEL-NEXT:    s_cmp_lg_u32 s4, 0
+; GFX11-GISEL-NEXT:    s_cselect_b32 s2, s2, s3
+; GFX11-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_2) | instid1(SALU_CYCLE_1)
+; GFX11-GISEL-NEXT:    v_cmp_gt_f32_e64 s3, s2, 2.0
+; GFX11-GISEL-NEXT:    s_cmp_lg_u32 s3, 0
+; GFX11-GISEL-NEXT:    s_cselect_b32 s2, s2, 2.0
+; GFX11-GISEL-NEXT:    v_cmp_lt_f32_e64 s3, s2, 4.0
+; GFX11-GISEL-NEXT:    s_cmp_lg_u32 s3, 0
+; GFX11-GISEL-NEXT:    s_cselect_b32 s2, s2, 4.0
+; GFX11-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
+; GFX11-GISEL-NEXT:    v_mov_b32_e32 v0, s2
+; GFX11-GISEL-NEXT:    global_store_b32 v1, v0, s[0:1]
+; GFX11-GISEL-NEXT:    s_endpgm
+  %cmp0 = fcmp nnan olt float %a, %b
+  %inner = select nnan i1 %cmp0, float %a, float %b
+  %cmp1 = fcmp nnan ogt float %inner, 2.0
+  %max = select nnan i1 %cmp1, float %inner, float 2.0
+  %cmp2 = fcmp nnan olt float %max, 4.0
+  %med = select nnan i1 %cmp2, float %max, float 4.0
+  store float %med, ptr addrspace(1) %out
+  ret void
+}
+
 declare i32 @llvm.amdgcn.workitem.id.x() #0
 declare float @llvm.fabs.f32(float) #0
 declare float @llvm.minnum.f32(float, float) #0
@@ -9257,6 +9664,8 @@ declare double @llvm.maxnum.f64(double, double) #0
 declare half @llvm.fabs.f16(half) #0
 declare half @llvm.minnum.f16(half, half) #0
 declare half @llvm.maxnum.f16(half, half) #0
+declare float @llvm.canonicalize.f32(float)
 
 attributes #0 = { nounwind readnone }
 attributes #2 = { nounwind "no-nans-fp-math"="true" }
+attributes #3 = { nounwind "denormal-fp-math-f32"="ieee" }

>From b0660bdbb59c37cc831b1b615992bacad027997f Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 14 Aug 2026 17:32:58 +0200
Subject: [PATCH 2/3] simplify

---
 llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp | 2 --
 llvm/test/CodeGen/AMDGPU/fmed3.ll             | 4 +---
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index b1c1e4bd7deb1..d1b17b0881df8 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -6285,8 +6285,6 @@ bool AMDGPUTargetLowering::isKnownNeverNaNForTargetNode(
   switch (Opcode) {
   case AMDGPUISD::FMIN_LEGACY:
   case AMDGPUISD::FMAX_LEGACY:
-    return DAG.isKnownNeverNaN(Op.getOperand(0), SNaN, Depth + 1) &&
-           DAG.isKnownNeverNaN(Op.getOperand(1), SNaN, Depth + 1);
   case AMDGPUISD::FMUL_LEGACY:
   case AMDGPUISD::CVT_PKRTZ_F16_F32: {
     if (SNaN)
diff --git a/llvm/test/CodeGen/AMDGPU/fmed3.ll b/llvm/test/CodeGen/AMDGPU/fmed3.ll
index f73d1b2eec328..124b1cf627a41 100644
--- a/llvm/test/CodeGen/AMDGPU/fmed3.ll
+++ b/llvm/test/CodeGen/AMDGPU/fmed3.ll
@@ -9262,10 +9262,9 @@ define amdgpu_kernel void @canonicalize_fmin_legacy(ptr addrspace(1) %out, float
 ; SI-SDAG-NEXT:    s_mov_b32 s6, -1
 ; SI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
 ; SI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
-; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
 ; SI-SDAG-NEXT:    s_mov_b32 s4, s0
 ; SI-SDAG-NEXT:    s_mov_b32 s5, s1
-; SI-SDAG-NEXT:    v_mul_f32_e32 v0, 1.0, v0
+; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
 ; SI-SDAG-NEXT:    buffer_store_dword v0, off, s[4:7], 0
 ; SI-SDAG-NEXT:    s_endpgm
 ;
@@ -9378,7 +9377,6 @@ define amdgpu_kernel void @med3_fmin_legacy(ptr addrspace(1) %out, float %a, flo
 ; SI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
 ; SI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
 ; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
-; SI-SDAG-NEXT:    v_mul_f32_e32 v0, 1.0, v0
 ; SI-SDAG-NEXT:    s_mov_b32 s4, s0
 ; SI-SDAG-NEXT:    s_mov_b32 s5, s1
 ; SI-SDAG-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0

>From 5a307a0453b59e9ce8c6f41f4bda02a0315da3be Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 17 Aug 2026 07:50:47 +0200
Subject: [PATCH 3/3] Revert simplification and simplify the comment

---
 llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp |  2 ++
 llvm/test/CodeGen/AMDGPU/fmed3.ll             | 16 ++++++----------
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index d1b17b0881df8..b1c1e4bd7deb1 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -6285,6 +6285,8 @@ bool AMDGPUTargetLowering::isKnownNeverNaNForTargetNode(
   switch (Opcode) {
   case AMDGPUISD::FMIN_LEGACY:
   case AMDGPUISD::FMAX_LEGACY:
+    return DAG.isKnownNeverNaN(Op.getOperand(0), SNaN, Depth + 1) &&
+           DAG.isKnownNeverNaN(Op.getOperand(1), SNaN, Depth + 1);
   case AMDGPUISD::FMUL_LEGACY:
   case AMDGPUISD::CVT_PKRTZ_F16_F32: {
     if (SNaN)
diff --git a/llvm/test/CodeGen/AMDGPU/fmed3.ll b/llvm/test/CodeGen/AMDGPU/fmed3.ll
index 124b1cf627a41..7c860879cb39c 100644
--- a/llvm/test/CodeGen/AMDGPU/fmed3.ll
+++ b/llvm/test/CodeGen/AMDGPU/fmed3.ll
@@ -9248,12 +9248,8 @@ define double @v_test_nnan_input_fmed3_r_i_i_f64_maximum_minimum(double %a) {
   ret double %med
 }
 
-; FMIN_LEGACY/FMAX_LEGACY are compare-selects that return one of the operands
-; bit-for-bit, so a signaling NaN operand passes through unquieted. Their
-; result must not be treated as known-never-sNaN.
-
-; The canonicalize of the min_legacy result must be kept to quiet a
-; passed-through sNaN.
+; FMIN_LEGACY passes an sNaN operand through bit-for-bit, so the canonicalize
+; must not be folded away.
 define amdgpu_kernel void @canonicalize_fmin_legacy(ptr addrspace(1) %out, float %a, float %b) #3 {
 ; SI-SDAG-LABEL: canonicalize_fmin_legacy:
 ; SI-SDAG:       ; %bb.0:
@@ -9262,9 +9258,10 @@ define amdgpu_kernel void @canonicalize_fmin_legacy(ptr addrspace(1) %out, float
 ; SI-SDAG-NEXT:    s_mov_b32 s6, -1
 ; SI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
 ; SI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
+; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
 ; SI-SDAG-NEXT:    s_mov_b32 s4, s0
 ; SI-SDAG-NEXT:    s_mov_b32 s5, s1
-; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
+; SI-SDAG-NEXT:    v_mul_f32_e32 v0, 1.0, v0
 ; SI-SDAG-NEXT:    buffer_store_dword v0, off, s[4:7], 0
 ; SI-SDAG-NEXT:    s_endpgm
 ;
@@ -9365,9 +9362,7 @@ define amdgpu_kernel void @canonicalize_fmin_legacy(ptr addrspace(1) %out, float
   ret void
 }
 
-; The min_legacy result feeding the clamp chain may be an sNaN; med3 must
-; not be formed on it directly (med3 with an sNaN first operand under
-; IEEE=1 yields 4.0, the select chain yields 2.0).
+; A possibly-sNaN FMIN_LEGACY result must not feed med3 directly.
 define amdgpu_kernel void @med3_fmin_legacy(ptr addrspace(1) %out, float %a, float %b) #3 {
 ; SI-SDAG-LABEL: med3_fmin_legacy:
 ; SI-SDAG:       ; %bb.0:
@@ -9377,6 +9372,7 @@ define amdgpu_kernel void @med3_fmin_legacy(ptr addrspace(1) %out, float %a, flo
 ; SI-SDAG-NEXT:    s_waitcnt lgkmcnt(0)
 ; SI-SDAG-NEXT:    v_mov_b32_e32 v0, s3
 ; SI-SDAG-NEXT:    v_min_legacy_f32_e32 v0, s2, v0
+; SI-SDAG-NEXT:    v_mul_f32_e32 v0, 1.0, v0
 ; SI-SDAG-NEXT:    s_mov_b32 s4, s0
 ; SI-SDAG-NEXT:    s_mov_b32 s5, s1
 ; SI-SDAG-NEXT:    v_med3_f32 v0, v0, 2.0, 4.0



More information about the llvm-commits mailing list