[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:33:13 PDT 2026


================
@@ -3347,6 +3347,106 @@ 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:
+//   %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
+//     =>
+//   %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;
+
+  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 &&
----------------
imkiva wrote:

Thanks. Fixed now

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


More information about the llvm-commits mailing list