[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