[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
Thu Sep 10 02:18:04 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/221832
>From cebb0ca27430d0f0865de1dcfb649ddc1d1adfff 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 a8061894271f05391ea4d3ce3b02b4d45a02610e 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