[llvm] [LLVM][Transforms][Attributor] - Optimize AAIsDeadFunction::isAssumedDead (PR #189467)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 30 12:56:31 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Pranav Bhandarkar (bhandarkar-pranav)
<details>
<summary>Changes</summary>
This patch optimizes `AAIsDeadFunction::isAssumedDead` to address a performance bottleneck identified during the linking of a representative workload.
**Optimized `AAIsDeadFunction::isAssumedDead`**:
- Replaced backward linear scan for instruction liveness with a
cached "First Dead Instruction" approach.
- Added `FirstDeadInstCache` to `AAIsDeadFunction`.
- On the first query, the block is scanned once ($O(N)$) to find the first
dead instruction. Subsequent queries use this cache and `comesBefore` check
(or simple identity check), reducing the complexity significantly.
- Added proper cache invalidation in `updateImpl` and `manifest`.
Assisted-by: Cursor.
---
Full diff: https://github.com/llvm/llvm-project/pull/189467.diff
1 Files Affected:
- (modified) llvm/lib/Transforms/IPO/AttributorAttributes.cpp (+48-7)
``````````diff
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 95c0531c2183b..f6453037c3752 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -4556,6 +4556,9 @@ struct AAIsDeadFunction : public AAIsDead {
HasChanged = ChangeStatus::CHANGED;
}
+ if (HasChanged == ChangeStatus::CHANGED)
+ FirstDeadInstCache.clear();
+
return HasChanged;
}
@@ -4606,14 +4609,41 @@ struct AAIsDeadFunction : public AAIsDead {
if (!AssumedLiveBlocks.count(I->getParent()))
return true;
- // If it is not after a liveness barrier it is live.
- const Instruction *PrevI = I->getPrevNode();
- while (PrevI) {
- if (KnownDeadEnds.count(PrevI) || ToBeExploredFrom.count(PrevI))
- return true;
- PrevI = PrevI->getPrevNode();
+ // We cache the *first* dead instruction in the block.
+ // If such an instruction exists and precedes I, then I is dead.
+ // Previously, we used to a do a backwards linear scan from I to
+ // the beginning of the block, checking KnownDeadEnds and ToBeExploredFrom
+ // at each step. By caching we trade complexity for storage.
+
+ const BasicBlock *BB = I->getParent();
+ auto It = FirstDeadInstCache.find(BB);
+ if (It == FirstDeadInstCache.end()) {
+ // Cache miss. Scan the block forward to find the first dead end.
+ const Instruction *FirstDead = nullptr;
+ for (const Instruction &Inst : *BB) {
+ if (KnownDeadEnds.count(&Inst) || ToBeExploredFrom.count(&Inst)) {
+ FirstDead = &Inst;
+ break;
+ }
+ }
+ It = FirstDeadInstCache.insert({BB, FirstDead}).first;
}
- return false;
+
+ const Instruction *FirstDead = It->second;
+
+ // If no dead end in the block, I is not dead (via this mechanism).
+ if (!FirstDead)
+ return false;
+
+ // If I is the first dead end, it is not dead *after* a barrier (it IS the
+ // barrier).
+ if (FirstDead == I)
+ return false;
+
+ // If FirstDead comes before I, then I is dead.
+ // Note: comesBefore is O(N), but it avoids the hash lookups of the original
+ // loop. Also, we only scan from FirstDead to I, not from I to start.
+ return FirstDead->comesBefore(I);
}
/// See AAIsDead::isKnownDead(Instruction *I).
@@ -4651,6 +4681,12 @@ struct AAIsDeadFunction : public AAIsDead {
/// Collection of all assumed live BasicBlocks.
DenseSet<const BasicBlock *> AssumedLiveBlocks;
+
+ /// Cache to store the first "dead end" instruction for each basic block.
+ /// A "dead end" is an instruction in KnownDeadEnds or ToBeExploredFrom.
+ /// If the mapped value is nullptr, the block has no dead ends.
+ /// If it is non-null, it points to the first such instruction in the block.
+ mutable DenseMap<const BasicBlock *, const Instruction *> FirstDeadInstCache;
};
static bool
@@ -4885,6 +4921,11 @@ ChangeStatus AAIsDeadFunction::updateImpl(Attributor &A) {
ToBeExploredFrom = std::move(NewToBeExploredFrom);
}
+ // If the state changed (KnownDeadEnds or ToBeExploredFrom), the cache is
+ // invalid.
+ if (Change == ChangeStatus::CHANGED)
+ FirstDeadInstCache.clear();
+
// If we know everything is live there is no need to query for liveness.
// Instead, indicating a pessimistic fixpoint will cause the state to be
// "invalid" and all queries to be answered conservatively without lookups.
``````````
</details>
https://github.com/llvm/llvm-project/pull/189467
More information about the llvm-commits
mailing list