[llvm] 3ee35ae - [InstCombine] support zext in ceiling division fold (#216470)

via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 15 03:18:55 PDT 2026


Author: Andreas Jonson
Date: 2026-08-15T12:18:50+02:00
New Revision: 3ee35aec0bc1d98dd724b95b7eb5d69109c40ca1

URL: https://github.com/llvm/llvm-project/commit/3ee35aec0bc1d98dd724b95b7eb5d69109c40ca1
DIFF: https://github.com/llvm/llvm-project/commit/3ee35aec0bc1d98dd724b95b7eb5d69109c40ca1.diff

LOG: [InstCombine] support zext in ceiling division fold (#216470)

Proof https://alive2.llvm.org/ce/z/vxRGV7

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 30812cafd2f80..5aa83f787d479 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1919,20 +1919,26 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
 
     // Match: (X >> C) + zext((X & Mask) != 0)
     // or:    zext((X & Mask) != 0) + (X >> C)
-    if (match(&I, m_c_Add(m_OneUse(m_LShr(m_Value(X), m_APInt(ShiftAmt))),
-                          m_ZExt(m_SpecificICmp(
-                              ICmpInst::ICMP_NE,
-                              m_And(m_Deferred(X), m_LowBitMask(Mask)),
-                              m_ZeroInt())))) &&
+    if (match(&I,
+              m_c_Add(m_ZExt(m_SpecificICmp(
+                          ICmpInst::ICMP_NE,
+                          m_And(m_Value(X), m_LowBitMask(Mask)), m_ZeroInt())),
+                      m_OneUse(m_ZExtOrSelf(m_OneUse(
+                          m_LShr(m_Deferred(X), m_APInt(ShiftAmt))))))) &&
         Mask->popcount() == *ShiftAmt) {
 
       // Check if X + Mask doesn't overflow
-      Constant *MaskC = ConstantInt::get(X->getType(), *Mask);
-      if (willNotOverflowUnsignedAdd(X, MaskC, I)) {
+      unsigned Xbits = X->getType()->getScalarSizeInBits();
+      unsigned Ibits = Ty->getScalarSizeInBits();
+      bool NeedZext = Ibits > Xbits;
+      Constant *MaskC = ConstantInt::get(Ty, Mask->zext(Ibits));
+      if (NeedZext || willNotOverflowUnsignedAdd(X, MaskC, I)) {
+        if (NeedZext)
+          X = Builder.CreateZExt(X, Ty);
         // (X + Mask) >> ShiftAmt
         Value *Add = Builder.CreateNUWAdd(X, MaskC);
         return BinaryOperator::CreateLShr(
-            Add, ConstantInt::get(X->getType(), *ShiftAmt));
+            Add, ConstantInt::get(Ty, ShiftAmt->zext(Ibits)));
       }
     }
   }

diff  --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index 1b84ecaf5eeeb..22472b0114fd7 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -4992,6 +4992,22 @@ define i32 @ceil_div_by_8_known_range(i32 range(i32 0, 100) %x) {
   ret i32 %r
 }
 
+define i32 @ceil_div_by_8_zext(i8 %x) {
+; CHECK-LABEL: @ceil_div_by_8_zext(
+; CHECK-NEXT:    [[TMP1:%.*]] = zext i8 [[X:%.*]] to i32
+; CHECK-NEXT:    [[TMP2:%.*]] = add nuw nsw i32 [[TMP1]], 7
+; CHECK-NEXT:    [[R:%.*]] = lshr i32 [[TMP2]], 3
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %shr = lshr i8 %x, 3
+  %zext = zext i8 %shr to i32
+  %and = and i8 %x, 7
+  %cmp = icmp ne i8 %and, 0
+  %ext = zext i1 %cmp to i32
+  %r = add i32 %zext, %ext
+  ret i32 %r
+}
+
 ; Test with the exact IR from the original testcase
 define i32 @ceil_div_from_clz(i32 %v) {
 ; CHECK-LABEL: @ceil_div_from_clz(
@@ -5154,6 +5170,33 @@ define i32 @ceil_div_multi_use(i32 range(i32 0, 100) %x) {
   ret i32 %r
 }
 
+define i32 @ceil_div_by_8_zext_multi_use(i8 %x) {
+; CHECK-LABEL: @ceil_div_by_8_zext_multi_use(
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i8 [[X:%.*]], 3
+; CHECK-NEXT:    call void @use_i8(i8 [[SHR]])
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext nneg i8 [[SHR]] to i32
+; CHECK-NEXT:    call void @use_i32(i32 [[ZEXT]])
+; CHECK-NEXT:    [[AND:%.*]] = and i8 [[X]], 7
+; CHECK-NEXT:    call void @use_i8(i8 [[AND]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[AND]], 0
+; CHECK-NEXT:    [[EXT:%.*]] = zext i1 [[CMP]] to i32
+; CHECK-NEXT:    call void @use_i32(i32 [[EXT]])
+; CHECK-NEXT:    [[R:%.*]] = add nuw nsw i32 [[ZEXT]], [[EXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %shr = lshr i8 %x, 3
+  call void @use_i8(i8 %shr)
+  %zext = zext i8 %shr to i32
+  call void @use_i32(i32 %zext)
+  %and = and i8 %x, 7
+  call void @use_i8(i8 %and)
+  %cmp = icmp ne i8 %and, 0
+  %ext = zext i1 %cmp to i32
+  call void @use_i32(i32 %ext)
+  %r = add i32 %zext, %ext
+  ret i32 %r
+}
+
 ; Commuted test: add operands are swapped
 define i32 @ceil_div_commuted(i32 range(i32 0, 100) %x) {
 ; CHECK-LABEL: @ceil_div_commuted(
@@ -5231,6 +5274,7 @@ define <2 x i32> @ceil_div_vec_multi_use(<2 x i32> range(i32 0, 1000) %x) {
   ret <2 x i32> %r
 }
 
+declare void @use_i8(i8)
 declare void @use_i32(i32)
 declare void @use_vec(<2 x i32>)
 declare void @fake_func(i32)


        


More information about the llvm-commits mailing list