[llvm] [AMDGPU] Fix fmed3 InstCombine fold miscompiling NaN inputs (PR #216970)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 02:46:48 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Arseniy Obolenskiy (aobolensk)

<details>
<summary>Changes</summary>

Gate the infinity-to-min/max fold and the src1->src2 constant canonicalization on the operands being known never (s)nan, since a nan input takes precedence over infinity in fmed3 semantics

---

Patch is 24.50 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/216970.diff


3 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/AMDGPUInstCombineIntrinsic.cpp (+29-4) 
- (modified) llvm/test/Transforms/InstCombine/AMDGPU/fmed3-fpext-fold.ll (+10-10) 
- (modified) llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll (+182-32) 


``````````diff
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUInstCombineIntrinsic.cpp b/llvm/lib/Target/AMDGPU/AMDGPUInstCombineIntrinsic.cpp
index c34a143e4b0b4..1a37e9f3ca809 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUInstCombineIntrinsic.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUInstCombineIntrinsic.cpp
@@ -1583,8 +1583,22 @@ GCNTTIImpl::instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const {
     const APFloat *ConstSrc1 = nullptr;
     const APFloat *ConstSrc2 = nullptr;
 
+    SimplifyQuery SQ = IC.getSimplifyQuery().getWithInstruction(&II);
+    auto IsNeverFPClass = [&](Value *Op, FPClassTest Mask) {
+      return computeKnownFPClass(Op, II.getFastMathFlags(), Mask, SQ)
+          .isKnownNever(Mask);
+    };
+    auto IsNeverNaN = [&](Value *Op) { return IsNeverFPClass(Op, fcNan); };
+
+    // Nan rows take precedence over infinity rows: only fold an infinity
+    // constant to min/max when neither other operand can be nan.
+    auto IsFoldableConst = [&](const APFloat *C, Value *OtherA, Value *OtherB) {
+      return C->isNaN() ||
+             (C->isInfinity() && IsNeverNaN(OtherA) && IsNeverNaN(OtherB));
+    };
+
     if ((match(Src0, m_APFloat(ConstSrc0)) &&
-         (ConstSrc0->isNaN() || ConstSrc0->isInfinity())) ||
+         IsFoldableConst(ConstSrc0, Src1, Src2)) ||
         isa<UndefValue>(Src0)) {
       const bool IsPosInfinity = ConstSrc0 && ConstSrc0->isPosInfinity();
       switch (fpenvIEEEMode(II)) {
@@ -1604,7 +1618,7 @@ GCNTTIImpl::instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const {
         break;
       }
     } else if ((match(Src1, m_APFloat(ConstSrc1)) &&
-                (ConstSrc1->isNaN() || ConstSrc1->isInfinity())) ||
+                IsFoldableConst(ConstSrc1, Src0, Src2)) ||
                isa<UndefValue>(Src1)) {
       const bool IsPosInfinity = ConstSrc1 && ConstSrc1->isPosInfinity();
       switch (fpenvIEEEMode(II)) {
@@ -1624,7 +1638,7 @@ GCNTTIImpl::instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const {
         break;
       }
     } else if ((match(Src2, m_APFloat(ConstSrc2)) &&
-                (ConstSrc2->isNaN() || ConstSrc2->isInfinity())) ||
+                IsFoldableConst(ConstSrc2, Src0, Src1)) ||
                isa<UndefValue>(Src2)) {
       switch (fpenvIEEEMode(II)) {
       case KnownIEEEMode::On:
@@ -1664,7 +1678,18 @@ GCNTTIImpl::instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const {
       Swap = true;
     }
 
-    if (isa<Constant>(Src1) && !isa<Constant>(Src2)) {
+    // Under ieee=1, an snan in src0/src1 selects src2 but an snan in src2
+    // yields qnan, so src2 can't swap in freely; ieee=0 is symmetric.
+    auto CanMoveConstantToSrc2 = [&]() {
+      if (fpenvIEEEMode(II) == KnownIEEEMode::Off)
+        return true;
+      return llvm::all_of(std::array{Src0, Src1, Src2}, [&](Value *Op) {
+        return IsNeverFPClass(Op, fcSNan);
+      });
+    };
+
+    if (isa<Constant>(Src1) && !isa<Constant>(Src2) &&
+        CanMoveConstantToSrc2()) {
       std::swap(Src1, Src2);
       Swap = true;
     }
diff --git a/llvm/test/Transforms/InstCombine/AMDGPU/fmed3-fpext-fold.ll b/llvm/test/Transforms/InstCombine/AMDGPU/fmed3-fpext-fold.ll
index a288b967666dd..f7c6c866c5949 100644
--- a/llvm/test/Transforms/InstCombine/AMDGPU/fmed3-fpext-fold.ll
+++ b/llvm/test/Transforms/InstCombine/AMDGPU/fmed3-fpext-fold.ll
@@ -62,12 +62,12 @@ define float @fmed3_f32_fpext_f16_k0(half %arg1, half %arg2) #1 {
 ; NO-FMED3F16-SAME: (half [[ARG1:%.*]], half [[ARG2:%.*]]) #[[ATTR1]] {
 ; NO-FMED3F16-NEXT:    [[ARG1_EXT:%.*]] = fpext half [[ARG1]] to float
 ; NO-FMED3F16-NEXT:    [[ARG2_EXT:%.*]] = fpext half [[ARG2]] to float
-; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG1_EXT]], float [[ARG2_EXT]], float 2.000000e+00)
+; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG1_EXT]], float 2.000000e+00, float [[ARG2_EXT]])
 ; NO-FMED3F16-NEXT:    ret float [[MED3]]
 ;
 ; GFX9-LABEL: define float @fmed3_f32_fpext_f16_k0
 ; GFX9-SAME: (half [[ARG1:%.*]], half [[ARG2:%.*]]) #[[ATTR1]] {
