[llvm] [InstCombine] Fold redundant FP clamp selects; relax min-max-pattern bailout in visitFCmp (PR #173452)

Wenju He via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 23 21:59:12 PST 2025


https://github.com/wenju-he updated https://github.com/llvm/llvm-project/pull/173452

>From 05678b90bfae7c73c6cea73279d08b0ab89b6698 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Wed, 24 Dec 2025 06:38:16 +0100
Subject: [PATCH 1/2] [InstCombine] Fold redundant FP clamp selects; relax
 min-max-pattern bailout in visitFCmp()

visitFCmp() previously bailed out when a following select matched a clamp
pattern. This blocks simplifications when the clamp is provably redundant.

This PR allows simplification for clamp selects of flavor SPF_FMAXNUM/
SPF_FMINNUM when one arm is a constant and the other is a sitofp/uitofp
of an integer value, and the constant equals the exact min/max of that
integer domain:
* SPF_FMAXNUM (pattern max(X,C)): redundant if C is the minimum integer
  mapped exactly to FP (e.g. X = sitofp i8, C = -128.0f).
* SPF_FMINNUM (pattern min(X,C)): redundant if C is the maximum integer
  mapped exactly to FP (e.g. X = uitofp i8, C = 255.0f).
---
 .../InstCombine/InstCombineCompares.cpp       | 38 +++++++++++++++-
 .../Transforms/InstCombine/fcmp-select.ll     | 44 +++++++++++++++++++
 2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 7f1ced9505b9b..67d36bcd5f0c1 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -8679,6 +8679,40 @@ static Instruction *foldFCmpWithFloorAndCeil(FCmpInst &I,
   return nullptr;
 }
 
+/// Returns true if a select that implements a min/max is redundant and
+/// select result can be replaced with its non-constant operand, e.g.,
+///   select ( (si/ui-to-fp A) <= C ), C, (si/ui-to-fp A)
+/// where C is the FP constant equal to the minimum integer value
+/// representable by A.
+static bool isMinMaxCmpSelectEliminable(SelectPatternFlavor Flavor, Value *A,
+                                        Value *B) {
+  Constant *C = dyn_cast<Constant>(B);
+  if (isa<Constant>(A) || !C)
+    return false;
+
+  if (C->getType()->isVectorTy())
+    C = C->getSplatValue();
+  auto *CFP = dyn_cast_or_null<ConstantFP>(C);
+  if (!CFP)
+    return false;
+
+  auto *I = dyn_cast<Instruction>(A);
+  if (!I || !(I->getOpcode() == Instruction::SIToFP ||
+              I->getOpcode() == Instruction::UIToFP))
+    return false;
+
+  bool IsUnsigned = I->getOpcode() == Instruction::UIToFP;
+  unsigned BitWidth =
+      I->getOperand(0)->getType()->getScalarType()->getIntegerBitWidth();
+  APSInt MinOrMaxInt = (Flavor == SPF_FMAXNUM)
+                           ? APSInt::getMinValue(BitWidth, IsUnsigned)
+                           : APSInt::getMaxValue(BitWidth, IsUnsigned);
+  APSInt ToInt(BitWidth, IsUnsigned);
+  bool IsExact;
+  CFP->getValueAPF().convertToInteger(ToInt, APFloat::rmTowardZero, &IsExact);
+  return IsExact && ToInt == MinOrMaxInt;
+}
+
 Instruction *InstCombinerImpl::visitFCmpInst(FCmpInst &I) {
   bool Changed = false;
 
@@ -8762,7 +8796,9 @@ Instruction *InstCombinerImpl::visitFCmpInst(FCmpInst &I) {
     if (SelectInst *SI = dyn_cast<SelectInst>(I.user_back())) {
       Value *A, *B;
       SelectPatternResult SPR = matchSelectPattern(SI, A, B);
-      if (SPR.Flavor != SPF_UNKNOWN)
+      if (SPR.Flavor != SPF_UNKNOWN &&
+          !((SPR.Flavor == SPF_FMAXNUM || SPR.Flavor == SPF_FMINNUM) &&
+            isMinMaxCmpSelectEliminable(SPR.Flavor, A, B)))
         return nullptr;
     }
 
diff --git a/llvm/test/Transforms/InstCombine/fcmp-select.ll b/llvm/test/Transforms/InstCombine/fcmp-select.ll
index b622c8636eccb..65ef5de3117fc 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-select.ll
@@ -303,3 +303,47 @@ define float @test_select_nnan_nsz_fcmp_ult(float %x) {
   %sel = select nnan nsz i1 %cmp, float %x, float -0.000000e+00
   ret float %sel
 }
+
+define float @test_select_fcmp_sitofp_max(i8 %x) {
+; CHECK-LABEL: @test_select_fcmp_sitofp_max(
+; CHECK-NEXT:    [[TMP2:%.*]] = sitofp i8 [[TMP0:%.*]] to float
+; CHECK-NEXT:    ret float [[TMP2]]
+;
+  %f = sitofp i8 %x to float
+  %cmp = fcmp ole float %f, -1.280000e+02
+  %sel = select i1 %cmp, float -1.280000e+02, float %f
+  ret float %sel
+}
+
+define <2 x float> @test_select_fcmp_sitofp_max_vec(<2 x i8> %x) {
+; CHECK-LABEL: @test_select_fcmp_sitofp_max_vec(
+; CHECK-NEXT:    [[F:%.*]] = sitofp <2 x i8> [[X:%.*]] to <2 x float>
+; CHECK-NEXT:    ret <2 x float> [[F]]
+;
+  %f = sitofp <2 x i8> %x to <2 x float>
+  %cmp = fcmp ole <2 x float> %f, splat (float -1.280000e+02)
+  %sel = select <2 x i1> %cmp, <2 x float> splat (float -1.280000e+02), <2 x float> %f
+  ret <2 x float> %sel
+}
+
+define float @test_select_fcmp_sitofp_min(i8 %x) {
+; CHECK-LABEL: @test_select_fcmp_sitofp_min(
+; CHECK-NEXT:    [[TMP2:%.*]] = sitofp i8 [[TMP0:%.*]] to float
+; CHECK-NEXT:    ret float [[TMP2]]
+;
+  %f = sitofp i8 %x to float
+  %cmp = fcmp oge float %f, 1.270000e+02
+  %sel = select i1 %cmp, float 1.270000e+02, float %f
+  ret float %sel
+}
+
+define float @test_select_fcmp_uitofp_min(i8 %x) {
+; CHECK-LABEL: @test_select_fcmp_uitofp_min(
+; CHECK-NEXT:    [[TMP2:%.*]] = uitofp i8 [[TMP0:%.*]] to float
+; CHECK-NEXT:    ret float [[TMP2]]
+;
+  %f = uitofp i8 %x to float
+  %cmp = fcmp oge float %f, 2.550000e+02
+  %sel = select i1 %cmp, float 2.550000e+02, float %f
+  ret float %sel
+}

>From 488c5ef8f65415b07d55fb65c4b25bb5e13b023c Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Wed, 24 Dec 2025 06:59:01 +0100
Subject: [PATCH 2/2] run update_test_checks.py

---
 llvm/test/Transforms/InstCombine/fcmp-select.ll | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/fcmp-select.ll b/llvm/test/Transforms/InstCombine/fcmp-select.ll
index 65ef5de3117fc..bb41f18c74f5f 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-select.ll
@@ -306,8 +306,8 @@ define float @test_select_nnan_nsz_fcmp_ult(float %x) {
 
 define float @test_select_fcmp_sitofp_max(i8 %x) {
 ; CHECK-LABEL: @test_select_fcmp_sitofp_max(
-; CHECK-NEXT:    [[TMP2:%.*]] = sitofp i8 [[TMP0:%.*]] to float
-; CHECK-NEXT:    ret float [[TMP2]]
+; CHECK-NEXT:    [[F:%.*]] = sitofp i8 [[X:%.*]] to float
+; CHECK-NEXT:    ret float [[F]]
 ;
   %f = sitofp i8 %x to float
   %cmp = fcmp ole float %f, -1.280000e+02
@@ -328,8 +328,8 @@ define <2 x float> @test_select_fcmp_sitofp_max_vec(<2 x i8> %x) {
 
 define float @test_select_fcmp_sitofp_min(i8 %x) {
 ; CHECK-LABEL: @test_select_fcmp_sitofp_min(
-; CHECK-NEXT:    [[TMP2:%.*]] = sitofp i8 [[TMP0:%.*]] to float
-; CHECK-NEXT:    ret float [[TMP2]]
+; CHECK-NEXT:    [[F:%.*]] = sitofp i8 [[X:%.*]] to float
+; CHECK-NEXT:    ret float [[F]]
 ;
   %f = sitofp i8 %x to float
   %cmp = fcmp oge float %f, 1.270000e+02
@@ -339,8 +339,8 @@ define float @test_select_fcmp_sitofp_min(i8 %x) {
 
 define float @test_select_fcmp_uitofp_min(i8 %x) {
 ; CHECK-LABEL: @test_select_fcmp_uitofp_min(
-; CHECK-NEXT:    [[TMP2:%.*]] = uitofp i8 [[TMP0:%.*]] to float
-; CHECK-NEXT:    ret float [[TMP2]]
+; CHECK-NEXT:    [[F:%.*]] = uitofp i8 [[X:%.*]] to float
+; CHECK-NEXT:    ret float [[F]]
 ;
   %f = uitofp i8 %x to float
   %cmp = fcmp oge float %f, 2.550000e+02



More information about the llvm-commits mailing list