[llvm] 457e5ba - [InstCombine] Use context instruction in divceil overflow check (#227158)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 03:15:42 PDT 2026


Author: Gurvir Dhillon
Date: 2026-09-30T10:15:30Z
New Revision: 457e5bad6d6b7012a123a9272c9deb83dc3d6c04

URL: https://github.com/llvm/llvm-project/commit/457e5bad6d6b7012a123a9272c9deb83dc3d6c04
DIFF: https://github.com/llvm/llvm-project/commit/457e5bad6d6b7012a123a9272c9deb83dc3d6c04.diff

LOG: [InstCombine] Use context instruction in divceil overflow check (#227158)

Divceil optimization only happens if we can prove x + y - 1 does not
overflow. Originally, a SimplifyQuery with no context instruction was
passed to checkDivCeilNUW which made it so that the optimization never
happened if the bound came from an assume. This meant that even when an
assume proved that x + y - 1 did not overflow, the optimization did not
happen.

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

AI Disclosure: I used Claude to help explain LLVM concepts while I was
trying to understand the problem. It also walked me through syntax,
helper functions that I should utilize, good test cases, and general
formatting for things like test case comments.

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
    llvm/test/Transforms/InstCombine/divceil.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 317d587ddedd6..27d884f66a9ce 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 c9f3ba750d7ec..b1fece4f9cfc2 100644
--- a/llvm/test/Transforms/InstCombine/divceil.ll
+++ b/llvm/test/Transforms/InstCombine/divceil.ll
@@ -208,3 +208,99 @@ 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:    [[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:    ret i16 [[Q_EXT]]
+;
+  %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:    [[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]]
+;
+  %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
+}


        


More information about the llvm-commits mailing list