-; GFX9-NEXT:    [[MED31:%.*]] = call half @llvm.amdgcn.fmed3.f16(half [[ARG1]], half [[ARG2]], half 2.000000e+00)
+; GFX9-NEXT:    [[MED31:%.*]] = call half @llvm.amdgcn.fmed3.f16(half [[ARG1]], half 2.000000e+00, half [[ARG2]])
 ; GFX9-NEXT:    [[MED3:%.*]] = fpext half [[MED31]] to float
 ; GFX9-NEXT:    ret float [[MED3]]
 ;
@@ -82,12 +82,12 @@ define float @fmed3_f32_fpext_f16_k1(half %arg0, half %arg2) #1 {
 ; NO-FMED3F16-SAME: (half [[ARG0:%.*]], half [[ARG2:%.*]]) #[[ATTR1]] {
 ; NO-FMED3F16-NEXT:    [[ARG0_EXT:%.*]] = fpext half [[ARG0]] to float
 ; NO-FMED3F16-NEXT:    [[ARG2_EXT:%.*]] = fpext half [[ARG2]] to float
-; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG0_EXT]], float [[ARG2_EXT]], float 2.000000e+00)
+; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG0_EXT]], float 2.000000e+00, float [[ARG2_EXT]])
 ; NO-FMED3F16-NEXT:    ret float [[MED3]]
 ;
 ; GFX9-LABEL: define float @fmed3_f32_fpext_f16_k1
 ; GFX9-SAME: (half [[ARG0:%.*]], half [[ARG2:%.*]]) #[[ATTR1]] {
-; GFX9-NEXT:    [[MED31:%.*]] = call half @llvm.amdgcn.fmed3.f16(half [[ARG0]], half [[ARG2]], half 2.000000e+00)
+; GFX9-NEXT:    [[MED31:%.*]] = call half @llvm.amdgcn.fmed3.f16(half [[ARG0]], half 2.000000e+00, half [[ARG2]])
 ; GFX9-NEXT:    [[MED3:%.*]] = fpext half [[MED31]] to float
 ; GFX9-NEXT:    ret float [[MED3]]
 ;
