[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