Skip to content

Commit d9e9276

Browse files
authored
[ConstantRange] Improve ConstantRange::binaryXor (#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.
1 parent 3ad6359 commit d9e9276

File tree

3 files changed

+77
-1
lines changed

3 files changed

+77
-1
lines changed

llvm/lib/IR/ConstantRange.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1467,7 +1467,22 @@ ConstantRange ConstantRange::binaryXor(const ConstantRange &Other) const {
14671467
if (isSingleElement() && getSingleElement()->isAllOnes())
14681468
return Other.binaryNot();
14691469

1470-
return fromKnownBits(toKnownBits() ^ Other.toKnownBits(), /*IsSigned*/false);
1470+
KnownBits LHSKnown = toKnownBits();
1471+
KnownBits RHSKnown = Other.toKnownBits();
1472+
KnownBits Known = LHSKnown ^ RHSKnown;
1473+
ConstantRange CR = fromKnownBits(Known, /*IsSigned*/ false);
1474+
// Typically the following code doesn't improve the result if BW = 1.
1475+
if (getBitWidth() == 1)
1476+
return CR;
1477+
1478+
// If LHS is known to be the subset of RHS, treat LHS ^ RHS as RHS -nuw/nsw
1479+
// LHS. If RHS is known to be the subset of LHS, treat LHS ^ RHS as LHS
1480+
// -nuw/nsw RHS.
1481+
if ((~LHSKnown.Zero).isSubsetOf(RHSKnown.One))
1482+
CR = CR.intersectWith(Other.sub(*this), PreferredRangeType::Unsigned);
1483+
else if ((~RHSKnown.Zero).isSubsetOf(LHSKnown.One))
1484+
CR = CR.intersectWith(this->sub(Other), PreferredRangeType::Unsigned);
1485+
return CR;
14711486
}
14721487

14731488
ConstantRange

llvm/test/Transforms/SCCP/pr79696.ll

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt < %s -passes=ipsccp -S | FileCheck %s
3+
4+
; Tests from PR79696
5+
6+
define i1 @constant_range_xor(i64 %a) {
7+
; CHECK-LABEL: define i1 @constant_range_xor(
8+
; CHECK-SAME: i64 [[A:%.*]]) {
9+
; CHECK-NEXT: entry:
10+
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i64 [[A]], 8192
11+
; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
12+
; CHECK: then:
13+
; CHECK-NEXT: [[CTLZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[A]], i1 true)
14+
; CHECK-NEXT: [[CONV:%.*]] = xor i64 [[CTLZ]], 63
15+
; CHECK-NEXT: ret i1 false
16+
; CHECK: else:
17+
; CHECK-NEXT: ret i1 false
18+
;
19+
entry:
20+
%cmp = icmp ugt i64 %a, 8192
21+
br i1 %cmp, label %then, label %else
22+
then:
23+
%ctlz = call i64 @llvm.ctlz.i64(i64 %a, i1 true) ;[0, 50]
24+
%conv = xor i64 %ctlz, 63 ;[13, 63]
25+
%cmp1 = icmp ult i64 %conv, 13
26+
ret i1 %cmp1
27+
else:
28+
ret i1 false
29+
}
30+
31+
define i1 @constant_range_xor_negative(i64 %a) {
32+
; CHECK-LABEL: define i1 @constant_range_xor_negative(
33+
; CHECK-SAME: i64 [[A:%.*]]) {
34+
; CHECK-NEXT: entry:
35+
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i64 [[A]], 8192
36+
; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
37+
; CHECK: then:
38+
; CHECK-NEXT: [[CTLZ:%.*]] = call i64 @llvm.ctlz.i64(i64 [[A]], i1 true)
39+
; CHECK-NEXT: [[CONV:%.*]] = xor i64 [[CTLZ]], 62
40+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i64 [[CONV]], 13
41+
; CHECK-NEXT: ret i1 [[CMP1]]
42+
; CHECK: else:
43+
; CHECK-NEXT: ret i1 false
44+
;
45+
entry:
46+
%cmp = icmp ugt i64 %a, 8192
47+
br i1 %cmp, label %then, label %else
48+
then:
49+
%ctlz = call i64 @llvm.ctlz.i64(i64 %a, i1 true) ;[0, 50]
50+
%conv = xor i64 %ctlz, 62 ;[12, 63]
51+
%cmp1 = icmp ult i64 %conv, 13
52+
ret i1 %cmp1
53+
else:
54+
ret i1 false
55+
}

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,6 +2565,12 @@ TEST_F(ConstantRangeTest, binaryXor) {
25652565
EXPECT_EQ(R16_35.binaryXor(R0_99), ConstantRange(APInt(8, 0), APInt(8, 128)));
25662566
EXPECT_EQ(R0_99.binaryXor(R16_35), ConstantRange(APInt(8, 0), APInt(8, 128)));
25672567

2568+
// Treat xor A, B as sub nsw nuw A, B
2569+
ConstantRange R0_51(APInt(8, 0), APInt(8, 51));
2570+
ConstantRange R63(APInt(8, 63));
2571+
EXPECT_EQ(R0_51.binaryXor(R63), ConstantRange(APInt(8, 13), APInt(8, 64)));
2572+
EXPECT_EQ(R63.binaryXor(R0_51), ConstantRange(APInt(8, 13), APInt(8, 64)));
2573+
25682574
TestBinaryOpExhaustive(
25692575
[](const ConstantRange &CR1, const ConstantRange &CR2) {
25702576
return CR1.binaryXor(CR2);

0 commit comments

Comments
 (0)