@@ -121,12 +121,12 @@ define float @fmed3_f32_fpext_f16_k0_k1(half %arg2) #1 {
 ; NO-FMED3F16-LABEL: define float @fmed3_f32_fpext_f16_k0_k1
 ; NO-FMED3F16-SAME: (half [[ARG2:%.*]]) #[[ATTR1]] {
 ; NO-FMED3F16-NEXT:    [[ARG2_EXT:%.*]] = fpext half [[ARG2]] to float
-; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG2_EXT]], float 0.000000e+00, float 1.600000e+01)
+; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float 0.000000e+00, float 1.600000e+01, float [[ARG2_EXT]])
 ; NO-FMED3F16-NEXT:    ret float [[MED3]]
 ;
 ; GFX9-LABEL: define float @fmed3_f32_fpext_f16_k0_k1
 ; GFX9-SAME: (half [[ARG2:%.*]]) #[[ATTR1]] {
-; GFX9-NEXT:    [[MED31:%.*]] = call half @llvm.amdgcn.fmed3.f16(half [[ARG2]], half 0.000000e+00, half 1.600000e+01)
+; GFX9-NEXT:    [[MED31:%.*]] = call half @llvm.amdgcn.fmed3.f16(half 0.000000e+00, half 1.600000e+01, half [[ARG2]])
 ; GFX9-NEXT:    [[MED3:%.*]] = fpext half [[MED31]] to float
 ; GFX9-NEXT:    ret float [[MED3]]
 ;
@@ -547,14 +547,14 @@ define float @fmed3_f32_fpext_f16_unrepresentable_k0(half %arg1, half %arg2) #1
 ; NO-FMED3F16-SAME: (half [[ARG1:%.*]], half [[ARG2:%.*]]) #[[ATTR1]] {
 ; NO-FMED3F16-NEXT:    [[ARG1_EXT:%.*]] = fpext half [[ARG1]] to float
 ; NO-FMED3F16-NEXT:    [[ARG2_EXT:%.*]] = fpext half [[ARG2]] to float
-; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG1_EXT]], float [[ARG2_EXT]], float f0x4F800000)
+; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG1_EXT]], float f0x4F800000, float [[ARG2_EXT]])
 ; NO-FMED3F16-NEXT:    ret float [[MED3]]
 ;
 ; GFX9-LABEL: define float @fmed3_f32_fpext_f16_unrepresentable_k0
 ; GFX9-SAME: (half [[ARG1:%.*]], half [[ARG2:%.*]]) #[[ATTR1]] {
 ; GFX9-NEXT:    [[ARG1_EXT:%.*]] = fpext half [[ARG1]] to float
 ; GFX9-NEXT:    [[ARG2_EXT:%.*]] = fpext half [[ARG2]] to float
-; GFX9-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG1_EXT]], float [[ARG2_EXT]], float f0x4F800000)
+; GFX9-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG1_EXT]], float f0x4F800000, float [[ARG2_EXT]])
 ; GFX9-NEXT:    ret float [[MED3]]
 ;
   %arg1.ext = fpext half %arg1 to float
@@ -568,14 +568,14 @@ define float @fmed3_f32_fpext_f16_unrepresentable_k1(half %arg0, half %arg2) #1
 ; NO-FMED3F16-SAME: (half [[ARG0:%.*]], half [[ARG2:%.*]]) #[[ATTR1]] {
 ; NO-FMED3F16-NEXT:    [[ARG0_EXT:%.*]] = fpext half [[ARG0]] to float
 ; NO-FMED3F16-NEXT:    [[ARG2_EXT:%.*]] = fpext half [[ARG2]] to float
-; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG0_EXT]], float [[ARG2_EXT]], float f0x4F800000)
+; NO-FMED3F16-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG0_EXT]], float f0x4F800000, float [[ARG2_EXT]])
 ; NO-FMED3F16-NEXT:    ret float [[MED3]]
 ;
 ; GFX9-LABEL: define float @fmed3_f32_fpext_f16_unrepresentable_k1
 ; GFX9-SAME: (half [[ARG0:%.*]], half [[ARG2:%.*]]) #[[ATTR1]] {
 ; GFX9-NEXT:    [[ARG0_EXT:%.*]] = fpext half [[ARG0]] to float
 ; GFX9-NEXT:    [[ARG2_EXT:%.*]] = fpext half [[ARG2]] to float
-; GFX9-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG0_EXT]], float [[ARG2_EXT]], float f0x4F800000)
+; GFX9-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[ARG0_EXT]], float f0x4F800000, float [[ARG2_EXT]])
 ; GFX9-NEXT:    ret float [[MED3]]
 ;
   %arg0.ext = fpext half %arg0 to float
diff --git a/llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll b/llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll
index ac350f8abeea3..5cf322e9b76f5 100644
--- a/llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll
+++ b/llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll
@@ -45,10 +45,15 @@ define float @fmed3_canonicalize_c0_x_c1_f32(float %x) #1 {
 }
 
 define float @fmed3_canonicalize_c0_c1_x_f32(float %x) #1 {
-; CHECK-LABEL: define float @fmed3_canonicalize_c0_c1_x_f32(
-; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float 0.000000e+00, float 1.000000e+00)
-; CHECK-NEXT:    ret float [[MED3]]
+; IEEE1-LABEL: define float @fmed3_canonicalize_c0_c1_x_f32(
+; IEEE1-SAME: float [[X:%.*]]) #[[ATTR1]] {
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float 0.000000e+00, float 1.000000e+00, float [[X]])
+; IEEE1-NEXT:    ret float [[MED3]]
+;
+; IEEE0-LABEL: define float @fmed3_canonicalize_c0_c1_x_f32(
+; IEEE0-SAME: float [[X:%.*]]) #[[ATTR1]] {
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float 0.000000e+00, float 1.000000e+00)
+; IEEE0-NEXT:    ret float [[MED3]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float 0.0, float 1.0, float %x)
   ret float %med3
@@ -65,22 +70,58 @@ define float @fmed3_canonicalize_x_y_c_f32(float %x, float %y) #1 {
 }
 
 define float @fmed3_canonicalize_x_c_y_f32(float %x, float %y) #1 {
-; CHECK-LABEL: define float @fmed3_canonicalize_x_c_y_f32(
-; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float 1.000000e+00)
-; CHECK-NEXT:    ret float [[MED3]]
+; IEEE1-LABEL: define float @fmed3_canonicalize_x_c_y_f32(
+; IEEE1-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float 1.000000e+00, float [[Y]])
+; IEEE1-NEXT:    ret float [[MED3]]
+;
+; IEEE0-LABEL: define float @fmed3_canonicalize_x_c_y_f32(
+; IEEE0-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float 1.000000e+00)
+; IEEE0-NEXT:    ret float [[MED3]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float %x, float 1.0, float %y)
   ret float %med3
 }
 
 define float @fmed3_canonicalize_c_x_y_f32(float %x, float %y) #1 {
-; CHECK-LABEL: define float @fmed3_canonicalize_c_x_y_f32(
+; IEEE1-LABEL: define float @fmed3_canonicalize_c_x_y_f32(
+; IEEE1-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float 1.000000e+00, float [[Y]])
+; IEEE1-NEXT:    ret float [[MED3]]
+;
+; IEEE0-LABEL: define float @fmed3_canonicalize_c_x_y_f32(
+; IEEE0-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float 1.000000e+00)
+; IEEE0-NEXT:    ret float [[MED3]]
+;
+  %med3 = call float @llvm.amdgcn.fmed3.f32(float 1.0, float %x, float %y)
+  ret float %med3
+}
+
+; With ieee=1, fmed3(%x, 1.0, snan) is qnan but fmed3(%x, snan, 1.0) is 1.0; nnan makes the swap safe.
+define float @fmed3_canonicalize_x_c_y_nnan_f32(float %x, float %y) #1 {
+; CHECK-LABEL: define float @fmed3_canonicalize_x_c_y_nnan_f32(
 ; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float 1.000000e+00)
