[llvm] [InstCombine] Fold select of ordered fcmps of fabs over `isKnownNeverNaN`-selects to a single select (PR #192182)

via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 19 23:31:42 PDT 2026


https://github.com/imkiva updated https://github.com/llvm/llvm-project/pull/192182

>From e009c9eac05ae5a6de4da24a5eaf29912ce40323 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Tue, 14 Apr 2026 17:11:13 +0800
Subject: [PATCH 1/4] [InstCombine] Fold ordered fcmps of fabs over
 NaN-scrubbed selects

---
 .../InstCombine/InstCombineSelect.cpp         | 71 +++++++++++++++++++
 .../Transforms/InstCombine/fcmp-select.ll     | 65 +++++++++++++++++
 2 files changed, 136 insertions(+)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index bffc887d93535..7ecb4ece56015 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3347,6 +3347,74 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
   return ChangedFMF ? &SI : nullptr;
 }
 
+// Fold a select of an ordered fcmp using fabs of a NaN-scrubbed value:
+//   %ord = fcmp ord T %x, 0.0
+//   %s   = select i1 %ord, T %x, T %y
+//   %a   = call T @llvm.fabs.T(T %s)
+//   %c   = fcmp <ordered-pred> T %a, %k
+//   %r   = select i1 %c, T %s, T %y
+//     =>
+//   %a2  = call T @llvm.fabs.T(T %x)
+//   %c2  = fcmp <ordered-pred> T %a2, %k
+//   %r2  = select i1 %c2, T %x, T %y
+static Instruction *
+foldSelectOfOrderedFAbsCmpOfNaNScrubbedValue(SelectInst &SI,
+                                             InstCombinerImpl &IC) {
+  auto *OuterCmp = dyn_cast<FCmpInst>(SI.getCondition());
+  if (!OuterCmp || !OuterCmp->hasOneUse() ||
+      !FCmpInst::isOrdered(OuterCmp->getPredicate()))
+    return nullptr;
+
+  // The NaN path now evaluates fabs(X) instead of fabs(Y), so preserving nnan
+  // on the fcmp could introduce poison when X is NaN.
+  if (OuterCmp->hasNoNaNs())
+    return nullptr;
+
+  Value *Y = SI.getFalseValue();
+  Value *X = nullptr;
+
+  auto *InnerSel = dyn_cast<SelectInst>(SI.getTrueValue());
+  if (!InnerSel)
+    return nullptr;
+
+  // Match: select (fcmp ord X, 0.0), X, Y
+  auto *InnerCmp = dyn_cast<FCmpInst>(InnerSel->getCondition());
+  if (!InnerCmp || !InnerCmp->hasOneUse() ||
+      InnerCmp->getPredicate() != FCmpInst::FCMP_ORD ||
+      InnerSel->getFalseValue() != Y)
+    return nullptr;
+
+  X = InnerSel->getTrueValue();
+  if (InnerCmp->getOperand(0) != X ||
+      !match(InnerCmp->getOperand(1), m_AnyZeroFP()))
+    return nullptr;
+
+  bool Swapped = false;
+  Value *OtherOp = nullptr;
+  auto *FAbs = dyn_cast<IntrinsicInst>(OuterCmp->getOperand(0));
+
+  if (FAbs && FAbs->hasOneUse() && FAbs->getIntrinsicID() == Intrinsic::fabs &&
+      FAbs->getArgOperand(0) == InnerSel) {
+    OtherOp = OuterCmp->getOperand(1);
+  } else if ((FAbs = dyn_cast<IntrinsicInst>(OuterCmp->getOperand(1))) &&
+             FAbs->hasOneUse() && FAbs->getIntrinsicID() == Intrinsic::fabs &&
+             FAbs->getArgOperand(0) == InnerSel) {
+    Swapped = true;
+    OtherOp = OuterCmp->getOperand(0);
+  } else {
+    return nullptr;
+  }
+
+  Value *NewAbs = IC.Builder.CreateUnaryIntrinsic(Intrinsic::fabs, X);
+  Value *NewCmp = Swapped
+                      ? IC.Builder.CreateFCmpFMF(OuterCmp->getPredicate(),
+                                                 OtherOp, NewAbs, OuterCmp)
+                      : IC.Builder.CreateFCmpFMF(OuterCmp->getPredicate(),
+                                                 NewAbs, OtherOp, OuterCmp);
+  Value *NewSel = IC.Builder.CreateSelectFMF(NewCmp, X, Y, &SI);
+  return IC.replaceInstUsesWith(SI, NewSel);
+}
+
 // Match the following IR pattern:
 //   %x.lowbits = and i8 %x, %lowbitmask
 //   %x.lowbits.are.zero = icmp eq i8 %x.lowbits, 0
