[llvm] [ConstantRange] Improve ConstantRange::binaryXor (PR #80146)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 31 06:40:12 PST 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/80146

`ConstantRange::binaryXor` gives poor results as it currently depends on `KnownBits::operator^`.
Since `sub A, B` is canonicalized into `xor A, B` if `B` is the subset of `A`, this patch reverts the transform in `ConstantRange::binaryXor`, which will give better results.

Alive2: https://alive2.llvm.org/ce/z/bmTMV9
Fixes #79696.


>From 7c32d241b89b085bceff3b32b57b204b444af03f Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 31 Jan 2024 22:12:38 +0800
Subject: [PATCH 1/2] [ConstantRange] Add pre-commit tests. NFC.

---
 llvm/test/Transforms/SCCP/pr79696.ll | 56 ++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 llvm/test/Transforms/SCCP/pr79696.ll

diff --git a/llvm/test/Transforms/SCCP/pr79696.ll b/llvm/test/Transforms/SCCP/pr79696.ll
new file mode 100644
index 0000000000000..58529b8900c34
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/pr79696.ll
@@ -0,0 +1,56 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt < %s -passes=ipsccp -S | FileCheck %s
+
+; Tests from PR79696
+
+define i1 @constant_range_xor(i64 %a) {
+; CHECK-LABEL: define i1 @constant_range_xor(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[A]], 8192
+; CHECK-NEXT:    br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK:       then:
+; CHECK-NEXT:    [[CTLZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[A]], i1 true)
+; CHECK-NEXT:    [[CONV:%.*]] = xor i64 [[CTLZ]], 63
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i64 [[CONV]], 13
+; CHECK-NEXT:    ret i1 [[CMP1]]
+; CHECK:       else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cmp = icmp ugt i64 %a, 8192
+  br i1 %cmp, label %then, label %else
+then:
+  %ctlz = call i64 @llvm.ctlz.i64(i64 %a, i1 true) ;[0, 50]
+  %conv = xor i64 %ctlz, 63                        ;[13, 63]
+  %cmp1 = icmp ult i64 %conv, 13
+  ret i1 %cmp1
+else:
+  ret i1 false
+}
+
+define i1 @constant_range_xor_negative(i64 %a) {
+; CHECK-LABEL: define i1 @constant_range_xor_negative(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i64 [[A]], 8192
+; CHECK-NEXT:    br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK:       then:
+; CHECK-NEXT:    [[CTLZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[A]], i1 true)
+; CHECK-NEXT:    [[CONV:%.*]] = xor i64 [[CTLZ]], 62
+; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i64 [[CONV]], 13
+; CHECK-NEXT:    ret i1 [[CMP1]]
+; CHECK:       else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cmp = icmp ugt i64 %a, 8192
+  br i1 %cmp, label %then, label %else
+then:
+  %ctlz = call i64 @llvm.ctlz.i64(i64 %a, i1 true) ;[0, 50]
+  %conv = xor i64 %ctlz, 62                        ;[12, 63]
+  %cmp1 = icmp ult i64 %conv, 13
+  ret i1 %cmp1
+else:
+  ret i1 false
+}

>From 835946b2a24c57d32bdf523ebae0584952514168 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 31 Jan 2024 22:15:46 +0800
Subject: [PATCH 2/2] [ConstantRange] Improve ConstantRange::binaryXor

---
 llvm/lib/IR/ConstantRange.cpp        | 23 ++++++++++++++++++++++-
 llvm/test/Transforms/SCCP/pr79696.ll |  3 +--
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index cbb64b299e648..f951bd9b8354d 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -1467,7 +1467,28 @@ ConstantRange ConstantRange::binaryXor(const ConstantRange &Other) const {
   if (isSingleElement() && getSingleElement()->isAllOnes())
     return Other.binaryNot();
 
-  return fromKnownBits(toKnownBits() ^ Other.toKnownBits(), /*IsSigned*/false);
+  KnownBits LHSKnown = toKnownBits();
+  KnownBits RHSKnown = Other.toKnownBits();
+  KnownBits Known = LHSKnown ^ RHSKnown;
+  ConstantRange CR = fromKnownBits(Known, /*IsSigned*/ false);
+  // Typically the following code doesn't improve the result if BW = 1.
+  if (getBitWidth() == 1)
+    return CR;
+
+  // If LHS is known to be the subset of RHS, treat LHS ^ RHS as RHS -nuw/nsw
+  // LHS. If RHS is known to be the subset of LHS, treat LHS ^ RHS as LHS
+  // -nuw/nsw RHS.
+  if (LHSKnown.getMaxValue().isSubsetOf(RHSKnown.getMinValue()))
+    CR = CR.intersectWith(
+        Other.subWithNoWrap(*this, OverflowingBinaryOperator::NoUnsignedWrap |
+                                       OverflowingBinaryOperator::NoSignedWrap),
+        PreferredRangeType::Unsigned);
+  else if (RHSKnown.getMaxValue().isSubsetOf(LHSKnown.getMinValue()))
+    CR = CR.intersectWith(
+        subWithNoWrap(Other, OverflowingBinaryOperator::NoUnsignedWrap |
+                                 OverflowingBinaryOperator::NoSignedWrap),
+        PreferredRangeType::Unsigned);
+  return CR;
 }
 
 ConstantRange
diff --git a/llvm/test/Transforms/SCCP/pr79696.ll b/llvm/test/Transforms/SCCP/pr79696.ll
index 58529b8900c34..a860112d5ef36 100644
--- a/llvm/test/Transforms/SCCP/pr79696.ll
+++ b/llvm/test/Transforms/SCCP/pr79696.ll
@@ -12,8 +12,7 @@ define i1 @constant_range_xor(i64 %a) {
 ; CHECK:       then:
 ; CHECK-NEXT:    [[CTLZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[A]], i1 true)
 ; CHECK-NEXT:    [[CONV:%.*]] = xor i64 [[CTLZ]], 63
-; CHECK-NEXT:    [[CMP1:%.*]] = icmp ult i64 [[CONV]], 13
-; CHECK-NEXT:    ret i1 [[CMP1]]
+; CHECK-NEXT:    ret i1 false
 ; CHECK:       else:
 ; CHECK-NEXT:    ret i1 false
 ;



More information about the llvm-commits mailing list