+; CHECK-NEXT:    [[MED3:%.*]] = call nnan float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float 1.000000e+00)
 ; CHECK-NEXT:    ret float [[MED3]]
 ;
-  %med3 = call float @llvm.amdgcn.fmed3.f32(float 1.0, float %x, float %y)
+  %med3 = call nnan float @llvm.amdgcn.fmed3.f32(float %x, float 1.0, float %y)
+  ret float %med3
+}
+
+; sitofp results can never be nan.
+define float @fmed3_canonicalize_x_c_y_not_snan_f32(i32 %x, i32 %y) #1 {
+; CHECK-LABEL: define float @fmed3_canonicalize_x_c_y_not_snan_f32(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[X_CVT:%.*]] = sitofp i32 [[X]] to float
+; CHECK-NEXT:    [[Y_CVT:%.*]] = sitofp i32 [[Y]] to float
+; CHECK-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X_CVT]], float [[Y_CVT]], float 1.000000e+00)
+; CHECK-NEXT:    ret float [[MED3]]
+;
+  %x.cvt = sitofp i32 %x to float
+  %y.cvt = sitofp i32 %y to float
+  %med3 = call float @llvm.amdgcn.fmed3.f32(float %x.cvt, float 1.0, float %y.cvt)
   ret float %med3
 }
 
@@ -559,12 +600,12 @@ define float @fmed3_neg2_3_snan1_f32(float %x, float %y) #1 {
 define float @fmed3_inf_x_y_f32(float %x, float %y) #1 {
 ; IEEE1-LABEL: define float @fmed3_inf_x_y_f32(
 ; IEEE1-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.maxnum.f32(float [[X]], float [[Y]])
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float +inf, float [[Y]])
 ; IEEE1-NEXT:    ret float [[MED3]]
 ;
 ; IEEE0-LABEL: define float @fmed3_inf_x_y_f32(
 ; IEEE0-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float [[Y]])
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float +inf)
 ; IEEE0-NEXT:    ret float [[MED3]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float 0x7FF0000000000000, float %x, float %y)
@@ -574,12 +615,12 @@ define float @fmed3_inf_x_y_f32(float %x, float %y) #1 {
 define float @fmed3_x_inf_y_f32(float %x, float %y) #1 {
 ; IEEE1-LABEL: define float @fmed3_x_inf_y_f32(
 ; IEEE1-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.maxnum.f32(float [[X]], float [[Y]])
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float +inf, float [[Y]])
 ; IEEE1-NEXT:    ret float [[MED3]]
 ;
 ; IEEE0-LABEL: define float @fmed3_x_inf_y_f32(
 ; IEEE0-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float [[Y]])
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float +inf)
 ; IEEE0-NEXT:    ret float [[MED3]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float %x, float 0x7FF0000000000000, float %y)
@@ -587,15 +628,10 @@ define float @fmed3_x_inf_y_f32(float %x, float %y) #1 {
 }
 
 define float @fmed3_x_y_inf_f32(float %x, float %y) #1 {
-; IEEE1-LABEL: define float @fmed3_x_y_inf_f32(
-; IEEE1-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.maxnum.f32(float [[X]], float [[Y]])
-; IEEE1-NEXT:    ret float [[MED3]]
-;
-; IEEE0-LABEL: define float @fmed3_x_y_inf_f32(
-; IEEE0-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float [[Y]])
-; IEEE0-NEXT:    ret float [[MED3]]
+; CHECK-LABEL: define float @fmed3_x_y_inf_f32(
+; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float +inf)
+; CHECK-NEXT:    ret float [[MED3]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float %x, float %y, float 0x7FF0000000000000)
   ret float %med3
@@ -604,12 +640,12 @@ define float @fmed3_x_y_inf_f32(float %x, float %y) #1 {
 define float @fmed3_ninf_x_y_f32(float %x, float %y) #1 {
 ; IEEE1-LABEL: define float @fmed3_ninf_x_y_f32(
 ; IEEE1-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.minnum.f32(float [[X]], float [[Y]])
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float -inf, float [[Y]])
 ; IEEE1-NEXT:    ret float [[MED3]]
 ;
 ; IEEE0-LABEL: define float @fmed3_ninf_x_y_f32(
 ; IEEE0-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float [[Y]])
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float -inf)
 ; IEEE0-NEXT:    ret float [[MED3]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float 0xFFF0000000000000, float %x, float %y)
