[llvm] [InstSimplify] Optimize fcmp(min/maxnimumnum) (PR #185868)

Lewis Crawford via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 11 05:30:32 PDT 2026


https://github.com/LewisCrawford created https://github.com/llvm/llvm-project/pull/185868

Extend the fcmp(min/maxnum(X, C2), C1) optimization to apply to min/maximumnum too.

>From 52777befadee375c686d3c725e575998615623de Mon Sep 17 00:00:00 2001
From: Lewis Crawford <lcrawford at nvidia.com>
Date: Tue, 10 Mar 2026 16:53:22 +0000
Subject: [PATCH] [InstSimplify] Optimize fcmp(min/maxnimumnum)

Extend the fcmp(min/maxnum(X, C2), C1) optimization
to apply to min/maximumnum too.
---
 llvm/include/llvm/IR/PatternMatch.h           |  14 +
 llvm/lib/Analysis/InstructionSimplify.cpp     |  20 +-
 .../InstSimplify/floating-point-compare.ll    | 328 ++++++++++++++++++
 3 files changed, 351 insertions(+), 11 deletions(-)

diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index cd0f7187e134c..a30cfa8c0bb4e 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -3030,6 +3030,20 @@ m_FMaximumNum(const Opnd0 &Op0, const Opnd1 &Op1) {
   return m_Intrinsic<Intrinsic::maximumnum>(Op0, Op1);
 }
 
+template <typename Opnd0, typename Opnd1>
+inline match_combine_or<typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty,
+                        typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty>
+m_FMaxNum_or_FMaximumNum(const Opnd0 &Op0, const Opnd1 &Op1) {
+  return m_CombineOr(m_FMaxNum(Op0, Op1), m_FMaximumNum(Op0, Op1));
+}
+
+template <typename Opnd0, typename Opnd1>
+inline match_combine_or<typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty,
+                        typename m_Intrinsic_Ty<Opnd0, Opnd1>::Ty>
+m_FMinNum_or_FMinimumNum(const Opnd0 &Op0, const Opnd1 &Op1) {
+  return m_CombineOr(m_FMinNum(Op0, Op1), m_FMinimumNum(Op0, Op1));
+}
+
 template <typename Opnd0, typename Opnd1, typename Opnd2>
 inline typename m_Intrinsic_Ty<Opnd0, Opnd1, Opnd2>::Ty
 m_FShl(const Opnd0 &Op0, const Opnd1 &Op1, const Opnd2 &Op2) {
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 4da15280029d9..224fada1a053b 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4294,16 +4294,14 @@ static Value *simplifyFCmpInst(CmpPredicate Pred, Value *LHS, Value *RHS,
         break;
       }
     }
-    // Check comparison of [minnum/maxnum with constant] with other constant.
+    // Check FCmp of [min/maxnum or min/maximumnum with const] with other const.
     const APFloat *C2;
-    if ((match(LHS, m_Intrinsic<Intrinsic::minnum>(m_Value(), m_APFloat(C2))) &&
-         *C2 < *C) ||
-        (match(LHS, m_Intrinsic<Intrinsic::maxnum>(m_Value(), m_APFloat(C2))) &&
-         *C2 > *C)) {
-      bool IsMaxNum =
-          cast<IntrinsicInst>(LHS)->getIntrinsicID() == Intrinsic::maxnum;
-      // The ordered relationship and minnum/maxnum guarantee that we do not
-      // have NaN constants, so ordered/unordered preds are handled the same.
+    bool IsMax = match(LHS, m_FMaxNum_or_FMaximumNum(m_Value(), m_APFloat(C2)));
+    bool IsMin = match(LHS, m_FMinNum_or_FMinimumNum(m_Value(), m_APFloat(C2)));
+    if ((IsMax && *C2 > *C) || (IsMin && *C2 < *C)) {
+      // The ordered relationship and min/maxnum or min/maximumnum guarantee
+      // that we do not have NaN constants, so ordered/unordered preds are
+      // handled the same.
       switch (Pred) {
       case FCmpInst::FCMP_OEQ:
       case FCmpInst::FCMP_UEQ:
@@ -4323,7 +4321,7 @@ static Value *simplifyFCmpInst(CmpPredicate Pred, Value *LHS, Value *RHS,
         // minnum(X, LesserC)  >  C --> false
         // maxnum(X, GreaterC) >= C --> true
         // maxnum(X, GreaterC) >  C --> true
-        return ConstantInt::get(RetTy, IsMaxNum);
+        return ConstantInt::get(RetTy, IsMax);
       case FCmpInst::FCMP_OLE:
       case FCmpInst::FCMP_ULE:
       case FCmpInst::FCMP_OLT:
@@ -4332,7 +4330,7 @@ static Value *simplifyFCmpInst(CmpPredicate Pred, Value *LHS, Value *RHS,
         // minnum(X, LesserC)  <  C --> true
         // maxnum(X, GreaterC) <= C --> false
         // maxnum(X, GreaterC) <  C --> false
-        return ConstantInt::get(RetTy, !IsMaxNum);
+        return ConstantInt::get(RetTy, !IsMax);
       default:
         // TRUE/FALSE/ORD/UNO should be handled before this.
         llvm_unreachable("Unexpected fcmp predicate");
diff --git a/llvm/test/Transforms/InstSimplify/floating-point-compare.ll b/llvm/test/Transforms/InstSimplify/floating-point-compare.ll
index 259d98acb1a2d..912869fb581b3 100644
--- a/llvm/test/Transforms/InstSimplify/floating-point-compare.ll
+++ b/llvm/test/Transforms/InstSimplify/floating-point-compare.ll
@@ -1102,6 +1102,330 @@ define i1 @maxnum_une_nan_max_constant(float %x) {
   ret i1 %cmp
 }
 
+; minimumnum with constant compared to larger constant
+
+; minimumnum(x, 0.5) == 1.0 --> false
+
+define i1 @minimumnum_oeq_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_oeq_small_min_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp oeq float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) > 1.0 --> false
+
+define i1 @minimumnum_ogt_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_ogt_small_min_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp ogt float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) >= 1.0 --> false
+
+define i1 @minimumnum_oge_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_oge_small_min_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp oge float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) == 1.0 --> false
+
+define i1 @minimumnum_ueq_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_ueq_small_min_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp ueq float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) > 1.0 --> false
+
+define i1 @minimumnum_ugt_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_ugt_small_min_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp ugt float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) >= 1.0 --> false
+
+define <2 x i1> @minimumnum_uge_small_min_constant(<2 x float> %x) {
+; CHECK-LABEL: @minimumnum_uge_small_min_constant(
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
+;
+  %min = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0.5, float 0.5>)
+  %cmp = fcmp uge <2 x float> %min, <float 1.0, float 1.0>
+  ret <2 x i1> %cmp
+}
+
+; minimumnum(x, 0.5) < 1.0 --> true
+
+define <2 x i1> @minimumnum_olt_small_min_constant(<2 x float> %x) {
+; CHECK-LABEL: @minimumnum_olt_small_min_constant(
+; CHECK-NEXT:    ret <2 x i1> splat (i1 true)
+;
+  %min = call <2 x float> @llvm.minimumnum.v2f32(<2 x float> %x, <2 x float> <float 0.5, float 0.5>)
+  %cmp = fcmp olt <2 x float> %min, <float 1.0, float 1.0>
+  ret <2 x i1> %cmp
+}
+
+; minimumnum(x, 0.5) <= 1.0 --> true
+
+define i1 @minimumnum_ole_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_ole_small_min_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp ole float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) != 1.0 --> true
+
+define i1 @minimumnum_one_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_one_small_min_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp one float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) < 1.0 --> true
+
+define i1 @minimumnum_ult_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_ult_small_min_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp ult float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) <= 1.0 --> true
+
+define i1 @minimumnum_ule_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_ule_small_min_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp ule float %min, 1.0
+  ret i1 %cmp
+}
+
+; minimumnum(x, 0.5) != 1.0 --> true
+
+define i1 @minimumnum_une_small_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_une_small_min_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 0.5)
+  %cmp = fcmp une float %min, 1.0
+  ret i1 %cmp
+}
+
+; Negative test:
+; minimumnum(x, 1.0) != 1.0 --> ?
+
+define i1 @minimumnum_une_equal_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_une_equal_min_constant(
+; CHECK-NEXT:    [[MIN:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float 1.000000e+00)
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp une float [[MIN]], 1.000000e+00
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 1.0)
+  %cmp = fcmp une float %min, 1.0
+  ret i1 %cmp
+}
+
+; Negative test:
+; minimumnum(x, 2.0) != 1.0 --> ?
+
+define i1 @minimumnum_une_large_min_constant(float %x) {
+; CHECK-LABEL: @minimumnum_une_large_min_constant(
+; CHECK-NEXT:    [[MIN:%.*]] = call float @llvm.minimumnum.f32(float [[X:%.*]], float 2.000000e+00)
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp une float [[MIN]], 1.000000e+00
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %min = call float @llvm.minimumnum.f32(float %x, float 2.0)
+  %cmp = fcmp une float %min, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum with constant compared to smaller constant
+
+; maximumnum(x, 1.5) == 1.0 --> false
+
+define i1 @maximumnum_oeq_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_oeq_large_max_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp oeq float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) < 1.0 --> false
+
+define i1 @maximumnum_olt_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_olt_large_max_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp olt float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) <= 1.0 --> false
+
+define i1 @maximumnum_ole_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_ole_large_max_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp ole float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) == 1.0 --> false
+
+define i1 @maximumnum_ueq_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_ueq_large_max_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp ueq float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) < 1.0 --> false
+
+define i1 @maximumnum_ult_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_ult_large_max_constant(
+; CHECK-NEXT:    ret i1 false
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp ult float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) <= 1.0 --> false
+
+define <2 x i1> @maximumnum_ule_large_max_constant(<2 x float> %x) {
+; CHECK-LABEL: @maximumnum_ule_large_max_constant(
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
+;
+  %max = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 1.5, float 1.5>)
+  %cmp = fcmp ule <2 x float> %max, <float 1.0, float 1.0>
+  ret <2 x i1> %cmp
+}
+
+; maximumnum(x, 1.5) > 1.0 --> true
+
+define <2 x i1> @maximumnum_ogt_large_max_constant(<2 x float> %x) {
+; CHECK-LABEL: @maximumnum_ogt_large_max_constant(
+; CHECK-NEXT:    ret <2 x i1> splat (i1 true)
+;
+  %max = call <2 x float> @llvm.maximumnum.v2f32(<2 x float> %x, <2 x float> <float 1.5, float 1.5>)
+  %cmp = fcmp ogt <2 x float> %max, <float 1.0, float 1.0>
+  ret <2 x i1> %cmp
+}
+
+; maximumnum(x, 1.5) >= 1.0 --> true
+
+define i1 @maximumnum_oge_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_oge_large_max_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp oge float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) != 1.0 --> true
+
+define i1 @maximumnum_one_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_one_large_max_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp one float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) > 1.0 --> true
+
+define i1 @maximumnum_ugt_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_ugt_large_max_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp ugt float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) >= 1.0 --> true
+
+define i1 @maximumnum_uge_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_uge_large_max_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp uge float %max, 1.0
+  ret i1 %cmp
+}
+
+; maximumnum(x, 1.5) != 1.0 --> true
+
+define i1 @maximumnum_une_large_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_une_large_max_constant(
+; CHECK-NEXT:    ret i1 true
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.5)
+  %cmp = fcmp une float %max, 1.0
+  ret i1 %cmp
+}
+
+; Negative test:
+; maximumnum(x, 1.0) != 1.0 --> ?
+
+define i1 @maximumnum_une_equal_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_une_equal_max_constant(
+; CHECK-NEXT:    [[MAX:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float 1.000000e+00)
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp une float [[MAX]], 1.000000e+00
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 1.0)
+  %cmp = fcmp une float %max, 1.0
+  ret i1 %cmp
+}
+
+; Negative test:
+; maximumnum(x, 0.5) != 1.0 --> ?
+
+define i1 @maximumnum_une_small_max_constant(float %x) {
+; CHECK-LABEL: @maximumnum_une_small_max_constant(
+; CHECK-NEXT:    [[MAX:%.*]] = call float @llvm.maximumnum.f32(float [[X:%.*]], float 5.000000e-01)
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp une float [[MAX]], 1.000000e+00
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %max = call float @llvm.maximumnum.f32(float %x, float 0.5)
+  %cmp = fcmp une float %max, 1.0
+  ret i1 %cmp
+}
+
 define i1 @known_positive_olt_with_negative_constant(double %a) {
 ; CHECK-LABEL: @known_positive_olt_with_negative_constant(
 ; CHECK-NEXT:    ret i1 false
@@ -1804,7 +2128,9 @@ define i1 @is_fabs_olt_smallest_normal_positive_zero(float %x) denormal_fpenv(dy
 declare <2 x double> @llvm.fabs.v2f64(<2 x double>)
 declare <2 x float> @llvm.fabs.v2f32(<2 x float>)
 declare <2 x float> @llvm.maxnum.v2f32(<2 x float>, <2 x float>)
+declare <2 x float> @llvm.maximumnum.v2f32(<2 x float>, <2 x float>)
 declare <2 x float> @llvm.minnum.v2f32(<2 x float>, <2 x float>)
+declare <2 x float> @llvm.minimumnum.v2f32(<2 x float>, <2 x float>)
 declare <3 x float> @llvm.fabs.v3f32(<3 x float>)
 declare double @llvm.exp2.f64(double)
 declare double @llvm.fabs.f64(double)
@@ -1813,7 +2139,9 @@ declare float @llvm.exp.f32(float)
 declare float @llvm.fabs.f32(float)
 declare float @llvm.fma.f32(float, float, float)
 declare float @llvm.maximum.f32(float, float)
+declare float @llvm.maximumnum.f32(float, float)
 declare float @llvm.maxnum.f32(float, float)
+declare float @llvm.minimumnum.f32(float, float)
 declare float @llvm.minnum.f32(float, float)
 declare float @llvm.sqrt.f32(float)
 declare double @llvm.sqrt.f64(double)



More information about the llvm-commits mailing list