@@ -4599,6 +4667,9 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
   if (Instruction *Fabs = foldSelectWithFCmpToFabs(SI, *this))
     return Fabs;
 
+  if (Instruction *I = foldSelectOfOrderedFAbsCmpOfNaNScrubbedValue(SI, *this))
+    return I;
+
   // See if we are selecting two values based on a comparison of the two values.
   if (CmpInst *CI = dyn_cast<CmpInst>(CondVal))
     if (Instruction *NewSel = foldSelectValueEquivalence(SI, *CI))
diff --git a/llvm/test/Transforms/InstCombine/fcmp-select.ll b/llvm/test/Transforms/InstCombine/fcmp-select.ll
index 268cd4675c6cc..0b4e9ebff1c92 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-select.ll
@@ -3,6 +3,7 @@
 
 declare void @use(i1)
 declare void @usef64(double)
+declare double @llvm.fabs.f64(double)
 
 ; X == 42.0 ? X : 42.0 --> 42.0
 
@@ -280,6 +281,70 @@ define i1 @test_fcmp_ord_select_fcmp_oeq_var_const(double %x) {
   ret i1 %cmp2
 }
 
+define double @test_fcmp_ord_select_fabs_fcmp_one_select_var_const(double %x) {
+; CHECK-LABEL: @test_fcmp_ord_select_fabs_fcmp_one_select_var_const(
+; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double 0.000000e+00
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %cmp1 = fcmp ord double %x, 0.000000e+00
+  %sel1 = select i1 %cmp1, double %x, double 0.000000e+00
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double 0.000000e+00
+  ret double %sel2
+}
+
+define double @test_fcmp_ord_select_fabs_fcmp_une_select_var_const(double %x) {
+; CHECK-LABEL: @test_fcmp_ord_select_fabs_fcmp_une_select_var_const(
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ord double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[CMP1]], double [[X]], double 0.000000e+00
+; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[SEL1]])
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp une double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[CMP2]], double [[SEL1]], double 0.000000e+00
+; CHECK-NEXT:    ret double [[SEL2]]
+;
+  %cmp1 = fcmp ord double %x, 0.000000e+00
+  %sel1 = select i1 %cmp1, double %x, double 0.000000e+00
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp une double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double 0.000000e+00
+  ret double %sel2
+}
+
+define double @test_fcmp_ord_select_fabs_fcmp_nnan_one_select_var_var(double %x, double %y) {
+; CHECK-LABEL: @test_fcmp_ord_select_fabs_fcmp_nnan_one_select_var_var(
+; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ord double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[CMP1]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[SEL1]])
+; CHECK-NEXT:    [[CMP2:%.*]] = fcmp nnan one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[CMP2]], double [[SEL1]], double [[Y]]
+; CHECK-NEXT:    ret double [[SEL2]]
+;
+  %cmp1 = fcmp ord double %x, 0.000000e+00
+  %sel1 = select i1 %cmp1, double %x, double %y
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp nnan one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
+define double @test_fcmp_ord_select_fabs_rhs_fcmp_ogt_select_var_var(double %x, double %y, double %k) {
+; CHECK-LABEL: @test_fcmp_ord_select_fabs_rhs_fcmp_ogt_select_var_var(
+; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ninf ogt double [[K:%.*]], [[ABS]]
+; CHECK-NEXT:    [[SEL:%.*]] = select nnan i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %cmp1 = fcmp ord double %x, 0.000000e+00
+  %sel1 = select i1 %cmp1, double %x, double %y
+  %abs = call ninf double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp ninf ogt double %k, %abs
+  %sel2 = select nnan i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
 ; Make sure that we recognize the SPF correctly.
 
 define float @test_select_nnan_nsz_fcmp_olt(float %x) {

>From 9dc2eb2f2e76fdbd6bc7bddb528852d4f9c04b63 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Wed, 15 Apr 2026 13:08:11 +0800
Subject: [PATCH 2/4] [InstCombine] Use `isKnownNeverNaN`

---
 .../InstCombine/InstCombineSelect.cpp         |  57 ++++++--
 .../Transforms/InstCombine/fcmp-select.ll     | 124 ++++++++++++++++++
 2 files changed, 169 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 7ecb4ece56015..21556d8aabee1 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3347,9 +3347,50 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
   return ChangedFMF ? &SI : nullptr;
 }
 
+static bool matchIsNaNTest(FCmpInst *Cmp, Value *X, bool MatchIsNaN,
+                           InstCombinerImpl &IC) {
+  FCmpInst::Predicate ExpectedPred =
+      MatchIsNaN ? FCmpInst::FCMP_UNO : FCmpInst::FCMP_ORD;
+  if (Cmp->getPredicate() != ExpectedPred)
+    return false;
+
+  Value *Cmp0 = Cmp->getOperand(0), *Cmp1 = Cmp->getOperand(1);
+  if (Cmp0 == X && Cmp1 == X)
+    return true;
+
+  auto IsKnownNeverNaN = [&](Value *V) {
+    return match(V, m_NonNaN()) ||
+           isKnownNeverNaN(V, IC.getSimplifyQuery().getWithInstruction(Cmp));
+  };
+
+  return (Cmp0 == X && IsKnownNeverNaN(Cmp1)) ||
+         (Cmp1 == X && IsKnownNeverNaN(Cmp0));
+}
+
+// Match a select that returns X when X is not NaN, and Y otherwise.
+static Value *matchNaNScrubbedValue(SelectInst *SI, Value *Y,
+                                    InstCombinerImpl &IC) {
+  auto *Cmp = dyn_cast<FCmpInst>(SI->getCondition());
+  if (!Cmp || !Cmp->hasOneUse())
+    return nullptr;
+
+  Value *X;
+  bool MatchIsNaN;
+  if (SI->getFalseValue() == Y) {
+    X = SI->getTrueValue();
+    MatchIsNaN = false;
+  } else if (SI->getTrueValue() == Y) {
+    X = SI->getFalseValue();
+    MatchIsNaN = true;
+  } else {
+    return nullptr;
+  }
+
+  return matchIsNaNTest(Cmp, X, MatchIsNaN, IC) ? X : nullptr;
+}
+
 // Fold a select of an ordered fcmp using fabs of a NaN-scrubbed value:
-//   %ord = fcmp ord T %x, 0.0
-//   %s   = select i1 %ord, T %x, T %y
+//   %s   = select i1 (isnotnan T %x), T %x, T %y
 //   %a   = call T @llvm.fabs.T(T %s)
 //   %c   = fcmp <ordered-pred> T %a, %k
 //   %r   = select i1 %c, T %s, T %y
@@ -3377,16 +3418,8 @@ foldSelectOfOrderedFAbsCmpOfNaNScrubbedValue(SelectInst &SI,
   if (!InnerSel)
     return nullptr;
 
-  // Match: select (fcmp ord X, 0.0), X, Y
-  auto *InnerCmp = dyn_cast<FCmpInst>(InnerSel->getCondition());
-  if (!InnerCmp || !InnerCmp->hasOneUse() ||
-      InnerCmp->getPredicate() != FCmpInst::FCMP_ORD ||
-      InnerSel->getFalseValue() != Y)
-    return nullptr;
-
-  X = InnerSel->getTrueValue();
-  if (InnerCmp->getOperand(0) != X ||
-      !match(InnerCmp->getOperand(1), m_AnyZeroFP()))
+  X = matchNaNScrubbedValue(InnerSel, Y, IC);
+  if (!X)
     return nullptr;
 
   bool Swapped = false;
diff --git a/llvm/test/Transforms/InstCombine/fcmp-select.ll b/llvm/test/Transforms/InstCombine/fcmp-select.ll
index 0b4e9ebff1c92..4cc009c066865 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-select.ll
@@ -296,6 +296,130 @@ define double @test_fcmp_ord_select_fabs_fcmp_one_select_var_const(double %x) {
   ret double %sel2
 }
 
+define double @test_fcmp_ord_commuted_select_fabs_fcmp_one_select_var_const(double %x) {
+; CHECK-LABEL: @test_fcmp_ord_commuted_select_fabs_fcmp_one_select_var_const(
+; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double 0.000000e+00
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %cmp1 = fcmp ord double 0.000000e+00, %x
+  %sel1 = select i1 %cmp1, double %x, double 0.000000e+00
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double 0.000000e+00
+  ret double %sel2
+}
+
+define double @test_fcmp_uno_select_fabs_fcmp_one_select_var_var(double %x, double %y) {
+; CHECK-LABEL: @test_fcmp_uno_select_fabs_fcmp_one_select_var_var(
+; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %cmp1 = fcmp uno double %x, 0.000000e+00
+  %sel1 = select i1 %cmp1, double %y, double %x
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
+define double @test_fcmp_ord_select_fabs_fcmp_one_select_nonnan_const(double %x, double %y) {
+; CHECK-LABEL: @test_fcmp_ord_select_fabs_fcmp_one_select_nonnan_const(
+; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %cmp1 = fcmp ord double %x, 1.000000e+00
+  %sel1 = select i1 %cmp1, double %x, double %y
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
+define double @test_fcmp_ord_self_select_fabs_fcmp_one_select_var_var(double %x, double %y) {
+; CHECK-LABEL: @test_fcmp_ord_self_select_fabs_fcmp_one_select_var_var(
+; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %cmp1 = fcmp ord double %x, %x
+  %sel1 = select i1 %cmp1, double %x, double %y
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
+define double @test_fcmp_ord_select_fabs_fcmp_one_select_uitofp_nonnan(double %x, double %y, i32 %i) {
+; CHECK-LABEL: @test_fcmp_ord_select_fabs_fcmp_one_select_uitofp_nonnan(
+; CHECK:         [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %nonnan = uitofp i32 %i to double
+  %cmp1 = fcmp ord double %x, %nonnan
+  %sel1 = select i1 %cmp1, double %x, double %y
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
+define double @test_fcmp_ord_commuted_select_fabs_fcmp_one_select_sitofp_nonnan(double %x, double %y, i32 %i) {
+; CHECK-LABEL: @test_fcmp_ord_commuted_select_fabs_fcmp_one_select_sitofp_nonnan(
+; CHECK:         [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %nonnan = sitofp i32 %i to double
+  %cmp1 = fcmp ord double %nonnan, %x
+  %sel1 = select i1 %cmp1, double %x, double %y
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
+define double @test_fcmp_uno_select_fabs_fcmp_one_select_uitofp_nonnan(double %x, double %y, i32 %i) {
+; CHECK-LABEL: @test_fcmp_uno_select_fabs_fcmp_one_select_uitofp_nonnan(
+; CHECK:         [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %nonnan = uitofp i32 %i to double
+  %cmp1 = fcmp uno double %x, %nonnan
+  %sel1 = select i1 %cmp1, double %y, double %x
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
+  %sel2 = select i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
+define double @test_fcmp_ord_select_fabs_rhs_fcmp_ogt_select_uitofp_nonnan(double %x, double %y, double %k, i32 %i) {
+; CHECK-LABEL: @test_fcmp_ord_select_fabs_rhs_fcmp_ogt_select_uitofp_nonnan(
+; CHECK:         [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt double [[K:%.*]], [[ABS]]
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
+; CHECK-NEXT:    ret double [[SEL]]
+;
+  %nonnan = uitofp i32 %i to double
+  %cmp1 = fcmp ord double %x, %nonnan
+  %sel1 = select i1 %cmp1, double %x, double %y
+  %abs = call double @llvm.fabs.f64(double %sel1)
+  %cmp2 = fcmp ogt double %k, %abs
+  %sel2 = select i1 %cmp2, double %sel1, double %y
+  ret double %sel2
+}
+
 define double @test_fcmp_ord_select_fabs_fcmp_une_select_var_const(double %x) {
 ; CHECK-LABEL: @test_fcmp_ord_select_fabs_fcmp_une_select_var_const(
 ; CHECK-NEXT:    [[CMP1:%.*]] = fcmp ord double [[X:%.*]], 0.000000e+00

>From 82fe8ccb8aa255cc2f250baa147a0c40bb8ddf9d Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Wed, 15 Apr 2026 13:13:31 +0800
Subject: [PATCH 3/4] format code

---
 llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 21556d8aabee1..1d90697f16e3b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3439,11 +3439,10 @@ foldSelectOfOrderedFAbsCmpOfNaNScrubbedValue(SelectInst &SI,
   }
 
   Value *NewAbs = IC.Builder.CreateUnaryIntrinsic(Intrinsic::fabs, X);
-  Value *NewCmp = Swapped
-                      ? IC.Builder.CreateFCmpFMF(OuterCmp->getPredicate(),
-                                                 OtherOp, NewAbs, OuterCmp)
-                      : IC.Builder.CreateFCmpFMF(OuterCmp->getPredicate(),
-                                                 NewAbs, OtherOp, OuterCmp);
+  Value *NewCmp = Swapped ? IC.Builder.CreateFCmpFMF(OuterCmp->getPredicate(),
+                                                     OtherOp, NewAbs, OuterCmp)
+                          : IC.Builder.CreateFCmpFMF(OuterCmp->getPredicate(),
+                                                     NewAbs, OtherOp, OuterCmp);
   Value *NewSel = IC.Builder.CreateSelectFMF(NewCmp, X, Y, &SI);
   return IC.replaceInstUsesWith(SI, NewSel);
 }

>From b4bb8149ffa8073fe5e27b28d3aa0d9e81b978a1 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Mon, 20 Apr 2026 14:31:24 +0800
Subject: [PATCH 4/4] removed uno match case

---
 .../InstCombine/InstCombineSelect.cpp         | 69 +++++++------------
 .../Transforms/InstCombine/fcmp-select.ll     | 31 ---------
 2 files changed, 26 insertions(+), 74 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 1d90697f16e3b..315d5159b9f9f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3347,20 +3347,20 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
   return ChangedFMF ? &SI : nullptr;
 }
 
-static bool matchIsNaNTest(FCmpInst *Cmp, Value *X, bool MatchIsNaN,
-                           InstCombinerImpl &IC) {
-  FCmpInst::Predicate ExpectedPred =
-      MatchIsNaN ? FCmpInst::FCMP_UNO : FCmpInst::FCMP_ORD;
-  if (Cmp->getPredicate() != ExpectedPred)
+static bool matchIsNotNaNTest(Value *Cond, Value *X, InstCombinerImpl &IC) {
+  Instruction *CmpI;
+  Value *Cmp0, *Cmp1;
+  if (!match(Cond, m_OneUse(m_Instruction(
+                       CmpI, m_SpecificFCmp(FCmpInst::FCMP_ORD, m_Value(Cmp0),
+                                            m_Value(Cmp1))))))
     return false;
 
-  Value *Cmp0 = Cmp->getOperand(0), *Cmp1 = Cmp->getOperand(1);
   if (Cmp0 == X && Cmp1 == X)
     return true;
 
   auto IsKnownNeverNaN = [&](Value *V) {
     return match(V, m_NonNaN()) ||
-           isKnownNeverNaN(V, IC.getSimplifyQuery().getWithInstruction(Cmp));
+           isKnownNeverNaN(V, IC.getSimplifyQuery().getWithInstruction(CmpI));
   };
 
   return (Cmp0 == X && IsKnownNeverNaN(Cmp1)) ||
@@ -3368,25 +3368,12 @@ static bool matchIsNaNTest(FCmpInst *Cmp, Value *X, bool MatchIsNaN,
 }
 
 // Match a select that returns X when X is not NaN, and Y otherwise.
-static Value *matchNaNScrubbedValue(SelectInst *SI, Value *Y,
-                                    InstCombinerImpl &IC) {
-  auto *Cmp = dyn_cast<FCmpInst>(SI->getCondition());
-  if (!Cmp || !Cmp->hasOneUse())
+static Value *matchNaNScrubbedValue(Value *V, Value *Y, InstCombinerImpl &IC) {
+  Value *Cond, *X;
+  if (!match(V, m_Select(m_Value(Cond), m_Value(X), m_Specific(Y))))
     return nullptr;
 
-  Value *X;
-  bool MatchIsNaN;
-  if (SI->getFalseValue() == Y) {
-    X = SI->getTrueValue();
-    MatchIsNaN = false;
-  } else if (SI->getTrueValue() == Y) {
-    X = SI->getFalseValue();
-    MatchIsNaN = true;
-  } else {
-    return nullptr;
-  }
-
-  return matchIsNaNTest(Cmp, X, MatchIsNaN, IC) ? X : nullptr;
+  return matchIsNotNaNTest(Cond, X, IC) ? X : nullptr;
 }
 
 // Fold a select of an ordered fcmp using fabs of a NaN-scrubbed value:
@@ -3401,9 +3388,15 @@ static Value *matchNaNScrubbedValue(SelectInst *SI, Value *Y,
 static Instruction *
 foldSelectOfOrderedFAbsCmpOfNaNScrubbedValue(SelectInst &SI,
                                              InstCombinerImpl &IC) {
-  auto *OuterCmp = dyn_cast<FCmpInst>(SI.getCondition());
-  if (!OuterCmp || !OuterCmp->hasOneUse() ||
-      !FCmpInst::isOrdered(OuterCmp->getPredicate()))
+  Instruction *OuterCmpI;
+  Value *Cmp0, *Cmp1;
+  if (!match(SI.getCondition(),
+             m_OneUse(m_Instruction(OuterCmpI,
+                                    m_FCmp(m_Value(Cmp0), m_Value(Cmp1))))))
+    return nullptr;
+
+  auto *OuterCmp = cast<FCmpInst>(OuterCmpI);
+  if (!FCmpInst::isOrdered(OuterCmp->getPredicate()))
     return nullptr;
 
   // The NaN path now evaluates fabs(X) instead of fabs(Y), so preserving nnan
@@ -3412,28 +3405,18 @@ foldSelectOfOrderedFAbsCmpOfNaNScrubbedValue(SelectInst &SI,
     return nullptr;
 
   Value *Y = SI.getFalseValue();
-  Value *X = nullptr;
-
-  auto *InnerSel = dyn_cast<SelectInst>(SI.getTrueValue());
-  if (!InnerSel)
-    return nullptr;
-
-  X = matchNaNScrubbedValue(InnerSel, Y, IC);
+  Value *InnerSel = SI.getTrueValue();
+  Value *X = matchNaNScrubbedValue(InnerSel, Y, IC);
   if (!X)
     return nullptr;
 
   bool Swapped = false;
   Value *OtherOp = nullptr;
-  auto *FAbs = dyn_cast<IntrinsicInst>(OuterCmp->getOperand(0));
-
-  if (FAbs && FAbs->hasOneUse() && FAbs->getIntrinsicID() == Intrinsic::fabs &&
-      FAbs->getArgOperand(0) == InnerSel) {
-    OtherOp = OuterCmp->getOperand(1);
-  } else if ((FAbs = dyn_cast<IntrinsicInst>(OuterCmp->getOperand(1))) &&
-             FAbs->hasOneUse() && FAbs->getIntrinsicID() == Intrinsic::fabs &&
-             FAbs->getArgOperand(0) == InnerSel) {
+  if (match(Cmp0, m_OneUse(m_FAbs(m_Specific(InnerSel))))) {
+    OtherOp = Cmp1;
+  } else if (match(Cmp1, m_OneUse(m_FAbs(m_Specific(InnerSel))))) {
     Swapped = true;
-    OtherOp = OuterCmp->getOperand(0);
+    OtherOp = Cmp0;
   } else {
     return nullptr;
   }
diff --git a/llvm/test/Transforms/InstCombine/fcmp-select.ll b/llvm/test/Transforms/InstCombine/fcmp-select.ll
index 4cc009c066865..9704be6a70844 100644
--- a/llvm/test/Transforms/InstCombine/fcmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/fcmp-select.ll
@@ -311,21 +311,6 @@ define double @test_fcmp_ord_commuted_select_fabs_fcmp_one_select_var_const(doub
   ret double %sel2
 }
 
-define double @test_fcmp_uno_select_fabs_fcmp_one_select_var_var(double %x, double %y) {
-; CHECK-LABEL: @test_fcmp_uno_select_fabs_fcmp_one_select_var_var(
-; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
-; CHECK-NEXT:    ret double [[SEL]]
-;
-  %cmp1 = fcmp uno double %x, 0.000000e+00
-  %sel1 = select i1 %cmp1, double %y, double %x
-  %abs = call double @llvm.fabs.f64(double %sel1)
-  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
-  %sel2 = select i1 %cmp2, double %sel1, double %y
-  ret double %sel2
-}
-
 define double @test_fcmp_ord_select_fabs_fcmp_one_select_nonnan_const(double %x, double %y) {
 ; CHECK-LABEL: @test_fcmp_ord_select_fabs_fcmp_one_select_nonnan_const(
 ; CHECK-NEXT:    [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
@@ -388,22 +373,6 @@ define double @test_fcmp_ord_commuted_select_fabs_fcmp_one_select_sitofp_nonnan(
   ret double %sel2
 }
 
-define double @test_fcmp_uno_select_fabs_fcmp_one_select_uitofp_nonnan(double %x, double %y, i32 %i) {
-; CHECK-LABEL: @test_fcmp_uno_select_fabs_fcmp_one_select_uitofp_nonnan(
-; CHECK:         [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[CMP:%.*]] = fcmp one double [[ABS]], 0x7FF0000000000000
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[CMP]], double [[X]], double [[Y:%.*]]
-; CHECK-NEXT:    ret double [[SEL]]
-;
-  %nonnan = uitofp i32 %i to double
-  %cmp1 = fcmp uno double %x, %nonnan
-  %sel1 = select i1 %cmp1, double %y, double %x
-  %abs = call double @llvm.fabs.f64(double %sel1)
-  %cmp2 = fcmp one double %abs, 0x7FF0000000000000
-  %sel2 = select i1 %cmp2, double %sel1, double %y
-  ret double %sel2
-}
-
 define double @test_fcmp_ord_select_fabs_rhs_fcmp_ogt_select_uitofp_nonnan(double %x, double %y, double %k, i32 %i) {
 ; CHECK-LABEL: @test_fcmp_ord_select_fabs_rhs_fcmp_ogt_select_uitofp_nonnan(
 ; CHECK:         [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])



More information about the llvm-commits mailing list