[llvm] [LazyValueInfo] Treat 'or disjoint' as both 'add nuw nsw' and bin or. (PR #221832)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 7 14:05:00 PDT 2026
https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/221832
'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.
>From 927e322f8b98421d683f45da8bebf1ae79dc4e43 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/2] [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 ec2dce84bc2a87c8f69e4c94941425aa85818961 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/2] [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)
More information about the llvm-commits
mailing list