[llvm] [InstCombine] Div ceil optimizations (PR #190175)
Takashi Idobe via llvm-commits
llvm-commits at lists.llvm.org
Sun Apr 5 12:57:52 PDT 2026
https://github.com/Takashiidobe updated https://github.com/llvm/llvm-project/pull/190175
>From e36d4247f61da8144884eafa76c51cc24289133f Mon Sep 17 00:00:00 2001
From: Takashiidobe <idobetakashi at gmail.com>
Date: Thu, 2 Apr 2026 20:27:03 -0400
Subject: [PATCH 1/3] Add tests for div ceil folds
---
llvm/test/Transforms/InstCombine/divceil.ll | 203 ++++++++++++++++++++
1 file changed, 203 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/divceil.ll
diff --git a/llvm/test/Transforms/InstCombine/divceil.ll b/llvm/test/Transforms/InstCombine/divceil.ll
new file mode 100644
index 0000000000000..efef59119bc7c
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/divceil.ll
@@ -0,0 +1,203 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Optimizes div_ceil idioms:
+; same-type form: add(udiv(X, Y), zext(icmp ne(urem(X, Y), 0)))
+; -> udiv(add nuw(X, Y - 1), Y)
+; zext form: add(zext(udiv(X, Y)), zext(icmp ne(urem(X, Y), 0)))
+; -> zext(udiv(add nuw(X, Y - 1), Y))
+; when X + (Y-1) is provably non-wrapping (via range info on both X and Y).
+
+declare void @use(i8)
+declare void @use16(i16)
+
+; Variable divisor: both X in [0,100] and Y in [1,10], so max X+(Y-1) = 109 <= 255.
+define i8 @divceil_i8_var_divisor(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor(
+; CHECK-NEXT: [[RESULT:%.*]] = udiv i8 [[TMP2:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i8 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i8
+; CHECK-NEXT: [[RESULT1:%.*]] = add nuw i8 [[RESULT]], [[ROUND]]
+; CHECK-NEXT: ret i8 [[RESULT1]]
+;
+ %q = udiv i8 %x, %y
+ %r = urem i8 %x, %y
+ %cond = icmp ne i8 %r, 0
+ %round = zext i1 %cond to i8
+ %result = add i8 %q, %round
+ ret i8 %result
+}
+
+; Variable divisor, commuted add.
+define i8 @divceil_i8_var_divisor_commuted(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_commuted(
+; CHECK-NEXT: [[RESULT:%.*]] = udiv i8 [[TMP2:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i8 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i8
+; CHECK-NEXT: [[RESULT1:%.*]] = add nuw i8 [[RESULT]], [[ROUND]]
+; CHECK-NEXT: ret i8 [[RESULT1]]
+;
+ %q = udiv i8 %x, %y
+ %r = urem i8 %x, %y
+ %cond = icmp ne i8 %r, 0
+ %round = zext i1 %cond to i8
+ %result = add i8 %round, %q
+ ret i8 %result
+}
+
+; Variable divisor with i32: X in [0, 100], Y in [2, 8], max X+(Y-1) = 107 <= UINT32_MAX.
+define i32 @divceil_i32_var_divisor(i32 range(i32 0, 101) %x, i32 range(i32 2, 9) %y) {
+; CHECK-LABEL: @divceil_i32_var_divisor(
+; CHECK-NEXT: [[RESULT:%.*]] = udiv i32 [[TMP2:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i32 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[R]], 0
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i32
+; CHECK-NEXT: [[RESULT1:%.*]] = add nuw nsw i32 [[RESULT]], [[ROUND]]
+; CHECK-NEXT: ret i32 [[RESULT1]]
+;
+ %q = udiv i32 %x, %y
+ %r = urem i32 %x, %y
+ %cond = icmp ne i32 %r, 0
+ %round = zext i1 %cond to i32
+ %result = add i32 %q, %round
+ ret i32 %result
+}
+
+; Negative: Y unbounded -> max Y = 255, max X+(Y-1) = 100+254 overflows i8.
+define i8 @divceil_i8_var_divisor_y_unbounded(i8 range(i8 0, 101) %x, i8 %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_y_unbounded(
+; CHECK-NEXT: [[Q:%.*]] = udiv i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i8
+; CHECK-NEXT: [[RESULT:%.*]] = add nuw i8 [[Q]], [[ROUND]]
+; CHECK-NEXT: ret i8 [[RESULT]]
+;
+ %q = udiv i8 %x, %y
+ %r = urem i8 %x, %y
+ %cond = icmp ne i8 %r, 0
+ %round = zext i1 %cond to i8
+ %result = add i8 %q, %round
+ ret i8 %result
+}
+
+; Negative: X unbounded -> max X+(Y-1) overflows even with bounded Y.
+define i8 @divceil_i8_var_divisor_x_unbounded(i8 %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_x_unbounded(
+; CHECK-NEXT: [[Q:%.*]] = udiv i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i8
+; CHECK-NEXT: [[RESULT:%.*]] = add i8 [[Q]], [[ROUND]]
+; CHECK-NEXT: ret i8 [[RESULT]]
+;
+ %q = udiv i8 %x, %y
+ %r = urem i8 %x, %y
+ %cond = icmp ne i8 %r, 0
+ %round = zext i1 %cond to i8
+ %result = add i8 %q, %round
+ ret i8 %result
+}
+
+; Zext form: udiv/urem in i8, add in i16.
+; X in [0,100], Y in [1,10]: max X+(Y-1) = 109 <= 255.
+define i16 @divceil_i8_var_divisor_zext(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_zext(
+; CHECK-NEXT: [[TMP3:%.*]] = udiv i8 [[TMP2:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i8 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[Q_EXT:%.*]] = zext nneg i8 [[TMP3]] to i16
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i16
+; CHECK-NEXT: [[RESULT:%.*]] = add nuw nsw i16 [[Q_EXT]], [[ROUND]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %q = udiv i8 %x, %y
+ %r = urem i8 %x, %y
+ %cond = icmp ne i8 %r, 0
+ %q_ext = zext i8 %q to i16
+ %round = zext i1 %cond to i16
+ %result = add i16 %q_ext, %round
+ ret i16 %result
+}
+
+; Zext form, commuted add.
+define i16 @divceil_i8_var_divisor_commuted_zext(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_commuted_zext(
+; CHECK-NEXT: [[TMP3:%.*]] = udiv i8 [[TMP2:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i8 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[Q_EXT:%.*]] = zext nneg i8 [[TMP3]] to i16
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i16
+; CHECK-NEXT: [[RESULT:%.*]] = add nuw nsw i16 [[ROUND]], [[Q_EXT]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %q = udiv i8 %x, %y
+ %r = urem i8 %x, %y
+ %cond = icmp ne i8 %r, 0
+ %q_ext = zext i8 %q to i16
+ %round = zext i1 %cond to i16
+ %result = add i16 %round, %q_ext
+ ret i16 %result
+}
+
+; Zext form with i16->i32: X in [0, 100], Y in [2, 8].
+define i32 @divceil_i16_var_divisor_zext(i16 range(i16 0, 101) %x, i16 range(i16 2, 9) %y) {
+; CHECK-LABEL: @divceil_i16_var_divisor_zext(
+; CHECK-NEXT: [[TMP3:%.*]] = udiv i16 [[TMP2:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i16 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i16 [[R]], 0
+; CHECK-NEXT: [[Q_EXT:%.*]] = zext nneg i16 [[TMP3]] to i32
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i32
+; CHECK-NEXT: [[RESULT:%.*]] = add nuw nsw i32 [[Q_EXT]], [[ROUND]]
+; CHECK-NEXT: ret i32 [[RESULT]]
+;
+ %q = udiv i16 %x, %y
+ %r = urem i16 %x, %y
+ %cond = icmp ne i16 %r, 0
+ %q_ext = zext i16 %q to i32
+ %round = zext i1 %cond to i32
+ %result = add i32 %q_ext, %round
+ ret i32 %result
+}
+
+; Zext form negative: Y unbounded -> overflows in i8 type.
+define i16 @divceil_i8_var_divisor_y_unbounded_zext(i8 range(i8 0, 101) %x, i8 %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_y_unbounded_zext(
+; CHECK-NEXT: [[Q:%.*]] = udiv i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[Q_EXT:%.*]] = zext nneg i8 [[Q]] to i16
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i16
+; CHECK-NEXT: [[RESULT:%.*]] = add nuw nsw i16 [[Q_EXT]], [[ROUND]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %q = udiv i8 %x, %y
+ %r = urem i8 %x, %y
+ %cond = icmp ne i8 %r, 0
+ %q_ext = zext i8 %q to i16
+ %round = zext i1 %cond to i16
+ %result = add i16 %q_ext, %round
+ ret i16 %result
+}
+
+; Zext form negative: X unbounded -> overflows even with bounded Y.
+define i16 @divceil_i8_var_divisor_x_unbounded_zext(i8 %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_x_unbounded_zext(
+; CHECK-NEXT: [[Q:%.*]] = udiv i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[Q_EXT:%.*]] = zext i8 [[Q]] to i16
+; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i16
+; CHECK-NEXT: [[RESULT:%.*]] = add nuw nsw i16 [[Q_EXT]], [[ROUND]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %q = udiv i8 %x, %y
+ %r = urem i8 %x, %y
+ %cond = icmp ne i8 %r, 0
+ %q_ext = zext i8 %q to i16
+ %round = zext i1 %cond to i16
+ %result = add i16 %q_ext, %round
+ ret i16 %result
+}
>From 187a4404307bd842b844fccf5d0f552d16b429e5 Mon Sep 17 00:00:00 2001
From: Takashiidobe <idobetakashi at gmail.com>
Date: Thu, 2 Apr 2026 20:27:26 -0400
Subject: [PATCH 2/3] Implement div ceil folds:
add(udiv(X, Y), zext(icmp ne(urem(X, Y), 0))) -> udiv(add nuw(X, Y - 1), Y)
add(zext(udiv(X, Y)), zext(icmp ne(urem(X, Y), 0))) -> zext(udiv(add nuw(X, Y - 1), Y))
---
llvm/lib/Analysis/ValueTracking.cpp | 4 ++
.../InstCombine/InstCombineAddSub.cpp | 68 +++++++++++++++++++
.../InstCombine/InstCombineInternal.h | 6 ++
llvm/test/Transforms/InstCombine/divceil.ll | 60 +++++++---------
llvm/test/Transforms/InstCombine/fls.ll | 2 +-
5 files changed, 103 insertions(+), 37 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 340a616f13e19..53e8ff15f3931 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10361,6 +10361,10 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
SI->getFalseValue(), ForSigned, UseInstrInfo, AC, CtxI, DT, Depth + 1);
CR = CRTrue.unionWith(CRFalse);
CR = CR.intersectWith(getRangeForSelectPattern(*SI, IIQ));
+ } else if (auto *TI = dyn_cast<TruncInst>(V)) {
+ ConstantRange SrcCR = computeConstantRange(
+ TI->getOperand(0), ForSigned, UseInstrInfo, AC, CtxI, DT, Depth + 1);
+ CR = SrcCR.truncate(BitWidth);
} else if (isa<FPToUIInst>(V) || isa<FPToSIInst>(V)) {
APInt Lower = APInt(BitWidth, 0);
APInt Upper = APInt(BitWidth, 0);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index c781c6978b275..122bdcd8a6474 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1523,6 +1523,68 @@ static Instruction *foldBoxMultiply(BinaryOperator &I) {
return nullptr;
}
+// Return true if X + (Y-1) is provably non-wrapping in X's type
+static bool checkDivCeilNUW(Value *X, Value *Y, BinaryOperator &I,
+ AssumptionCache &AC, DominatorTree &DT) {
+ ConstantRange CRX = computeConstantRange(X, /*ForSigned=*/false,
+ /*UseInstrInfo=*/true, &AC, &I, &DT);
+ ConstantRange CRY = computeConstantRange(Y, /*ForSigned=*/false,
+ /*UseInstrInfo=*/true, &AC, &I, &DT);
+ APInt MaxX = CRX.getUnsignedMax();
+ APInt MaxY = CRY.getUnsignedMax();
+ unsigned BitWidth = MaxX.getBitWidth();
+ // MaxX + (MaxY - 1) <= UINT_MAX <==> MaxX <= UINT_MAX - (MaxY - 1)
+ return !MaxX.ugt(APInt::getMaxValue(BitWidth) - (MaxY - 1));
+}
+
+// Fold the same-type form of the div_ceil idiom:
+// add(udiv(X, Y), zext(icmp ne(urem(X, Y), 0)))
+// -> udiv(add nuw(X, Y - 1), Y)
+// where udiv and urem are the same type as the add.
+Instruction *InstCombinerImpl::foldSameTypeDivCeil(BinaryOperator &I) {
+ Value *X, *X2, *Y, *Y2;
+ CmpPredicate Pred;
+
+ auto UDivPat = m_OneUse(m_UDiv(m_Value(X), m_Value(Y)));
+ auto URemPat = m_OneUse(m_URem(m_Value(X2), m_Value(Y2)));
+ auto ICmpPat = m_OneUse(m_ICmp(Pred, URemPat, m_Zero()));
+ auto ZExtCmpPat = m_OneUse(m_ZExt(ICmpPat));
+
+ if (!match(&I, m_c_Add(UDivPat, ZExtCmpPat)) || Pred != ICmpInst::ICMP_NE ||
+ X != X2 || Y != Y2 || !checkDivCeilNUW(X, Y, I, AC, DT))
+ return nullptr;
+
+ Value *YMinusOne = Builder.CreateSub(Y, ConstantInt::get(Y->getType(), 1));
+ Value *NUWAdd = Builder.CreateAdd(X, YMinusOne, "", /*HasNUW=*/true);
+ return BinaryOperator::CreateUDiv(NUWAdd, Y);
+}
+
+// Fold the zext form of the div_ceil idiom:
+// add(zext(udiv(X, Y)), zext(icmp ne(urem(X, Y), 0)))
+// -> zext(udiv(add nuw(X, Y - 1), Y))
+// udiv and urem operate in a type narrower than the add; the result is
+// zexted back to the add's type.
+Instruction *InstCombinerImpl::foldZExtDivCeil(BinaryOperator &I) {
+ Value *X, *X2, *Y, *Y2;
+ CmpPredicate Pred;
+
+ auto UDivPat = m_OneUse(m_UDiv(m_Value(X), m_Value(Y)));
+ auto URemPat = m_OneUse(m_URem(m_Value(X2), m_Value(Y2)));
+ auto ICmpPat = m_OneUse(m_ICmp(Pred, URemPat, m_Zero()));
+ auto ZExtDivPat = m_OneUse(m_ZExt(UDivPat));
+ auto ZExtCmpPat = m_OneUse(m_ZExt(ICmpPat));
+
+ if (!match(&I, m_c_Add(ZExtDivPat, ZExtCmpPat)) ||
+ Pred != ICmpInst::ICMP_NE || X != X2 || Y != Y2 ||
+ !checkDivCeilNUW(X, Y, I, AC, DT))
+ return nullptr;
+
+ Value *YMinusOne = Builder.CreateSub(Y, ConstantInt::get(Y->getType(), 1));
+ Value *NUWAdd = Builder.CreateAdd(X, YMinusOne, "", /*HasNUW=*/true);
+ Value *Div = Builder.CreateUDiv(NUWAdd, Y);
+ return new ZExtInst(Div, I.getType());
+}
+
Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
if (Value *V = simplifyAddInst(I.getOperand(0), I.getOperand(1),
I.hasNoSignedWrap(), I.hasNoUnsignedWrap(),
@@ -1915,6 +1977,12 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
if (Instruction *Res = foldBinOpOfSelectAndCastOfSelectCondition(I))
return Res;
+ if (Instruction *Res = foldSameTypeDivCeil(I))
+ return Res;
+
+ if (Instruction *Res = foldZExtDivCeil(I))
+ return Res;
+
// Re-enqueue users of the induction variable of add recurrence if we infer
// new nuw/nsw flags.
if (Changed) {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 160f766b60973..6404ab3f5b390 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -544,6 +544,12 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
/// (Binop (cast C), (select C, T, F))
/// -> (select C, C0, C1)
Instruction *foldBinOpOfSelectAndCastOfSelectCondition(BinaryOperator &I);
+ /// Same-type form: (add (udiv X, Y), (zext (icmp ne (urem X, Y), 0)))
+ /// -> (udiv (add nuw X, Y-1), Y)
+ Instruction *foldSameTypeDivCeil(BinaryOperator &I);
+ /// Zext form: (add (zext (udiv X, Y)), (zext (icmp ne (urem X, Y), 0)))
+ /// -> (zext (udiv (add nuw X, Y-1), Y))
+ Instruction *foldZExtDivCeil(BinaryOperator &I);
/// This tries to simplify binary operations by factorizing out common terms
/// (e. g. "(A*B)+(A*C)" -> "A*(B+C)").
diff --git a/llvm/test/Transforms/InstCombine/divceil.ll b/llvm/test/Transforms/InstCombine/divceil.ll
index efef59119bc7c..e6400f2b70212 100644
--- a/llvm/test/Transforms/InstCombine/divceil.ll
+++ b/llvm/test/Transforms/InstCombine/divceil.ll
@@ -14,12 +14,10 @@ declare void @use16(i16)
; Variable divisor: both X in [0,100] and Y in [1,10], so max X+(Y-1) = 109 <= 255.
define i8 @divceil_i8_var_divisor(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y) {
; CHECK-LABEL: @divceil_i8_var_divisor(
-; CHECK-NEXT: [[RESULT:%.*]] = udiv i8 [[TMP2:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[R:%.*]] = urem i8 [[TMP2]], [[Y]]
-; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
-; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i8
-; CHECK-NEXT: [[RESULT1:%.*]] = add nuw i8 [[RESULT]], [[ROUND]]
-; CHECK-NEXT: ret i8 [[RESULT1]]
+; CHECK-NEXT: [[ROUND:%.*]] = add nsw i8 [[Y:%.*]], -1
+; CHECK-NEXT: [[RESULT1:%.*]] = add nuw i8 [[RESULT:%.*]], [[ROUND]]
+; CHECK-NEXT: [[RESULT2:%.*]] = udiv i8 [[RESULT1]], [[Y]]
+; CHECK-NEXT: ret i8 [[RESULT2]]
;
%q = udiv i8 %x, %y
%r = urem i8 %x, %y
@@ -32,12 +30,10 @@ define i8 @divceil_i8_var_divisor(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y)
; Variable divisor, commuted add.
define i8 @divceil_i8_var_divisor_commuted(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y) {
; CHECK-LABEL: @divceil_i8_var_divisor_commuted(
-; CHECK-NEXT: [[RESULT:%.*]] = udiv i8 [[TMP2:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[R:%.*]] = urem i8 [[TMP2]], [[Y]]
-; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
-; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i8
-; CHECK-NEXT: [[RESULT1:%.*]] = add nuw i8 [[RESULT]], [[ROUND]]
-; CHECK-NEXT: ret i8 [[RESULT1]]
+; CHECK-NEXT: [[ROUND:%.*]] = add nsw i8 [[Y:%.*]], -1
+; CHECK-NEXT: [[RESULT1:%.*]] = add nuw i8 [[RESULT:%.*]], [[ROUND]]
+; CHECK-NEXT: [[RESULT2:%.*]] = udiv i8 [[RESULT1]], [[Y]]
+; CHECK-NEXT: ret i8 [[RESULT2]]
;
%q = udiv i8 %x, %y
%r = urem i8 %x, %y
@@ -50,12 +46,10 @@ define i8 @divceil_i8_var_divisor_commuted(i8 range(i8 0, 101) %x, i8 range(i8 1
; Variable divisor with i32: X in [0, 100], Y in [2, 8], max X+(Y-1) = 107 <= UINT32_MAX.
define i32 @divceil_i32_var_divisor(i32 range(i32 0, 101) %x, i32 range(i32 2, 9) %y) {
; CHECK-LABEL: @divceil_i32_var_divisor(
-; CHECK-NEXT: [[RESULT:%.*]] = udiv i32 [[TMP2:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[R:%.*]] = urem i32 [[TMP2]], [[Y]]
-; CHECK-NEXT: [[COND:%.*]] = icmp ne i32 [[R]], 0
-; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i32
-; CHECK-NEXT: [[RESULT1:%.*]] = add nuw nsw i32 [[RESULT]], [[ROUND]]
-; CHECK-NEXT: ret i32 [[RESULT1]]
+; CHECK-NEXT: [[ROUND:%.*]] = add nsw i32 [[Y:%.*]], -1
+; CHECK-NEXT: [[RESULT1:%.*]] = add nuw nsw i32 [[RESULT:%.*]], [[ROUND]]
+; CHECK-NEXT: [[RESULT2:%.*]] = udiv i32 [[RESULT1]], [[Y]]
+; CHECK-NEXT: ret i32 [[RESULT2]]
;
%q = udiv i32 %x, %y
%r = urem i32 %x, %y
@@ -105,12 +99,10 @@ define i8 @divceil_i8_var_divisor_x_unbounded(i8 %x, i8 range(i8 1, 11) %y) {
; X in [0,100], Y in [1,10]: max X+(Y-1) = 109 <= 255.
define i16 @divceil_i8_var_divisor_zext(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y) {
; CHECK-LABEL: @divceil_i8_var_divisor_zext(
-; CHECK-NEXT: [[TMP3:%.*]] = udiv i8 [[TMP2:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[R:%.*]] = urem i8 [[TMP2]], [[Y]]
-; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
-; CHECK-NEXT: [[Q_EXT:%.*]] = zext nneg i8 [[TMP3]] to i16
-; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i16
-; CHECK-NEXT: [[RESULT:%.*]] = add nuw nsw i16 [[Q_EXT]], [[ROUND]]
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i8 [[Y:%.*]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = add nuw i8 [[X:%.*]], [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = udiv i8 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[RESULT:%.*]] = zext i8 [[TMP3]] to i16
; CHECK-NEXT: ret i16 [[RESULT]]
;
%q = udiv i8 %x, %y
@@ -125,12 +117,10 @@ define i16 @divceil_i8_var_divisor_zext(i8 range(i8 0, 101) %x, i8 range(i8 1, 1
; Zext form, commuted add.
define i16 @divceil_i8_var_divisor_commuted_zext(i8 range(i8 0, 101) %x, i8 range(i8 1, 11) %y) {
; CHECK-LABEL: @divceil_i8_var_divisor_commuted_zext(
-; CHECK-NEXT: [[TMP3:%.*]] = udiv i8 [[TMP2:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[R:%.*]] = urem i8 [[TMP2]], [[Y]]
-; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
-; CHECK-NEXT: [[Q_EXT:%.*]] = zext nneg i8 [[TMP3]] to i16
-; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i16
-; CHECK-NEXT: [[RESULT:%.*]] = add nuw nsw i16 [[ROUND]], [[Q_EXT]]
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i8 [[Y:%.*]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = add nuw i8 [[X:%.*]], [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = udiv i8 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[RESULT:%.*]] = zext i8 [[TMP3]] to i16
; CHECK-NEXT: ret i16 [[RESULT]]
;
%q = udiv i8 %x, %y
@@ -145,12 +135,10 @@ define i16 @divceil_i8_var_divisor_commuted_zext(i8 range(i8 0, 101) %x, i8 rang
; Zext form with i16->i32: X in [0, 100], Y in [2, 8].
define i32 @divceil_i16_var_divisor_zext(i16 range(i16 0, 101) %x, i16 range(i16 2, 9) %y) {
; CHECK-LABEL: @divceil_i16_var_divisor_zext(
-; CHECK-NEXT: [[TMP3:%.*]] = udiv i16 [[TMP2:%.*]], [[Y:%.*]]
-; CHECK-NEXT: [[R:%.*]] = urem i16 [[TMP2]], [[Y]]
-; CHECK-NEXT: [[COND:%.*]] = icmp ne i16 [[R]], 0
-; CHECK-NEXT: [[Q_EXT:%.*]] = zext nneg i16 [[TMP3]] to i32
-; CHECK-NEXT: [[ROUND:%.*]] = zext i1 [[COND]] to i32
-; CHECK-NEXT: [[RESULT:%.*]] = add nuw nsw i32 [[Q_EXT]], [[ROUND]]
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i16 [[Y:%.*]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = add nuw nsw i16 [[X:%.*]], [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = udiv i16 [[TMP2]], [[Y]]
+; CHECK-NEXT: [[RESULT:%.*]] = zext i16 [[TMP3]] to i32
; CHECK-NEXT: ret i32 [[RESULT]]
;
%q = udiv i16 %x, %y
diff --git a/llvm/test/Transforms/InstCombine/fls.ll b/llvm/test/Transforms/InstCombine/fls.ll
index 68bc0a2fc8a1d..ea757268259f5 100644
--- a/llvm/test/Transforms/InstCombine/fls.ll
+++ b/llvm/test/Transforms/InstCombine/fls.ll
@@ -33,7 +33,7 @@ define i32 @flsnotconst(i64 %z) {
; CHECK-LABEL: @flsnotconst(
; CHECK-NEXT: [[CTLZ:%.*]] = call range(i64 0, 65) i64 @llvm.ctlz.i64(i64 [[Z:%.*]], i1 false)
; CHECK-NEXT: [[TMP1:%.*]] = trunc nuw nsw i64 [[CTLZ]] to i32
-; CHECK-NEXT: [[GOO:%.*]] = sub nsw i32 64, [[TMP1]]
+; CHECK-NEXT: [[GOO:%.*]] = sub nuw nsw i32 64, [[TMP1]]
; CHECK-NEXT: ret i32 [[GOO]]
;
%goo = call i32 @flsl(i64 %z)
>From 6fe7875080c2dee84f4570dbe95b13f5dc194181 Mon Sep 17 00:00:00 2001
From: Takashiidobe <idobetakashi at gmail.com>
Date: Sun, 5 Apr 2026 15:57:40 -0400
Subject: [PATCH 3/3] code review feedback
---
llvm/lib/Analysis/ValueTracking.cpp | 2 +-
.../InstCombine/InstCombineAddSub.cpp | 39 +++++++++----------
2 files changed, 19 insertions(+), 22 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 53e8ff15f3931..4d7c11e10514e 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10364,7 +10364,7 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
} else if (auto *TI = dyn_cast<TruncInst>(V)) {
ConstantRange SrcCR = computeConstantRange(
TI->getOperand(0), ForSigned, UseInstrInfo, AC, CtxI, DT, Depth + 1);
- CR = SrcCR.truncate(BitWidth);
+ CR = SrcCR.truncate(BitWidth, TI->getNoWrapKind());
} else if (isa<FPToUIInst>(V) || isa<FPToSIInst>(V)) {
APInt Lower = APInt(BitWidth, 0);
APInt Upper = APInt(BitWidth, 0);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 122bdcd8a6474..75353004106a4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1523,7 +1523,7 @@ static Instruction *foldBoxMultiply(BinaryOperator &I) {
return nullptr;
}
-// Return true if X + (Y-1) is provably non-wrapping in X's type
+/// Return true if X + (Y-1) is provably non-wrapping in X's type
static bool checkDivCeilNUW(Value *X, Value *Y, BinaryOperator &I,
AssumptionCache &AC, DominatorTree &DT) {
ConstantRange CRX = computeConstantRange(X, /*ForSigned=*/false,
@@ -1537,21 +1537,20 @@ static bool checkDivCeilNUW(Value *X, Value *Y, BinaryOperator &I,
return !MaxX.ugt(APInt::getMaxValue(BitWidth) - (MaxY - 1));
}
-// Fold the same-type form of the div_ceil idiom:
-// add(udiv(X, Y), zext(icmp ne(urem(X, Y), 0)))
-// -> udiv(add nuw(X, Y - 1), Y)
-// where udiv and urem are the same type as the add.
+/// Fold the same-type form of the div_ceil idiom:
+/// add(udiv(X, Y), zext(icmp ne(urem(X, Y), 0)))
+/// -> udiv(add nuw(X, Y - 1), Y)
+/// where udiv and urem are the same type as the add.
Instruction *InstCombinerImpl::foldSameTypeDivCeil(BinaryOperator &I) {
- Value *X, *X2, *Y, *Y2;
- CmpPredicate Pred;
+ Value *X, *Y;
auto UDivPat = m_OneUse(m_UDiv(m_Value(X), m_Value(Y)));
- auto URemPat = m_OneUse(m_URem(m_Value(X2), m_Value(Y2)));
- auto ICmpPat = m_OneUse(m_ICmp(Pred, URemPat, m_Zero()));
+ auto URemPat = m_OneUse(m_URem(m_Deferred(X), m_Deferred(Y)));
+ auto ICmpPat = m_OneUse(m_SpecificICmp(ICmpInst::ICMP_NE, URemPat, m_Zero()));
auto ZExtCmpPat = m_OneUse(m_ZExt(ICmpPat));
- if (!match(&I, m_c_Add(UDivPat, ZExtCmpPat)) || Pred != ICmpInst::ICMP_NE ||
- X != X2 || Y != Y2 || !checkDivCeilNUW(X, Y, I, AC, DT))
+ if (!match(&I, m_c_Add(UDivPat, ZExtCmpPat)) ||
+ !checkDivCeilNUW(X, Y, I, AC, DT))
return nullptr;
Value *YMinusOne = Builder.CreateSub(Y, ConstantInt::get(Y->getType(), 1));
@@ -1559,23 +1558,21 @@ Instruction *InstCombinerImpl::foldSameTypeDivCeil(BinaryOperator &I) {
return BinaryOperator::CreateUDiv(NUWAdd, Y);
}
-// Fold the zext form of the div_ceil idiom:
-// add(zext(udiv(X, Y)), zext(icmp ne(urem(X, Y), 0)))
-// -> zext(udiv(add nuw(X, Y - 1), Y))
-// udiv and urem operate in a type narrower than the add; the result is
-// zexted back to the add's type.
+/// Fold the zext form of the div_ceil idiom:
+/// add(zext(udiv(X, Y)), zext(icmp ne(urem(X, Y), 0)))
+/// -> zext(udiv(add nuw(X, Y - 1), Y))
+/// udiv and urem operate in a type narrower than the add; the result is
+/// zexted back to the add's type.
Instruction *InstCombinerImpl::foldZExtDivCeil(BinaryOperator &I) {
- Value *X, *X2, *Y, *Y2;
- CmpPredicate Pred;
+ Value *X, *Y;
auto UDivPat = m_OneUse(m_UDiv(m_Value(X), m_Value(Y)));
- auto URemPat = m_OneUse(m_URem(m_Value(X2), m_Value(Y2)));
- auto ICmpPat = m_OneUse(m_ICmp(Pred, URemPat, m_Zero()));
+ auto URemPat = m_OneUse(m_URem(m_Deferred(X), m_Deferred(Y)));
+ auto ICmpPat = m_OneUse(m_SpecificICmp(ICmpInst::ICMP_NE, URemPat, m_Zero()));
auto ZExtDivPat = m_OneUse(m_ZExt(UDivPat));
auto ZExtCmpPat = m_OneUse(m_ZExt(ICmpPat));
if (!match(&I, m_c_Add(ZExtDivPat, ZExtCmpPat)) ||
- Pred != ICmpInst::ICMP_NE || X != X2 || Y != Y2 ||
!checkDivCeilNUW(X, Y, I, AC, DT))
return nullptr;
More information about the llvm-commits
mailing list