[llvm] [PredicateInfo] Extract information from assume operand bundles (PR #177349)

Nikolas Klauser via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 22 06:28:28 PST 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/177349

>From dd2ab9c7932bf6ca3cf2976571d12cc4fe35d34c Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 22 Jan 2026 13:09:18 +0100
Subject: [PATCH 1/2] [PredicateInfo] Extract information from assume operand
 bundles

---
 .../llvm/Transforms/Utils/PredicateInfo.h     |  22 ++-
 llvm/lib/Transforms/Utils/PredicateInfo.cpp   |  27 +++-
 .../Transforms/SCCP/assume-operand-bundles.ll | 128 ++++++++++++++++++
 3 files changed, 172 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/Transforms/SCCP/assume-operand-bundles.ll

diff --git a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
index 3df3495f84470..4cabfdd167203 100644
--- a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
+++ b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
@@ -67,7 +67,7 @@ class Value;
 class IntrinsicInst;
 class raw_ostream;
 
-enum PredicateType { PT_Branch, PT_Assume, PT_Switch };
+enum PredicateType { PT_Branch, PT_Assume, PT_BundleAssume, PT_Switch };
 
 /// Constraint for a predicate of the form "cmp Pred Op, OtherOp", where Op
 /// is the value the constraint applies to (the bitcast result).
@@ -118,7 +118,25 @@ class PredicateAssume : public PredicateBase {
       : PredicateBase(PT_Assume, Op, Condition), AssumeInst(AssumeInst) {}
   PredicateAssume() = delete;
   static bool classof(const PredicateBase *PB) {
-    return PB->Type == PT_Assume;
+    return PB->Type == PT_Assume || PB->Type == PT_BundleAssume;
+  }
+
+protected:
+  PredicateAssume(PredicateType PT, Value *Op, IntrinsicInst *AssumeInst,
+                  Value *Condition)
+      : PredicateBase(PT, Op, Condition), AssumeInst(AssumeInst) {}
+};
+
+class PredicateBundleAssume : public PredicateAssume {
+public:
+  Attribute::AttrKind AttrKind;
+  PredicateBundleAssume(Value *Op, IntrinsicInst *AssumeInst,
+                        Attribute::AttrKind AttrKind)
+      : PredicateAssume(PT_BundleAssume, Op, AssumeInst, nullptr),
+        AttrKind(AttrKind) {}
+
+  static bool classof(const PredicateBase *PB) {
+    return PB->Type == PT_BundleAssume;
   }
 };
 
diff --git a/llvm/lib/Transforms/Utils/PredicateInfo.cpp b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
index 27fed7340411b..1fa3195695fd6 100644
--- a/llvm/lib/Transforms/Utils/PredicateInfo.cpp
+++ b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
@@ -14,6 +14,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/AssumeBundleQueries.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/IR/AssemblyAnnotationWriter.h"
 #include "llvm/IR/Dominators.h"
@@ -217,7 +218,7 @@ class PredicateInfoBuilder {
   ValueInfo &getOrCreateValueInfo(Value *);
   const ValueInfo &getValueInfo(Value *) const;
 
-  void processAssume(IntrinsicInst *, BasicBlock *,
+  void processAssume(AssumeInst *, BasicBlock *,
                      SmallVectorImpl<Value *> &OpsToRename);
   void processBranch(BranchInst *, BasicBlock *,
                      SmallVectorImpl<Value *> &OpsToRename);
@@ -359,8 +360,20 @@ void PredicateInfoBuilder::addInfoFor(SmallVectorImpl<Value *> &OpsToRename,
 // Process an assume instruction and place relevant operations we want to rename
 // into OpsToRename.
 void PredicateInfoBuilder::processAssume(
-    IntrinsicInst *II, BasicBlock *AssumeBB,
+    AssumeInst *II, BasicBlock *AssumeBB,
     SmallVectorImpl<Value *> &OpsToRename) {
+  if (II->hasOperandBundles()) {
+    for (auto BOI : II->bundle_op_infos()) {
+      if (RetainedKnowledge RK = getKnowledgeFromBundle(*II, BOI)) {
+        if (RK.AttrKind == Attribute::NonNull)
+          addInfoFor(OpsToRename, RK.WasOn,
+                     new (Allocator) PredicateBundleAssume(RK.WasOn, II,
+                                                           Attribute::NonNull));
+      }
+    }
+    return;
+  }
+
   SmallVector<Value *, 4> Worklist;
   SmallPtrSet<Value *, 4> Visited;
   Worklist.push_back(II->getOperand(0));
@@ -489,7 +502,7 @@ void PredicateInfoBuilder::buildPredicateInfo() {
     }
   }
   for (auto &Assume : AC.assumptions()) {
-    if (auto *II = dyn_cast_or_null<IntrinsicInst>(Assume))
+    if (auto *II = cast_or_null<AssumeInst>(Assume))
       if (DT.isReachableFromEntry(II->getParent()))
         processAssume(II, II->getParent(), OpsToRename);
   }
@@ -712,6 +725,14 @@ PredicateInfo::PredicateInfo(Function &F, DominatorTree &DT,
 
 std::optional<PredicateConstraint> PredicateBase::getConstraint() const {
   switch (Type) {
+  case PT_BundleAssume: {
+    auto *BA = cast<PredicateBundleAssume>(this);
+    assert(BA->AttrKind == Attribute::NonNull &&
+           "Cannot handle anything other than NonNull");
+    return {{CmpInst::ICMP_NE, ConstantPointerNull::get(
+                                   cast<PointerType>(OriginalOp->getType()))}};
+  }
+
   case PT_Assume:
   case PT_Branch: {
     bool TrueEdge = true;
diff --git a/llvm/test/Transforms/SCCP/assume-operand-bundles.ll b/llvm/test/Transforms/SCCP/assume-operand-bundles.ll
new file mode 100644
index 0000000000000..6f86fc331422d
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/assume-operand-bundles.ll
@@ -0,0 +1,128 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt %s -passes=sccp -S | FileCheck %s
+
+declare void @use(i1)
+declare void @llvm.assume(i1)
+
+define void @nonnull(ptr %v) {
+; CHECK-LABEL: define void @nonnull(
+; CHECK-SAME: ptr [[V:%.*]]) {
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[V]]) ]
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "nonnull"(ptr %v) ]
+  %c1 = icmp eq ptr %v, null
+  call void @use(i1 %c1)
+  %c2 = icmp ne ptr %v, null
+  call void @use(i1 %c2)
+  %c3 = icmp eq ptr null, %v
+  call void @use(i1 %c3)
+  %c4 = icmp ne ptr null, %v
+  call void @use(i1 %c4)
+  ret void
+}
+
+define void @nonnull_after_branch(i1 %cond1, i1 %cond2, ptr %v) {
+; CHECK-LABEL: define void @nonnull_after_branch(
+; CHECK-SAME: i1 [[COND1:%.*]], i1 [[COND2:%.*]], ptr [[V:%.*]]) {
+; CHECK-NEXT:    [[C1:%.*]] = icmp eq ptr [[V]], null
+; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    [[C2:%.*]] = icmp ne ptr [[V]], null
+; CHECK-NEXT:    call void @use(i1 [[C2]])
+; CHECK-NEXT:    [[C3:%.*]] = icmp eq ptr null, [[V]]
+; CHECK-NEXT:    call void @use(i1 [[C3]])
+; CHECK-NEXT:    [[C4:%.*]] = icmp ne ptr null, [[V]]
+; CHECK-NEXT:    call void @use(i1 [[C4]])
+; CHECK-NEXT:    br i1 [[COND1]], label %[[TRUE:.*]], label %[[FALSE1:.*]]
+; CHECK:       [[TRUE]]:
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[V]]) ]
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    br i1 [[COND2]], label %[[TRUE2:.*]], label %[[FALSE:.*]]
+; CHECK:       [[TRUE2]]:
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    ret void
+; CHECK:       [[FALSE]]:
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 false)
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    ret void
+; CHECK:       [[FALSE1]]:
+; CHECK-NEXT:    [[CF1:%.*]] = icmp eq ptr [[V]], null
+; CHECK-NEXT:    call void @use(i1 [[CF1]])
+; CHECK-NEXT:    [[CF2:%.*]] = icmp ne ptr [[V]], null
+; CHECK-NEXT:    call void @use(i1 [[CF2]])
+; CHECK-NEXT:    [[CF3:%.*]] = icmp eq ptr null, [[V]]
+; CHECK-NEXT:    call void @use(i1 [[CF3]])
+; CHECK-NEXT:    [[CF4:%.*]] = icmp ne ptr null, [[V]]
+; CHECK-NEXT:    call void @use(i1 [[CF4]])
+; CHECK-NEXT:    ret void
+;
+
+  %c1 = icmp eq ptr %v, null
+  call void @use(i1 %c1)
+  %c2 = icmp ne ptr %v, null
+  call void @use(i1 %c2)
+  %c3 = icmp eq ptr null, %v
+  call void @use(i1 %c3)
+  %c4 = icmp ne ptr null, %v
+  call void @use(i1 %c4)
+  br i1 %cond1, label %true1, label %false1
+
+true1:
+  call void @llvm.assume(i1 true) [ "nonnull"(ptr %v) ]
+  %ct1 = icmp eq ptr %v, null
+  call void @use(i1 %ct1)
+  %ct2 = icmp ne ptr %v, null
+  call void @use(i1 %ct2)
+  %ct3 = icmp eq ptr null, %v
+  call void @use(i1 %ct3)
+  %ct4 = icmp ne ptr null, %v
+  call void @use(i1 %ct4)
+  br i1 %cond2, label %true2, label %false2
+
+true2:
+  %ctt1 = icmp eq ptr %v, null
+  call void @use(i1 %ctt1)
+  %ctt2 = icmp ne ptr %v, null
+  call void @use(i1 %ctt2)
+  %ctt3 = icmp eq ptr null, %v
+  call void @use(i1 %ctt3)
+  %ctt4 = icmp ne ptr null, %v
+  call void @use(i1 %ctt4)
+  ret void
+
+false2:
+  %ctf1 = icmp eq ptr %v, null
+  call void @use(i1 %ctf1)
+  %ctf2 = icmp ne ptr %v, null
+  call void @use(i1 %ctf2)
+  %ctf3 = icmp eq ptr null, %v
+  call void @use(i1 %ctf3)
+  %ctf4 = icmp ne ptr null, %v
+  call void @use(i1 %ctf4)
+  ret void
+
+
+false1:
+  %cf1 = icmp eq ptr %v, null
+  call void @use(i1 %cf1)
+  %cf2 = icmp ne ptr %v, null
+  call void @use(i1 %cf2)
+  %cf3 = icmp eq ptr null, %v
+  call void @use(i1 %cf3)
+  %cf4 = icmp ne ptr null, %v
+  call void @use(i1 %cf4)
+  ret void
+  ret void
+}

>From ecf2a2f69d4bfc3f3ca7740dd55d079fde09d170 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 22 Jan 2026 15:28:15 +0100
Subject: [PATCH 2/2] Address comments

---
 .../llvm/Transforms/Utils/PredicateInfo.h     | 23 +++++--
 llvm/lib/Transforms/Scalar/NewGVN.cpp         |  3 +-
 llvm/lib/Transforms/Utils/PredicateInfo.cpp   | 13 ++--
 .../PredicateInfo/assume-operand-bundles.ll   | 68 +++++++++++++++++++
 4 files changed, 95 insertions(+), 12 deletions(-)
 create mode 100644 llvm/test/Transforms/Util/PredicateInfo/assume-operand-bundles.ll

diff --git a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
index 4cabfdd167203..306f512cd1a4c 100644
--- a/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
+++ b/llvm/include/llvm/Transforms/Utils/PredicateInfo.h
@@ -67,7 +67,7 @@ class Value;
 class IntrinsicInst;
 class raw_ostream;
 
-enum PredicateType { PT_Branch, PT_Assume, PT_BundleAssume, PT_Switch };
+enum PredicateType { PT_Branch, PT_ConditionAssume, PT_BundleAssume, PT_Switch };
 
 /// Constraint for a predicate of the form "cmp Pred Op, OtherOp", where Op
 /// is the value the constraint applies to (the bitcast result).
@@ -96,8 +96,8 @@ class PredicateBase {
   PredicateBase &operator=(const PredicateBase &) = delete;
   PredicateBase() = delete;
   static bool classof(const PredicateBase *PB) {
-    return PB->Type == PT_Assume || PB->Type == PT_Branch ||
-           PB->Type == PT_Switch;
+    return PB->Type == PT_BundleAssume || PB->Type == PT_ConditionAssume ||
+           PB->Type == PT_Branch || PB->Type == PT_Switch;
   }
 
   /// Fetch condition in the form of PredicateConstraint, if possible.
@@ -114,11 +114,9 @@ class PredicateBase {
 class PredicateAssume : public PredicateBase {
 public:
   IntrinsicInst *AssumeInst;
-  PredicateAssume(Value *Op, IntrinsicInst *AssumeInst, Value *Condition)
-      : PredicateBase(PT_Assume, Op, Condition), AssumeInst(AssumeInst) {}
-  PredicateAssume() = delete;
+
   static bool classof(const PredicateBase *PB) {
-    return PB->Type == PT_Assume || PB->Type == PT_BundleAssume;
+    return PB->Type == PT_ConditionAssume || PB->Type == PT_BundleAssume;
   }
 
 protected:
@@ -140,6 +138,17 @@ class PredicateBundleAssume : public PredicateAssume {
   }
 };
 
+class PredicateConditionAssume : public PredicateAssume {
+public:
+  PredicateConditionAssume(Value *Op, IntrinsicInst *AssumeInst,
+                           Value *Condition)
+      : PredicateAssume(PT_ConditionAssume, Op, AssumeInst, Condition) {}
+
+  static bool classof(const PredicateBase *PB) {
+    return PB->Type == PT_ConditionAssume;
+  }
+};
+
 // Mixin class for edge predicates.  The FROM block is the block where the
 // predicate originates, and the TO block is the block where the predicate is
 // valid.
diff --git a/llvm/lib/Transforms/Scalar/NewGVN.cpp b/llvm/lib/Transforms/Scalar/NewGVN.cpp
index b4528cdb56d51..2fbd2bd99fd76 100644
--- a/llvm/lib/Transforms/Scalar/NewGVN.cpp
+++ b/llvm/lib/Transforms/Scalar/NewGVN.cpp
@@ -2098,7 +2098,8 @@ void NewGVN::addAdditionalUsers(ExprResult &Res, Instruction *User) const {
   if (Res.PredDep) {
     if (const auto *PBranch = dyn_cast<PredicateBranch>(Res.PredDep))
       PredicateToUsers[PBranch->Condition].insert(User);
-    else if (const auto *PAssume = dyn_cast<PredicateAssume>(Res.PredDep))
+    else if (const auto *PAssume =
+                 dyn_cast<PredicateConditionAssume>(Res.PredDep))
       PredicateToUsers[PAssume->Condition].insert(User);
   }
   Res.PredDep = nullptr;
diff --git a/llvm/lib/Transforms/Utils/PredicateInfo.cpp b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
index 1fa3195695fd6..d500708d10153 100644
--- a/llvm/lib/Transforms/Utils/PredicateInfo.cpp
+++ b/llvm/lib/Transforms/Utils/PredicateInfo.cpp
@@ -399,7 +399,7 @@ void PredicateInfoBuilder::processAssume(
 
     for (Value *V : Values) {
       if (shouldRename(V)) {
-        auto *PA = new (Allocator) PredicateAssume(V, II, Cond);
+        auto *PA = new (Allocator) PredicateConditionAssume(V, II, Cond);
         addInfoFor(OpsToRename, V, PA);
       }
     }
@@ -733,7 +733,7 @@ std::optional<PredicateConstraint> PredicateBase::getConstraint() const {
                                    cast<PointerType>(OriginalOp->getType()))}};
   }
 
-  case PT_Assume:
+  case PT_ConditionAssume:
   case PT_Branch: {
     bool TrueEdge = true;
     if (auto *PBranch = dyn_cast<PredicateBranch>(this))
@@ -845,8 +845,13 @@ class PredicateInfoAnnotatedWriter : public AssemblyAnnotationWriter {
         PS->To->printAsOperand(OS);
         OS << "]";
       } else if (const auto *PA = dyn_cast<PredicateAssume>(PI)) {
-        OS << "; assume predicate info {"
-           << " Comparison:" << *PA->Condition;
+        OS << "; assume predicate info {";
+        if (auto * PBA = dyn_cast<PredicateBundleAssume>(PA)) {
+          OS << " Attribute: " << Attribute::getNameFromAttrKind(PBA->AttrKind);
+        } else {
+          assert(isa<PredicateConditionAssume>(PA));
+          OS << " Comparison:" << *PA->Condition;
+        }
       }
       OS << ", RenamedOp: ";
       PI->RenamedOp->printAsOperand(OS, false);
diff --git a/llvm/test/Transforms/Util/PredicateInfo/assume-operand-bundles.ll b/llvm/test/Transforms/Util/PredicateInfo/assume-operand-bundles.ll
new file mode 100644
index 0000000000000..ab23d3354da66
--- /dev/null
+++ b/llvm/test/Transforms/Util/PredicateInfo/assume-operand-bundles.ll
@@ -0,0 +1,68 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-inst-comments
+; RUN: opt -S -passes=print-predicateinfo %s 2>&1 >/dev/null | FileCheck %s
+
+declare void @use(i1)
+
+define void @nonnull0(ptr %v) {
+; CHECK-LABEL: @nonnull0(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[V:%.*]]) ]
+; CHECK-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "nonnull"(ptr %v) ]
+  ret void
+}
+
+define void @nonnull1(ptr %v) {
+; CHECK-LABEL: @nonnull1(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[V:%.*]]) ]
+; CHECK-NEXT:  ; assume predicate info { Attribute: nonnull, RenamedOp: [[V]] }
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast ptr [[V]] to ptr
+; CHECK-NEXT:    [[C1:%.*]] = icmp eq ptr [[TMP1]], null
+; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "nonnull"(ptr %v) ]
+  %c1 = icmp eq ptr %v, null
+  call void @use(i1 %c1)
+  ret void
+}
+
+define void @nonnull2(ptr %v, ptr %w) {
+; CHECK-LABEL: @nonnull2(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[V:%.*]]), "nonnull"(ptr [[W:%.*]]) ]
+; CHECK-NEXT:  ; assume predicate info { Attribute: nonnull, RenamedOp: [[W]] }
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast ptr [[W]] to ptr
+; CHECK-NEXT:  ; assume predicate info { Attribute: nonnull, RenamedOp: [[V]] }
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast ptr [[V]] to ptr
+; CHECK-NEXT:    [[C1:%.*]] = icmp eq ptr [[TMP2]], null
+; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    [[C2:%.*]] = icmp eq ptr [[TMP1]], null
+; CHECK-NEXT:    call void @use(i1 [[C2]])
+; CHECK-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "nonnull"(ptr %v), "nonnull"(ptr %w) ]
+  %c1 = icmp eq ptr %v, null
+  call void @use(i1 %c1)
+  %c2 = icmp eq ptr %w, null
+  call void @use(i1 %c2)
+  ret void
+}
+
+define void @nonnull3(ptr %v) {
+; CHECK-LABEL: @nonnull3(
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[V:%.*]]) ]
+; CHECK-NEXT:  ; assume predicate info { Attribute: nonnull, RenamedOp: [[V]] }
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast ptr [[V]] to ptr
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP1]]) ]
+; CHECK-NEXT:  ; assume predicate info { Attribute: nonnull, RenamedOp: [[TMP1]] }
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast ptr [[TMP1]] to ptr
+; CHECK-NEXT:    [[C1:%.*]] = icmp eq ptr [[TMP2]], null
+; CHECK-NEXT:    call void @use(i1 [[C1]])
+; CHECK-NEXT:    ret void
+;
+  call void @llvm.assume(i1 true) [ "nonnull"(ptr %v) ]
+  call void @llvm.assume(i1 true) [ "nonnull"(ptr %v) ]
+  %c1 = icmp eq ptr %v, null
+  call void @use(i1 %c1)
+  ret void
+}



More information about the llvm-commits mailing list