[llvm] [InstCombine] Fold the bound check idiom into sign bit test (PR #76439)

via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 27 05:38:41 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

<details>
<summary>Changes</summary>

This patch folds the bound check idiom:
```
T *end, *start;
(size_t)(end - start) > (size_t)(PTRDIFF_MAX / sizeof(T))
```
into
```
(ptrdiff_t)(end - start) < 0
```
i.e.:
```
icmp ugt (sdiv exact X, C2), (sdiv signed_max, C2) --> icmp slt X, 0
icmp ult (sdiv exact X, C2), (sdiv signed_max, C2) + 1 --> icmp sgt X, -1
```
Alive2: https://alive2.llvm.org/ce/z/feXjoj

NOTE: It also holds for `ashr exact` when `sizeof(T)` is a power of 2.


---
Full diff: https://github.com/llvm/llvm-project/pull/76439.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp (+20) 
- (modified) llvm/test/Transforms/InstCombine/icmp.ll (+95) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 0222c93faf24e9..43d1562a3177d4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -2636,6 +2636,26 @@ Instruction *InstCombinerImpl::foldICmpDivConstant(ICmpInst &Cmp,
   if (!match(Y, m_APInt(C2)))
     return nullptr;
 
+  // Fold the bound check idiom:
+  //  T *end, *start;
+  //  (size_t)(end - start) > (size_t)(PTRDIFF_MAX / sizeof(T))
+  // into:
+  //  (ptrdiff_t)(end - start) < 0
+  // i.e.:
+  //   icmp ugt (sdiv exact X, C2), (sdiv signed_max, C2) --> icmp slt X, 0
+  //   icmp ult (sdiv exact X, C2), (sdiv signed_max, C2) + 1 --> icmp sgt X, -1
+  // where C2 is positive.
+  if (DivIsSigned && Div->isExact() &&
+      (Pred == ICmpInst::ICMP_UGT || Pred == ICmpInst::ICMP_ULT) &&
+      C2->isStrictlyPositive() &&
+      APInt::getSignedMaxValue(C2->getBitWidth()).sdiv(*C2) +
+              APInt(C2->getBitWidth(), Pred == ICmpInst::ICMP_UGT ? 0 : 1) ==
+          C)
+    return new ICmpInst(
+        Pred == ICmpInst::ICMP_UGT ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_SGT, X,
+        Pred == ICmpInst::ICMP_UGT ? Constant::getNullValue(X->getType())
+                                   : Constant::getAllOnesValue(X->getType()));
+
   // FIXME: If the operand types don't match the type of the divide
   // then don't attempt this transform. The code below doesn't have the
   // logic to deal with a signed divide and an unsigned compare (and
diff --git a/llvm/test/Transforms/InstCombine/icmp.ll b/llvm/test/Transforms/InstCombine/icmp.ll
index 9b2e141bdb0506..48a33b65177714 100644
--- a/llvm/test/Transforms/InstCombine/icmp.ll
+++ b/llvm/test/Transforms/InstCombine/icmp.ll
@@ -5013,3 +5013,98 @@ define i1 @or_positive_sgt_zero_multi_use(i8 %a) {
   %cmp = icmp sgt i8 %b, 0
   ret i1 %cmp
 }
+
+define i1 @icmp_ugt_sdiv_by_constant(i64 %x) {
+; CHECK-LABEL: @icmp_ugt_sdiv_by_constant(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[X:%.*]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = sdiv exact i64 %x, 24
+  %cmp = icmp ugt i64 %sdiv, 384307168202282325
+  ret i1 %cmp
+}
+
+define i1 @icmp_ult_sdiv_by_constant(i64 %x) {
+; CHECK-LABEL: @icmp_ult_sdiv_by_constant(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = sdiv exact i64 %x, 24
+  %cmp = icmp ult i64 %sdiv, 384307168202282326
+  ret i1 %cmp
+}
+
+; TODO: This should be simplified to icmp slt i64 %x, 0
+define i1 @icmp_ugt_ashr_by_constant(i64 %x) {
+; CHECK-LABEL: @icmp_ugt_ashr_by_constant(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[X:%.*]], 9223372036854775804
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = ashr exact i64 %x, 2
+  %cmp = icmp ugt i64 %sdiv, 2305843009213693951
+  ret i1 %cmp
+}
+
+define i1 @icmp_ult_ashr_by_constant(i64 %x) {
+; CHECK-LABEL: @icmp_ult_ashr_by_constant(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[X:%.*]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = ashr exact i64 %x, 2
+  %cmp = icmp ult i64 %sdiv, 2305843009213693952
+  ret i1 %cmp
+}
+
+; Negative tests
+define i1 @icmp_ugt_sdiv_by_constant_without_exact(i64 %x) {
+; CHECK-LABEL: @icmp_ugt_sdiv_by_constant_without_exact(
+; CHECK-NEXT:    [[SDIV:%.*]] = sdiv i64 [[X:%.*]], 24
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[SDIV]], 384307168202282325
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = sdiv i64 %x, 24
+  %cmp = icmp ugt i64 %sdiv, 384307168202282325
+  ret i1 %cmp
+}
+
+define i1 @icmp_ugt_udiv_by_constant(i64 %x) {
+; CHECK-LABEL: @icmp_ugt_udiv_by_constant(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[X:%.*]], 9223372036854775800
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = udiv exact i64 %x, 24
+  %cmp = icmp ugt i64 %sdiv, 384307168202282325
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_sdiv_by_constant(i64 %x) {
+; CHECK-LABEL: @icmp_ne_sdiv_by_constant(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[X:%.*]], 9223372036854775800
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = sdiv exact i64 %x, 24
+  %cmp = icmp ne i64 %sdiv, 384307168202282325
+  ret i1 %cmp
+}
+
+define i1 @icmp_ugt_sdiv_by_constant_wrong_rhs(i64 %x) {
+; CHECK-LABEL: @icmp_ugt_sdiv_by_constant_wrong_rhs(
+; CHECK-NEXT:    [[SDIV:%.*]] = sdiv exact i64 [[X:%.*]], 24
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[SDIV]], 384307168202282324
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = sdiv exact i64 %x, 24
+  %cmp = icmp ugt i64 %sdiv, 384307168202282324
+  ret i1 %cmp
+}
+
+define i1 @icmp_ugt_sdiv_by_negative_constant(i64 %x) {
+; CHECK-LABEL: @icmp_ugt_sdiv_by_negative_constant(
+; CHECK-NEXT:    [[SDIV:%.*]] = sdiv i64 [[X:%.*]], -24
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[SDIV]], -384307168202282326
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sdiv = sdiv i64 %x, -24
+  %cmp = icmp ugt i64 %sdiv, -384307168202282326
+  ret i1 %cmp
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/76439


More information about the llvm-commits mailing list