Skip to content

[ConstantRange] Improve ConstantRange::binaryXor #80146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion llvm/lib/IR/ConstantRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1467,7 +1467,22 @@ 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.Zero).isSubsetOf(RHSKnown.One))
CR = CR.intersectWith(Other.sub(*this), PreferredRangeType::Unsigned);
else if ((~RHSKnown.Zero).isSubsetOf(LHSKnown.One))
CR = CR.intersectWith(this->sub(Other), PreferredRangeType::Unsigned);
return CR;
}

ConstantRange
Expand Down
55 changes: 55 additions & 0 deletions llvm/test/Transforms/SCCP/pr79696.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
; 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: ret i1 false
; 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
}
6 changes: 6 additions & 0 deletions llvm/unittests/IR/ConstantRangeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2565,6 +2565,12 @@ TEST_F(ConstantRangeTest, binaryXor) {
EXPECT_EQ(R16_35.binaryXor(R0_99), ConstantRange(APInt(8, 0), APInt(8, 128)));
EXPECT_EQ(R0_99.binaryXor(R16_35), ConstantRange(APInt(8, 0), APInt(8, 128)));

// Treat xor A, B as sub nsw nuw A, B
ConstantRange R0_51(APInt(8, 0), APInt(8, 51));
ConstantRange R63(APInt(8, 63));
EXPECT_EQ(R0_51.binaryXor(R63), ConstantRange(APInt(8, 13), APInt(8, 64)));
EXPECT_EQ(R63.binaryXor(R0_51), ConstantRange(APInt(8, 13), APInt(8, 64)));

TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.binaryXor(CR2);
Expand Down