[llvm] [InstCombine] Use context instruction in divceil overflow check (PR #227158)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 01:14:29 PDT 2026
https://github.com/nikic updated https://github.com/llvm/llvm-project/pull/227158
>From e2b6831db9f7a7bfc1f26819cf37ba49525012f1 Mon Sep 17 00:00:00 2001
From: gurvir dhillon <gman.dhillon at gmail.com>
Date: Mon, 28 Sep 2026 18:49:12 -0400
Subject: [PATCH 1/2] [InstCombine] Add tests for divceil with assume (NFC)
---
llvm/test/Transforms/InstCombine/divceil.ll | 100 ++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/divceil.ll b/llvm/test/Transforms/InstCombine/divceil.ll
index c9f3ba750d7ecf..b5288ba0189e06 100644
--- a/llvm/test/Transforms/InstCombine/divceil.ll
+++ b/llvm/test/Transforms/InstCombine/divceil.ll
@@ -208,3 +208,103 @@ define i32 @divceil_trunc_nuw_range(i32 range(i32 0, 33) %x_wide) {
%result = add i32 %q_ext, %round
ret i32 %result
}
+
+; X in [0,246] via an assume, Y in [1,10], so max X+(Y-1) = 255 <= 255.
+define i16 @divceil_i8_var_divisor_assume_zext(i8 %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_assume_zext(
+; CHECK-NEXT: [[BOUND:%.*]] = icmp ult i8 [[X:%.*]], -9
+; CHECK-NEXT: call void @llvm.assume(i1 [[BOUND]])
+; 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 [[ROUND]], [[Q_EXT]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %bound = icmp ule i8 %x, 246
+ call void @llvm.assume(i1 %bound)
+ %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
+}
+
+; X in [0,100] via an assume, Y in [1,10], so max X+(Y-1) = 109 <= 255.
+; Assume comes after add but still applies.
+define i16 @divceil_i8_var_divisor_assume_after_add_zext(i8 %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_assume_after_add_zext(
+; CHECK-NEXT: [[BOUND:%.*]] = icmp ult i8 [[X:%.*]], 101
+; 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 [[ROUND]], [[Q_EXT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[BOUND]])
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %bound = icmp ule i8 %x, 100
+ %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
+ call void @llvm.assume(i1 %bound)
+ ret i16 %result
+}
+
+; Negative: X in [0,247] via an assume, Y in [1,10] so max X+(Y-1) = 256 > 255.
+define i16 @divceil_i8_var_divisor_assume_too_large_zext(i8 %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_assume_too_large_zext(
+; CHECK-NEXT: [[BOUND:%.*]] = icmp ult i8 [[X:%.*]], -8
+; CHECK-NEXT: call void @llvm.assume(i1 [[BOUND]])
+; 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 [[ROUND]], [[Q_EXT]]
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %bound = icmp ule i8 %x, 247
+ call void @llvm.assume(i1 %bound)
+ %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
+}
+
+; Negative: @use16 may not return, so the assume after it is not guaranteed
+; to execute and X is unbounded at the add.
+define i16 @divceil_i8_var_divisor_assume_not_guaranteed_zext(i8 %x, i8 range(i8 1, 11) %y) {
+; CHECK-LABEL: @divceil_i8_var_divisor_assume_not_guaranteed_zext(
+; CHECK-NEXT: [[BOUND:%.*]] = icmp ult i8 [[X:%.*]], 101
+; 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 [[ROUND]], [[Q_EXT]]
+; CHECK-NEXT: call void @use16(i16 [[RESULT]])
+; CHECK-NEXT: call void @llvm.assume(i1 [[BOUND]])
+; CHECK-NEXT: ret i16 [[RESULT]]
+;
+ %bound = icmp ule i8 %x, 100
+ %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
+ call void @use16(i16 %result)
+ call void @llvm.assume(i1 %bound)
+ ret i16 %result
+}
>From db7a221c28cff0093ed14a1db6e0f3f723bfd3e1 Mon Sep 17 00:00:00 2001
From: gurvir dhillon <gman.dhillon at gmail.com>
Date: Mon, 28 Sep 2026 18:53:41 -0400
Subject: [PATCH 2/2] [InstCombine] Use context instruction in divceil overflow
check
The divceil fold only fires if it can prove x + y - 1 does not
overflow. foldDivCeil passed checkDivCeilNUW a SimplifyQuery with no
context instruction, so computeConstantRange ignored facts from
llvm.assume calls. This led to the fold being missed when the bound on
x came from an assume instead of a range attribute.
This change passes SQ.getWithInstruction(&I) so the range of x is
computed at the add being folded, and assumes that apply there are
used when deciding whether we can fold.
Fixes #220849
---
.../InstCombine/InstCombineAddSub.cpp | 3 ++-
llvm/test/Transforms/InstCombine/divceil.ll | 20 ++++++++-----------
2 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 317d587ddedd66..27d884f66a9ce5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1588,7 +1588,8 @@ Instruction *InstCombinerImpl::foldDivCeil(BinaryOperator &I) {
auto DivPat = m_OneUse(m_ZExtOrSelf(UDivPat));
auto ZExtCmpPat = m_OneUse(m_ZExt(ICmpPat));
- if (!match(&I, m_c_Add(DivPat, ZExtCmpPat)) || !checkDivCeilNUW(X, Y, SQ))
+ if (!match(&I, m_c_Add(DivPat, ZExtCmpPat)) ||
+ !checkDivCeilNUW(X, Y, SQ.getWithInstruction(&I)))
return nullptr;
Value *YMinusOne =
diff --git a/llvm/test/Transforms/InstCombine/divceil.ll b/llvm/test/Transforms/InstCombine/divceil.ll
index b5288ba0189e06..b1fece4f9cfc27 100644
--- a/llvm/test/Transforms/InstCombine/divceil.ll
+++ b/llvm/test/Transforms/InstCombine/divceil.ll
@@ -214,13 +214,11 @@ define i16 @divceil_i8_var_divisor_assume_zext(i8 %x, i8 range(i8 1, 11) %y) {
; CHECK-LABEL: @divceil_i8_var_divisor_assume_zext(
; CHECK-NEXT: [[BOUND:%.*]] = icmp ult i8 [[X:%.*]], -9
; CHECK-NEXT: call void @llvm.assume(i1 [[BOUND]])
-; CHECK-NEXT: [[Q:%.*]] = udiv i8 [[X]], [[Y:%.*]]
-; CHECK-NEXT: [[R:%.*]] = urem i8 [[X]], [[Y]]
-; CHECK-NEXT: [[COND:%.*]] = icmp ne i8 [[R]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i8 [[Y:%.*]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = add nuw i8 [[X]], [[TMP1]]
+; CHECK-NEXT: [[Q:%.*]] = udiv i8 [[TMP2]], [[Y]]
; CHECK-NEXT: [[Q_EXT:%.*]] = zext i8 [[Q]] 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]]
+; CHECK-NEXT: ret i16 [[Q_EXT]]
;
%bound = icmp ule i8 %x, 246
call void @llvm.assume(i1 %bound)
@@ -238,12 +236,10 @@ define i16 @divceil_i8_var_divisor_assume_zext(i8 %x, i8 range(i8 1, 11) %y) {
define i16 @divceil_i8_var_divisor_assume_after_add_zext(i8 %x, i8 range(i8 1, 11) %y) {
; CHECK-LABEL: @divceil_i8_var_divisor_assume_after_add_zext(
; CHECK-NEXT: [[BOUND:%.*]] = icmp ult i8 [[X:%.*]], 101
-; 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 [[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: call void @llvm.assume(i1 [[BOUND]])
; CHECK-NEXT: ret i16 [[RESULT]]
;
More information about the llvm-commits
mailing list