[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