[llvm] [LVI] Extract information from assume operand bundles (PR #176734)
Nikolas Klauser via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 19 03:36:14 PST 2026
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/176734
This patch teaches LVI to extract information from assume operand bundles. Specifically, it now uses nonnull and dereferenceable information to remove comparisons to null pointers.
>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] [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
+}
More information about the llvm-commits
mailing list