@@ -619,12 +655,12 @@ define float @fmed3_ninf_x_y_f32(float %x, float %y) #1 {
 define float @fmed3_x_ninf_y_f32(float %x, float %y) #1 {
 ; IEEE1-LABEL: define float @fmed3_x_ninf_y_f32(
 ; IEEE1-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.minnum.f32(float [[X]], float [[Y]])
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float -inf, float [[Y]])
 ; IEEE1-NEXT:    ret float [[MED3]]
 ;
 ; IEEE0-LABEL: define float @fmed3_x_ninf_y_f32(
 ; IEEE0-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float [[Y]])
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float -inf)
 ; IEEE0-NEXT:    ret float [[MED3]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float %x, float 0xFFF0000000000000, float %y)
@@ -632,15 +668,129 @@ define float @fmed3_x_ninf_y_f32(float %x, float %y) #1 {
 }
 
 define float @fmed3_x_y_ninf_f32(float %x, float %y) #1 {
-; IEEE1-LABEL: define float @fmed3_x_y_ninf_f32(
-; IEEE1-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
+; CHECK-LABEL: define float @fmed3_x_y_ninf_f32(
+; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[MED3:%.*]] = call float @llvm.amdgcn.fmed3.f32(float [[X]], float [[Y]], float -inf)
+; CHECK-NEXT:    ret float [[MED3]]
+;
+  %med3 = call float @llvm.amdgcn.fmed3.f32(float %x, float %y, float 0xFFF0000000000000)
+  ret float %med3
+}
+
+; Infinity folds above only fire when the other operands can't be nan:
+; fmed3(qnan, x, -inf) is -inf, not minnum(qnan, x) = x.
+
+define float @fmed3_inf_x_y_nofpclass_nan_f32(float nofpclass(nan) %x, float nofpclass(nan) %y) #1 {
+; IEEE1-LABEL: define float @fmed3_inf_x_y_nofpclass_nan_f32(
+; IEEE1-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR1]] {
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.maxnum.f32(float [[X]], float [[Y]])
+; IEEE1-NEXT:    ret float [[MED3]]
+;
+; IEEE0-LABEL: define float @fmed3_inf_x_y_nofpclass_nan_f32(
+; IEEE0-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR1]] {
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float [[Y]])
+; IEEE0-NEXT:    ret float [[MED3]]
+;
+  %med3 = call float @llvm.amdgcn.fmed3.f32(float 0x7FF0000000000000, float %x, float %y)
+  ret float %med3
+}
+
+define float @fmed3_x_inf_y_nofpclass_nan_f32(float nofpclass(nan) %x, float nofpclass(nan) %y) #1 {
+; IEEE1-LABEL: define float @fmed3_x_inf_y_nofpclass_nan_f32(
+; IEEE1-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR1]] {
+; IEEE1-NEXT:    [[MED3:%.*]] = call float @llvm.maxnum.f32(float [[X]], float [[Y]])
+; IEEE1-NEXT:    ret float [[MED3]]
+;
+; IEEE0-LABEL: define float @fmed3_x_inf_y_nofpclass_nan_f32(
+; IEEE0-SAME: float nofpclass(nan) [[X:%.*]], float nofpclass(nan) [[Y:%.*]]) #[[ATTR1]] {
+; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float [[Y]])
+; IEEE0-NEXT:    ret float [[MED3]]
+;
+  %med3 = call fl...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/216970


More information about the llvm-commits mailing list