[llvm] [LVI] Extract information from assume operand bundles (PR #176734)
Nikolas Klauser via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 19 06:54:31 PST 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/176734
>From 6bd56cfa6d7fc90372d3792975a1e4e19701be43 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 19 Jan 2026 12:33:40 +0100
Subject: [PATCH 1/2] [LVI] Extract information from assume operand bundles
---
llvm/lib/Analysis/LazyValueInfo.cpp | 35 +++++++++--
.../JumpThreading/assume-operand-bundles.ll | 60 +++++++++++++++++++
2 files changed, 91 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/Transforms/JumpThreading/assume-operand-bundles.ll
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index df75999eb6080..35bfab115c521 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -14,6 +14,7 @@
#include "llvm/Analysis/LazyValueInfo.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Analysis/AssumeBundleQueries.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/InstructionSimplify.h"
@@ -833,13 +834,39 @@ void LazyValueInfoImpl::intersectAssumeOrGuardBlockValueConstantRange(
// Only check assumes in the block of the context instruction. Other
// assumes will have already been taken into account when the value was
// propagated from predecessor blocks.
- auto *I = cast<CallInst>(AssumeVH);
+ auto *I = cast<AssumeInst>(AssumeVH);
+
if (I->getParent() != BB || !isValidAssumeForContext(I, BBI))
continue;
- BBLV = BBLV.intersect(*getValueFromCondition(Val, I->getArgOperand(0),
- /*IsTrueDest*/ true,
- /*UseBlockValue*/ false));
+ if (I->hasOperandBundles()) {
+ for (auto Info : I->bundle_op_infos()) {
+ if (RetainedKnowledge RK = getKnowledgeFromBundle(*I, Info)) {
+ if (RK.WasOn != Val)
+ continue;
+ switch (RK.AttrKind) {
+ case Attribute::NonNull:
+ BBLV = BBLV.intersect(ValueLatticeElement::getNot(
+ Constant::getNullValue(RK.WasOn->getType())));
+ break;
+
+ case Attribute::Dereferenceable:
+ if (auto *CI = dyn_cast<ConstantInt>(RK.IRArgValue);
+ CI && !CI->isZero())
+ BBLV = BBLV.intersect(ValueLatticeElement::getNot(
+ Constant::getNullValue(RK.IRArgValue->getType())));
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ } else {
+ BBLV = BBLV.intersect(*getValueFromCondition(Val, I->getArgOperand(0),
+ /*IsTrueDest*/ true,
+ /*UseBlockValue*/ false));
+ }
}
// If guards are not used in the module, don't spend time looking for them
diff --git a/llvm/test/Transforms/JumpThreading/assume-operand-bundles.ll b/llvm/test/Transforms/JumpThreading/assume-operand-bundles.ll
new file mode 100644
index 0000000000000..822b08f676a04
--- /dev/null
+++ b/llvm/test/Transforms/JumpThreading/assume-operand-bundles.ll
@@ -0,0 +1,60 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=jump-threading,dce %s | FileCheck %s
+
+define i32 @nonull(ptr %ptr) {
+; CHECK-LABEL: define i32 @nonull(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[TRUE:.*:]]
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[PTR]]) ]
+; CHECK-NEXT: ret i32 1
+;
+ call void @llvm.assume(i1 true) [ "nonnull"(ptr %ptr) ]
+ %is_nonnull = icmp ne ptr %ptr, null
+ br i1 %is_nonnull, label %true, label %false
+
+true:
+ ret i32 1
+
+false:
+ ret i32 2
+}
+
+define i32 @nonull_on_other_ptr(ptr %ptr, ptr %other_ptr) {
+; CHECK-LABEL: define i32 @nonull_on_other_ptr(
+; CHECK-SAME: ptr [[PTR:%.*]], ptr [[OTHER_PTR:%.*]]) {
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[OTHER_PTR]]) ]
+; CHECK-NEXT: [[IS_NONNULL:%.*]] = icmp ne ptr [[PTR]], null
+; CHECK-NEXT: br i1 [[IS_NONNULL]], label %[[TRUE:.*]], label %[[FALSE:.*]]
+; CHECK: [[TRUE]]:
+; CHECK-NEXT: ret i32 1
+; CHECK: [[FALSE]]:
+; CHECK-NEXT: ret i32 2
+;
+ call void @llvm.assume(i1 true) [ "nonnull"(ptr %other_ptr) ]
+ %is_nonnull = icmp ne ptr %ptr, null
+ br i1 %is_nonnull, label %true, label %false
+
+true:
+ ret i32 1
+
+false:
+ ret i32 2
+}
+
+define i32 @dereferenceable(ptr %ptr) {
+; CHECK-LABEL: define i32 @dereferenceable(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT: [[TRUE:.*:]]
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[PTR]], i32 1) ]
+; CHECK-NEXT: ret i32 1
+;
+ call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %ptr, i32 1) ]
+ %is_nonnull = icmp ne ptr %ptr, null
+ br i1 %is_nonnull, label %true, label %false
+
+true:
+ ret i32 1
+
+false:
+ ret i32 2
+}
>From f4ba8e38011f4546f2c27498118ea4e40c871dee Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 19 Jan 2026 15:54:18 +0100
Subject: [PATCH 2/2] Address comments
---
llvm/lib/Analysis/LazyValueInfo.cpp | 39 ++++++------
.../assume-operand-bundles.ll | 36 +++++++++++
.../JumpThreading/assume-operand-bundles.ll | 60 -------------------
3 files changed, 55 insertions(+), 80 deletions(-)
create mode 100644 llvm/test/Transforms/CorrelatedValuePropagation/assume-operand-bundles.ll
delete mode 100644 llvm/test/Transforms/JumpThreading/assume-operand-bundles.ll
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index 35bfab115c521..0bdd158970dc7 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -839,27 +839,26 @@ void LazyValueInfoImpl::intersectAssumeOrGuardBlockValueConstantRange(
if (I->getParent() != BB || !isValidAssumeForContext(I, BBI))
continue;
- if (I->hasOperandBundles()) {
- for (auto Info : I->bundle_op_infos()) {
- if (RetainedKnowledge RK = getKnowledgeFromBundle(*I, Info)) {
- if (RK.WasOn != Val)
- continue;
- switch (RK.AttrKind) {
- case Attribute::NonNull:
+ if (AssumeVH.Index != AssumptionCache::ExprResultIdx) {
+ if (RetainedKnowledge RK = getKnowledgeFromBundle(
+ *I, I->bundle_op_info_begin()[AssumeVH.Index])) {
+ if (RK.WasOn != Val)
+ continue;
+ switch (RK.AttrKind) {
+ case Attribute::NonNull:
+ BBLV = BBLV.intersect(ValueLatticeElement::getNot(
+ Constant::getNullValue(RK.WasOn->getType())));
+ break;
+
+ case Attribute::Dereferenceable:
+ if (auto *CI = dyn_cast<ConstantInt>(RK.IRArgValue);
+ CI && !CI->isZero())
BBLV = BBLV.intersect(ValueLatticeElement::getNot(
- Constant::getNullValue(RK.WasOn->getType())));
- break;
-
- case Attribute::Dereferenceable:
- if (auto *CI = dyn_cast<ConstantInt>(RK.IRArgValue);
- CI && !CI->isZero())
- BBLV = BBLV.intersect(ValueLatticeElement::getNot(
- Constant::getNullValue(RK.IRArgValue->getType())));
- break;
-
- default:
- break;
- }
+ Constant::getNullValue(RK.IRArgValue->getType())));
+ break;
+
+ default:
+ break;
}
}
} else {
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/assume-operand-bundles.ll b/llvm/test/Transforms/CorrelatedValuePropagation/assume-operand-bundles.ll
new file mode 100644
index 0000000000000..20d7861231194
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/assume-operand-bundles.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=correlated-propagation %s | FileCheck %s
+
+define i1 @nonnull(ptr %ptr) {
+; CHECK-LABEL: define i1 @nonnull(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[PTR]]) ]
+; CHECK-NEXT: ret i1 true
+;
+ call void @llvm.assume(i1 true) [ "nonnull"(ptr %ptr) ]
+ %is_nonnull = icmp ne ptr %ptr, null
+ ret i1 %is_nonnull
+}
+
+define i1 @nonnull_on_other_ptr(ptr %ptr, ptr %other_ptr) {
+; CHECK-LABEL: define i1 @nonnull_on_other_ptr(
+; CHECK-SAME: ptr [[PTR:%.*]], ptr [[OTHER_PTR:%.*]]) {
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[OTHER_PTR]]) ]
+; CHECK-NEXT: [[IS_NONNULL:%.*]] = icmp ne ptr [[PTR]], null
+; CHECK-NEXT: ret i1 [[IS_NONNULL]]
+;
+ call void @llvm.assume(i1 true) [ "nonnull"(ptr %other_ptr) ]
+ %is_nonnull = icmp ne ptr %ptr, null
+ ret i1 %is_nonnull
+}
+
+define i1 @dereferenceable(ptr %ptr) {
+; CHECK-LABEL: define i1 @dereferenceable(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[PTR]], i32 1) ]
+; CHECK-NEXT: ret i1 true
+;
+ call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %ptr, i32 1) ]
+ %is_nonnull = icmp ne ptr %ptr, null
+ ret i1 %is_nonnull
+}
diff --git a/llvm/test/Transforms/JumpThreading/assume-operand-bundles.ll b/llvm/test/Transforms/JumpThreading/assume-operand-bundles.ll
deleted file mode 100644
index 822b08f676a04..0000000000000
--- a/llvm/test/Transforms/JumpThreading/assume-operand-bundles.ll
+++ /dev/null
@@ -1,60 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -S -passes=jump-threading,dce %s | FileCheck %s
-
-define i32 @nonull(ptr %ptr) {
-; CHECK-LABEL: define i32 @nonull(
-; CHECK-SAME: ptr [[PTR:%.*]]) {
-; CHECK-NEXT: [[TRUE:.*:]]
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[PTR]]) ]
-; CHECK-NEXT: ret i32 1
-;
- call void @llvm.assume(i1 true) [ "nonnull"(ptr %ptr) ]
- %is_nonnull = icmp ne ptr %ptr, null
- br i1 %is_nonnull, label %true, label %false
-
-true:
- ret i32 1
-
-false:
- ret i32 2
-}
-
-define i32 @nonull_on_other_ptr(ptr %ptr, ptr %other_ptr) {
-; CHECK-LABEL: define i32 @nonull_on_other_ptr(
-; CHECK-SAME: ptr [[PTR:%.*]], ptr [[OTHER_PTR:%.*]]) {
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[OTHER_PTR]]) ]
-; CHECK-NEXT: [[IS_NONNULL:%.*]] = icmp ne ptr [[PTR]], null
-; CHECK-NEXT: br i1 [[IS_NONNULL]], label %[[TRUE:.*]], label %[[FALSE:.*]]
-; CHECK: [[TRUE]]:
-; CHECK-NEXT: ret i32 1
-; CHECK: [[FALSE]]:
-; CHECK-NEXT: ret i32 2
-;
- call void @llvm.assume(i1 true) [ "nonnull"(ptr %other_ptr) ]
- %is_nonnull = icmp ne ptr %ptr, null
- br i1 %is_nonnull, label %true, label %false
-
-true:
- ret i32 1
-
-false:
- ret i32 2
-}
-
-define i32 @dereferenceable(ptr %ptr) {
-; CHECK-LABEL: define i32 @dereferenceable(
-; CHECK-SAME: ptr [[PTR:%.*]]) {
-; CHECK-NEXT: [[TRUE:.*:]]
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[PTR]], i32 1) ]
-; CHECK-NEXT: ret i32 1
-;
- call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %ptr, i32 1) ]
- %is_nonnull = icmp ne ptr %ptr, null
- br i1 %is_nonnull, label %true, label %false
-
-true:
- ret i32 1
-
-false:
- ret i32 2
-}
More information about the llvm-commits
mailing list