[llvm] [InstSimplify] Optimize maximumnum and minimumnum (PR #139581)

Lewis Crawford via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 7 03:33:25 PDT 2025


https://github.com/LewisCrawford updated https://github.com/llvm/llvm-project/pull/139581

>From b0a057b944b3c064c71fc6f4d18721ffcd80cfe7 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Tue, 13 May 2025 09:23:03 +0000
Subject: [PATCH 01/19] [InstSimplify] Optimize maximumnum and minimumnum

Add support for the new maximumnum and minimumnum intrinsics in various optimizations in InstSimplify.

Also, change the behavior of optimizing maxnum(sNaN, x) to simplify to qNaN instead of x to better match the LLVM IR spec, and add more tests for sNaN behavior for all 3 max/min intrinsic types.
---
 llvm/include/llvm/IR/PatternMatch.h           |  15 +
 llvm/lib/Analysis/InstructionSimplify.cpp     |  49 +-
 llvm/lib/Analysis/ValueTracking.cpp           |   4 +
 .../Transforms/InstSimplify/fminmax-folds.ll  | 931 +++++++++++++++++-
 4 files changed, 978 insertions(+), 21 deletions(-)

diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 1f86cdfd94e17..6596fa431d2cb 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -707,10 +707,25 @@ m_SpecificInt_ICMP(ICmpInst::Predicate Predicate, const APInt &Threshold) {
 struct is_nan {
   bool isValue(const APFloat &C) const { return C.isNaN(); }
 };
+
+struct is_snan {
+  bool isValue(const APFloat &C) const { return C.isSignaling(); }
+};
+
+struct is_qnan {
+  bool isValue(const APFloat &C) const { return C.isNaN() && !C.isSignaling(); }
+};
+
 /// Match an arbitrary NaN constant. This includes quiet and signalling nans.
 /// For vectors, this includes constants with undefined elements.
 inline cstfp_pred_ty<is_nan> m_NaN() { return cstfp_pred_ty<is_nan>(); }
 
+/// Match quiet NaN constants, including vectors with undefined elements.
+inline cstfp_pred_ty<is_qnan> m_qNaN() { return cstfp_pred_ty<is_qnan>(); }
+
+/// Match signalling NaN constants, including vectors with undefined elements.
+inline cstfp_pred_ty<is_snan> m_sNaN() { return cstfp_pred_ty<is_snan>(); }
+
 struct is_nonnan {
   bool isValue(const APFloat &C) const { return !C.isNaN(); }
 };
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index cb1dae92faf92..a10306520c066 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6416,7 +6416,8 @@ static Value *foldMinMaxSharedOp(Intrinsic::ID IID, Value *Op0, Value *Op1) {
 static Value *foldMinimumMaximumSharedOp(Intrinsic::ID IID, Value *Op0,
                                          Value *Op1) {
   assert((IID == Intrinsic::maxnum || IID == Intrinsic::minnum ||
-          IID == Intrinsic::maximum || IID == Intrinsic::minimum) &&
+          IID == Intrinsic::maximum || IID == Intrinsic::minimum ||
+          IID == Intrinsic::maximumnum || IID == Intrinsic::minimumnum) &&
          "Unsupported intrinsic");
 
   auto *M0 = dyn_cast<IntrinsicInst>(Op0);
@@ -6712,7 +6713,16 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
   case Intrinsic::maxnum:
   case Intrinsic::minnum:
   case Intrinsic::maximum:
-  case Intrinsic::minimum: {
+  case Intrinsic::minimum:
+  case Intrinsic::maximumnum:
+  case Intrinsic::minimumnum: {
+    // In several cases here, we deviate from exact IEEE 754 semantics
+    // to enable optimizations (as allowed by the LLVM IR spec).
+    //
+    // For instance, we often return one of the arguments unmodified instead of
+    // inserting an llvm.canonicalize to transform input sNaNs into qNaNs or to
+    // respect any FTZ semantics, and sometimes assume all NaN inputs are qNaNs.
+
     // If the arguments are the same, this is a no-op.
     if (Op0 == Op1)
       return Op0;
@@ -6726,32 +6736,43 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
       return Op0;
 
     bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
-    bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum;
-
-    // minnum(X, nan) -> X
-    // maxnum(X, nan) -> X
-    // minimum(X, nan) -> nan
-    // maximum(X, nan) -> nan
-    if (match(Op1, m_NaN()))
-      return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
+    bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
+    bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
+                 IID == Intrinsic::minimumnum;
+
+    // minnum(x, qnan) -> x
+    // maxnum(x, qnan) -> x
+    // minnum(x, snan) -> qnan
+    // maxnum(x, snan) -> qnan
+    // minimum(X, nan) -> qnan
+    // maximum(X, nan) -> qnan
+    if (match(Op1, m_NaN())) {
+      if (PropagateNaN || (PropagateSNaN && match(Op1, m_sNaN())))
+        return propagateNaN(cast<Constant>(Op1));
+      return Op0;
+    }
 
     // In the following folds, inf can be replaced with the largest finite
     // float, if the ninf flag is set.
     const APFloat *C;
     if (match(Op1, m_APFloat(C)) &&
         (C->isInfinity() || (Call && Call->hasNoInfs() && C->isLargest()))) {
-      // minnum(X, -inf) -> -inf
-      // maxnum(X, +inf) -> +inf
+      // minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
+      // maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
       // minimum(X, -inf) -> -inf if nnan
       // maximum(X, +inf) -> +inf if nnan
+      // minimumnum(X, -inf) -> -inf
+      // maximumnum(X, +inf) -> +inf
       if (C->isNegative() == IsMin &&
           (!PropagateNaN || (Call && Call->hasNoNaNs())))
         return ConstantFP::get(ReturnType, *C);
 
       // minnum(X, +inf) -> X if nnan
       // maxnum(X, -inf) -> X if nnan
-      // minimum(X, +inf) -> X
-      // maximum(X, -inf) -> X
+      // minimum(X, +inf) -> X (ignoring quieting of sNaNs)
+      // maximum(X, -inf) -> X (ignoring quieting of sNaNs)
+      // maximumnum(X, -inf) -> X if nnan
+      // minimumnum(X, +inf) -> X if nnan
       if (C->isNegative() != IsMin &&
           (PropagateNaN || (Call && Call->hasNoNaNs())))
         return Op0;
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index e576f4899810a..0f8bf94a18f1b 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9015,6 +9015,10 @@ Intrinsic::ID llvm::getInverseMinMaxIntrinsic(Intrinsic::ID MinMaxID) {
   case Intrinsic::minimum: return Intrinsic::maximum;
   case Intrinsic::maxnum: return Intrinsic::minnum;
   case Intrinsic::minnum: return Intrinsic::maxnum;
+  case Intrinsic::maximumnum:
+    return Intrinsic::minimumnum;
+  case Intrinsic::minimumnum:
+    return Intrinsic::maximumnum;
   default: llvm_unreachable("Unexpected intrinsic");
   }
 }
diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index fff6cfd8a3b4b..ae5b965ff2723 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -54,6 +54,70 @@ define float @test_minimum_const_nan(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_nan(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_nan(
+; CHECK-NEXT:    ret float [[R:%.*]]
+;
+  %r = call float @llvm.maximumnum.f32(float %x, float 0x7fff000000000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_nan(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_nan(
+; CHECK-NEXT:    ret float [[R:%.*]]
+;
+  %r = call float @llvm.minimumnum.f32(float %x, float 0x7fff000000000000)
+  ret float %r
+}
+
+define float @test_minnum_const_snan(float %x) {
+; CHECK-LABEL: @test_minnum_const_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %r = call float @llvm.minnum.f32(float %x, float 0x7ff4000000000000)
+  ret float %r
+}
+
+define float @test_maxnum_const_snan(float %x) {
+; CHECK-LABEL: @test_maxnum_const_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %r = call float @llvm.maxnum.f32(float %x, float 0x7ff4000000000000)
+  ret float %r
+}
+
+define float @test_maximum_const_snan(float %x) {
+; CHECK-LABEL: @test_maximum_const_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %r = call float @llvm.maximum.f32(float %x, float 0x7ff4000000000000)
+  ret float %r
+}
+
+define float @test_minimum_const_snan(float %x) {
+; CHECK-LABEL: @test_minimum_const_snan(
+; CHECK-NEXT:    ret float 0x7FFC000000000000
+;
+  %r = call float @llvm.minimum.f32(float %x, float 0x7ff4000000000000)
+  ret float %r
+}
+
+define float @test_maximumnum_const_snan(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_snan(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call float @llvm.maximumnum.f32(float %x, float 0x7ff4000000000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_snan(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_snan(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call float @llvm.minimumnum.f32(float %x, float 0x7ff4000000000000)
+  ret float %r
+}
+
 define float @test_minnum_const_inf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_inf(
 ; CHECK-NEXT:    [[R:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float 0x7FF0000000000000)
@@ -88,6 +152,23 @@ define float @test_minimum_const_inf(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_inf(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_inf(
+; CHECK-NEXT:    ret float 0x7FF0000000000000
+;
+  %r = call float @llvm.maximumnum.f32(float %x, float 0x7ff0000000000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_inf(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_inf(
+; CHECK-NEXT:    [[R:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float 0x7FF0000000000000)
+; CHECK-NEXT:    ret float [[R]]
+;
+  %r = call float @llvm.minimumnum.f32(float %x, float 0x7ff0000000000000)
+  ret float %r
+}
+
 define float @test_minnum_const_neg_inf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_inf(
 ; CHECK-NEXT:    ret float 0xFFF0000000000000
@@ -122,6 +203,23 @@ define float @test_minimum_const_neg_inf(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_neg_inf(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_inf(
+; CHECK-NEXT:    [[X:%.*]] = call float @llvm.maximumnum.f32(float [[X1:%.*]], float 0xFFF0000000000000)
+; CHECK-NEXT:    ret float [[X]]
+;
+  %r = call float @llvm.maximumnum.f32(float %x, float 0xfff0000000000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_neg_inf(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_inf(
+; CHECK-NEXT:    ret float 0xFFF0000000000000
+;
+  %r = call float @llvm.minimumnum.f32(float %x, float 0xfff0000000000000)
+  ret float %r
+}
+
 define float @test_minnum_const_inf_nnan(float %x) {
 ; CHECK-LABEL: @test_minnum_const_inf_nnan(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -154,6 +252,22 @@ define float @test_minimum_const_inf_nnan(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_inf_nnan(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_inf_nnan(
+; CHECK-NEXT:    ret float 0x7FF0000000000000
+;
+  %r = call nnan float @llvm.maximumnum.f32(float %x, float 0x7ff0000000000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_inf_nnan(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_inf_nnan(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call nnan float @llvm.minimumnum.f32(float %x, float 0x7ff0000000000000)
+  ret float %r
+}
+
 define float @test_minnum_const_inf_nnan_comm(float %x) {
 ; CHECK-LABEL: @test_minnum_const_inf_nnan_comm(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -186,6 +300,22 @@ define float @test_minimum_const_inf_nnan_comm(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_inf_nnan_comm(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_inf_nnan_comm(
+; CHECK-NEXT:    ret float 0x7FF0000000000000
+;
+  %r = call nnan float @llvm.maximumnum.f32(float 0x7ff0000000000000, float %x)
+  ret float %r
+}
+
+define float @test_minimumnum_const_inf_nnan_comm(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_inf_nnan_comm(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call nnan float @llvm.minimumnum.f32(float 0x7ff0000000000000, float %x)
+  ret float %r
+}
+
 define <2 x float> @test_minnum_const_inf_nnan_comm_vec(<2 x float> %x) {
 ; CHECK-LABEL: @test_minnum_const_inf_nnan_comm_vec(
 ; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
@@ -218,6 +348,22 @@ define <2 x float> @test_minimum_const_inf_nnan_comm_vec(<2 x float> %x) {
   ret <2 x float> %r
 }
 
+define <2 x float> @test_maximumnum_const_inf_nnan_comm_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_inf_nnan_comm_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x7FF0000000000000)
+;
+  %r = call nnan <2 x float> @llvm.maximumnum.v2f32(<2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>, <2 x float> %x)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_inf_nnan_comm_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_inf_nnan_comm_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.minimumnum.v2f32(<2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>, <2 x float> %x)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_neg_inf_nnan(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_inf_nnan(
 ; CHECK-NEXT:    ret float 0xFFF0000000000000
@@ -250,6 +396,22 @@ define float @test_minimum_const_neg_inf_nnan(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_neg_inf_nnan(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_inf_nnan(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call nnan float @llvm.maximumnum.f32(float %x, float 0xfff0000000000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_neg_inf_nnan(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_inf_nnan(
+; CHECK-NEXT:    ret float 0xFFF0000000000000
+;
+  %r = call nnan float @llvm.minimumnum.f32(float %x, float 0xfff0000000000000)
+  ret float %r
+}
+
 define float @test_minnum_const_max(float %x) {
 ; CHECK-LABEL: @test_minnum_const_max(
 ; CHECK-NEXT:    [[R:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float 0x47EFFFFFE0000000)
@@ -286,6 +448,24 @@ define float @test_minimum_const_max(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_max(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_max(
+; CHECK-NEXT:    [[R:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float 0x47EFFFFFE0000000)
+; CHECK-NEXT:    ret float [[R]]
+;
+  %r = call float @llvm.maximumnum.f32(float %x, float 0x47efffffe0000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_max(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_max(
+; CHECK-NEXT:    [[R:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float 0x47EFFFFFE0000000)
+; CHECK-NEXT:    ret float [[R]]
+;
+  %r = call float @llvm.minimumnum.f32(float %x, float 0x47efffffe0000000)
+  ret float %r
+}
+
 define float @test_minnum_const_neg_max(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_max(
 ; CHECK-NEXT:    [[R:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float 0xC7EFFFFFE0000000)
@@ -322,6 +502,24 @@ define float @test_minimum_const_neg_max(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_neg_max(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_max(
+; CHECK-NEXT:    [[R:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float 0xC7EFFFFFE0000000)
+; CHECK-NEXT:    ret float [[R]]
+;
+  %r = call float @llvm.maximumnum.f32(float %x, float 0xc7efffffe0000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_neg_max(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_max(
+; CHECK-NEXT:    [[R:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float 0xC7EFFFFFE0000000)
+; CHECK-NEXT:    ret float [[R]]
+;
+  %r = call float @llvm.minimumnum.f32(float %x, float 0xc7efffffe0000000)
+  ret float %r
+}
+
 define float @test_minnum_const_max_ninf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_max_ninf(
 ; CHECK-NEXT:    [[R:%.*]] = call ninf float @llvm.minnum.f32(float [[X:%.*]], float 0x47EFFFFFE0000000)
@@ -356,6 +554,23 @@ define float @test_minimum_const_max_ninf(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_max_ninf(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_max_ninf(
+; CHECK-NEXT:    ret float 0x47EFFFFFE0000000
+;
+  %r = call ninf float @llvm.maximumnum.f32(float %x, float 0x47efffffe0000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_max_ninf(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_max_ninf(
+; CHECK-NEXT:    [[R:%.*]] = call ninf float @llvm.minimumnum.f32(float [[X:%.*]], float 0x47EFFFFFE0000000)
+; CHECK-NEXT:    ret float [[R]]
+;
+  %r = call ninf float @llvm.minimumnum.f32(float %x, float 0x47efffffe0000000)
+  ret float %r
+}
+
 define float @test_minnum_const_neg_max_ninf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_max_ninf(
 ; CHECK-NEXT:    ret float 0xC7EFFFFFE0000000
@@ -390,6 +605,23 @@ define float @test_minimum_const_neg_max_ninf(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_neg_max_ninf(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_max_ninf(
+; CHECK-NEXT:    [[X:%.*]] = call ninf float @llvm.maximumnum.f32(float [[X1:%.*]], float 0xC7EFFFFFE0000000)
+; CHECK-NEXT:    ret float [[X]]
+;
+  %r = call ninf float @llvm.maximumnum.f32(float %x, float 0xc7efffffe0000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_neg_max_ninf(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_max_ninf(
+; CHECK-NEXT:    ret float 0xC7EFFFFFE0000000
+;
+  %r = call ninf float @llvm.minimumnum.f32(float %x, float 0xc7efffffe0000000)
+  ret float %r
+}
+
 define float @test_minnum_const_max_nnan_ninf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_max_nnan_ninf(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -422,6 +654,22 @@ define float @test_minimum_const_max_nnan_ninf(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_max_nnan_ninf(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_max_nnan_ninf(
+; CHECK-NEXT:    ret float 0x47EFFFFFE0000000
+;
+  %r = call nnan ninf float @llvm.maximumnum.f32(float %x, float 0x47efffffe0000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_max_nnan_ninf(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_max_nnan_ninf(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call nnan ninf float @llvm.minimumnum.f32(float %x, float 0x47efffffe0000000)
+  ret float %r
+}
+
 define float @test_minnum_const_neg_max_nnan_ninf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_max_nnan_ninf(
 ; CHECK-NEXT:    ret float 0xC7EFFFFFE0000000
@@ -454,8 +702,24 @@ define float @test_minimum_const_neg_max_nnan_ninf(float %x) {
   ret float %r
 }
 
+define float @test_maximumnum_const_neg_max_nnan_ninf(float %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_max_nnan_ninf(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %r = call nnan ninf float @llvm.maximumnum.f32(float %x, float 0xc7efffffe0000000)
+  ret float %r
+}
+
+define float @test_minimumnum_const_neg_max_nnan_ninf(float %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_max_nnan_ninf(
+; CHECK-NEXT:    ret float 0xC7EFFFFFE0000000
+;
+  %r = call nnan ninf float @llvm.minimumnum.f32(float %x, float 0xc7efffffe0000000)
+  ret float %r
+}
+
 ; From the LangRef for minnum/maxnum:
-; "If either operand is a NaN, returns the other non-NaN operand."
+; "If either operand is a qNaN, returns the other non-NaN operand. Returns NaN only if both operands are NaN or if either operand is sNaN."
 
 define double @maxnum_nan_op0(double %x) {
 ; CHECK-LABEL: @maxnum_nan_op0(
@@ -521,6 +785,70 @@ define <2 x double> @minnum_nan_op1_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
+define double @maxnum_snan_op0(double %x) {
+; CHECK-LABEL: @maxnum_snan_op0(
+; CHECK-NEXT:    ret double 0x7FFC000000000000
+;
+  %r = call double @llvm.maxnum.f64(double 0x7ff4000000000000, double %x)
+  ret double %r
+}
+
+define double @maxnum_snan_op1(double %x) {
+; CHECK-LABEL: @maxnum_snan_op1(
+; CHECK-NEXT:    ret double 0x7FFC00000000DEAD
+;
+  %r = call double @llvm.maxnum.f64(double %x, double 0x7ff400000000dead)
+  ret double %r
+}
+
+define double @minnum_snan_op0(double %x) {
+; CHECK-LABEL: @minnum_snan_op0(
+; CHECK-NEXT:    ret double 0x7FFC000DEAD00000
+;
+  %r = call double @llvm.minnum.f64(double 0x7ff4000dead00000, double %x)
+  ret double %r
+}
+
+define double @minnum_snan_op1(double %x) {
+; CHECK-LABEL: @minnum_snan_op1(
+; CHECK-NEXT:    ret double 0x7FFC00DEAD00DEAD
+;
+  %r = call double @llvm.minnum.f64(double %x, double 0x7ff400dead00dead)
+  ret double %r
+}
+
+define <2 x double> @maxnum_snan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @maxnum_snan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC000000000000, double poison>
+;
+  %r = call <2 x double> @llvm.maxnum.v2f64(<2 x double> <double 0x7ff4000000000000, double poison>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maxnum_snan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @maxnum_snan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC00000000DEAD, double 0x7FFCFFFFFFFFFFFF>
+;
+  %r = call <2 x double> @llvm.maxnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400000000dead, double 0x7ff4ffffffffffff>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minnum_snan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @minnum_snan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> <double poison, double 0x7FFC000DEAD00000>
+;
+  %r = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double poison, double 0x7ff4000dead00000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minnum_snan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @minnum_snan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> splat (double 0x7FFC00DEAD00DEAD)
+;
+  %r = call <2 x double> @llvm.minnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400dead00dead, double 0x7ff400dead00dead>)
+  ret <2 x double> %r
+}
+
 define float @maxnum_undef_op1(float %x) {
 ; CHECK-LABEL: @maxnum_undef_op1(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -609,6 +937,14 @@ define float @minnum_undef_poison(float %x) {
   ret float %val
 }
 
+define float @minnum_poison_poison(float %x) {
+; CHECK-LABEL: @minnum_poison_poison(
+; CHECK-NEXT:    ret float poison
+;
+  %val = call float @llvm.minnum.f32(float poison, float poison)
+  ret float %val
+}
+
 define float @maxnum_undef_undef(float %x) {
 ; CHECK-LABEL: @maxnum_undef_undef(
 ; CHECK-NEXT:    ret float undef
@@ -633,6 +969,14 @@ define float @maxnum_undef_poison(float %x) {
   ret float %val
 }
 
+define float @maxnum_poison_poison(float %x) {
+; CHECK-LABEL: @maxnum_poison_poison(
+; CHECK-NEXT:    ret float poison
+;
+  %val = call float @llvm.maxnum.f32(float poison, float poison)
+  ret float %val
+}
+
 define float @minnum_same_args(float %x) {
 ; CHECK-LABEL: @minnum_same_args(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -743,6 +1087,7 @@ define float @minnum_inf(float %x) {
   %val = call float @llvm.minnum.f32(float 0x7FF0000000000000, float %x)
   ret float %val
 }
+
 define float @maxnum_x_maxnum_x_y(float %x, float %y) {
 ; CHECK-LABEL: @maxnum_x_maxnum_x_y(
 ; CHECK-NEXT:    [[A:%.*]] = call float @llvm.maxnum.f32(float [[X:%.*]], float [[Y:%.*]])
@@ -921,6 +1266,86 @@ define <2 x double> @minimum_nan_op1_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
+define double @maximum_snan_op0(double %x) {
+; CHECK-LABEL: @maximum_snan_op0(
+; CHECK-NEXT:    ret double 0x7FFC000000000000
+;
+  %r = call double @llvm.maximum.f64(double 0x7ff4000000000000, double %x)
+  ret double %r
+}
+
+define double @maximum_snan_op1(double %x) {
+; CHECK-LABEL: @maximum_snan_op1(
+; CHECK-NEXT:    ret double 0x7FFC00000000DEAD
+;
+  %r = call double @llvm.maximum.f64(double %x, double 0x7ff400000000dead)
+  ret double %r
+}
+
+define double @minimum_snan_op0(double %x) {
+; CHECK-LABEL: @minimum_snan_op0(
+; CHECK-NEXT:    ret double 0x7FFC000DEAD00000
+;
+  %r = call double @llvm.minimum.f64(double 0x7ff4000dead00000, double %x)
+  ret double %r
+}
+
+define double @minimum_snan_op1(double %x) {
+; CHECK-LABEL: @minimum_snan_op1(
+; CHECK-NEXT:    ret double 0x7FFC00DEAD00DEAD
+;
+  %r = call double @llvm.minimum.f64(double %x, double 0x7ff400dead00dead)
+  ret double %r
+}
+
+define <2 x double> @maximum_snan_op0_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @maximum_snan_op0_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC000000000000, double poison>
+;
+  %r = call <2 x double> @llvm.maximum.v2f64(<2 x double> <double 0x7ff4000000000000, double poison>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximum_snan_op1_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @maximum_snan_op1_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC000000000000, double poison>
+;
+  %r = call <2 x double> @llvm.maximum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff4000000000000, double poison>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximum_snan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximum_snan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC00000000DEAD, double 0x7FFCFFFFFFFFFFFF>
+;
+  %r = call <2 x double> @llvm.maximum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400000000dead, double 0x7ff4ffffffffffff>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimum_snan_op0_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @minimum_snan_op0_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> <double poison, double 0x7FFC000DEAD00000>
+;
+  %r = call <2 x double> @llvm.minimum.v2f64(<2 x double> <double poison, double 0x7ff4000dead00000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimum_snan_op1_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @minimum_snan_op1_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> <double poison, double 0x7FFC000DEAD00000>
+;
+  %r = call <2 x double> @llvm.minimum.v2f64(<2 x double> %x, <2 x double> <double poison, double 0x7ff4000dead00000>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimum_snan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimum_snan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> splat (double 0x7FFC00DEAD00DEAD)
+;
+  %r = call <2 x double> @llvm.minimum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400dead00dead, double 0x7ff400dead00dead>)
+  ret <2 x double> %r
+}
+
 define float @maximum_undef_op1(float %x) {
 ; CHECK-LABEL: @maximum_undef_op1(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -1001,6 +1426,22 @@ define float @maximum_undef_undef(float %x) {
   ret float %val
 }
 
+define float @minimum_poison_poison(float %x) {
+; CHECK-LABEL: @minimum_poison_poison(
+; CHECK-NEXT:    ret float poison
+;
+  %val = call float @llvm.minimum.f32(float poison, float poison)
+  ret float %val
+}
+
+define float @maximum_poison_poison(float %x) {
+; CHECK-LABEL: @maximum_poison_poison(
+; CHECK-NEXT:    ret float poison
+;
+  %val = call float @llvm.maximum.f32(float poison, float poison)
+  ret float %val
+}
+
 define float @minimum_same_args(float %x) {
 ; CHECK-LABEL: @minimum_same_args(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -1171,8 +1612,6 @@ define <2 x double> @minimum_neginf_commute_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
-; TODO: minimum(INF, X) --> X
-
 define float @minimum_inf(float %x) {
 ; CHECK-LABEL: @minimum_inf(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -1317,8 +1756,8 @@ define <2 x float> @maxnum_minnum_maxnum(<2 x float> %x, <2 x float> %y) {
   ret <2 x float> %val
 }
 
-define <2 x double> @maxnum_minnum_minmum(<2 x double> %x, <2 x double> %y) {
-; CHECK-LABEL: @maxnum_minnum_minmum(
+define <2 x double> @maxnum_minnum_minnum(<2 x double> %x, <2 x double> %y) {
+; CHECK-LABEL: @maxnum_minnum_minnum(
 ; CHECK-NEXT:    [[MIN1:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> [[X:%.*]], <2 x double> [[Y:%.*]])
 ; CHECK-NEXT:    [[MIN2:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> [[X]], <2 x double> [[Y]])
 ; CHECK-NEXT:    [[VAL:%.*]] = call <2 x double> @llvm.maxnum.v2f64(<2 x double> [[MIN1]], <2 x double> [[MIN2]])
@@ -1376,8 +1815,8 @@ define <2 x float> @minnum_maxnum_maxnum(<2 x float> %x, <2 x float> %y) {
   ret <2 x float> %val
 }
 
-define <2 x double> @minnum_minnum_minmum(<2 x double> %x, <2 x double> %y) {
-; CHECK-LABEL: @minnum_minnum_minmum(
+define <2 x double> @minnum_minnum_minnum(<2 x double> %x, <2 x double> %y) {
+; CHECK-LABEL: @minnum_minnum_minnum(
 ; CHECK-NEXT:    [[MIN1:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> [[X:%.*]], <2 x double> [[Y:%.*]])
 ; CHECK-NEXT:    ret <2 x double> [[MIN1]]
 ;
@@ -1386,3 +1825,481 @@ define <2 x double> @minnum_minnum_minmum(<2 x double> %x, <2 x double> %y) {
   %val = call <2 x double> @llvm.minnum.v2f64(<2 x double> %min1, <2 x double> %min2)
   ret <2 x double> %val
 }
+
+; From the LangRef for minimumnum/maximumnum:
+; "If both operands are NaNs (including sNaN), returns qNaN.
+;  If one operand is NaN (including sNaN) and another operand is a number, return the number."
+
+define double @maximumnum_nan_op0(double %x) {
+; CHECK-LABEL: @maximumnum_nan_op0(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.maximumnum.f64(double 0x7ff8000000000000, double %x)
+  ret double %r
+}
+
+define double @maximumnum_nan_op1(double %x) {
+; CHECK-LABEL: @maximumnum_nan_op1(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.maximumnum.f64(double %x, double 0x7ff800000000dead)
+  ret double %r
+}
+
+define double @minimumnum_nan_op0(double %x) {
+; CHECK-LABEL: @minimumnum_nan_op0(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.minimumnum.f64(double 0x7ff8000dead00000, double %x)
+  ret double %r
+}
+
+define double @minimumnum_nan_op1(double %x) {
+; CHECK-LABEL: @minimumnum_nan_op1(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.minimumnum.f64(double %x, double 0x7ff800dead00dead)
+  ret double %r
+}
+
+define <2 x double> @maximumnum_nan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_nan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double 0x7ff8000000000000, double poison>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximumnum_nan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_nan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff800000000dead, double 0x7ff8ffffffffffff>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_nan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_nan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double poison, double 0x7ff8000dead00000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_nan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_nan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff800dead00dead, double 0x7ff800dead00dead>)
+  ret <2 x double> %r
+}
+
+define double @maximumnum_snan_op0(double %x) {
+; CHECK-LABEL: @maximumnum_snan_op0(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.maximumnum.f64(double 0x7ff4000000000000, double %x)
+  ret double %r
+}
+
+define double @maximumnum_snan_op1(double %x) {
+; CHECK-LABEL: @maximumnum_snan_op1(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.maximumnum.f64(double %x, double 0x7ff400000000dead)
+  ret double %r
+}
+
+define double @minimumnum_snan_op0(double %x) {
+; CHECK-LABEL: @minimumnum_snan_op0(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.minimumnum.f64(double 0x7ff4000dead00000, double %x)
+  ret double %r
+}
+
+define double @minimumnum_snan_op1(double %x) {
+; CHECK-LABEL: @minimumnum_snan_op1(
+; CHECK-NEXT:    ret double [[X:%.*]]
+;
+  %r = call double @llvm.minimumnum.f64(double %x, double 0x7ff400dead00dead)
+  ret double %r
+}
+
+define <2 x double> @maximumnum_snan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_snan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double 0x7ff4000000000000, double poison>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximumnum_snan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_snan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400000000dead, double 0x7ff4ffffffffffff>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_snan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_snan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double poison, double 0x7ff4000dead00000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_snan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_snan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400dead00dead, double 0x7ff400dead00dead>)
+  ret <2 x double> %r
+}
+
+define float @maximumnum_poison_op1(float %x) {
+; CHECK-LABEL: @maximumnum_poison_op1(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %val = call float @llvm.maximumnum.f32(float %x, float poison)
+  ret float %val
+}
+
+define float @maximumnum_poison_op0(float %x) {
+; CHECK-LABEL: @maximumnum_poison_op0(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %val = call float @llvm.maximumnum.f32(float poison, float %x)
+  ret float %val
+}
+
+define float @minimumnum_poison_op1(float %x) {
+; CHECK-LABEL: @minimumnum_poison_op1(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %val = call float @llvm.minimumnum.f32(float %x, float poison)
+  ret float %val
+}
+
+define float @minimumnum_poison_op0(float %x) {
+; CHECK-LABEL: @minimumnum_poison_op0(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %val = call float @llvm.minimumnum.f32(float poison, float %x)
+  ret float %val
+}
+
+define float @minimumnum_poison_poison(float %x) {
+; CHECK-LABEL: @minimumnum_poison_poison(
+; CHECK-NEXT:    ret float poison
+;
+  %val = call float @llvm.minimumnum.f32(float poison, float poison)
+  ret float %val
+}
+
+define float @maximumnum_poison_poison(float %x) {
+; CHECK-LABEL: @maximumnum_poison_poison(
+; CHECK-NEXT:    ret float poison
+;
+  %val = call float @llvm.maximumnum.f32(float poison, float poison)
+  ret float %val
+}
+
+define float @minimumnum_same_args(float %x) {
+; CHECK-LABEL: @minimumnum_same_args(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %y = call float @llvm.minimumnum.f32(float %x, float %x)
+  ret float %y
+}
+
+define float @maximumnum_same_args(float %x) {
+; CHECK-LABEL: @maximumnum_same_args(
+; CHECK-NEXT:    ret float [[X:%.*]]
+;
+  %y = call float @llvm.maximumnum.f32(float %x, float %x)
+  ret float %y
+}
+
+define float @minimumnum_x_minimumnum_x_y(float %x, float %y) {
+; CHECK-LABEL: @minimumnum_x_minimumnum_x_y(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[A]]
+;
+  %a = call float @llvm.minimumnum.f32(float %x, float %y)
+  %b = call float @llvm.minimumnum.f32(float %x, float %a)
+  ret float %b
+}
+
+define float @minimumnum_y_minimumnum_x_y(float %x, float %y) {
+; CHECK-LABEL: @minimumnum_y_minimumnum_x_y(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[A]]
+;
+  %a = call float @llvm.minimumnum.f32(float %x, float %y)
+  %b = call float @llvm.minimumnum.f32(float %y, float %a)
+  ret float %b
+}
+
+define float @minimumnum_x_y_minimumnum_x(float %x, float %y) {
+; CHECK-LABEL: @minimumnum_x_y_minimumnum_x(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[A]]
+;
+  %a = call float @llvm.minimumnum.f32(float %x, float %y)
+  %b = call float @llvm.minimumnum.f32(float %a, float %x)
+  ret float %b
+}
+
+define float @minimumnum_x_y_minimumnum_y(float %x, float %y) {
+; CHECK-LABEL: @minimumnum_x_y_minimumnum_y(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[A]]
+;
+  %a = call float @llvm.minimumnum.f32(float %x, float %y)
+  %b = call float @llvm.minimumnum.f32(float %a, float %y)
+  ret float %b
+}
+
+; negative test
+
+define float @minimumnum_z_minimumnum_x_y(float %x, float %y, float %z) {
+; CHECK-LABEL: @minimumnum_z_minimumnum_x_y(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    [[B:%.*]] = call float @llvm.minimumnum.f32(float [[Z:%.*]], float [[A]])
+; CHECK-NEXT:    ret float [[B]]
+;
+  %a = call float @llvm.minimumnum.f32(float %x, float %y)
+  %b = call float @llvm.minimumnum.f32(float %z, float %a)
+  ret float %b
+}
+
+; negative test
+
+define float @minimumnum_x_y_minimumnum_z(float %x, float %y, float %z) {
+; CHECK-LABEL: @minimumnum_x_y_minimumnum_z(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    [[B:%.*]] = call float @llvm.minimumnum.f32(float [[A]], float [[Z:%.*]])
+; CHECK-NEXT:    ret float [[B]]
+;
+  %a = call float @llvm.minimumnum.f32(float %x, float %y)
+  %b = call float @llvm.minimumnum.f32(float %a, float %z)
+  ret float %b
+}
+
+; minimumnum(X, -INF) --> -INF
+
+define float @minimumnum_neginf(float %x) {
+; CHECK-LABEL: @minimumnum_neginf(
+; CHECK-NEXT:    ret float 0xFFF0000000000000
+;
+  %val = call float @llvm.minimumnum.f32(float %x, float 0xFFF0000000000000)
+  ret float %val
+}
+
+define <2 x double> @minimumnum_neginf_commute_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_neginf_commute_vec(
+; CHECK-NEXT:    ret <2 x double> splat (double 0xFFF0000000000000)
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double 0xFFF0000000000000, double 0xFFF0000000000000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+; negative test
+
+define float @minimumnum_inf(float %x) {
+; CHECK-LABEL: @minimumnum_inf(
+; CHECK-NEXT:    [[VAL:%.*]] = call float @llvm.minimumnum.f32(float 0x7FF0000000000000, float [[X:%.*]])
+; CHECK-NEXT:    ret float [[VAL]]
+;
+  %val = call float @llvm.minimumnum.f32(float 0x7FF0000000000000, float %x)
+  ret float %val
+}
+
+define float @maximumnum_x_maximumnum_x_y(float %x, float %y) {
+; CHECK-LABEL: @maximumnum_x_maximumnum_x_y(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[A]]
+;
+  %a = call float @llvm.maximumnum.f32(float %x, float %y)
+  %b = call float @llvm.maximumnum.f32(float %x, float %a)
+  ret float %b
+}
+
+define float @maximumnum_y_maximumnum_x_y(float %x, float %y) {
+; CHECK-LABEL: @maximumnum_y_maximumnum_x_y(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[A]]
+;
+  %a = call float @llvm.maximumnum.f32(float %x, float %y)
+  %b = call float @llvm.maximumnum.f32(float %y, float %a)
+  ret float %b
+}
+
+define float @maximumnum_x_y_maximumnum_x(float %x, float %y) {
+; CHECK-LABEL: @maximumnum_x_y_maximumnum_x(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[A]]
+;
+  %a = call float @llvm.maximumnum.f32(float %x, float %y)
+  %b = call float @llvm.maximumnum.f32(float %a, float %x)
+  ret float %b
+}
+
+define float @maximumnum_x_y_maximumnum_y(float %x, float %y) {
+; CHECK-LABEL: @maximumnum_x_y_maximumnum_y(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[A]]
+;
+  %a = call float @llvm.maximumnum.f32(float %x, float %y)
+  %b = call float @llvm.maximumnum.f32(float %a, float %y)
+  ret float %b
+}
+
+; negative test
+
+define float @maximumnum_z_maximumnum_x_y(float %x, float %y, float %z) {
+; CHECK-LABEL: @maximumnum_z_maximumnum_x_y(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    [[B:%.*]] = call float @llvm.maximumnum.f32(float [[Z:%.*]], float [[A]])
+; CHECK-NEXT:    ret float [[B]]
+;
+  %a = call float @llvm.maximumnum.f32(float %x, float %y)
+  %b = call float @llvm.maximumnum.f32(float %z, float %a)
+  ret float %b
+}
+
+; negative test
+
+define float @maximumnum_x_y_maximumnum_z(float %x, float %y, float %z) {
+; CHECK-LABEL: @maximumnum_x_y_maximumnum_z(
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    [[B:%.*]] = call float @llvm.maximumnum.f32(float [[A]], float [[Z:%.*]])
+; CHECK-NEXT:    ret float [[B]]
+;
+  %a = call float @llvm.maximumnum.f32(float %x, float %y)
+  %b = call float @llvm.maximumnum.f32(float %a, float %z)
+  ret float %b
+}
+
+; maximumnum(X, INF) --> INF
+
+define <2 x double> @maximumnum_inf(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_inf(
+; CHECK-NEXT:    ret <2 x double> splat (double 0x7FF0000000000000)
+;
+  %val = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> %x, <2 x double><double 0x7FF0000000000000, double 0x7FF0000000000000>)
+  ret <2 x double> %val
+}
+
+define float @maximumnum_inf_commute(float %x) {
+; CHECK-LABEL: @maximumnum_inf_commute(
+; CHECK-NEXT:    ret float 0x7FF0000000000000
+;
+  %val = call float @llvm.maximumnum.f32(float 0x7FF0000000000000, float %x)
+  ret float %val
+}
+
+; negative test
+
+define float @maximumnum_neginf(float %x) {
+; CHECK-LABEL: @maximumnum_neginf(
+; CHECK-NEXT:    [[VAL:%.*]] = call float @llvm.maximumnum.f32(float 0xFFF0000000000000, float [[X:%.*]])
+; CHECK-NEXT:    ret float [[VAL]]
+;
+  %val = call float @llvm.maximumnum.f32(float 0xFFF0000000000000, float %x)
+  ret float %val
+}
+
+define double @maximumnum_maximumnum_minimumnum(double %x, double %y) {
+; CHECK-LABEL: @maximumnum_maximumnum_minimumnum(
+; CHECK-NEXT:    [[MAX:%.*]] = call double @llvm.maximumnum.f64(double [[X:%.*]], double [[Y:%.*]])
+; CHECK-NEXT:    ret double [[MAX]]
+;
+  %max = call double @llvm.maximumnum.f64(double %x, double %y)
+  %min = call double @llvm.minimumnum.f64(double %x, double %y)
+  %val = call double @llvm.maximumnum.f64(double %max, double %min)
+  ret double %val
+}
+
+define <2 x float> @maximumnum_minimumnum_maximumnum(<2 x float> %x, <2 x float> %y) {
+; CHECK-LABEL: @maximumnum_minimumnum_maximumnum(
+; CHECK-NEXT:    [[MAX:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X:%.*]], <2 x float> [[Y:%.*]])
+; CHECK-NEXT:    ret <2 x float> [[MAX]]
+;
+  %max = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> %y)
+  %min = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> %y)
+  %val = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %min, <2 x float> %max)
+  ret <2 x float> %val
+}
+
+define <2 x double> @maximumnum_minimumnum_minimumnum(<2 x double> %x, <2 x double> %y) {
+; CHECK-LABEL: @maximumnum_minimumnum_minimumnum(
+; CHECK-NEXT:    [[MIN1:%.*]] = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> [[X:%.*]], <2 x double> [[Y:%.*]])
+; CHECK-NEXT:    [[MIN2:%.*]] = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> [[X]], <2 x double> [[Y]])
+; CHECK-NEXT:    [[VAL:%.*]] = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> [[MIN1]], <2 x double> [[MIN2]])
+; CHECK-NEXT:    ret <2 x double> [[VAL]]
+;
+  %min1 = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> %y)
+  %min2 = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> %y)
+  %val = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> %min1, <2 x double> %min2)
+  ret <2 x double> %val
+}
+
+define float @maximumnum_maximumnum_maximumnum(float %x, float %y) {
+; CHECK-LABEL: @maximumnum_maximumnum_maximumnum(
+; CHECK-NEXT:    [[MAX1:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[MAX1]]
+;
+  %max1 = call float @llvm.maximumnum.f32(float %x, float %y)
+  %max2 = call float @llvm.maximumnum.f32(float %x, float %y)
+  %val = call float @llvm.maximumnum.f32(float %max1, float %max2)
+  ret float %val
+}
+
+define double @minimumnum_maximumnum_minimumnum(double %x, double %y) {
+; CHECK-LABEL: @minimumnum_maximumnum_minimumnum(
+; CHECK-NEXT:    [[MIN:%.*]] = call double @llvm.minimumnum.f64(double [[X:%.*]], double [[Y:%.*]])
+; CHECK-NEXT:    ret double [[MIN]]
+;
+  %max = call double @llvm.maximumnum.f64(double %x, double %y)
+  %min = call double @llvm.minimumnum.f64(double %x, double %y)
+  %val = call double @llvm.minimumnum.f64(double %max, double %min)
+  ret double %val
+}
+
+define float @minimumnum_minimumnum_maximumnum(float %x, float %y) {
+; CHECK-LABEL: @minimumnum_minimumnum_maximumnum(
+; CHECK-NEXT:    [[MIN:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float [[Y:%.*]])
+; CHECK-NEXT:    ret float [[MIN]]
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float %y)
+  %min = call float @llvm.minimumnum.f32(float %x, float %y)
+  %val = call float @llvm.minimumnum.f32(float %min, float %max)
+  ret float %val
+}
+
+define <2 x float> @minimumnum_maximumnum_maximumnum(<2 x float> %x, <2 x float> %y) {
+; CHECK-LABEL: @minimumnum_maximumnum_maximumnum(
+; CHECK-NEXT:    [[MAX1:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X:%.*]], <2 x float> [[Y:%.*]])
+; CHECK-NEXT:    [[MAX2:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
+; CHECK-NEXT:    [[VAL:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[MAX1]], <2 x float> [[MAX2]])
+; CHECK-NEXT:    ret <2 x float> [[VAL]]
+;
+  %max1 = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> %y)
+  %max2 = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> %y)
+  %val = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %max1, <2 x float> %max2)
+  ret <2 x float> %val
+}
+
+define <2 x double> @minimumnum_minimumnum_minimumnum(<2 x double> %x, <2 x double> %y) {
+; CHECK-LABEL: @minimumnum_minimumnum_minimumnum(
+; CHECK-NEXT:    [[MIN1:%.*]] = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> [[X:%.*]], <2 x double> [[Y:%.*]])
+; CHECK-NEXT:    ret <2 x double> [[MIN1]]
+;
+  %min1 = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> %y)
+  %min2 = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> %y)
+  %val = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %min1, <2 x double> %min2)
+  ret <2 x double> %val
+}

>From 9c0559147dea5454af7b3c1064d7625ce7da8239 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Tue, 13 May 2025 12:15:09 +0000
Subject: [PATCH 02/19] Add more vector tests + cleanup

- Add tests for vectors of <snan, qnan> pairs.
- Add more vector tests for <qnan, poison> and <snan, poison>
- Remove comment wording about FTZ.
- Clarify behavior of fmaxnum(x, <snan, qnan>) -> x in comments (assume snan == qnan)
---
 llvm/lib/Analysis/InstructionSimplify.cpp     |  19 ++-
 .../Transforms/InstSimplify/fminmax-folds.ll  | 152 ++++++++++++++++++
 2 files changed, 161 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index a10306520c066..241161c5d6d50 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6719,9 +6719,9 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     // In several cases here, we deviate from exact IEEE 754 semantics
     // to enable optimizations (as allowed by the LLVM IR spec).
     //
-    // For instance, we often return one of the arguments unmodified instead of
-    // inserting an llvm.canonicalize to transform input sNaNs into qNaNs or to
-    // respect any FTZ semantics, and sometimes assume all NaN inputs are qNaNs.
+    // For instance, we may return one of the arguments unmodified instead of
+    // inserting an llvm.canonicalize to transform input sNaNs into qNaNs,
+    // or may assume all NaN inputs are qNaNs.
 
     // If the arguments are the same, this is a no-op.
     if (Op0 == Op1)
@@ -6742,15 +6742,14 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
 
     // minnum(x, qnan) -> x
     // maxnum(x, qnan) -> x
-    // minnum(x, snan) -> qnan
-    // maxnum(x, snan) -> qnan
+    // minnum(x, snan) -> qnan (or x for vectors mixing snans and qnans)
+    // maxnum(x, snan) -> qnan (or x for vectors mixing snans and qnans)
     // minimum(X, nan) -> qnan
     // maximum(X, nan) -> qnan
-    if (match(Op1, m_NaN())) {
-      if (PropagateNaN || (PropagateSNaN && match(Op1, m_sNaN())))
-        return propagateNaN(cast<Constant>(Op1));
-      return Op0;
-    }
+    if (PropagateSNaN && match(Op1, m_sNaN()))
+      return propagateNaN(cast<Constant>(Op1));
+    else if (match(Op1, m_NaN()))
+      return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
 
     // In the following folds, inf can be replaced with the largest finite
     // float, if the ninf flag is set.
diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index ae5b965ff2723..27467cf05f798 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -849,6 +849,38 @@ define <2 x double> @minnum_snan_op1_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
+define <2 x double> @maxnum_snan_qnan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @maxnum_snan_qnan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maxnum.v2f64(<2 x double> <double 0x7ff4000000000000, double 0x7fff000000000000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maxnum_snan_qnan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @maxnum_snan_qnan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maxnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400000000dead, double 0x7fff000000000000>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minnum_snan_qnan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @minnum_snan_qnan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7ff4000dead00000, double 0x7fff000000000000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minnum_snan_qnan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @minnum_snan_qnan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400dead00dead, double 0x7fff00dead00dead>)
+  ret <2 x double> %r
+}
+
 define float @maxnum_undef_op1(float %x) {
 ; CHECK-LABEL: @maxnum_undef_op1(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -1346,6 +1378,38 @@ define <2 x double> @minimum_snan_op1_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
+define <2 x double> @maximum_snan_qnan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximum_snan_qnan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC000000000000, double 0x7FFF000000000000>
+;
+  %r = call <2 x double> @llvm.maximum.v2f64(<2 x double> <double 0x7ff4000000000000, double 0x7fff000000000000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximum_snan_qnan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximum_snan_qnan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC00000000DEAD, double 0x7FFF000000000000>
+;
+  %r = call <2 x double> @llvm.maximum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400000000dead, double 0x7fff000000000000>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimum_snan_qnan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimum_snan_qnan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC000DEAD00000, double 0x7FFF000000000000>
+;
+  %r = call <2 x double> @llvm.minimum.v2f64(<2 x double> <double 0x7ff4000dead00000, double 0x7fff000000000000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimum_snan_qnan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimum_snan_qnan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> <double 0x7FFC00DEAD00DEAD, double 0x7FFF00DEAD00DEAD>
+;
+  %r = call <2 x double> @llvm.minimum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400dead00dead, double 0x7fff00dead00dead>)
+  ret <2 x double> %r
+}
+
 define float @maximum_undef_op1(float %x) {
 ; CHECK-LABEL: @maximum_undef_op1(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -1894,6 +1958,30 @@ define <2 x double> @minimumnum_nan_op1_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
+define <2 x double> @maximumnum_nan_op0_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_nan_op0_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double 0x7ff8000000000000, double poison>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximumnum_nan_op1_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_nan_op1_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff8000000000000, double poison>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_nan_op1_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_nan_op1_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> <double poison, double 0x7ff8000dead00000>)
+  ret <2 x double> %r
+}
+
 define double @maximumnum_snan_op0(double %x) {
 ; CHECK-LABEL: @maximumnum_snan_op0(
 ; CHECK-NEXT:    ret double [[X:%.*]]
@@ -1958,6 +2046,70 @@ define <2 x double> @minimumnum_snan_op1_vec(<2 x double> %x) {
   ret <2 x double> %r
 }
 
+define <2 x double> @maximumnum_snan_op0_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_snan_op0_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double 0x7ff4000000000000, double poison>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximumnum_snan_op1_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_snan_op1_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff4000000000000, double poison>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_snan_op0_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_snan_op0_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double poison, double 0x7ff4000dead00000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_snan_op1_vec_partial_poison(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_snan_op1_vec_partial_poison(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> <double poison, double 0x7ff4000dead00000>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximumnum_snan_qnan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_snan_qnan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double 0x7ff4000000000000, double 0x7fff000000000000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @maximumnum_snan_qnan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @maximumnum_snan_qnan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400000000dead, double 0x7fff000000000000>)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_snan_qnan_op0_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_snan_qnan_op0_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double 0x7ff4000dead00000, double 0x7fff000000000000>, <2 x double> %x)
+  ret <2 x double> %r
+}
+
+define <2 x double> @minimumnum_snan_qnan_op1_vec(<2 x double> %x) {
+; CHECK-LABEL: @minimumnum_snan_qnan_op1_vec(
+; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+;
+  %r = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400dead00dead, double 0x7fff00dead00dead>)
+  ret <2 x double> %r
+}
+
 define float @maximumnum_poison_op1(float %x) {
 ; CHECK-LABEL: @maximumnum_poison_op1(
 ; CHECK-NEXT:    ret float [[X:%.*]]

>From 35b7d0855f89e32c3dffb9812f5d4e06df1ecf51 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Tue, 13 May 2025 13:27:35 +0000
Subject: [PATCH 03/19] Add vector tests for all inf/max tests

Add a vec2 variant for all the +inf/-inf/+max/-max
tests for maxnum/maximum/maximumnum.
---
 .../Transforms/InstSimplify/fminmax-folds.ll  | 504 ++++++++++++++++++
 1 file changed, 504 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index 27467cf05f798..a33dd6e6766d7 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -169,6 +169,57 @@ define float @test_minimumnum_const_inf(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_inf_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minnum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0x7FF0000000000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_inf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x7FF0000000000000)
+;
+  %r = call <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_inf_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maximum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0x7FF0000000000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_inf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_inf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x7FF0000000000000)
+;
+  %r = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_inf_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0x7FF0000000000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_neg_inf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_inf(
 ; CHECK-NEXT:    ret float 0xFFF0000000000000
@@ -220,6 +271,57 @@ define float @test_minimumnum_const_neg_inf(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_neg_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_neg_inf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xFFF0000000000000)
+;
+  %r = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_neg_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_neg_inf_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maxnum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0xFFF0000000000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_neg_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_neg_inf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_neg_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_neg_inf_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0xFFF0000000000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_neg_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_inf_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0xFFF0000000000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_neg_inf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_inf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xFFF0000000000000)
+;
+  %r = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_inf_nnan(float %x) {
 ; CHECK-LABEL: @test_minnum_const_inf_nnan(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -268,6 +370,54 @@ define float @test_minimumnum_const_inf_nnan(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x7FF0000000000000)
+;
+  %r = call nnan <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x7FF0000000000000)
+;
+  %r = call nnan <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x7FF0000000000000)
+;
+  %r = call nnan <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float 0x7ff0000000000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_inf_nnan_comm(float %x) {
 ; CHECK-LABEL: @test_minnum_const_inf_nnan_comm(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -412,6 +562,54 @@ define float @test_minimumnum_const_neg_inf_nnan(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_neg_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_neg_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xFFF0000000000000)
+;
+  %r = call nnan <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_neg_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_neg_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_neg_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_neg_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_neg_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_neg_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xFFF0000000000000)
+;
+  %r = call nnan <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_neg_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_neg_inf_nnan_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_inf_nnan_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xFFF0000000000000)
+;
+  %r = call nnan <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0xfff0000000000000, float 0xfff0000000000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_max(float %x) {
 ; CHECK-LABEL: @test_minnum_const_max(
 ; CHECK-NEXT:    [[R:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float 0x47EFFFFFE0000000)
@@ -466,6 +664,60 @@ define float @test_minimumnum_const_max(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_max_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minnum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_max_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maxnum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_max_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maximum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_max_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_max_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_max_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_neg_max(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_max(
 ; CHECK-NEXT:    [[R:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float 0xC7EFFFFFE0000000)
@@ -520,6 +772,60 @@ define float @test_minimumnum_const_neg_max(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_neg_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_neg_max_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minnum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_neg_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_neg_max_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maxnum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_neg_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_neg_max_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maximum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_neg_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_neg_max_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minimum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_neg_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_max_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_neg_max_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_max_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_max_ninf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_max_ninf(
 ; CHECK-NEXT:    [[R:%.*]] = call ninf float @llvm.minnum.f32(float [[X:%.*]], float 0x47EFFFFFE0000000)
@@ -571,6 +877,57 @@ define float @test_minimumnum_const_max_ninf(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_max_ninf_vec(
+; CHECK-NEXT:    [[X:%.*]] = call ninf <2 x float> @llvm.minnum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call ninf <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_max_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x47EFFFFFE0000000)
+;
+  %r = call ninf <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_max_ninf_vec(
+; CHECK-NEXT:    [[R:%.*]] = call ninf <2 x float> @llvm.maximum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call ninf <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_max_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call ninf <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_max_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x47EFFFFFE0000000)
+;
+  %r = call ninf <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_max_ninf_vec(
+; CHECK-NEXT:    [[X:%.*]] = call ninf <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0x47EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call ninf <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_neg_max_ninf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_max_ninf(
 ; CHECK-NEXT:    ret float 0xC7EFFFFFE0000000
@@ -622,6 +979,57 @@ define float @test_minimumnum_const_neg_max_ninf(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_neg_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_neg_max_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xC7EFFFFFE0000000)
+;
+  %r = call ninf <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_neg_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_neg_max_ninf_vec(
+; CHECK-NEXT:    [[R:%.*]] = call ninf <2 x float> @llvm.maxnum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call ninf <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_neg_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_neg_max_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call ninf <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_neg_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_neg_max_ninf_vec(
+; CHECK-NEXT:    [[X:%.*]] = call ninf <2 x float> @llvm.minimum.v2f32(<2 x float> [[X1:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[X]]
+;
+  %r = call ninf <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_neg_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_max_ninf_vec(
+; CHECK-NEXT:    [[R:%.*]] = call ninf <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X:%.*]], <2 x float> splat (float 0xC7EFFFFFE0000000))
+; CHECK-NEXT:    ret <2 x float> [[R]]
+;
+  %r = call ninf <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_neg_max_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_max_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xC7EFFFFFE0000000)
+;
+  %r = call ninf <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_max_nnan_ninf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_max_nnan_ninf(
 ; CHECK-NEXT:    ret float [[X:%.*]]
@@ -670,6 +1078,54 @@ define float @test_minimumnum_const_max_nnan_ninf(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan ninf <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x47EFFFFFE0000000)
+;
+  %r = call nnan ninf <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x47EFFFFFE0000000)
+;
+  %r = call nnan ninf <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan ninf <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0x47EFFFFFE0000000)
+;
+  %r = call nnan ninf <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan ninf <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0x47efffffe0000000, float 0x47efffffe0000000>)
+  ret <2 x float> %r
+}
+
 define float @test_minnum_const_neg_max_nnan_ninf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_max_nnan_ninf(
 ; CHECK-NEXT:    ret float 0xC7EFFFFFE0000000
@@ -718,6 +1174,54 @@ define float @test_minimumnum_const_neg_max_nnan_ninf(float %x) {
   ret float %r
 }
 
+define <2 x float> @test_minnum_const_neg_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_neg_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xC7EFFFFFE0000000)
+;
+  %r = call nnan ninf <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_neg_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_neg_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan ninf <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_neg_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_neg_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan ninf <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_neg_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_neg_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xC7EFFFFFE0000000)
+;
+  %r = call nnan ninf <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_neg_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_neg_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan ninf <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_neg_max_nnan_ninf_vec(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_neg_max_nnan_ninf_vec(
+; CHECK-NEXT:    ret <2 x float> splat (float 0xC7EFFFFFE0000000)
+;
+  %r = call nnan ninf <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0xc7efffffe0000000, float 0xc7efffffe0000000>)
+  ret <2 x float> %r
+}
+
 ; From the LangRef for minnum/maxnum:
 ; "If either operand is a qNaN, returns the other non-NaN operand. Returns NaN only if both operands are NaN or if either operand is sNaN."
 

>From aa652bf52664ad2d2db276ad1d38a245a9e32764 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Tue, 13 May 2025 14:41:48 +0000
Subject: [PATCH 04/19] Avoid optimization for maxnum(x, <sNaN, qNaN>)

Instead of transforming maxnum(x, <sNaN, qNaN>) -> x,
avoid folding the maxnum away so the sNaN element can
correctly be propagated as a qNaN.
---
 llvm/lib/Analysis/InstructionSimplify.cpp      | 18 +++++++++++++-----
 .../Transforms/InstSimplify/fminmax-folds.ll   | 12 ++++++++----
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 241161c5d6d50..ef564503f7931 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6742,14 +6742,22 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
 
     // minnum(x, qnan) -> x
     // maxnum(x, qnan) -> x
-    // minnum(x, snan) -> qnan (or x for vectors mixing snans and qnans)
-    // maxnum(x, snan) -> qnan (or x for vectors mixing snans and qnans)
+    // minnum(x, snan) -> qnan
+    // maxnum(x, snan) -> qnan
     // minimum(X, nan) -> qnan
     // maximum(X, nan) -> qnan
-    if (PropagateSNaN && match(Op1, m_sNaN()))
+    if (PropagateSNaN && match(Op1, m_sNaN())) {
       return propagateNaN(cast<Constant>(Op1));
-    else if (match(Op1, m_NaN()))
-      return PropagateNaN ? propagateNaN(cast<Constant>(Op1)) : Op0;
+    } else if (match(Op1, m_NaN())) {
+      if (PropagateNaN)
+        return propagateNaN(cast<Constant>(Op1));
+      // In cases like mixed <sNaN, qNaN> vectors, avoid the optimization to
+      // allow correct sNaN propagation where necessary.
+      else if (PropagateSNaN && !match(Op1, m_qNaN()))
+        break;
+      else
+        return Op0;
+    }
 
     // In the following folds, inf can be replaced with the largest finite
     // float, if the ninf flag is set.
diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index a33dd6e6766d7..db7bf06873036 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -1355,7 +1355,8 @@ define <2 x double> @minnum_snan_op1_vec(<2 x double> %x) {
 
 define <2 x double> @maxnum_snan_qnan_op0_vec(<2 x double> %x) {
 ; CHECK-LABEL: @maxnum_snan_qnan_op0_vec(
-; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+; CHECK-NEXT:    [[X:%.*]] = call <2 x double> @llvm.maxnum.v2f64(<2 x double> <double 0x7FF4000000000000, double 0x7FFF000000000000>, <2 x double> [[X1:%.*]])
+; CHECK-NEXT:    ret <2 x double> [[X]]
 ;
   %r = call <2 x double> @llvm.maxnum.v2f64(<2 x double> <double 0x7ff4000000000000, double 0x7fff000000000000>, <2 x double> %x)
   ret <2 x double> %r
@@ -1363,7 +1364,8 @@ define <2 x double> @maxnum_snan_qnan_op0_vec(<2 x double> %x) {
 
 define <2 x double> @maxnum_snan_qnan_op1_vec(<2 x double> %x) {
 ; CHECK-LABEL: @maxnum_snan_qnan_op1_vec(
-; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+; CHECK-NEXT:    [[X:%.*]] = call <2 x double> @llvm.maxnum.v2f64(<2 x double> [[X1:%.*]], <2 x double> <double 0x7FF400000000DEAD, double 0x7FFF000000000000>)
+; CHECK-NEXT:    ret <2 x double> [[X]]
 ;
   %r = call <2 x double> @llvm.maxnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400000000dead, double 0x7fff000000000000>)
   ret <2 x double> %r
@@ -1371,7 +1373,8 @@ define <2 x double> @maxnum_snan_qnan_op1_vec(<2 x double> %x) {
 
 define <2 x double> @minnum_snan_qnan_op0_vec(<2 x double> %x) {
 ; CHECK-LABEL: @minnum_snan_qnan_op0_vec(
-; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+; CHECK-NEXT:    [[X:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7FF4000DEAD00000, double 0x7FFF000000000000>, <2 x double> [[X1:%.*]])
+; CHECK-NEXT:    ret <2 x double> [[X]]
 ;
   %r = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7ff4000dead00000, double 0x7fff000000000000>, <2 x double> %x)
   ret <2 x double> %r
@@ -1379,7 +1382,8 @@ define <2 x double> @minnum_snan_qnan_op0_vec(<2 x double> %x) {
 
 define <2 x double> @minnum_snan_qnan_op1_vec(<2 x double> %x) {
 ; CHECK-LABEL: @minnum_snan_qnan_op1_vec(
-; CHECK-NEXT:    ret <2 x double> [[X:%.*]]
+; CHECK-NEXT:    [[X:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> [[X1:%.*]], <2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FFF00DEAD00DEAD>)
+; CHECK-NEXT:    ret <2 x double> [[X]]
 ;
   %r = call <2 x double> @llvm.minnum.v2f64(<2 x double> %x, <2 x double> <double 0x7ff400dead00dead, double 0x7fff00dead00dead>)
   ret <2 x double> %r

>From 65c4e84469b2d57b388b9eb0c626aefbe8c14365 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Tue, 13 May 2025 15:02:45 +0000
Subject: [PATCH 05/19] Update failing AMDGPU tests

Update some failing AMDGPU tests using maxnum's updated sNaN behaviour
and maximumnum's new set of optimizations.
---
 llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll | 6 ++----
 llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll         | 6 ++----
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll b/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
index ab476dd96c707..11e510df2d147 100644
--- a/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcanonicalize-elimination.ll
@@ -497,12 +497,10 @@ define amdgpu_kernel void @test_fold_canonicalize_minnum_value_f32(ptr addrspace
   ret void
 }
 
-; FIXME: Should there be more checks here? minnum with NaN operand is simplified away.
+; FIXME: Should there be more checks here? minnum with sNaN operand is simplified to qNaN.
 
 ; GCN-LABEL: test_fold_canonicalize_sNaN_value_f32:
-; GCN: {{flat|global}}_load_dword [[LOAD:v[0-9]+]]
-; VI: v_mul_f32_e32 v{{[0-9]+}}, 1.0, [[LOAD]]
-; GFX9: v_max_f32_e32 v{{[0-9]+}}, [[LOAD]], [[LOAD]]
+; GCN: v_mov_b32_e32 v{{.+}}, 0x7fc00000
 define amdgpu_kernel void @test_fold_canonicalize_sNaN_value_f32(ptr addrspace(1) %arg) {
   %id = tail call i32 @llvm.amdgcn.workitem.id.x()
   %gep = getelementptr inbounds float, ptr addrspace(1) %arg, i32 %id
diff --git a/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll b/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
index e687745469014..c5670d51a7719 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
@@ -2055,8 +2055,7 @@ define float @v_fneg_self_minimumnum_f32_ieee(float %a) #0 {
 ; GCN-LABEL: v_fneg_self_minimumnum_f32_ieee:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GCN-NEXT:    v_mul_f32_e32 v0, -1.0, v0
-; GCN-NEXT:    v_max_f32_e32 v0, v0, v0
+; GCN-NEXT:    v_xor_b32_e32 v0, 0x80000000, v0
 ; GCN-NEXT:    s_setpc_b64 s[30:31]
   %min = call float @llvm.minimumnum.f32(float %a, float %a)
   %min.fneg = fneg float %min
@@ -2391,8 +2390,7 @@ define float @v_fneg_self_maximumnum_f32_ieee(float %a) #0 {
 ; GCN-LABEL: v_fneg_self_maximumnum_f32_ieee:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GCN-NEXT:    v_mul_f32_e32 v0, -1.0, v0
-; GCN-NEXT:    v_min_f32_e32 v0, v0, v0
+; GCN-NEXT:    v_xor_b32_e32 v0, 0x80000000, v0
 ; GCN-NEXT:    s_setpc_b64 s[30:31]
   %max = call float @llvm.maximumnum.f32(float %a, float %a)
   %max.fneg = fneg float %max

>From a80f4f031fd8197d00c2da4ec50ed3be7ed8d9b0 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 15 May 2025 18:00:13 +0000
Subject: [PATCH 06/19] Tidy up if/else chain

---
 llvm/lib/Analysis/InstructionSimplify.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index ef564503f7931..3c14278427a81 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6746,9 +6746,9 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     // maxnum(x, snan) -> qnan
     // minimum(X, nan) -> qnan
     // maximum(X, nan) -> qnan
-    if (PropagateSNaN && match(Op1, m_sNaN())) {
+    if (PropagateSNaN && match(Op1, m_sNaN()))
       return propagateNaN(cast<Constant>(Op1));
-    } else if (match(Op1, m_NaN())) {
+    if (match(Op1, m_NaN())) {
       if (PropagateNaN)
         return propagateNaN(cast<Constant>(Op1));
       // In cases like mixed <sNaN, qNaN> vectors, avoid the optimization to

>From 4e7a52eb493888d2cbafe32b340e7a40e817e4ca Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Fri, 30 May 2025 10:25:22 +0000
Subject: [PATCH 07/19] Rewrite fmax/fmin InstSimplify elementwise

Rewrite all the fmin/fmax(x, const) optimizations in
InstSimplify to use elementwise checking instead of
pattern-matching.

This allows for improved splat-vector handling, as well as
handling of mixed constant vectors e.g. <sNan, Inf, poison>.
---
 llvm/lib/Analysis/InstructionSimplify.cpp     | 181 +++++++++++++-----
 .../Transforms/InstSimplify/fminmax-folds.ll  | 163 ++++++++++++++++
 2 files changed, 296 insertions(+), 48 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 3c14278427a81..bcf41cf86ffc8 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6723,7 +6723,7 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     // inserting an llvm.canonicalize to transform input sNaNs into qNaNs,
     // or may assume all NaN inputs are qNaNs.
 
-    // If the arguments are the same, this is a no-op.
+    // If the arguments are the same, this is a no-op (ignoring NaN quieting)
     if (Op0 == Op1)
       return Op0;
 
@@ -6735,54 +6735,139 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     if (Q.isUndefValue(Op1))
       return Op0;
 
-    bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
-    bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
-    bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
-                 IID == Intrinsic::minimumnum;
-
-    // minnum(x, qnan) -> x
-    // maxnum(x, qnan) -> x
-    // minnum(x, snan) -> qnan
-    // maxnum(x, snan) -> qnan
-    // minimum(X, nan) -> qnan
-    // maximum(X, nan) -> qnan
-    if (PropagateSNaN && match(Op1, m_sNaN()))
-      return propagateNaN(cast<Constant>(Op1));
-    if (match(Op1, m_NaN())) {
-      if (PropagateNaN)
-        return propagateNaN(cast<Constant>(Op1));
-      // In cases like mixed <sNaN, qNaN> vectors, avoid the optimization to
-      // allow correct sNaN propagation where necessary.
-      else if (PropagateSNaN && !match(Op1, m_qNaN()))
-        break;
-      else
-        return Op0;
-    }
+    if (Constant *C = dyn_cast<Constant>(Op1)) {
+      bool PropagateNaN =
+          IID == Intrinsic::minimum || IID == Intrinsic::maximum;
+      bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
+      bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
+                   IID == Intrinsic::minimumnum;
+
+      // Get the optimized value for a constant scalar input. The result may
+      // indicate either to use the non-const LHS value, or return a pointer
+      // to a new constant value to use instead of the input (after e.g.
+      // quieting NaNs). Returns empty optional value if it cannot be optimized.
+      typedef struct {
+        bool UseNonConstVal;
+        Constant *NewConstVal;
+      } OptResult;
+      auto GetOptResultFor = [PropagateNaN, PropagateSNaN, IsMin,
+                              Call](Constant *C) -> std::optional<OptResult> {
+        auto UseNonConstVal = []() -> OptResult { return {true, nullptr}; };
+        auto UseConstVal = [](Constant *C) -> OptResult { return {false, C}; };
+
+        // min/max(opt, poison) -> poison
+        if (isa<UndefValue>(C))
+          return UseConstVal(C);
+
+        const ConstantFP *CFP = dyn_cast<ConstantFP>(C);
+        if (!CFP)
+          return {};
+        APFloat CAPF = CFP->getValueAPF();
+
+        // minnum(x, qnan) -> x
+        // maxnum(x, qnan) -> x
+        // minnum(x, snan) -> qnan
+        // maxnum(x, snan) -> qnan
+        // minimum(X, nan) -> qnan
+        // maximum(X, nan) -> qnan
+        // minimumnum(X, nan) -> x
+        // maximumnum(X, nan) -> x
+        if (CAPF.isNaN()) {
+          if (PropagateNaN || (PropagateSNaN && CAPF.isSignaling()))
+            return UseConstVal(ConstantFP::get(C->getType(), CAPF.makeQuiet()));
+          else
+            return UseNonConstVal();
+        }
 
-    // In the following folds, inf can be replaced with the largest finite
-    // float, if the ninf flag is set.
-    const APFloat *C;
-    if (match(Op1, m_APFloat(C)) &&
-        (C->isInfinity() || (Call && Call->hasNoInfs() && C->isLargest()))) {
-      // minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
-      // maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
-      // minimum(X, -inf) -> -inf if nnan
-      // maximum(X, +inf) -> +inf if nnan
-      // minimumnum(X, -inf) -> -inf
-      // maximumnum(X, +inf) -> +inf
-      if (C->isNegative() == IsMin &&
-          (!PropagateNaN || (Call && Call->hasNoNaNs())))
-        return ConstantFP::get(ReturnType, *C);
-
-      // minnum(X, +inf) -> X if nnan
-      // maxnum(X, -inf) -> X if nnan
-      // minimum(X, +inf) -> X (ignoring quieting of sNaNs)
-      // maximum(X, -inf) -> X (ignoring quieting of sNaNs)
-      // maximumnum(X, -inf) -> X if nnan
-      // minimumnum(X, +inf) -> X if nnan
-      if (C->isNegative() != IsMin &&
-          (PropagateNaN || (Call && Call->hasNoNaNs())))
-        return Op0;
+        if (CAPF.isInfinity() ||
+            (Call && Call->hasNoInfs() && CAPF.isLargest())) {
+          // minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
+          // maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
+          // minimum(X, -inf) -> -inf if nnan
+          // maximum(X, +inf) -> +inf if nnan
+          // minimumnum(X, -inf) -> -inf
+          // maximumnum(X, +inf) -> +inf
+          if (CAPF.isNegative() == IsMin &&
+              (!PropagateNaN || (Call && Call->hasNoNaNs())))
+            return UseConstVal(C);
+
+          // minnum(X, +inf) -> X if nnan
+          // maxnum(X, -inf) -> X if nnan
+          // minimum(X, +inf) -> X (ignoring quieting of sNaNs)
+          // maximum(X, -inf) -> X (ignoring quieting of sNaNs)
+          // maximumnum(X, -inf) -> X if nnan
+          // minimumnum(X, +inf) -> X if nnan
+          if (CAPF.isNegative() != IsMin &&
+              (PropagateNaN || (Call && Call->hasNoNaNs())))
+            return UseNonConstVal();
+        }
+
+        // Cannot optimize this element
+        return {};
+      };
+
+      if (VectorType *VTy = dyn_cast<VectorType>(C->getType())) {
+        // Handle splat vectors (including scalable vectors)
+        if (Constant *SplatVal = C->getSplatValue()) {
+          std::optional<OptResult> OptSplatVal = GetOptResultFor(SplatVal);
+          if (OptSplatVal.has_value()) {
+            if (OptSplatVal.value().UseNonConstVal)
+              return Op0;
+            assert(OptSplatVal.value().NewConstVal != nullptr);
+            return ConstantVector::getSplat(VTy->getElementCount(),
+                                            OptSplatVal.value().NewConstVal);
+          }
+        }
+
+        // Check elementwise whether we can optimize to either a constant value
+        // or return the LHS value. We cannot mix and match LHS + constant
+        // elements, as this would require inserting a new VectorShuffle
+        // instruction, which is not allowed in simplifyBinOp, so bail early if
+        // any element cannot be optimized, or if lhs vs const optimizations
+        // start to mismatch. However, we can turn undef/poison into the LHS
+        // value, so only bail if we need at least 1 non undef/poison RHS const.
+        bool CanOptimize = true;
+        bool AllConstValsAreUndef = true;
+        if (auto *FVty = dyn_cast<FixedVectorType>(VTy)) {
+          unsigned NumElts = FVty->getNumElements();
+          // Storage to build up the constant return value (possible altered
+          // from the input RHS value by quieting NaNs)
+          SmallVector<Constant *, 16> NewC(NumElts);
+
+          bool NeedsConstElement = false;
+          bool NeedsLHSElement = false;
+          for (unsigned i = 0; i != NumElts; ++i) {
+            Constant *EltC = C->getAggregateElement(i);
+            std::optional<OptResult> OptElemVal = GetOptResultFor(EltC);
+            if (!OptElemVal.has_value()) {
+              CanOptimize = false;
+              break;
+            }
+            if (OptElemVal.value().UseNonConstVal) {
+              NeedsLHSElement = true;
+              if (NeedsConstElement && !AllConstValsAreUndef)
+                break;
+            } else {
+              NeedsConstElement = true;
+              assert(OptElemVal.value().NewConstVal != nullptr);
+              NewC[i] = OptElemVal.value().NewConstVal;
+              AllConstValsAreUndef &= isa<UndefValue>(NewC[i]);
+              if (NeedsLHSElement && !AllConstValsAreUndef)
+                break;
+            }
+          }
+
+          if (CanOptimize && (!NeedsLHSElement || AllConstValsAreUndef))
+            return NeedsLHSElement ? Op0 : ConstantVector::get(NewC);
+        }
+      } else {
+        // Handle scalar inputs
+        std::optional<OptResult> OptScalarVal = GetOptResultFor(C);
+        if (OptScalarVal.has_value()) {
+          OptResult Res = OptScalarVal.value();
+          return Res.UseNonConstVal ? Op0 : Res.NewConstVal;
+        }
+      }
     }
 
     // Min/max of the same operation with common operand:
diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index db7bf06873036..85387ca114f8a 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -8,10 +8,20 @@ declare float @llvm.minnum.f32(float, float)
 declare float @llvm.maxnum.f32(float, float)
 declare float @llvm.minimum.f32(float, float)
 declare float @llvm.maximum.f32(float, float)
+declare float @llvm.minimumnum.f32(float, float)
+declare float @llvm.maximumnum.f32(float, float)
 declare <2 x float> @llvm.minnum.v2f32(<2 x float>, <2 x float>)
 declare <2 x float> @llvm.maxnum.v2f32(<2 x float>, <2 x float>)
 declare <2 x float> @llvm.minimum.v2f32(<2 x float>, <2 x float>)
 declare <2 x float> @llvm.maximum.v2f32(<2 x float>, <2 x float>)
+declare <2 x float> @llvm.minimumnum.v2f32(<2 x float>, <2 x float>)
+declare <2 x float> @llvm.maximumnum.v2f32(<2 x float>, <2 x float>)
+declare <3 x float> @llvm.minnum.v3f32(<3 x float>, <3 x float>)
+declare <3 x float> @llvm.maxnum.v3f32(<3 x float>, <3 x float>)
+declare <3 x float> @llvm.minimum.v3f32(<3 x float>, <3 x float>)
+declare <3 x float> @llvm.maximum.v3f32(<3 x float>, <3 x float>)
+declare <3 x float> @llvm.minimumnum.v3f32(<3 x float>, <3 x float>)
+declare <3 x float> @llvm.maximumnum.v3f32(<3 x float>, <3 x float>)
 
 declare double @llvm.minnum.f64(double, double)
 declare double @llvm.maxnum.f64(double, double)
@@ -21,6 +31,10 @@ declare double @llvm.minimum.f64(double, double)
 declare double @llvm.maximum.f64(double, double)
 declare <2 x double> @llvm.minimum.v2f64(<2 x double>, <2 x double>)
 declare <2 x double> @llvm.maximum.v2f64(<2 x double>, <2 x double>)
+declare double @llvm.minimumnum.f64(double, double)
+declare double @llvm.maximumnum.f64(double, double)
+declare <2 x double> @llvm.minimumnum.v2f64(<2 x double>, <2 x double>)
+declare <2 x double> @llvm.maximumnum.v2f64(<2 x double>, <2 x double>)
 
 define float @test_minnum_const_nan(float %x) {
 ; CHECK-LABEL: @test_minnum_const_nan(
@@ -220,6 +234,155 @@ define <2 x float> @test_minimumnum_const_inf_vec(<2 x float> %x) {
   ret <2 x float> %r
 }
 
+define <vscale x 2 x float> @test_minnum_const_snan_scalable_vec(<vscale x 2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_snan_scalable_vec(
+; CHECK-NEXT:    ret <vscale x 2 x float> splat (float 0x7FFC000000000000)
+;
+  %r = call <vscale x 2 x float> @llvm.minnum.nxv2f32(<vscale x 2 x float> %x, <vscale x 2 x float> splat (float 0x7ff4000000000000))
+  ret <vscale x 2 x float> %r
+}
+
+define <vscale x 2 x float> @test_maxnum_const_snan_scalable_vec(<vscale x 2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_snan_scalable_vec(
+; CHECK-NEXT:    ret <vscale x 2 x float> splat (float 0x7FFC000000000000)
+;
+  %r = call <vscale x 2 x float> @llvm.maxnum.nxv2f32(<vscale x 2 x float> %x, <vscale x 2 x float> splat (float 0x7ff4000000000000))
+  ret <vscale x 2 x float> %r
+}
+
+define <vscale x 2 x float> @test_maximum_const_snan_scalable_vec(<vscale x 2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_snan_scalable_vec(
+; CHECK-NEXT:    ret <vscale x 2 x float> splat (float 0x7FFC000000000000)
+;
+  %r = call <vscale x 2 x float> @llvm.maximum.nxv2f32(<vscale x 2 x float> %x, <vscale x 2 x float> splat (float 0x7ff4000000000000))
+  ret <vscale x 2 x float> %r
+}
+
+define <vscale x 2 x float> @test_minimum_const_snan_scalable_vec(<vscale x 2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_snan_scalable_vec(
+; CHECK-NEXT:    ret <vscale x 2 x float> splat (float 0x7FFC000000000000)
+;
+  %r = call <vscale x 2 x float> @llvm.minimum.nxv2f32(<vscale x 2 x float> %x, <vscale x 2 x float> splat (float 0x7ff4000000000000))
+  ret <vscale x 2 x float> %r
+}
+
+define <vscale x 2 x float> @test_maximumnum_const_snan_scalable_vec(<vscale x 2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_snan_scalable_vec(
+; CHECK-NEXT:    ret <vscale x 2 x float> [[R:%.*]]
+;
+  %r = call <vscale x 2 x float> @llvm.maximumnum.nxv2f32(<vscale x 2 x float> %x, <vscale x 2 x float> splat (float 0x7ff4000000000000))
+  ret <vscale x 2 x float> %r
+}
+
+define <vscale x 2 x float> @test_minimumnum_const_snan_scalable_vec(<vscale x 2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_snan_scalable_vec(
+; CHECK-NEXT:    ret <vscale x 2 x float> [[X:%.*]]
+;
+  %r = call <vscale x 2 x float> @llvm.minimumnum.nxv2f32(<vscale x 2 x float> %x, <vscale x 2 x float> splat (float 0x7ff4000000000000))
+  ret <vscale x 2 x float> %r
+}
+
+define <2 x float> @test_minnum_const_inf_poison_vec_nnan(<2 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_inf_poison_vec_nnan(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float poison>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maxnum_const_inf_poison_vec_nnan(<2 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_inf_poison_vec_nnan(
+; CHECK-NEXT:    ret <2 x float> <float 0x7FF0000000000000, float poison>
+;
+  %r = call nnan <2 x float> @llvm.maxnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float poison>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximum_const_inf_poison_vec_nnan(<2 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_inf_poison_vec_nnan(
+; CHECK-NEXT:    ret <2 x float> <float 0x7FF0000000000000, float poison>
+;
+  %r = call nnan <2 x float> @llvm.maximum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float poison>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimum_const_inf_poison_vec_nnan(<2 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_inf_poison_vec_nnan(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.minimum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float poison>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_maximumnum_const_inf_poison_vec_nnan(<2 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_inf_poison_vec_nnan(
+; CHECK-NEXT:    ret <2 x float> <float 0x7FF0000000000000, float poison>
+;
+  %r = call nnan <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float poison>)
+  ret <2 x float> %r
+}
+
+define <2 x float> @test_minimumnum_const_inf_poison_vec_nnan(<2 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_inf_poison_vec_nnan(
+; CHECK-NEXT:    ret <2 x float> [[X:%.*]]
+;
+  %r = call nnan <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0x7ff0000000000000, float poison>)
+  ret <2 x float> %r
+}
+
+define <3 x float> @test_minnum_const_inf_poison_snan_vec(<3 x float> %x) {
+; CHECK-LABEL: @test_minnum_const_inf_poison_snan_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <3 x float> @llvm.minnum.v3f32(<3 x float> [[X1:%.*]], <3 x float> <float 0x7FF0000000000000, float poison, float 0x7FF4000000000000>)
+; CHECK-NEXT:    ret <3 x float> [[X]]
+;
+  %r = call <3 x float> @llvm.minnum.v3f32(<3 x float> %x, <3 x float> <float 0x7ff0000000000000, float poison, float 0x7ff4000000000000>)
+  ret <3 x float> %r
+}
+
+define <3 x float> @test_maxnum_const_inf_poison_snan_vec(<3 x float> %x) {
+; CHECK-LABEL: @test_maxnum_const_inf_poison_snan_vec(
+; CHECK-NEXT:    ret <3 x float> <float 0x7FF0000000000000, float poison, float 0x7FFC000000000000>
+;
+  %r = call <3 x float> @llvm.maxnum.v3f32(<3 x float> %x, <3 x float> <float 0x7ff0000000000000, float poison, float 0x7ff4000000000000>)
+  ret <3 x float> %r
+}
+
+define <3 x float> @test_maximum_const_inf_poison_snan_vec(<3 x float> %x) {
+; CHECK-LABEL: @test_maximum_const_inf_poison_snan_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <3 x float> @llvm.maximum.v3f32(<3 x float> [[X:%.*]], <3 x float> <float 0x7FF0000000000000, float poison, float 0x7FF4000000000000>)
+; CHECK-NEXT:    ret <3 x float> [[R]]
+;
+  %r = call <3 x float> @llvm.maximum.v3f32(<3 x float> %x, <3 x float> <float 0x7ff0000000000000, float poison, float 0x7ff4000000000000>)
+  ret <3 x float> %r
+}
+
+define <3 x float> @test_minimum_const_inf_poison_snan_vec(<3 x float> %x) {
+; CHECK-LABEL: @test_minimum_const_inf_poison_snan_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <3 x float> @llvm.minimum.v3f32(<3 x float> [[X1:%.*]], <3 x float> <float 0x7FF0000000000000, float poison, float 0x7FF4000000000000>)
+; CHECK-NEXT:    ret <3 x float> [[X]]
+;
+  %r = call <3 x float> @llvm.minimum.v3f32(<3 x float> %x, <3 x float> <float 0x7ff0000000000000, float poison, float 0x7ff4000000000000>)
+  ret <3 x float> %r
+}
+
+define <3 x float> @test_maximumnum_const_inf_poison_snan_vec(<3 x float> %x) {
+; CHECK-LABEL: @test_maximumnum_const_inf_poison_snan_vec(
+; CHECK-NEXT:    [[R:%.*]] = call <3 x float> @llvm.maximumnum.v3f32(<3 x float> [[X:%.*]], <3 x float> <float 0x7FF0000000000000, float poison, float 0x7FF4000000000000>)
+; CHECK-NEXT:    ret <3 x float> [[R]]
+;
+  %r = call <3 x float> @llvm.maximumnum.v3f32(<3 x float> %x, <3 x float> <float 0x7ff0000000000000, float poison, float 0x7ff4000000000000>)
+  ret <3 x float> %r
+}
+
+define <3 x float> @test_minimumnum_const_inf_poison_snan_vec(<3 x float> %x) {
+; CHECK-LABEL: @test_minimumnum_const_inf_poison_snan_vec(
+; CHECK-NEXT:    [[X:%.*]] = call <3 x float> @llvm.minimumnum.v3f32(<3 x float> [[X1:%.*]], <3 x float> <float 0x7FF0000000000000, float poison, float 0x7FF4000000000000>)
+; CHECK-NEXT:    ret <3 x float> [[X]]
+;
+  %r = call <3 x float> @llvm.minimumnum.v3f32(<3 x float> %x, <3 x float> <float 0x7ff0000000000000, float poison, float 0x7ff4000000000000>)
+  ret <3 x float> %r
+}
+
 define float @test_minnum_const_neg_inf(float %x) {
 ; CHECK-LABEL: @test_minnum_const_neg_inf(
 ; CHECK-NEXT:    ret float 0xFFF0000000000000

>From 97fa62606ab265d5eaf32cc0a8f8ec485c147d12 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 3 Jul 2025 15:08:39 +0000
Subject: [PATCH 08/19] Remove unused pattern match functions

Remove sNaN + qNaN patterns added in earlier version of
the patch, since they are no longer used in the new
elementwise approach.
---
 llvm/include/llvm/IR/PatternMatch.h | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 6596fa431d2cb..1f86cdfd94e17 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -707,25 +707,10 @@ m_SpecificInt_ICMP(ICmpInst::Predicate Predicate, const APInt &Threshold) {
 struct is_nan {
   bool isValue(const APFloat &C) const { return C.isNaN(); }
 };
-
-struct is_snan {
-  bool isValue(const APFloat &C) const { return C.isSignaling(); }
-};
-
-struct is_qnan {
-  bool isValue(const APFloat &C) const { return C.isNaN() && !C.isSignaling(); }
-};
-
 /// Match an arbitrary NaN constant. This includes quiet and signalling nans.
 /// For vectors, this includes constants with undefined elements.
 inline cstfp_pred_ty<is_nan> m_NaN() { return cstfp_pred_ty<is_nan>(); }
 
-/// Match quiet NaN constants, including vectors with undefined elements.
-inline cstfp_pred_ty<is_qnan> m_qNaN() { return cstfp_pred_ty<is_qnan>(); }
-
-/// Match signalling NaN constants, including vectors with undefined elements.
-inline cstfp_pred_ty<is_snan> m_sNaN() { return cstfp_pred_ty<is_snan>(); }
-
 struct is_nonnan {
   bool isValue(const APFloat &C) const { return !C.isNaN(); }
 };

>From 7b1b7507b9573719df515b724d4501b6b13cda0e Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 3 Jul 2025 15:16:08 +0000
Subject: [PATCH 09/19] Fix AMDGPU tests

Re-generate 2 AMDGPU tests that are failing after rebasing this change:
 - llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
 - llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll
---
 llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll |  4 +-
 .../Transforms/InstCombine/AMDGPU/fmed3.ll    | 39 ++++++-------------
 2 files changed, 13 insertions(+), 30 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll b/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
index c5670d51a7719..017f24d47e1cb 100644
--- a/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
+++ b/llvm/test/CodeGen/AMDGPU/fneg-combines.new.ll
@@ -2066,7 +2066,7 @@ define float @v_fneg_self_minimumnum_f32_no_ieee(float %a) #4 {
 ; GCN-LABEL: v_fneg_self_minimumnum_f32_no_ieee:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GCN-NEXT:    v_max_f32_e64 v0, -v0, -v0
+; GCN-NEXT:    v_xor_b32_e32 v0, 0x80000000, v0
 ; GCN-NEXT:    s_setpc_b64 s[30:31]
   %min = call float @llvm.minimumnum.f32(float %a, float %a)
   %min.fneg = fneg float %min
@@ -2401,7 +2401,7 @@ define float @v_fneg_self_maximumnum_f32_no_ieee(float %a) #4 {
 ; GCN-LABEL: v_fneg_self_maximumnum_f32_no_ieee:
 ; GCN:       ; %bb.0:
 ; GCN-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GCN-NEXT:    v_min_f32_e64 v0, -v0, -v0
+; GCN-NEXT:    v_xor_b32_e32 v0, 0x80000000, v0
 ; GCN-NEXT:    s_setpc_b64 s[30:31]
   %max = call float @llvm.maximumnum.f32(float %a, float %a)
   %max.fneg = fneg float %max
diff --git a/llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll b/llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll
index 361a2b8280910..378ca1fa44c17 100644
--- a/llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll
+++ b/llvm/test/Transforms/InstCombine/AMDGPU/fmed3.ll
@@ -269,42 +269,27 @@ define float @fmed3_constant_src2_1_f32(float %x, float %y) #1 {
 }
 
 define float @fmed3_x_qnan0_qnan1_f32(float %x) #1 {
-; IEEE1-LABEL: define float @fmed3_x_qnan0_qnan1_f32(
-; IEEE1-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; IEEE1-NEXT:    ret float [[X]]
-;
-; IEEE0-LABEL: define float @fmed3_x_qnan0_qnan1_f32(
-; IEEE0-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0x7FF8002000000000)
-; IEEE0-NEXT:    ret float [[MED3]]
+; CHECK-LABEL: define float @fmed3_x_qnan0_qnan1_f32(
+; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    ret float [[X]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float %x, float 0x7FF8001000000000, float 0x7FF8002000000000)
   ret float %med3
 }
 
 define float @fmed3_qnan0_x_qnan1_f32(float %x) #1 {
-; IEEE1-LABEL: define float @fmed3_qnan0_x_qnan1_f32(
-; IEEE1-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; IEEE1-NEXT:    ret float [[X]]
-;
-; IEEE0-LABEL: define float @fmed3_qnan0_x_qnan1_f32(
-; IEEE0-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0x7FF8002000000000)
-; IEEE0-NEXT:    ret float [[MED3]]
+; CHECK-LABEL: define float @fmed3_qnan0_x_qnan1_f32(
+; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    ret float [[X]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float 0x7FF8001000000000, float %x, float 0x7FF8002000000000)
   ret float %med3
 }
 
 define float @fmed3_qnan0_qnan1_x_f32(float %x) #1 {
-; IEEE1-LABEL: define float @fmed3_qnan0_qnan1_x_f32(
-; IEEE1-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; IEEE1-NEXT:    ret float [[X]]
-;
-; IEEE0-LABEL: define float @fmed3_qnan0_qnan1_x_f32(
-; IEEE0-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0x7FF8002000000000)
-; IEEE0-NEXT:    ret float [[MED3]]
+; CHECK-LABEL: define float @fmed3_qnan0_qnan1_x_f32(
+; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    ret float [[X]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float 0x7FF8001000000000, float 0x7FF8002000000000, float %x)
   ret float %med3
@@ -448,8 +433,7 @@ define float @fmed3_snan1_x_snan2_f32(float %x) #1 {
 ;
 ; IEEE0-LABEL: define float @fmed3_snan1_x_snan2_f32(
 ; IEEE0-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0x7FF0000040000000)
-; IEEE0-NEXT:    ret float [[MED3]]
+; IEEE0-NEXT:    ret float [[X]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float 0x7FF0000020000000, float %x, float 0x7FF0000040000000)
   ret float %med3
@@ -462,8 +446,7 @@ define float @fmed3_x_snan1_snan2_f32(float %x) #1 {
 ;
 ; IEEE0-LABEL: define float @fmed3_x_snan1_snan2_f32(
 ; IEEE0-SAME: float [[X:%.*]]) #[[ATTR1]] {
-; IEEE0-NEXT:    [[MED3:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0x7FF0000040000000)
-; IEEE0-NEXT:    ret float [[MED3]]
+; IEEE0-NEXT:    ret float [[X]]
 ;
   %med3 = call float @llvm.amdgcn.fmed3.f32(float %x, float 0x7FF0000020000000, float 0x7FF0000040000000)
   ret float %med3

>From 75cff983529222f74a57aaed8807250881e0cdd5 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 3 Jul 2025 15:22:09 +0000
Subject: [PATCH 10/19] Avoid elementwise testing for failed splats

Put the elementwise checks for fixed-vector elements in an else block
after checking for splats so that fixed-vector splats do not get
checked twice if they fail to be optimized in the first check.
---
 llvm/lib/Analysis/InstructionSimplify.cpp | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index bcf41cf86ffc8..df6d309664ee5 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6817,18 +6817,17 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
             return ConstantVector::getSplat(VTy->getElementCount(),
                                             OptSplatVal.value().NewConstVal);
           }
-        }
-
-        // Check elementwise whether we can optimize to either a constant value
-        // or return the LHS value. We cannot mix and match LHS + constant
-        // elements, as this would require inserting a new VectorShuffle
-        // instruction, which is not allowed in simplifyBinOp, so bail early if
-        // any element cannot be optimized, or if lhs vs const optimizations
-        // start to mismatch. However, we can turn undef/poison into the LHS
-        // value, so only bail if we need at least 1 non undef/poison RHS const.
-        bool CanOptimize = true;
-        bool AllConstValsAreUndef = true;
-        if (auto *FVty = dyn_cast<FixedVectorType>(VTy)) {
+        } else if (auto *FVty = dyn_cast<FixedVectorType>(VTy)) {
+          // Check elementwise whether we can optimize to either a constant
+          // value or return the LHS value. We cannot mix and match LHS +
+          // constant elements, as this would require inserting a new
+          // VectorShuffle instruction, which is not allowed in simplifyBinOp,
+          // so bail early if any element cannot be optimized, or if lhs vs
+          // const optimizations start to mismatch. However, we can turn
+          // undef/poison into the LHS value, so only bail if we need at least 1
+          // non undef/poison RHS const.
+          bool CanOptimize = true;
+          bool AllConstValsAreUndef = true;
           unsigned NumElts = FVty->getNumElements();
           // Storage to build up the constant return value (possible altered
           // from the input RHS value by quieting NaNs)

>From 23c002e13276ff7eafb6c34b78cc032dea1d65a8 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Mon, 18 Aug 2025 10:18:33 +0000
Subject: [PATCH 11/19] Fix comment consistency

Swap min/max lines to be in consistent order with other similar
comments.
---
 llvm/lib/Analysis/InstructionSimplify.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index df6d309664ee5..de4cfd1ae18dd 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6795,8 +6795,8 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
           // maxnum(X, -inf) -> X if nnan
           // minimum(X, +inf) -> X (ignoring quieting of sNaNs)
           // maximum(X, -inf) -> X (ignoring quieting of sNaNs)
-          // maximumnum(X, -inf) -> X if nnan
           // minimumnum(X, +inf) -> X if nnan
+          // maximumnum(X, -inf) -> X if nnan
           if (CAPF.isNegative() != IsMin &&
               (PropagateNaN || (Call && Call->hasNoNaNs())))
             return UseNonConstVal();

>From 12c05acce95d0b7c9cd47594516febdae032ade1 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Wed, 20 Aug 2025 12:59:55 +0000
Subject: [PATCH 12/19] Refactor to simplify

Make the code simpler, shorter, and clearer by:
 * Splitting the lambda out into a separate function
 * Instead of returning an optional of a bool + value, use an enum +
   value
 * The new enum specifies whether to avoid optimizing, return LHS,
   return const, or return either.
 * Use the same enum to cover the logic of both the individual scalar
   elements, and the vector as a whole.
 * Use this enum to cover the fact that undef/poison could choose either
   side, rather than tracking it with a separate check.
 * Extract common code from the splat vs fixed vs scalar paths.
 * Merge undef handling with the above approach too, rather than using a
   separate check.
 * Simplify some comments.
---
 llvm/lib/Analysis/InstructionSimplify.cpp | 231 +++++++++++-----------
 1 file changed, 110 insertions(+), 121 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index c93b42d5964df..f6c6c73d310ee 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6456,6 +6456,82 @@ static Value *foldMinimumMaximumSharedOp(Intrinsic::ID IID, Value *Op0,
   return nullptr;
 }
 
+enum class MinMaxOptResult {
+  CannotOptimize = 0,
+  UseNewConstVal = 1,
+  UseOtherVal = 2,
+  // For undef/poison, we can choose to either propgate undef/poison or
+  // use the LHS value depending on what will allow more optimization.
+  UseEither = 3
+};
+// Get the optimized value for a min/max instruction with a single constant
+// input (either undef or scalar constantFP). The result may indicate to
+// use the non-const LHS value, use a new constant value instead (with NaNs
+// quieted), or to choose either option in the case of undef/poison.
+static MinMaxOptResult OptimizeConstMinMax(const Constant *RHSConst,
+                                           const Intrinsic::ID IID,
+                                           const CallBase *Call,
+                                           Constant **OutNewConstVal) {
+  assert(OutNewConstVal != nullptr);
+
+  bool PropagateNaN = IID == Intrinsic::minimum || IID == Intrinsic::maximum;
+  bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
+  bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
+               IID == Intrinsic::minimumnum;
+
+  // min/max(x, poison) -> either x or poison
+  if (isa<UndefValue>(RHSConst)) {
+    *OutNewConstVal = const_cast<Constant *>(RHSConst);
+    return MinMaxOptResult::UseEither;
+  }
+
+  const ConstantFP *CFP = dyn_cast<ConstantFP>(RHSConst);
+  if (!CFP)
+    return MinMaxOptResult::CannotOptimize;
+  APFloat CAPF = CFP->getValueAPF();
+
+  // minnum(x, qnan) -> x
+  // maxnum(x, qnan) -> x
+  // minnum(x, snan) -> qnan
+  // maxnum(x, snan) -> qnan
+  // minimum(X, nan) -> qnan
+  // maximum(X, nan) -> qnan
+  // minimumnum(X, nan) -> x
+  // maximumnum(X, nan) -> x
+  if (CAPF.isNaN()) {
+    if (PropagateNaN || (PropagateSNaN && CAPF.isSignaling())) {
+      *OutNewConstVal = ConstantFP::get(CFP->getType(), CAPF.makeQuiet());
+      return MinMaxOptResult::UseNewConstVal;
+    }
+    return MinMaxOptResult::UseOtherVal;
+  }
+
+  if (CAPF.isInfinity() || (Call && Call->hasNoInfs() && CAPF.isLargest())) {
+    // minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
+    // maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
+    // minimum(X, -inf) -> -inf if nnan
+    // maximum(X, +inf) -> +inf if nnan
+    // minimumnum(X, -inf) -> -inf
+    // maximumnum(X, +inf) -> +inf
+    if (CAPF.isNegative() == IsMin &&
+        (!PropagateNaN || (Call && Call->hasNoNaNs()))) {
+      *OutNewConstVal = const_cast<Constant *>(RHSConst);
+      return MinMaxOptResult::UseNewConstVal;
+    }
+
+    // minnum(X, +inf) -> X if nnan
+    // maxnum(X, -inf) -> X if nnan
+    // minimum(X, +inf) -> X (ignoring quieting of sNaNs)
+    // maximum(X, -inf) -> X (ignoring quieting of sNaNs)
+    // minimumnum(X, +inf) -> X if nnan
+    // maximumnum(X, -inf) -> X if nnan
+    if (CAPF.isNegative() != IsMin &&
+        (PropagateNaN || (Call && Call->hasNoNaNs())))
+      return MinMaxOptResult::UseOtherVal;
+  }
+  return MinMaxOptResult::CannotOptimize;
+}
+
 Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
                                      Value *Op0, Value *Op1,
                                      const SimplifyQuery &Q,
@@ -6721,142 +6797,55 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
     if (isa<Constant>(Op0))
       std::swap(Op0, Op1);
 
-    // If an argument is undef, return the other argument.
-    if (Q.isUndefValue(Op1))
-      return Op0;
-
     if (Constant *C = dyn_cast<Constant>(Op1)) {
-      bool PropagateNaN =
-          IID == Intrinsic::minimum || IID == Intrinsic::maximum;
-      bool PropagateSNaN = IID == Intrinsic::minnum || IID == Intrinsic::maxnum;
-      bool IsMin = IID == Intrinsic::minimum || IID == Intrinsic::minnum ||
-                   IID == Intrinsic::minimumnum;
-
-      // Get the optimized value for a constant scalar input. The result may
-      // indicate either to use the non-const LHS value, or return a pointer
-      // to a new constant value to use instead of the input (after e.g.
-      // quieting NaNs). Returns empty optional value if it cannot be optimized.
-      typedef struct {
-        bool UseNonConstVal;
-        Constant *NewConstVal;
-      } OptResult;
-      auto GetOptResultFor = [PropagateNaN, PropagateSNaN, IsMin,
-                              Call](Constant *C) -> std::optional<OptResult> {
-        auto UseNonConstVal = []() -> OptResult { return {true, nullptr}; };
-        auto UseConstVal = [](Constant *C) -> OptResult { return {false, C}; };
-
-        // min/max(opt, poison) -> poison
-        if (isa<UndefValue>(C))
-          return UseConstVal(C);
-
-        const ConstantFP *CFP = dyn_cast<ConstantFP>(C);
-        if (!CFP)
-          return {};
-        APFloat CAPF = CFP->getValueAPF();
-
-        // minnum(x, qnan) -> x
-        // maxnum(x, qnan) -> x
-        // minnum(x, snan) -> qnan
-        // maxnum(x, snan) -> qnan
-        // minimum(X, nan) -> qnan
-        // maximum(X, nan) -> qnan
-        // minimumnum(X, nan) -> x
-        // maximumnum(X, nan) -> x
-        if (CAPF.isNaN()) {
-          if (PropagateNaN || (PropagateSNaN && CAPF.isSignaling()))
-            return UseConstVal(ConstantFP::get(C->getType(), CAPF.makeQuiet()));
-          else
-            return UseNonConstVal();
-        }
-
-        if (CAPF.isInfinity() ||
-            (Call && Call->hasNoInfs() && CAPF.isLargest())) {
-          // minnum(X, -inf) -> -inf (ignoring sNaN -> qNaN propagation)
-          // maxnum(X, +inf) -> +inf (ignoring sNaN -> qNaN propagation)
-          // minimum(X, -inf) -> -inf if nnan
-          // maximum(X, +inf) -> +inf if nnan
-          // minimumnum(X, -inf) -> -inf
-          // maximumnum(X, +inf) -> +inf
-          if (CAPF.isNegative() == IsMin &&
-              (!PropagateNaN || (Call && Call->hasNoNaNs())))
-            return UseConstVal(C);
-
-          // minnum(X, +inf) -> X if nnan
-          // maxnum(X, -inf) -> X if nnan
-          // minimum(X, +inf) -> X (ignoring quieting of sNaNs)
-          // maximum(X, -inf) -> X (ignoring quieting of sNaNs)
-          // minimumnum(X, +inf) -> X if nnan
-          // maximumnum(X, -inf) -> X if nnan
-          if (CAPF.isNegative() != IsMin &&
-              (PropagateNaN || (Call && Call->hasNoNaNs())))
-            return UseNonConstVal();
-        }
-
-        // Cannot optimize this element
-        return {};
-      };
+      MinMaxOptResult OptResult = MinMaxOptResult::CannotOptimize;
+      Constant *NewConst = nullptr;
 
       if (VectorType *VTy = dyn_cast<VectorType>(C->getType())) {
-        // Handle splat vectors (including scalable vectors)
+        ElementCount ElemCount = VTy->getElementCount();
+
         if (Constant *SplatVal = C->getSplatValue()) {
-          std::optional<OptResult> OptSplatVal = GetOptResultFor(SplatVal);
-          if (OptSplatVal.has_value()) {
-            if (OptSplatVal.value().UseNonConstVal)
-              return Op0;
-            assert(OptSplatVal.value().NewConstVal != nullptr);
-            return ConstantVector::getSplat(VTy->getElementCount(),
-                                            OptSplatVal.value().NewConstVal);
-          }
+          // Handle splat vectors (including scalable vectors)
+          OptResult = OptimizeConstMinMax(SplatVal, IID, Call, &NewConst);
+          if (OptResult == MinMaxOptResult::UseNewConstVal)
+            NewConst = ConstantVector::getSplat(ElemCount, NewConst);
+
         } else if (auto *FVty = dyn_cast<FixedVectorType>(VTy)) {
+          // Storage to build up new const return value (with NaNs quieted)
+          SmallVector<Constant *, 16> NewC(ElemCount.getFixedValue());
+
           // Check elementwise whether we can optimize to either a constant
           // value or return the LHS value. We cannot mix and match LHS +
           // constant elements, as this would require inserting a new
-          // VectorShuffle instruction, which is not allowed in simplifyBinOp,
-          // so bail early if any element cannot be optimized, or if lhs vs
-          // const optimizations start to mismatch. However, we can turn
-          // undef/poison into the LHS value, so only bail if we need at least 1
-          // non undef/poison RHS const.
-          bool CanOptimize = true;
-          bool AllConstValsAreUndef = true;
-          unsigned NumElts = FVty->getNumElements();
-          // Storage to build up the constant return value (possible altered
-          // from the input RHS value by quieting NaNs)
-          SmallVector<Constant *, 16> NewC(NumElts);
-
-          bool NeedsConstElement = false;
-          bool NeedsLHSElement = false;
-          for (unsigned i = 0; i != NumElts; ++i) {
-            Constant *EltC = C->getAggregateElement(i);
-            std::optional<OptResult> OptElemVal = GetOptResultFor(EltC);
-            if (!OptElemVal.has_value()) {
-              CanOptimize = false;
+          // VectorShuffle instruction, which is not allowed in simplifyBinOp.
+          OptResult = MinMaxOptResult::UseEither;
+          for (unsigned i = 0; i != ElemCount.getFixedValue(); ++i) {
+            auto ElemResult = OptimizeConstMinMax(C->getAggregateElement(i),
+                                                  IID, Call, &NewConst);
+            if (ElemResult == MinMaxOptResult::CannotOptimize ||
+                (ElemResult != OptResult &&
+                 OptResult != MinMaxOptResult::UseEither &&
+                 ElemResult != MinMaxOptResult::UseEither)) {
+              OptResult = MinMaxOptResult::CannotOptimize;
               break;
             }
-            if (OptElemVal.value().UseNonConstVal) {
-              NeedsLHSElement = true;
-              if (NeedsConstElement && !AllConstValsAreUndef)
-                break;
-            } else {
-              NeedsConstElement = true;
-              assert(OptElemVal.value().NewConstVal != nullptr);
-              NewC[i] = OptElemVal.value().NewConstVal;
-              AllConstValsAreUndef &= isa<UndefValue>(NewC[i]);
-              if (NeedsLHSElement && !AllConstValsAreUndef)
-                break;
-            }
+            NewC[i] = NewConst;
+            if (ElemResult != MinMaxOptResult::UseEither)
+              OptResult = ElemResult;
           }
-
-          if (CanOptimize && (!NeedsLHSElement || AllConstValsAreUndef))
-            return NeedsLHSElement ? Op0 : ConstantVector::get(NewC);
+          if (OptResult == MinMaxOptResult::UseNewConstVal)
+            NewConst = ConstantVector::get(NewC);
         }
       } else {
         // Handle scalar inputs
-        std::optional<OptResult> OptScalarVal = GetOptResultFor(C);
-        if (OptScalarVal.has_value()) {
-          OptResult Res = OptScalarVal.value();
-          return Res.UseNonConstVal ? Op0 : Res.NewConstVal;
-        }
+        OptResult = OptimizeConstMinMax(C, IID, Call, &NewConst);
       }
+
+      if (OptResult == MinMaxOptResult::UseOtherVal ||
+          OptResult == MinMaxOptResult::UseEither)
+        return Op0; // Return the other arg (ignoring NaN quieting)
+      else if (OptResult == MinMaxOptResult::UseNewConstVal)
+        return NewConst;
     }
 
     // Min/max of the same operation with common operand:

>From 08b0df575c529e995d2ab73947506a49c08eae97 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 25 Sep 2025 10:40:56 +0000
Subject: [PATCH 13/19] Regenerate test results

Run update_test_checks.py on fminmax-folds.ll to get the
results that change from this patchset (especially the snan/qnan
changes, and adding maximumnum/minimumnum.
---
 .../Transforms/InstSimplify/fminmax-folds.ll  | 124 ++++++------------
 1 file changed, 43 insertions(+), 81 deletions(-)

diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index 26b51146057e9..0ce8057ddb5fb 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -19,10 +19,8 @@ define void @minmax_qnan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %mi
 ; CHECK-NEXT:    store float [[X]], ptr [[MAXNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float 0x7FFF000000000000, ptr [[MINIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float 0x7FFF000000000000, ptr [[MAXIMUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0x7FFF000000000000)
-; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float 0x7FFF000000000000)
-; CHECK-NEXT:    store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call float @llvm.minnum.f32(float %x, float 0x7FFF000000000000)
@@ -45,14 +43,12 @@ define void @minmax_qnan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %mi
 ; TODO currently snan is treated the same as qnan, but maxnum/minnum should really return qnan for these cases, not X
 define void @minmax_snan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_snan_f32(
-; CHECK-NEXT:    store float [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 4
-; CHECK-NEXT:    store float [[X]], ptr [[MAXNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MINIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MAXIMUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0x7FF4000000000000)
-; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float 0x7FF4000000000000)
-; CHECK-NEXT:    store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MINIMUM_RES1:%.*]], align 4
+; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MAXIMUM_RES1:%.*]], align 4
+; CHECK-NEXT:    store float [[MINIMUMNUM:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call float @llvm.minnum.f32(float %x, float 0x7FF4000000000000)
@@ -78,10 +74,8 @@ define void @minmax_qnan_nxv2f64_op0(<vscale x 2 x double> %x, ptr %minnum_res,
 ; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MAXNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FF8000DEAD00000), ptr [[MINIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FF8000DEAD00000), ptr [[MAXIMUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call <vscale x 2 x double> @llvm.minimumnum.nxv2f64(<vscale x 2 x double> splat (double 0x7FF8000DEAD00000), <vscale x 2 x double> [[X]])
-; CHECK-NEXT:    store <vscale x 2 x double> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call <vscale x 2 x double> @llvm.maximumnum.nxv2f64(<vscale x 2 x double> splat (double 0x7FF8000DEAD00000), <vscale x 2 x double> [[X]])
-; CHECK-NEXT:    store <vscale x 2 x double> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call <vscale x 2 x double> @llvm.minnum.nxv2f64(<vscale x 2 x double> splat (double 0x7FF8000DEAD00000), <vscale x 2 x double> %x)
@@ -104,14 +98,12 @@ define void @minmax_qnan_nxv2f64_op0(<vscale x 2 x double> %x, ptr %minnum_res,
 ; TODO currently snan is treated the same as qnan, but maxnum/minnum should really return qnan for these cases, not X
 define void @minmax_snan_nxv2f64_op1(<vscale x 2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_snan_nxv2f64_op1(
-; CHECK-NEXT:    store <vscale x 2 x double> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 16
-; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MAXNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MINIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MAXIMUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call <vscale x 2 x double> @llvm.minimumnum.nxv2f64(<vscale x 2 x double> splat (double 0x7FF400DEAD00DEAD), <vscale x 2 x double> [[X]])
-; CHECK-NEXT:    store <vscale x 2 x double> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call <vscale x 2 x double> @llvm.maximumnum.nxv2f64(<vscale x 2 x double> splat (double 0x7FF400DEAD00DEAD), <vscale x 2 x double> [[X]])
-; CHECK-NEXT:    store <vscale x 2 x double> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MINIMUM_RES1:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MAXIMUM_RES1:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> [[MINIMUMNUM:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> [[MINIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call <vscale x 2 x double> @llvm.minnum.nxv2f64(<vscale x 2 x double> splat (double 0x7FF400DEAD00DEAD), <vscale x 2 x double> %x)
@@ -134,14 +126,14 @@ define void @minmax_snan_nxv2f64_op1(<vscale x 2 x double> %x, ptr %minnum_res,
 ; TODO Currently, we treat SNaN and QNaN the same. However, for maxnum and minnum, we should not optimize this, as we should return <%x0, QNaN> instead of <%x0, %x1>
 define void @minmax_mixed_snan_qnan_v2f64(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_mixed_snan_qnan_v2f64(
-; CHECK-NEXT:    store <2 x double> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 16
-; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MAXNUM_RES:%.*]], align 16
+; CHECK-NEXT:    [[X:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[MINIMUMNUM:%.*]])
+; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MINNUM_RES:%.*]], align 16
+; CHECK-NEXT:    [[MAXNUM:%.*]] = call <2 x double> @llvm.maxnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[MINIMUMNUM]])
+; CHECK-NEXT:    store <2 x double> [[MAXNUM]], ptr [[MAXNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <2 x double> <double 0x7FFC00DEAD00DEAD, double 0x7FF8000FEED00000>, ptr [[MINIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <2 x double> <double 0x7FFC00DEAD00DEAD, double 0x7FF8000FEED00000>, ptr [[MAXIMUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[X]])
 ; CHECK-NEXT:    store <2 x double> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[X]])
-; CHECK-NEXT:    store <2 x double> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> [[MINIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> %x)
@@ -169,10 +161,8 @@ define void @minmax_mixed_qnan_poison_v2f64(<2 x double> %x, ptr %minnum_res, pt
 ; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MAXNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <2 x double> <double poison, double 0x7FF8000DEAD00000>, ptr [[MINIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <2 x double> <double poison, double 0x7FF8000DEAD00000>, ptr [[MAXIMUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double poison, double 0x7FF8000DEAD00000>, <2 x double> [[X]])
-; CHECK-NEXT:    store <2 x double> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double poison, double 0x7FF8000DEAD00000>, <2 x double> [[X]])
-; CHECK-NEXT:    store <2 x double> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double poison, double 0x7FF8000DEAD00000>, <2 x double> %x)
@@ -201,10 +191,8 @@ define void @minmax_poison_op0_f16(half %x, ptr %minnum_res, ptr %maxnum_res, pt
 ; CHECK-NEXT:    store half [[X]], ptr [[MAXNUM_RES:%.*]], align 2
 ; CHECK-NEXT:    store half [[X]], ptr [[MINIMUM_RES:%.*]], align 2
 ; CHECK-NEXT:    store half [[X]], ptr [[MAXIMUM_RES:%.*]], align 2
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call half @llvm.minimumnum.f16(half poison, half [[X]])
-; CHECK-NEXT:    store half [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 2
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call half @llvm.maximumnum.f16(half poison, half [[X]])
-; CHECK-NEXT:    store half [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 2
+; CHECK-NEXT:    store half [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 2
+; CHECK-NEXT:    store half [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 2
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call half @llvm.minnum.f16(half poison, half %x)
@@ -230,10 +218,8 @@ define void @minmax_poison_op1_nxv2f64(<vscale x 2 x double> %x, ptr %minnum_res
 ; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MAXNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MINIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MAXIMUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call nnan <vscale x 2 x double> @llvm.minimumnum.nxv2f64(<vscale x 2 x double> [[X]], <vscale x 2 x double> poison)
-; CHECK-NEXT:    store <vscale x 2 x double> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call nnan <vscale x 2 x double> @llvm.maximumnum.nxv2f64(<vscale x 2 x double> [[X]], <vscale x 2 x double> poison)
-; CHECK-NEXT:    store <vscale x 2 x double> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call nnan <vscale x 2 x double> @llvm.minnum.nxv2f64(<vscale x 2 x double> %x, <vscale x 2 x double> poison)
@@ -274,8 +260,7 @@ define void @minmax_pos_inf_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr
 ; CHECK-NEXT:    store float [[MAXIMUM]], ptr [[MAXIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0x7FF0000000000000)
 ; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float 0x7FF0000000000000)
-; CHECK-NEXT:    store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float 0x7FF0000000000000, ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call float @llvm.minnum.f32(float %x, float 0x7FF0000000000000)
@@ -303,10 +288,8 @@ define void @minmax_pos_inf_nnan_v2f32(<2 x float> %x, ptr %minnum_res, ptr %max
 ; CHECK-NEXT:    store <2 x float> splat (float 0x7FF0000000000000), ptr [[MAXNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    store <2 x float> [[X]], ptr [[MINIMUM_RES:%.*]], align 8
 ; CHECK-NEXT:    store <2 x float> splat (float 0x7FF0000000000000), ptr [[MAXIMUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call nnan <2 x float> @llvm.minimumnum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> [[X]])
-; CHECK-NEXT:    store <2 x float> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call nnan <2 x float> @llvm.maximumnum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> [[X]])
-; CHECK-NEXT:    store <2 x float> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    store <2 x float> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    store <2 x float> splat (float 0x7FF0000000000000), ptr [[MAXIMUMNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call nnan <2 x float> @llvm.minnum.v2f32(<2 x float> splat (float 0x7FF0000000000000), <2 x float> %x)
@@ -345,8 +328,7 @@ define void @minmax_neg_inf_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr
 ; CHECK-NEXT:    [[MINIMUM:%.*]] = call float @llvm.minimum.f32(float [[X]], float 0xFFF0000000000000)
 ; CHECK-NEXT:    store float [[MINIMUM]], ptr [[MINIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float [[X]], ptr [[MAXIMUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float 0xFFF0000000000000)
-; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float 0xFFF0000000000000, ptr [[MINIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float 0xFFF0000000000000)
 ; CHECK-NEXT:    store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
@@ -376,10 +358,8 @@ define void @minmax_neg_inf_nnan_v2f64(<2 x double> %x, ptr %minnum_res, ptr %ma
 ; CHECK-NEXT:    store <2 x double> [[X:%.*]], ptr [[MAXNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <2 x double> splat (double 0xFFF0000000000000), ptr [[MINIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MAXIMUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call nnan <2 x double> @llvm.minimumnum.v2f64(<2 x double> [[X]], <2 x double> splat (double 0xFFF0000000000000))
-; CHECK-NEXT:    store <2 x double> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call nnan <2 x double> @llvm.maximumnum.v2f64(<2 x double> [[X]], <2 x double> splat (double 0xFFF0000000000000))
-; CHECK-NEXT:    store <2 x double> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> splat (double 0xFFF0000000000000), ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call nnan <2 x double> @llvm.minnum.v2f64(<2 x double> %x, <2 x double> splat (double 0xFFF0000000000000))
@@ -454,8 +434,7 @@ define void @minmax_largest_f32_ninf(float %x, ptr %minnum_res, ptr %maxnum_res,
 ; CHECK-NEXT:    store float [[MAXIMUM]], ptr [[MAXIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call ninf float @llvm.minimumnum.f32(float [[X]], float 0x47EFFFFFE0000000)
 ; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call ninf float @llvm.maximumnum.f32(float [[X]], float 0x47EFFFFFE0000000)
-; CHECK-NEXT:    store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float 0x47EFFFFFE0000000, ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call ninf float @llvm.minnum.f32(float %x, float 0x47EFFFFFE0000000)
@@ -483,10 +462,8 @@ define void @minmax_largest_v2f32_ninf_nnan(<2 x float> %x, ptr %minnum_res, ptr
 ; CHECK-NEXT:    store <2 x float> splat (float 0x47EFFFFFE0000000), ptr [[MAXNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    store <2 x float> [[X]], ptr [[MINIMUM_RES:%.*]], align 8
 ; CHECK-NEXT:    store <2 x float> splat (float 0x47EFFFFFE0000000), ptr [[MAXIMUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call nnan ninf <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X]], <2 x float> splat (float 0x47EFFFFFE0000000))
-; CHECK-NEXT:    store <2 x float> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call nnan ninf <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X]], <2 x float> splat (float 0x47EFFFFFE0000000))
-; CHECK-NEXT:    store <2 x float> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    store <2 x float> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    store <2 x float> splat (float 0x47EFFFFFE0000000), ptr [[MAXIMUMNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call ninf nnan <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> splat (float 0x47EFFFFFE0000000))
@@ -559,8 +536,7 @@ define void @minmax_neg_largest_f32_ninf(float %x, ptr %minnum_res, ptr %maxnum_
 ; CHECK-NEXT:    [[MINIMUM:%.*]] = call ninf float @llvm.minimum.f32(float [[X]], float 0xC7EFFFFFE0000000)
 ; CHECK-NEXT:    store float [[MINIMUM]], ptr [[MINIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float [[X]], ptr [[MAXIMUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call ninf float @llvm.minimumnum.f32(float [[X]], float 0xC7EFFFFFE0000000)
-; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float 0xC7EFFFFFE0000000, ptr [[MINIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call ninf float @llvm.maximumnum.f32(float [[X]], float 0xC7EFFFFFE0000000)
 ; CHECK-NEXT:    store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
@@ -590,10 +566,8 @@ define void @minmax_neg_largest_nxv2f32_nnan_ninf(<vscale x 2 x float> %x, ptr %
 ; CHECK-NEXT:    store <vscale x 2 x float> [[X:%.*]], ptr [[MAXNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    store <vscale x 2 x float> splat (float 0xC7EFFFFFE0000000), ptr [[MINIMUM_RES:%.*]], align 8
 ; CHECK-NEXT:    store <vscale x 2 x float> [[X]], ptr [[MAXIMUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call nnan ninf <vscale x 2 x float> @llvm.minimumnum.nxv2f32(<vscale x 2 x float> [[X]], <vscale x 2 x float> splat (float 0xC7EFFFFFE0000000))
-; CHECK-NEXT:    store <vscale x 2 x float> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call nnan ninf <vscale x 2 x float> @llvm.maximumnum.nxv2f32(<vscale x 2 x float> [[X]], <vscale x 2 x float> splat (float 0xC7EFFFFFE0000000))
-; CHECK-NEXT:    store <vscale x 2 x float> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    store <vscale x 2 x float> splat (float 0xC7EFFFFFE0000000), ptr [[MINIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    store <vscale x 2 x float> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call nnan ninf <vscale x 2 x float> @llvm.minnum.nxv2f32(<vscale x 2 x float> %x, <vscale x 2 x float> splat (float 0xC7EFFFFFE0000000))
@@ -623,10 +597,8 @@ define void @minmax_same_args(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %m
 ; CHECK-NEXT:    store float [[X]], ptr [[MAXNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float [[X]], ptr [[MINIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float [[X]], ptr [[MAXIMUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MINIMUMNUM:%.*]] = call float @llvm.minimumnum.f32(float [[X]], float [[X]])
-; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 4
-; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call float @llvm.maximumnum.f32(float [[X]], float [[X]])
-; CHECK-NEXT:    store float [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call float @llvm.minnum.f32(float %x, float %x)
@@ -659,11 +631,9 @@ define void @minmax_x_minmax_xy(<2 x float> %x, <2 x float> %y, ptr %minnum_res,
 ; CHECK-NEXT:    store <2 x float> [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 8
 ; CHECK-NEXT:    [[MAXIMUM_XY:%.*]] = call <2 x float> @llvm.maximum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
 ; CHECK-NEXT:    store <2 x float> [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MINIMUMNUM_XY:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
-; CHECK-NEXT:    [[MINIMUMNUM_NESTED:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X]], <2 x float> [[MINIMUMNUM_XY]])
+; CHECK-NEXT:    [[MINIMUMNUM_NESTED:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
 ; CHECK-NEXT:    store <2 x float> [[MINIMUMNUM_NESTED]], ptr [[MINIMUMNUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MAXIMUMNUM_XY:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
-; CHECK-NEXT:    [[MAXIMUMNUM_NESTED:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X]], <2 x float> [[MAXIMUMNUM_XY]])
+; CHECK-NEXT:    [[MAXIMUMNUM_NESTED:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
 ; CHECK-NEXT:    store <2 x float> [[MAXIMUMNUM_NESTED]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
@@ -757,13 +727,9 @@ define void @minmax_minmax_xy_minmax_yx(half %x, half %y, ptr %minnum_res, ptr %
 ; CHECK-NEXT:    store half [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 2
 ; CHECK-NEXT:    [[MAXIMUM_XY:%.*]] = call half @llvm.maximum.f16(half [[X]], half [[Y]])
 ; CHECK-NEXT:    store half [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 2
-; CHECK-NEXT:    [[MINIMUMNUM_XY:%.*]] = call half @llvm.minimumnum.f16(half [[X]], half [[Y]])
-; CHECK-NEXT:    [[MINIMUMNUM_YX:%.*]] = call half @llvm.minimumnum.f16(half [[Y]], half [[X]])
-; CHECK-NEXT:    [[FINAL_MINIMUMNUM:%.*]] = call half @llvm.minimumnum.f16(half [[MINIMUMNUM_XY]], half [[MINIMUMNUM_YX]])
+; CHECK-NEXT:    [[FINAL_MINIMUMNUM:%.*]] = call half @llvm.minimumnum.f16(half [[X]], half [[Y]])
 ; CHECK-NEXT:    store half [[FINAL_MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 2
-; CHECK-NEXT:    [[MAXIMUMNUM_XY:%.*]] = call half @llvm.maximumnum.f16(half [[X]], half [[Y]])
-; CHECK-NEXT:    [[MAXIMUMNUM_YX:%.*]] = call half @llvm.maximumnum.f16(half [[Y]], half [[X]])
-; CHECK-NEXT:    [[FINAL_MAXIMUMNUM:%.*]] = call half @llvm.maximumnum.f16(half [[MAXIMUMNUM_XY]], half [[MAXIMUMNUM_YX]])
+; CHECK-NEXT:    [[FINAL_MAXIMUMNUM:%.*]] = call half @llvm.maximumnum.f16(half [[X]], half [[Y]])
 ; CHECK-NEXT:    store half [[FINAL_MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 2
 ; CHECK-NEXT:    ret void
 ;
@@ -811,13 +777,9 @@ define void @minmax_minmax_xy_maxmin_yx(double %x, double %y, ptr %minnum_res, p
 ; CHECK-NEXT:    store double [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 8
 ; CHECK-NEXT:    [[MAXIMUM_XY:%.*]] = call double @llvm.maximum.f64(double [[Y]], double [[X]])
 ; CHECK-NEXT:    store double [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MINIMUMNUM_XY:%.*]] = call double @llvm.minimumnum.f64(double [[Y]], double [[X]])
-; CHECK-NEXT:    [[MAXIMUMNUM_XY:%.*]] = call double @llvm.maximumnum.f64(double [[X]], double [[Y]])
-; CHECK-NEXT:    [[FINAL_MINIMUMNUM:%.*]] = call double @llvm.minimumnum.f64(double [[MINIMUMNUM_XY]], double [[MAXIMUMNUM_XY]])
+; CHECK-NEXT:    [[FINAL_MINIMUMNUM:%.*]] = call double @llvm.minimumnum.f64(double [[Y]], double [[X]])
 ; CHECK-NEXT:    store double [[FINAL_MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MAXIMUMNUM_XY1:%.*]] = call double @llvm.maximumnum.f64(double [[Y]], double [[X]])
-; CHECK-NEXT:    [[MINIMUMNUM_YX:%.*]] = call double @llvm.minimumnum.f64(double [[X]], double [[Y]])
-; CHECK-NEXT:    [[FINAL_MAXIMUMNUM:%.*]] = call double @llvm.maximumnum.f64(double [[MAXIMUMNUM_XY1]], double [[MINIMUMNUM_YX]])
+; CHECK-NEXT:    [[FINAL_MAXIMUMNUM:%.*]] = call double @llvm.maximumnum.f64(double [[Y]], double [[X]])
 ; CHECK-NEXT:    store double [[FINAL_MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;

>From 349bbb3717a02472680262ec72fc5965fe0217f6 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 25 Sep 2025 10:52:12 +0000
Subject: [PATCH 14/19] Update test comments (remove TODOs)

---
 .../Transforms/InstSimplify/fminmax-folds.ll  | 43 +++++++++----------
 1 file changed, 20 insertions(+), 23 deletions(-)

diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index 0ce8057ddb5fb..e793efc228267 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -6,12 +6,12 @@
 ;###############################################################
 ; minnum(X, qnan) -> X
 ; maxnum(X, qnan) -> X
-; TODO: minnum(X, snan) -> qnan (currently we treat SNaN the same as QNaN)
-; TODO: maxnum(X, snan) -> qnan (currently we treat SNaN the same as QNaN)
+; minnum(X, snan) -> qnan
+; maxnum(X, snan) -> qnan
 ; minimum(X, nan) -> qnan
 ; maximum(X, nan) -> qnan
-; TODO: minimumnum(X, nan) -> X
-; TODO: maximumnum(X, nan) -> X
+; minimumnum(X, nan) -> X
+; maximumnum(X, nan) -> X
 
 define void @minmax_qnan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_qnan_f32(
@@ -40,7 +40,7 @@ define void @minmax_qnan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %mi
   ret void
 }
 
-; TODO currently snan is treated the same as qnan, but maxnum/minnum should really return qnan for these cases, not X
+; Note that maxnum/minnum return qnan here for snan inputs, unlike maximumnum/minimumnum
 define void @minmax_snan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_snan_f32(
 ; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MINIMUM_RES:%.*]], align 4
@@ -95,7 +95,7 @@ define void @minmax_qnan_nxv2f64_op0(<vscale x 2 x double> %x, ptr %minnum_res,
   ret void
 }
 
-; TODO currently snan is treated the same as qnan, but maxnum/minnum should really return qnan for these cases, not X
+; Note that maxnum/minnum return qnan here for snan inputs, unlike maximumnum/minimumnum
 define void @minmax_snan_nxv2f64_op1(<vscale x 2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_snan_nxv2f64_op1(
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MINIMUM_RES:%.*]], align 16
@@ -123,7 +123,8 @@ define void @minmax_snan_nxv2f64_op1(<vscale x 2 x double> %x, ptr %minnum_res,
   ret void
 }
 
-; TODO Currently, we treat SNaN and QNaN the same. However, for maxnum and minnum, we should not optimize this, as we should return <%x0, QNaN> instead of <%x0, %x1>
+; For maxnum and minnum, we cannot optimize this in InstSimplify, as the result should
+; return <%x0, QNaN> and InstSimplify cannot create the extra instructions required to construct this.
 define void @minmax_mixed_snan_qnan_v2f64(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_mixed_snan_qnan_v2f64(
 ; CHECK-NEXT:    [[X:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[MINIMUMNUM:%.*]])
@@ -246,10 +247,10 @@ define void @minmax_poison_op1_nxv2f64(<vscale x 2 x double> %x, ptr %minnum_res
 ; minnum(X, +inf) -> X if nnan (ignoring NaN quieting)
 ; maximum(X, +inf) -> +inf if nnan
 ; minimum(X, +inf) -> X (ignoring NaN quieting)
-; TODO: maximumnum(X, +inf) -> +inf
-; TODO: minimumnum(X, +inf) -> X if nnan (ignoring NaN quieting)
+; maximumnum(X, +inf) -> +inf
+; minimumnum(X, +inf) -> X if nnan (ignoring NaN quieting)
 
-; Can only optimize maxnum and minimum without the nnan flag
+; Can only optimize maxnum, minimum, and maximumnum without the nnan flag
 define void @minmax_pos_inf_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_pos_inf_f32(
 ; CHECK-NEXT:    [[MINNUM:%.*]] = call float @llvm.minnum.f32(float [[X:%.*]], float 0x7FF0000000000000)
@@ -281,7 +282,6 @@ define void @minmax_pos_inf_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr
 }
 
 ; Can optimize all minmax variants if the nnan flag is set
-; TODO maximumnum/minimumnum
 define void @minmax_pos_inf_nnan_v2f32(<2 x float> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_pos_inf_nnan_v2f32(
 ; CHECK-NEXT:    store <2 x float> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 8
@@ -316,10 +316,10 @@ define void @minmax_pos_inf_nnan_v2f32(<2 x float> %x, ptr %minnum_res, ptr %max
 ; maxnum(X, -inf) -> X if nnan
 ; minimum(X, -inf) -> -inf if nnan
 ; maximum(X, -inf) -> X (Ignoring NaN quieting)
-; TODO: minimumnum(X, -inf) -> -inf
-; TODO: maximumnum(X, -inf) -> X if nnan
+; minimumnum(X, -inf) -> -inf
+; maximumnum(X, -inf) -> X if nnan
 
-; Can only optimize minnum and maximum without the nnan flag
+; Can only optimize minnum, maximum, and minimumnum without the nnan flag
 define void @minmax_neg_inf_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_neg_inf_f32(
 ; CHECK-NEXT:    store float 0xFFF0000000000000, ptr [[MINNUM_RES:%.*]], align 4
@@ -351,7 +351,6 @@ define void @minmax_neg_inf_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr
 }
 
 ; Can optimize all minmax variants if the nnan flag is set
-; TODO maximumnum/minimumnum
 define void @minmax_neg_inf_nnan_v2f64(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_neg_inf_nnan_v2f64(
 ; CHECK-NEXT:    store <2 x double> splat (double 0xFFF0000000000000), ptr [[MINNUM_RES:%.*]], align 16
@@ -386,8 +385,8 @@ define void @minmax_neg_inf_nnan_v2f64(<2 x double> %x, ptr %minnum_res, ptr %ma
 ; minnum(X, +largest) -> X if ninf && nnan
 ; maximum(X, +largest) -> +largest if ninf && nnan
 ; minimum(X, +largest) -> X if ninf (ignoring quieting of sNaNs)
-; TODO: maximumnum(X, +largest) -> +largest if ninf && nnan
-; TODO: minimumnum(X, +largest) -> X if ninf && nnan
+; maximumnum(X, +largest) -> +largest if ninf
+; minimumnum(X, +largest) -> X if ninf && nnan
 
 ; None of these should be optimized away without the nnan/ninf flags
 define void @minmax_largest_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
@@ -423,7 +422,7 @@ define void @minmax_largest_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr
   ret void
 }
 
-; We can optimize maxnum and minimum if we know ninf is set
+; We can optimize maxnum, minimum, and maximumnum if we know ninf is set
 define void @minmax_largest_f32_ninf(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_largest_f32_ninf(
 ; CHECK-NEXT:    [[MINNUM:%.*]] = call ninf float @llvm.minnum.f32(float [[X:%.*]], float 0x47EFFFFFE0000000)
@@ -455,7 +454,6 @@ define void @minmax_largest_f32_ninf(float %x, ptr %minnum_res, ptr %maxnum_res,
 }
 
 ; All can be optimized if both the ninf and nnan flags are set (ignoring SNaN propagation in minnum/maxnum)
-; TODO maximumnum/minimumnum
 define void @minmax_largest_v2f32_ninf_nnan(<2 x float> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_largest_v2f32_ninf_nnan(
 ; CHECK-NEXT:    store <2 x float> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 8
@@ -490,8 +488,8 @@ define void @minmax_largest_v2f32_ninf_nnan(<2 x float> %x, ptr %minnum_res, ptr
 ; minnum(X, -largest) -> -largest if ninf (ignoring SNaN -> QNaN propagation)
 ; maximum(X, -largest) -> X if ninf (ignoring quieting of sNaNs)
 ; minimum(X, -largest) -> -largest if ninf && nnan
-; TODO: maximumnum(X, -largest) -> X if ninf && nnan
-; TODO: minimumnum(X, -largest) -> -largest if ninf
+; maximumnum(X, -largest) -> X if ninf && nnan
+; minimumnum(X, -largest) -> -largest if ninf
 
 ; None of these should be optimized away without the nnan/ninf flags
 define void @minmax_neg_largest_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
@@ -527,7 +525,7 @@ define void @minmax_neg_largest_f32(float %x, ptr %minnum_res, ptr %maxnum_res,
   ret void
 }
 
-; We can optimize minnum and maximum if we know ninf is set
+; We can optimize minnum, maximum, and minimumnum if we know ninf is set
 define void @minmax_neg_largest_f32_ninf(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_neg_largest_f32_ninf(
 ; CHECK-NEXT:    store float 0xC7EFFFFFE0000000, ptr [[MINNUM_RES:%.*]], align 4
@@ -559,7 +557,6 @@ define void @minmax_neg_largest_f32_ninf(float %x, ptr %minnum_res, ptr %maxnum_
 }
 
 ; All can be optimized if both the ninf and nnan flags are set (ignoring SNaN propagation in minnum/maxnum)
-; TODO maximumnum/minimumnum
 define void @minmax_neg_largest_nxv2f32_nnan_ninf(<vscale x 2 x float> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_neg_largest_nxv2f32_nnan_ninf(
 ; CHECK-NEXT:    store <vscale x 2 x float> splat (float 0xC7EFFFFFE0000000), ptr [[MINNUM_RES:%.*]], align 8

>From d3c2f6f917563ebeac547d9a9a75151f0ec802df Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 25 Sep 2025 10:57:04 +0000
Subject: [PATCH 15/19] Rename some CHECK variables for consistentcy

The update_test_checks.py script seems to have used the old
variable names from the pervious version of the tests, rather
than updating them to match the variable names they now represent.

Changed several nested min/max tests to use names like
MAXIMUMNUM_XY instead of MAXIMUMNUM_NESTED to make this consistent
with the other CHECK lines and avoid confusion with the nested
value that the variable used to represent in the old version of the
test.
---
 .../Transforms/InstSimplify/fminmax-folds.ll  | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index e793efc228267..0f9b86738af5e 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -628,10 +628,10 @@ define void @minmax_x_minmax_xy(<2 x float> %x, <2 x float> %y, ptr %minnum_res,
 ; CHECK-NEXT:    store <2 x float> [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 8
 ; CHECK-NEXT:    [[MAXIMUM_XY:%.*]] = call <2 x float> @llvm.maximum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
 ; CHECK-NEXT:    store <2 x float> [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MINIMUMNUM_NESTED:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
-; CHECK-NEXT:    store <2 x float> [[MINIMUMNUM_NESTED]], ptr [[MINIMUMNUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[MAXIMUMNUM_NESTED:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
-; CHECK-NEXT:    store <2 x float> [[MAXIMUMNUM_NESTED]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    [[MINIMUMNUM_XY:%.*]] = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
+; CHECK-NEXT:    store <2 x float> [[MINIMUMNUM_XY]], ptr [[MINIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    [[MAXIMUMNUM_XY:%.*]] = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> [[X]], <2 x float> [[Y]])
+; CHECK-NEXT:    store <2 x float> [[MAXIMUMNUM_XY]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %minnum_xy = call <2 x float> @llvm.minnum.v2f32(<2 x float> %x, <2 x float> %y)
@@ -724,10 +724,10 @@ define void @minmax_minmax_xy_minmax_yx(half %x, half %y, ptr %minnum_res, ptr %
 ; CHECK-NEXT:    store half [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 2
 ; CHECK-NEXT:    [[MAXIMUM_XY:%.*]] = call half @llvm.maximum.f16(half [[X]], half [[Y]])
 ; CHECK-NEXT:    store half [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 2
-; CHECK-NEXT:    [[FINAL_MINIMUMNUM:%.*]] = call half @llvm.minimumnum.f16(half [[X]], half [[Y]])
-; CHECK-NEXT:    store half [[FINAL_MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 2
-; CHECK-NEXT:    [[FINAL_MAXIMUMNUM:%.*]] = call half @llvm.maximumnum.f16(half [[X]], half [[Y]])
-; CHECK-NEXT:    store half [[FINAL_MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 2
+; CHECK-NEXT:    [[MINIMUMNUM_XY:%.*]] = call half @llvm.minimumnum.f16(half [[X]], half [[Y]])
+; CHECK-NEXT:    store half [[MINIMUMNUM_XY]], ptr [[MINIMUMNUM_RES:%.*]], align 2
+; CHECK-NEXT:    [[MAXIMUMNUM_XY:%.*]] = call half @llvm.maximumnum.f16(half [[X]], half [[Y]])
+; CHECK-NEXT:    store half [[MAXIMUMNUM_XY]], ptr [[MAXIMUMNUM_RES:%.*]], align 2
 ; CHECK-NEXT:    ret void
 ;
   %minnum_xy = call half @llvm.minnum.f16(half %x, half %y)
@@ -774,10 +774,10 @@ define void @minmax_minmax_xy_maxmin_yx(double %x, double %y, ptr %minnum_res, p
 ; CHECK-NEXT:    store double [[MINIMUM_XY]], ptr [[MINIMUM_RES:%.*]], align 8
 ; CHECK-NEXT:    [[MAXIMUM_XY:%.*]] = call double @llvm.maximum.f64(double [[Y]], double [[X]])
 ; CHECK-NEXT:    store double [[MAXIMUM_XY]], ptr [[MAXIMUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[FINAL_MINIMUMNUM:%.*]] = call double @llvm.minimumnum.f64(double [[Y]], double [[X]])
-; CHECK-NEXT:    store double [[FINAL_MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 8
-; CHECK-NEXT:    [[FINAL_MAXIMUMNUM:%.*]] = call double @llvm.maximumnum.f64(double [[Y]], double [[X]])
-; CHECK-NEXT:    store double [[FINAL_MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    [[MINIMUMNUM_XY:%.*]] = call double @llvm.minimumnum.f64(double [[Y]], double [[X]])
+; CHECK-NEXT:    store double [[MINIMUMNUM_XY]], ptr [[MINIMUMNUM_RES:%.*]], align 8
+; CHECK-NEXT:    [[MAXIMUMNUM_XY:%.*]] = call double @llvm.maximumnum.f64(double [[Y]], double [[X]])
+; CHECK-NEXT:    store double [[MAXIMUMNUM_XY]], ptr [[MAXIMUMNUM_RES:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %minnum_xy = call double @llvm.minnum.f64(double %x, double %y)

>From 989bd58e88735682e0b78dfc76a8c36356d8f057 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 25 Sep 2025 11:44:12 +0000
Subject: [PATCH 16/19] Add new mixed vector element tests

Add test-cases for the newly added elementwise optimizations
covering vectors like <poison, Inf> and <poison, Inf, SNaN>.
---
 .../Transforms/InstSimplify/fminmax-folds.ll  | 74 +++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index 0f9b86738af5e..889574a94d292 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -584,6 +584,80 @@ define void @minmax_neg_largest_nxv2f32_nnan_ninf(<vscale x 2 x float> %x, ptr %
   ret void
 }
 
+;###############################################################
+;#                  Mixed Constant Vector Elements             #
+;###############################################################
+; Tests elementwise handling of different combinations of the above optimizable constants
+
+; Test with vector variants (v2f64) with +Inf and poison
+; Poison element allows for flexibility to choose either X or <poison, +Inf> where applicable
+define void @minmax_mixed_pos_inf_poison_v2f64_nnan(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
+; CHECK-LABEL: @minmax_mixed_pos_inf_poison_v2f64_nnan(
+; CHECK-NEXT:    store <2 x double> [[X:%.*]], ptr [[MINNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> <double poison, double 0x7FF0000000000000>, ptr [[MAXNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MINIMUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> <double poison, double 0x7FF0000000000000>, ptr [[MAXIMUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> <double poison, double 0x7FF0000000000000>, ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    ret void
+;
+  %minnum = call nnan <2 x double> @llvm.minnum.v2f64(<2 x double> <double poison, double 0x7FF0000000000000>, <2 x double> %x)
+  store <2 x double> %minnum, ptr %minnum_res
+  %maxnum = call nnan <2 x double> @llvm.maxnum.v2f64(<2 x double> <double poison, double 0x7FF0000000000000>, <2 x double> %x)
+  store <2 x double> %maxnum, ptr %maxnum_res
+
+  %minimum = call nnan <2 x double> @llvm.minimum.v2f64(<2 x double> <double poison, double 0x7FF0000000000000>, <2 x double> %x)
+  store <2 x double> %minimum, ptr %minimum_res
+  %maximum = call nnan <2 x double> @llvm.maximum.v2f64(<2 x double> <double poison, double 0x7FF0000000000000>, <2 x double> %x)
+  store <2 x double> %maximum, ptr %maximum_res
+
+  %minimumnum = call nnan <2 x double> @llvm.minimumnum.v2f64(<2 x double> <double poison, double 0x7FF0000000000000>, <2 x double> %x)
+  store <2 x double> %minimumnum, ptr %minimumnum_res
+  %maximumnum = call nnan <2 x double> @llvm.maximumnum.v2f64(<2 x double> <double poison, double 0x7FF0000000000000>, <2 x double> %x)
+  store <2 x double> %maximumnum, ptr %maximumnum_res
+  ret void
+}
+
+; Tests to show that we can optimize different classes of constatn (inf/nan/poison) in different vector elements.
+; We can only optimize if the result would be choosing all elements of the input X, or all constant elements though
+; (where poison allows us to choose either).
+;
+; nnan minnum(<poison, +Inf, SNaN>, X) = <???, X1, QNaN> (Cannot mix elements from X and constant vector)
+; nnan maxnum(<poison, +Inf, SNaN>, X) = <poison +Inf, QNaN>
+; nnan minimum(<poison, +Inf, SNaN>, X) = <???, X1, QNaN> (Cannot mix elements from X and constant vector)
+; nnan maximum(<poison, +Inf, SNaN>, X) = <poison +Inf, QNaN>
+; nnan minimumnum(<poison, +Inf, SNaN>, X) = <X0, X1, X2> (Poison can be either X or constant value)
+; nnan maximumnum(<poison, +Inf, SNaN>, X) = <???, +Inf, X2>
+define void @minmax_mixed_pos_inf_poison_snan_v3f32(<3 x float> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
+; CHECK-LABEL: @minmax_mixed_pos_inf_poison_snan_v3f32(
+; CHECK-NEXT:    [[MINNUM:%.*]] = call nnan <3 x float> @llvm.minnum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> [[X:%.*]])
+; CHECK-NEXT:    store <3 x float> [[MINNUM]], ptr [[MINNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <3 x float> <float poison, float 0x7FF0000000000000, float 0x7FFC000000000000>, ptr [[MAXNUM_RES:%.*]], align 16
+; CHECK-NEXT:    [[MINIMUM:%.*]] = call nnan <3 x float> @llvm.minimum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> [[X]])
+; CHECK-NEXT:    store <3 x float> [[MINIMUM]], ptr [[MINIMUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <3 x float> <float poison, float 0x7FF0000000000000, float 0x7FFC000000000000>, ptr [[MAXIMUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <3 x float> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    [[MAXIMUMNUM:%.*]] = call nnan <3 x float> @llvm.maximumnum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> [[X]])
+; CHECK-NEXT:    store <3 x float> [[MAXIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    ret void
+;
+  %minnum = call nnan <3 x float> @llvm.minnum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> %x)
+  store <3 x float> %minnum, ptr %minnum_res
+  %maxnum = call nnan <3 x float> @llvm.maxnum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> %x)
+  store <3 x float> %maxnum, ptr %maxnum_res
+
+  %minimum = call nnan <3 x float> @llvm.minimum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> %x)
+  store <3 x float> %minimum, ptr %minimum_res
+  %maximum = call nnan <3 x float> @llvm.maximum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> %x)
+  store <3 x float> %maximum, ptr %maximum_res
+
+  %minimumnum = call nnan <3 x float> @llvm.minimumnum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> %x)
+  store <3 x float> %minimumnum, ptr %minimumnum_res
+  %maximumnum = call nnan <3 x float> @llvm.maximumnum.v3f32(<3 x float> <float poison, float 0x7FF0000000000000, float 0x7FF4000000000000>, <3 x float> %x)
+  store <3 x float> %maximumnum, ptr %maximumnum_res
+  ret void
+}
+
 ;###############################################################
 ;#                    Min(x, x) / Max(x, x)                    #
 ;###############################################################

>From 18fc2bafe0709c431b8551b898a20ccc961de42c Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 25 Sep 2025 11:52:41 +0000
Subject: [PATCH 17/19] Fix more variable naming inconsistencies

Fix cases where variable X was being renamed MINIMUMNUM
---
 .../Transforms/InstSimplify/fminmax-folds.ll   | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index 889574a94d292..b065b703bf48b 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -47,8 +47,8 @@ define void @minmax_snan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %mi
 ; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MAXIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MINIMUM_RES1:%.*]], align 4
 ; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MAXIMUM_RES1:%.*]], align 4
-; CHECK-NEXT:    store float [[MINIMUMNUM:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 4
-; CHECK-NEXT:    store float [[MINIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float [[X:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call float @llvm.minnum.f32(float %x, float 0x7FF4000000000000)
@@ -102,8 +102,8 @@ define void @minmax_snan_nxv2f64_op1(<vscale x 2 x double> %x, ptr %minnum_res,
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MAXIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MINIMUM_RES1:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MAXIMUM_RES1:%.*]], align 16
-; CHECK-NEXT:    store <vscale x 2 x double> [[MINIMUMNUM:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 16
-; CHECK-NEXT:    store <vscale x 2 x double> [[MINIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> [[X:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call <vscale x 2 x double> @llvm.minnum.nxv2f64(<vscale x 2 x double> splat (double 0x7FF400DEAD00DEAD), <vscale x 2 x double> %x)
@@ -127,14 +127,14 @@ define void @minmax_snan_nxv2f64_op1(<vscale x 2 x double> %x, ptr %minnum_res,
 ; return <%x0, QNaN> and InstSimplify cannot create the extra instructions required to construct this.
 define void @minmax_mixed_snan_qnan_v2f64(<2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_mixed_snan_qnan_v2f64(
-; CHECK-NEXT:    [[X:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[MINIMUMNUM:%.*]])
-; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MINNUM_RES:%.*]], align 16
-; CHECK-NEXT:    [[MAXNUM:%.*]] = call <2 x double> @llvm.maxnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[MINIMUMNUM]])
+; CHECK-NEXT:    [[MINNUM:%.*]] = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[X:%.*]])
+; CHECK-NEXT:    store <2 x double> [[MINNUM]], ptr [[MINNUM_RES:%.*]], align 16
+; CHECK-NEXT:    [[MAXNUM:%.*]] = call <2 x double> @llvm.maxnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> [[X]])
 ; CHECK-NEXT:    store <2 x double> [[MAXNUM]], ptr [[MAXNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <2 x double> <double 0x7FFC00DEAD00DEAD, double 0x7FF8000FEED00000>, ptr [[MINIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <2 x double> <double 0x7FFC00DEAD00DEAD, double 0x7FF8000FEED00000>, ptr [[MAXIMUM_RES:%.*]], align 16
-; CHECK-NEXT:    store <2 x double> [[MINIMUMNUM]], ptr [[MINIMUMNUM_RES:%.*]], align 16
-; CHECK-NEXT:    store <2 x double> [[MINIMUMNUM]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MINIMUMNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void
 ;
   %minnum = call <2 x double> @llvm.minnum.v2f64(<2 x double> <double 0x7FF400DEAD00DEAD, double 0x7FF8000FEED00000>, <2 x double> %x)

>From fd1d020200a0a3925418257c5e5b858fb1c2a06c Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 25 Sep 2025 11:56:35 +0000
Subject: [PATCH 18/19] Fix more variable naming issues

Fix problem with MINNUM_RES getting renamed to MINIMUM_RES
---
 llvm/test/Transforms/InstSimplify/fminmax-folds.ll | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
index b065b703bf48b..3a03f8627ab68 100644
--- a/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
+++ b/llvm/test/Transforms/InstSimplify/fminmax-folds.ll
@@ -43,10 +43,10 @@ define void @minmax_qnan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %mi
 ; Note that maxnum/minnum return qnan here for snan inputs, unlike maximumnum/minimumnum
 define void @minmax_snan_f32(float %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_snan_f32(
+; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MINNUM_RES:%.*]], align 4
+; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MAXNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MINIMUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MAXIMUM_RES:%.*]], align 4
-; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MINIMUM_RES1:%.*]], align 4
-; CHECK-NEXT:    store float 0x7FFC000000000000, ptr [[MAXIMUM_RES1:%.*]], align 4
 ; CHECK-NEXT:    store float [[X:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    store float [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 4
 ; CHECK-NEXT:    ret void
@@ -98,10 +98,10 @@ define void @minmax_qnan_nxv2f64_op0(<vscale x 2 x double> %x, ptr %minnum_res,
 ; Note that maxnum/minnum return qnan here for snan inputs, unlike maximumnum/minimumnum
 define void @minmax_snan_nxv2f64_op1(<vscale x 2 x double> %x, ptr %minnum_res, ptr %maxnum_res, ptr %minimum_res, ptr %maximum_res, ptr %minimumnum_res, ptr %maximumnum_res) {
 ; CHECK-LABEL: @minmax_snan_nxv2f64_op1(
+; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MINNUM_RES:%.*]], align 16
+; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MAXNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MINIMUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MAXIMUM_RES:%.*]], align 16
-; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MINIMUM_RES1:%.*]], align 16
-; CHECK-NEXT:    store <vscale x 2 x double> splat (double 0x7FFC00DEAD00DEAD), ptr [[MAXIMUM_RES1:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> [[X:%.*]], ptr [[MINIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    store <vscale x 2 x double> [[X]], ptr [[MAXIMUMNUM_RES:%.*]], align 16
 ; CHECK-NEXT:    ret void

>From 2bc2b18cbc4a05bb1600f8d0e695678c44eb79d8 Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Thu, 25 Sep 2025 12:29:44 +0000
Subject: [PATCH 19/19] Fix unused variable error

---
 llvm/lib/Analysis/InstructionSimplify.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 3ab6b615c0771..d8ee0007bb3e2 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -6884,7 +6884,7 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
           if (OptResult == MinMaxOptResult::UseNewConstVal)
             NewConst = ConstantVector::getSplat(ElemCount, NewConst);
 
-        } else if (auto *FVty = dyn_cast<FixedVectorType>(VTy)) {
+        } else if (ElemCount.isFixed()) {
           // Storage to build up new const return value (with NaNs quieted)
           SmallVector<Constant *, 16> NewC(ElemCount.getFixedValue());
 



More information about the llvm-commits mailing list