[SLP] Simplify eraseInstruction [NFC]

This simplifies the implementation of eraseInstruction by moving the odd-replace-users-with-undef handling back to the only caller which uses it.  This handling was not obviously correct, so add the asserts which make it clear why this is safe to do at all.  The result is simpler code and stronger assertions.
This commit is contained in:
Philip Reames 2022-03-25 11:34:31 -07:00
parent a78bd83264
commit f80aaa675f
1 changed files with 36 additions and 37 deletions

View File

@ -1964,8 +1964,12 @@ public:
/// Checks if the instruction is marked for deletion. /// Checks if the instruction is marked for deletion.
bool isDeleted(Instruction *I) const { return DeletedInstructions.count(I); } bool isDeleted(Instruction *I) const { return DeletedInstructions.count(I); }
/// Marks values operands for later deletion by replacing them with Undefs. /// Removes an instruction from its block and eventually deletes it.
void eraseInstructions(ArrayRef<Value *> AV); /// It's like Instruction::eraseFromParent() except that the actual deletion
/// is delayed until BoUpSLP is destructed.
void eraseInstruction(Instruction *I) {
DeletedInstructions.insert(I);
}
~BoUpSLP(); ~BoUpSLP();
@ -2521,20 +2525,12 @@ private:
// invalidates capture results. // invalidates capture results.
BatchAAResults BatchAA; BatchAAResults BatchAA;
/// Removes an instruction from its block and eventually deletes it.
/// It's like Instruction::eraseFromParent() except that the actual deletion
/// is delayed until BoUpSLP is destructed.
/// This is required to ensure that there are no incorrect collisions in the
/// AliasCache, which can happen if a new instruction is allocated at the
/// same address as a previously deleted instruction.
void eraseInstruction(Instruction *I, bool ReplaceOpsWithUndef = false) {
auto It = DeletedInstructions.try_emplace(I, ReplaceOpsWithUndef).first;
It->getSecond() = It->getSecond() && ReplaceOpsWithUndef;
}
/// Temporary store for deleted instructions. Instructions will be deleted /// Temporary store for deleted instructions. Instructions will be deleted
/// eventually when the BoUpSLP is destructed. /// eventually when the BoUpSLP is destructed. The deferral is required to
DenseMap<Instruction *, bool> DeletedInstructions; /// ensure that there are no incorrect collisions in the AliasCache, which
/// can happen if a new instruction is allocated at the same address as a
/// previously deleted instruction.
DenseSet<Instruction *> DeletedInstructions;
/// A list of values that need to extracted out of the tree. /// A list of values that need to extracted out of the tree.
/// This list holds pairs of (Internal Scalar : External User). External User /// This list holds pairs of (Internal Scalar : External User). External User
@ -3208,19 +3204,12 @@ template <> struct DOTGraphTraits<BoUpSLP *> : public DefaultDOTGraphTraits {
} // end namespace llvm } // end namespace llvm
BoUpSLP::~BoUpSLP() { BoUpSLP::~BoUpSLP() {
for (const auto &Pair : DeletedInstructions) { for (auto *I : DeletedInstructions)
// Replace operands of ignored instructions with Undefs in case if they were I->dropAllReferences();
// marked for deletion. for (auto *I : DeletedInstructions) {
if (Pair.getSecond()) { assert(I->use_empty() &&
Value *Undef = UndefValue::get(Pair.getFirst()->getType());
Pair.getFirst()->replaceAllUsesWith(Undef);
}
Pair.getFirst()->dropAllReferences();
}
for (const auto &Pair : DeletedInstructions) {
assert(Pair.getFirst()->use_empty() &&
"trying to erase instruction with users."); "trying to erase instruction with users.");
Pair.getFirst()->eraseFromParent(); I->eraseFromParent();
} }
#ifdef EXPENSIVE_CHECKS #ifdef EXPENSIVE_CHECKS
// If we could guarantee that this call is not extremely slow, we could // If we could guarantee that this call is not extremely slow, we could
@ -3229,13 +3218,6 @@ BoUpSLP::~BoUpSLP() {
#endif #endif
} }
void BoUpSLP::eraseInstructions(ArrayRef<Value *> AV) {
for (auto *V : AV) {
if (auto *I = dyn_cast<Instruction>(V))
eraseInstruction(I, /*ReplaceOpsWithUndef=*/true);
};
}
/// Reorders the given \p Reuses mask according to the given \p Mask. \p Reuses /// Reorders the given \p Reuses mask according to the given \p Mask. \p Reuses
/// contains original mask for the scalars reused in the node. Procedure /// contains original mask for the scalars reused in the node. Procedure
/// transform this mask in accordance with the given \p Mask. /// transform this mask in accordance with the given \p Mask.
@ -9831,9 +9813,26 @@ public:
ReductionRoot->replaceAllUsesWith(VectorizedTree); ReductionRoot->replaceAllUsesWith(VectorizedTree);
// Mark all scalar reduction ops for deletion, they are replaced by the // The original scalar reduction is expected to have no remaining
// vector reductions. // uses outside the reduction tree itself. Assert that we got this
V.eraseInstructions(IgnoreList); // correct, replace internal uses with undef, and mark for eventual
// deletion.
#ifndef NDEBUG
SmallSet<Value *, 4> IgnoreSet;
IgnoreSet.insert(IgnoreList.begin(), IgnoreList.end());
#endif
for (auto *Ignore : IgnoreList) {
#ifndef NDEBUG
for (auto *U : Ignore->users()) {
assert(IgnoreSet.count(U));
}
#endif
if (!Ignore->use_empty()) {
Value *Undef = UndefValue::get(Ignore->getType());
Ignore->replaceAllUsesWith(Undef);
}
V.eraseInstruction(cast<Instruction>(Ignore));
}
} }
return VectorizedTree; return VectorizedTree;
} }