[llvm] [InstCombine] Fold max/min when incrementing/decrementing by 1 (PR #142466)

Alex MacLean via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 5 09:56:49 PDT 2025


================
@@ -565,6 +565,59 @@ Instruction *InstCombinerImpl::foldSelectIntoOp(SelectInst &SI, Value *TrueVal,
   return nullptr;
 }
 
+/// Try to fold a select to a min/max intrinsic. Many cases are already handled
+/// by matchDecomposedSelectPattern but here we handle the cases where more
+/// exensive modification of the IR is required.
+static Value *foldSelectICmpMinMax(const ICmpInst *Cmp, Value *TVal,
+                                   Value *FVal,
+                                   InstCombiner::BuilderTy &Builder,
+                                   const SimplifyQuery &SQ) {
+  const Value *CmpLHS = Cmp->getOperand(0);
+  const Value *CmpRHS = Cmp->getOperand(1);
+  ICmpInst::Predicate Pred = Cmp->getPredicate();
+
+  // (X > Y) ? X : (Y - 1) ==> MIN(X, Y - 1)
+  // (X < Y) ? X : (Y + 1) ==> MAX(X, Y + 1)
+  // This transformation is valid when overflow corresponding to the sign of
+  // the comparison is poison and we must drop the non-matching overflow flag.
+  if (CmpRHS == TVal) {
+    std::swap(CmpLHS, CmpRHS);
+    Pred = CmpInst::getSwappedPredicate(Pred);
+  }
+
+  // TODO: consider handeling 'or disjoint' as well, though these would need to
+  // be converted to 'add' instructions.
+  if (CmpLHS == TVal && isa<Instruction>(FVal)) {
+    if (Pred == CmpInst::ICMP_SGT &&
+        match(FVal, m_NSWAdd(m_Specific(CmpRHS), m_One()))) {
+      cast<Instruction>(FVal)->setHasNoUnsignedWrap(false);
+      return Builder.CreateBinaryIntrinsic(Intrinsic::smax, TVal, FVal);
+    }
+
+    if (Pred == CmpInst::ICMP_SLT &&
+        match(FVal, m_NSWAdd(m_Specific(CmpRHS), m_AllOnes()))) {
+      cast<Instruction>(FVal)->setHasNoUnsignedWrap(false);
+      return Builder.CreateBinaryIntrinsic(Intrinsic::smin, TVal, FVal);
+    }
+
+    if (Pred == CmpInst::ICMP_UGT &&
+        match(FVal, m_NUWAdd(m_Specific(CmpRHS), m_One()))) {
+      cast<Instruction>(FVal)->setHasNoSignedWrap(false);
+      return Builder.CreateBinaryIntrinsic(Intrinsic::umax, TVal, FVal);
+    }
+
+    // Note: We must use isKnownNonZero here because "sub nuw %x, 1" will be
+    // canonicalize to "add %x, -1" discarding the nuw flag.
+    if (Pred == CmpInst::ICMP_ULT &&
+        match(FVal, m_Add(m_Specific(CmpRHS), m_AllOnes())) &&
+        isKnownNonZero(CmpRHS, SQ)) {
+      cast<Instruction>(FVal)->setHasNoSignedWrap(false);
----------------
AlexMaclean wrote:

Updated. In practice, I'm not sure we could ever reach this point in that case, at least I was not able to construct a test. But I agree it is good to stay on the safe side and not rely on unrelated optimizations to prevent a mis-compilation here. 

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


More information about the llvm-commits mailing list