[llvm] [ConstantRange] Add binaryOp supporting 'or disjoint'. (PR #222573)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 10 03:01:20 PDT 2026
https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/222573
Promote logic from https://github.com/llvm/llvm-project/pull/221832 to
ConstantRange::binaryOp so it can also be re-used from SCCP.
This enables a few additional folds & flag inference end-to-end:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1227
>From 2e0ad10ce93ba491e1f1451cd765e4e304b2ae47 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 7 Sep 2026 20:21:20 +0100
Subject: [PATCH 1/4] [ValueTracking] Add disjoint tests.
---
.../CorrelatedValuePropagation/or-disjoint.ll | 65 +++++++++++++++++++
1 file changed, 65 insertions(+)
create mode 100644 llvm/test/Transforms/CorrelatedValuePropagation/or-disjoint.ll
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/or-disjoint.ll b/llvm/test/Transforms/CorrelatedValuePropagation/or-disjoint.ll
new file mode 100644
index 0000000000000..5472c7463e7c9
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/or-disjoint.ll
@@ -0,0 +1,65 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=correlated-propagation -S %s | FileCheck %s
+
+define i1 @or_disjoint(i8 %x) {
+; CHECK-LABEL: define i1 @or_disjoint(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp uge i8 [[X]], 8
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[X]], 32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[O]], 40
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %c = icmp uge i8 %x, 8
+ call void @llvm.assume(i1 %c)
+ %o = or disjoint i8 %x, 32
+ %cmp = icmp ult i8 %o, 40
+ ret i1 %cmp
+}
+
+define i1 @or_not_disjoint(i8 %x) {
+; CHECK-LABEL: define i1 @or_not_disjoint(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp uge i8 [[X]], 8
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[O:%.*]] = or i8 [[X]], 32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[O]], 40
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %c = icmp uge i8 %x, 8
+ call void @llvm.assume(i1 %c)
+ %o = or i8 %x, 32
+ %cmp = icmp ult i8 %o, 40
+ ret i1 %cmp
+}
+
+define i1 @or_disjoint_upper_bound(i8 %x) {
+; CHECK-LABEL: define i1 @or_disjoint_upper_bound(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[X]], 24
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[X]], 32
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i8 [[O]], 55
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %c = icmp ult i8 %x, 24
+ call void @llvm.assume(i1 %c)
+ %o = or disjoint i8 %x, 32
+ %cmp = icmp ugt i8 %o, 55
+ ret i1 %cmp
+}
+
+define i1 @or_disjoint_known_bits_more_precise(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @or_disjoint_known_bits_more_precise(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = and i8 [[X]], 3
+; CHECK-NEXT: [[B:%.*]] = and i8 [[Y]], 3
+; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[A]], [[B]]
+; CHECK-NEXT: ret i1 false
+;
+ %a = and i8 %x, 3
+ %b = and i8 %y, 3
+ %o = or disjoint i8 %a, %b
+ %cmp = icmp ugt i8 %o, 3
+ ret i1 %cmp
+}
>From d6cb908f5c826e72a4081d3db96eae3cb695acdd Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 7 Sep 2026 19:04:36 +0100
Subject: [PATCH 2/4] [LazyValueInfo] Treat 'or disjoint' as both 'add nuw nsw'
and bin or.
'or disjoint' can be treated as 'add nuw nsw'. Update
solveBlockValueBinaryOp to try to treat 'or disjoint' as both 'add nuw
nsw' and binary or, taking the intersection as result. In some cases the
binary or logic can yield better results
(`@or_disjoint_known_bits_more_precise` test).
This updates LVI to support 'or disjoint' similar to
ConstraintElimination (https://github.com/llvm/llvm-project/pull/221175,
https://github.com/llvm/llvm-project/pull/220990)
Compile-time impact is in the noise
https://llvm-compile-time-tracker.com/compare.php?from=137d8e684984df207576c8f24bd8743ddb71c5c9&to=94f20b7fe67d745a55f4ba31f4f15249e7e22f1c&stat=instructions:u
Enables additional simplifications in a number of real-world cases
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1222.
---
llvm/lib/Analysis/LazyValueInfo.cpp | 12 ++++++++++++
.../CorrelatedValuePropagation/or-disjoint.ll | 6 ++----
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index a62d195036258..93a8604560f31 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -1167,6 +1167,7 @@ std::optional<ValueLatticeElement>
LazyValueInfoImpl::solveBlockValueBinaryOp(BinaryOperator *BO, BasicBlock *BB) {
assert(BO->getOperand(0)->getType()->isSized() &&
"all operands to binary operators are sized");
+
if (auto *OBO = dyn_cast<OverflowingBinaryOperator>(BO)) {
unsigned NoWrapKind = OBO->getNoWrapKind();
return solveBlockValueBinaryOpImpl(
@@ -1176,6 +1177,17 @@ LazyValueInfoImpl::solveBlockValueBinaryOp(BinaryOperator *BO, BasicBlock *BB) {
});
}
+ if (match(BO, m_DisjointOr(m_Value(), m_Value()))) {
+ // Treat 'or disjoint' as both 'add nuw nsw' and binary or, picking the best
+ // from both.
+ return solveBlockValueBinaryOpImpl(
+ BO, BB, [](const ConstantRange &CR1, const ConstantRange &CR2) {
+ using OBO = OverflowingBinaryOperator;
+ return CR1.addWithNoWrap(CR2, OBO::NoUnsignedWrap | OBO::NoSignedWrap)
+ .intersectWith(CR1.binaryOr(CR2));
+ });
+ }
+
return solveBlockValueBinaryOpImpl(
BO, BB, [BO](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.binaryOp(BO->getOpcode(), CR2);
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/or-disjoint.ll b/llvm/test/Transforms/CorrelatedValuePropagation/or-disjoint.ll
index 5472c7463e7c9..eccadb190381a 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/or-disjoint.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/or-disjoint.ll
@@ -7,8 +7,7 @@ define i1 @or_disjoint(i8 %x) {
; CHECK-NEXT: [[C:%.*]] = icmp uge i8 [[X]], 8
; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[X]], 32
-; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[O]], 40
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 false
;
%c = icmp uge i8 %x, 8
call void @llvm.assume(i1 %c)
@@ -39,8 +38,7 @@ define i1 @or_disjoint_upper_bound(i8 %x) {
; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[X]], 24
; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[X]], 32
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i8 [[O]], 55
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 false
;
%c = icmp ult i8 %x, 24
call void @llvm.assume(i1 %c)
>From bbb36b24f681243e9d56853a3ac5351ddfedfbc1 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 8 Sep 2026 11:35:28 +0100
Subject: [PATCH 3/4] [SCCP] Precommit disjoint or tests
---
llvm/test/Transforms/SCCP/or-disjoint.ll | 95 ++++++++++++++++++++++++
1 file changed, 95 insertions(+)
create mode 100644 llvm/test/Transforms/SCCP/or-disjoint.ll
diff --git a/llvm/test/Transforms/SCCP/or-disjoint.ll b/llvm/test/Transforms/SCCP/or-disjoint.ll
new file mode 100644
index 0000000000000..7f3188bf711b1
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/or-disjoint.ll
@@ -0,0 +1,95 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=sccp -S %s | FileCheck %s
+
+declare void @use(i1)
+
+define void @or_disjoint(i8 %p) {
+; CHECK-LABEL: define void @or_disjoint(
+; CHECK-SAME: i8 [[P:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[P]], 13
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[P]], 32
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i8 [[O]], 45
+; CHECK-NEXT: call void @use(i1 [[TMP1]])
+; CHECK-NEXT: [[F:%.*]] = icmp ugt i8 [[O]], 45
+; CHECK-NEXT: call void @use(i1 [[F]])
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[O]], 44
+; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: ret void
+;
+ %pre = icmp ult i8 %p, 13
+ call void @llvm.assume(i1 %pre)
+ %o = or disjoint i8 %p, 32
+ %t = icmp ult i8 %o, 45
+ call void @use(i1 %t)
+ %f = icmp ugt i8 %o, 45
+ call void @use(i1 %f)
+ %c = icmp ult i8 %o, 44
+ call void @use(i1 %c)
+ ret void
+}
+
+define void @or_not_disjoint(i8 %p) {
+; CHECK-LABEL: define void @or_not_disjoint(
+; CHECK-SAME: i8 [[P:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[P]], 13
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[O:%.*]] = or i8 [[P]], 32
+; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[O]], 45
+; CHECK-NEXT: call void @use(i1 [[R]])
+; CHECK-NEXT: [[F:%.*]] = icmp ugt i8 [[O]], 45
+; CHECK-NEXT: call void @use(i1 [[F]])
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[O]], 44
+; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: ret void
+;
+ %pre = icmp ult i8 %p, 13
+ call void @llvm.assume(i1 %pre)
+ %o = or i8 %p, 32
+ %t = icmp ult i8 %o, 45
+ call void @use(i1 %t)
+ %f = icmp ugt i8 %o, 45
+ call void @use(i1 %f)
+ %c = icmp ult i8 %o, 44
+ call void @use(i1 %c)
+ ret void
+}
+
+define void @or_disjoint_known_bits_better(i8 %p, i8 %q) {
+; CHECK-LABEL: define void @or_disjoint_known_bits_better(
+; CHECK-SAME: i8 [[P:%.*]], i8 [[Q:%.*]]) {
+; CHECK-NEXT: [[C_P:%.*]] = icmp ult i8 [[P]], 4
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_P]])
+; CHECK-NEXT: [[C_Q:%.*]] = icmp ult i8 [[Q]], 4
+; CHECK-NEXT: call void @llvm.assume(i1 [[C_Q]])
+; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[P]], [[Q]]
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: ret void
+;
+ %pre.p = icmp ult i8 %p, 4
+ call void @llvm.assume(i1 %pre.p)
+ %pre.q = icmp ult i8 %q, 4
+ call void @llvm.assume(i1 %pre.q)
+ %o = or disjoint i8 %p, %q
+ %t = icmp ult i8 %o, 4
+ call void @use(i1 %t)
+ ret void
+}
+
+; %p always has bit 7 set, so the operands are never disjoint, so %o is always poison.
+define void @or_disjoint_never_disjoint(i8 %p) {
+; CHECK-LABEL: define void @or_disjoint_never_disjoint(
+; CHECK-SAME: i8 [[P:%.*]]) {
+; CHECK-NEXT: [[C:%.*]] = icmp uge i8 [[P]], -128
+; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[P]], -128
+; CHECK-NEXT: call void @use(i1 false)
+; CHECK-NEXT: ret void
+;
+ %pre = icmp uge i8 %p, 128
+ call void @llvm.assume(i1 %pre)
+ %o = or disjoint i8 %p, 128
+ %r = icmp ult i8 %o, 45
+ call void @use(i1 %r)
+ ret void
+}
>From 8b764f9685c07b298c44eae555768ef91233510a Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 8 Sep 2026 08:38:30 +0100
Subject: [PATCH 4/4] [ConstantRange] Add binaryOp supporting 'or disjoint'.
Promote logic from https://github.com/llvm/llvm-project/pull/221832 to
ConstantRange::binaryOp so it can also be re-used from SCCP.
This enables a few additional folds & flag inference end-to-end:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1227
---
llvm/include/llvm/IR/ConstantRange.h | 7 ++++++
llvm/lib/Analysis/LazyValueInfo.cpp | 22 +------------------
llvm/lib/IR/ConstantRange.cpp | 17 +++++++++++++++
llvm/lib/Transforms/Utils/SCCPSolver.cpp | 6 +-----
llvm/test/Transforms/SCCP/or-disjoint.ll | 11 +++++-----
llvm/unittests/IR/ConstantRangeTest.cpp | 27 ++++++++++++++++++++++++
6 files changed, 58 insertions(+), 32 deletions(-)
diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h
index e553aab2b93ee..fc6e64d8dbff8 100644
--- a/llvm/include/llvm/IR/ConstantRange.h
+++ b/llvm/include/llvm/IR/ConstantRange.h
@@ -407,6 +407,13 @@ class [[nodiscard]] ConstantRange {
LLVM_ABI ConstantRange binaryOp(Instruction::BinaryOps BinOp,
const ConstantRange &Other) const;
+ /// Return a new range representing the possible values resulting from an
+ /// application of the specified binary operation \p BO (including
+ /// poison-generating flags) to a left hand side of this range and a right
+ /// hand side of \p Other.
+ LLVM_ABI ConstantRange binaryOp(const BinaryOperator &BO,
+ const ConstantRange &Other) const;
+
/// Return a new range representing the possible values resulting
/// from an application of the specified overflowing binary operator to a
/// left hand side of this range and a right hand side of \p Other given
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index 93a8604560f31..833d337fac875 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -1168,29 +1168,9 @@ LazyValueInfoImpl::solveBlockValueBinaryOp(BinaryOperator *BO, BasicBlock *BB) {
assert(BO->getOperand(0)->getType()->isSized() &&
"all operands to binary operators are sized");
- if (auto *OBO = dyn_cast<OverflowingBinaryOperator>(BO)) {
- unsigned NoWrapKind = OBO->getNoWrapKind();
- return solveBlockValueBinaryOpImpl(
- BO, BB,
- [BO, NoWrapKind](const ConstantRange &CR1, const ConstantRange &CR2) {
- return CR1.overflowingBinaryOp(BO->getOpcode(), CR2, NoWrapKind);
- });
- }
-
- if (match(BO, m_DisjointOr(m_Value(), m_Value()))) {
- // Treat 'or disjoint' as both 'add nuw nsw' and binary or, picking the best
- // from both.
- return solveBlockValueBinaryOpImpl(
- BO, BB, [](const ConstantRange &CR1, const ConstantRange &CR2) {
- using OBO = OverflowingBinaryOperator;
- return CR1.addWithNoWrap(CR2, OBO::NoUnsignedWrap | OBO::NoSignedWrap)
- .intersectWith(CR1.binaryOr(CR2));
- });
- }
-
return solveBlockValueBinaryOpImpl(
BO, BB, [BO](const ConstantRange &CR1, const ConstantRange &CR2) {
- return CR1.binaryOp(BO->getOpcode(), CR2);
+ return CR1.binaryOp(*BO, CR2);
});
}
diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index ffe51cdcbe2ba..0daa8010e6d1a 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -1044,6 +1044,23 @@ ConstantRange ConstantRange::overflowingBinaryOp(Instruction::BinaryOps BinOp,
}
}
+ConstantRange ConstantRange::binaryOp(const BinaryOperator &BO,
+ const ConstantRange &Other) const {
+ if (const auto *OBO = dyn_cast<OverflowingBinaryOperator>(&BO))
+ return overflowingBinaryOp(BO.getOpcode(), Other, OBO->getNoWrapKind());
+
+ // Treat 'or disjoint' as both 'add nuw nsw' and binary or, picking the best
+ // from both.
+ const auto *PDI = dyn_cast<PossiblyDisjointInst>(&BO);
+ if (PDI && PDI->isDisjoint()) {
+ using OBO = OverflowingBinaryOperator;
+ return addWithNoWrap(Other, OBO::NoUnsignedWrap | OBO::NoSignedWrap)
+ .intersectWith(binaryOr(Other));
+ }
+
+ return binaryOp(BO.getOpcode(), Other);
+}
+
bool ConstantRange::isIntrinsicSupported(Intrinsic::ID IntrinsicID) {
switch (IntrinsicID) {
case Intrinsic::uadd_sat:
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index c24e59aac14a5..0101a82bdbbcb 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -1806,11 +1806,7 @@ void SCCPInstVisitor::visitBinaryOperator(Instruction &I) {
V2State.asConstantRange(I.getType(), /*UndefAllowed=*/false);
auto *BO = cast<BinaryOperator>(&I);
- ConstantRange R = ConstantRange::getEmpty(I.getType()->getScalarSizeInBits());
- if (auto *OBO = dyn_cast<OverflowingBinaryOperator>(BO))
- R = A.overflowingBinaryOp(BO->getOpcode(), B, OBO->getNoWrapKind());
- else
- R = A.binaryOp(BO->getOpcode(), B);
+ ConstantRange R = A.binaryOp(*BO, B);
mergeInValue(ValueState[&I], &I, ValueLatticeElement::getRange(R));
// TODO: Currently we do not exploit special values that produce something
diff --git a/llvm/test/Transforms/SCCP/or-disjoint.ll b/llvm/test/Transforms/SCCP/or-disjoint.ll
index 7f3188bf711b1..6e61569120019 100644
--- a/llvm/test/Transforms/SCCP/or-disjoint.ll
+++ b/llvm/test/Transforms/SCCP/or-disjoint.ll
@@ -9,11 +9,9 @@ define void @or_disjoint(i8 %p) {
; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[P]], 13
; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[P]], 32
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i8 [[O]], 45
-; CHECK-NEXT: call void @use(i1 [[TMP1]])
-; CHECK-NEXT: [[F:%.*]] = icmp ugt i8 [[O]], 45
-; CHECK-NEXT: call void @use(i1 [[F]])
-; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[O]], 44
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: call void @use(i1 false)
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[P]], 12
; CHECK-NEXT: call void @use(i1 [[C1]])
; CHECK-NEXT: ret void
;
@@ -83,7 +81,8 @@ define void @or_disjoint_never_disjoint(i8 %p) {
; CHECK-NEXT: [[C:%.*]] = icmp uge i8 [[P]], -128
; CHECK-NEXT: call void @llvm.assume(i1 [[C]])
; CHECK-NEXT: [[O:%.*]] = or disjoint i8 [[P]], -128
-; CHECK-NEXT: call void @use(i1 false)
+; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i8 [[P]], -83
+; CHECK-NEXT: call void @use(i1 [[TMP1]])
; CHECK-NEXT: ret void
;
%pre = icmp uge i8 %p, 128
diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index 099a43bba34c4..2cd33d87ab4ba 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -2969,6 +2969,33 @@ TEST_F(ConstantRangeTest, binaryOr) {
CheckSingleElementsOnly);
}
+TEST_F(ConstantRangeTest, binaryOpDisjointOr) {
+ // TestBinaryOpExhaustive enumerates 1- and 4-bit ranges, create or disjoint
+ // binary ops up front for the bitwidths.
+ LLVMContext Ctx;
+ std::unique_ptr<BinaryOperator> DisjointOr[2];
+ for (auto [Idx, Bits] : enumerate(ArrayRef<unsigned>({1, 4}))) {
+ Value *Poison = PoisonValue::get(Type::getIntNTy(Ctx, Bits));
+ DisjointOr[Idx].reset(
+ BinaryOperator::CreateDisjoint(Instruction::Or, Poison, Poison));
+ }
+
+ TestBinaryOpExhaustive(
+ [&](const ConstantRange &CR1, const ConstantRange &CR2) {
+ const BinaryOperator &BO = *DisjointOr[CR1.getBitWidth() == 1 ? 0 : 1];
+ return CR1.binaryOp(BO, CR2);
+ },
+ [](const APInt &N1, const APInt &N2) -> std::optional<APInt> {
+ if (N1.intersects(N2))
+ return std::nullopt;
+ return N1 | N2;
+ },
+ PreferSmallest,
+ [](const ConstantRange &, const ConstantRange &) {
+ return false; // Check correctness only.
+ });
+}
+
TEST_F(ConstantRangeTest, binaryXor) {
// Single element ranges.
ConstantRange R16(APInt(8, 16));
More information about the llvm-commits
mailing list