mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 19:23:23 +01:00
b98fe759b3
Summary: This patch separates two semantics of `applyUpdates`: 1. User provides an accurate CFG diff and the dominator tree is updated according to the difference of `the number of edge insertions` and `the number of edge deletions` to infer the status of an edge before and after the update. 2. User provides a sequence of hints. Updates mentioned in this sequence might never happened and even duplicated. Logic changes: Previously, removing invalid updates is considered a side-effect of deduplication and is not guaranteed to be reliable. To handle the second semantic, `applyUpdates` does validity checking before deduplication, which can cause updates that have already been applied to be submitted again. Then, different calls to `applyUpdates` might cause unintended consequences, for example, ``` DTU(Lazy) and Edge A->B exists. 1. DTU.applyUpdates({{Delete, A, B}, {Insert, A, B}}) // User expects these 2 updates result in a no-op, but {Insert, A, B} is queued 2. Remove A->B 3. DTU.applyUpdates({{Delete, A, B}}) // DTU cancels this update with {Insert, A, B} mentioned above together (Unintended) ``` But by restricting the precondition that updates of an edge need to be strictly ordered as how CFG changes were made, we can infer the initial status of this edge to resolve this issue. Interface changes: The second semantic of `applyUpdates` is separated to `applyUpdatesPermissive`. These changes enable DTU(Lazy) to use the first semantic if needed, which is quite useful in `transforms/utils`. Reviewers: kuhar, brzycki, dmgreen, grosser Reviewed By: brzycki Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D58170 llvm-svn: 354669
534 lines
15 KiB
C++
534 lines
15 KiB
C++
//===- DomTreeUpdater.cpp - DomTree/Post DomTree Updater --------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the DomTreeUpdater class, which provides a uniform way
|
|
// to update dominator tree related data structures.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Analysis/DomTreeUpdater.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/Analysis/PostDominators.h"
|
|
#include "llvm/IR/Dominators.h"
|
|
#include "llvm/Support/GenericDomTree.h"
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <utility>
|
|
|
|
namespace llvm {
|
|
|
|
bool DomTreeUpdater::isUpdateValid(
|
|
const DominatorTree::UpdateType Update) const {
|
|
const auto *From = Update.getFrom();
|
|
const auto *To = Update.getTo();
|
|
const auto Kind = Update.getKind();
|
|
|
|
// Discard updates by inspecting the current state of successors of From.
|
|
// Since isUpdateValid() must be called *after* the Terminator of From is
|
|
// altered we can determine if the update is unnecessary for batch updates
|
|
// or invalid for a single update.
|
|
const bool HasEdge = llvm::any_of(
|
|
successors(From), [To](const BasicBlock *B) { return B == To; });
|
|
|
|
// If the IR does not match the update,
|
|
// 1. In batch updates, this update is unnecessary.
|
|
// 2. When called by insertEdge*()/deleteEdge*(), this update is invalid.
|
|
// Edge does not exist in IR.
|
|
if (Kind == DominatorTree::Insert && !HasEdge)
|
|
return false;
|
|
|
|
// Edge exists in IR.
|
|
if (Kind == DominatorTree::Delete && HasEdge)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DomTreeUpdater::isSelfDominance(
|
|
const DominatorTree::UpdateType Update) const {
|
|
// Won't affect DomTree and PostDomTree.
|
|
return Update.getFrom() == Update.getTo();
|
|
}
|
|
|
|
void DomTreeUpdater::applyDomTreeUpdates() {
|
|
// No pending DomTreeUpdates.
|
|
if (Strategy != UpdateStrategy::Lazy || !DT)
|
|
return;
|
|
|
|
// Only apply updates not are applied by DomTree.
|
|
if (hasPendingDomTreeUpdates()) {
|
|
const auto I = PendUpdates.begin() + PendDTUpdateIndex;
|
|
const auto E = PendUpdates.end();
|
|
assert(I < E && "Iterator range invalid; there should be DomTree updates.");
|
|
DT->applyUpdates(ArrayRef<DominatorTree::UpdateType>(I, E));
|
|
PendDTUpdateIndex = PendUpdates.size();
|
|
}
|
|
}
|
|
|
|
void DomTreeUpdater::flush() {
|
|
applyDomTreeUpdates();
|
|
applyPostDomTreeUpdates();
|
|
dropOutOfDateUpdates();
|
|
}
|
|
|
|
void DomTreeUpdater::applyPostDomTreeUpdates() {
|
|
// No pending PostDomTreeUpdates.
|
|
if (Strategy != UpdateStrategy::Lazy || !PDT)
|
|
return;
|
|
|
|
// Only apply updates not are applied by PostDomTree.
|
|
if (hasPendingPostDomTreeUpdates()) {
|
|
const auto I = PendUpdates.begin() + PendPDTUpdateIndex;
|
|
const auto E = PendUpdates.end();
|
|
assert(I < E &&
|
|
"Iterator range invalid; there should be PostDomTree updates.");
|
|
PDT->applyUpdates(ArrayRef<DominatorTree::UpdateType>(I, E));
|
|
PendPDTUpdateIndex = PendUpdates.size();
|
|
}
|
|
}
|
|
|
|
void DomTreeUpdater::tryFlushDeletedBB() {
|
|
if (!hasPendingUpdates())
|
|
forceFlushDeletedBB();
|
|
}
|
|
|
|
bool DomTreeUpdater::forceFlushDeletedBB() {
|
|
if (DeletedBBs.empty())
|
|
return false;
|
|
|
|
for (auto *BB : DeletedBBs) {
|
|
// After calling deleteBB or callbackDeleteBB under Lazy UpdateStrategy,
|
|
// validateDeleteBB() removes all instructions of DelBB and adds an
|
|
// UnreachableInst as its terminator. So we check whether the BasicBlock to
|
|
// delete only has an UnreachableInst inside.
|
|
assert(BB->getInstList().size() == 1 &&
|
|
isa<UnreachableInst>(BB->getTerminator()) &&
|
|
"DelBB has been modified while awaiting deletion.");
|
|
BB->removeFromParent();
|
|
eraseDelBBNode(BB);
|
|
delete BB;
|
|
}
|
|
DeletedBBs.clear();
|
|
Callbacks.clear();
|
|
return true;
|
|
}
|
|
|
|
void DomTreeUpdater::recalculate(Function &F) {
|
|
|
|
if (Strategy == UpdateStrategy::Eager) {
|
|
if (DT)
|
|
DT->recalculate(F);
|
|
if (PDT)
|
|
PDT->recalculate(F);
|
|
return;
|
|
}
|
|
|
|
// There is little performance gain if we pend the recalculation under
|
|
// Lazy UpdateStrategy so we recalculate available trees immediately.
|
|
|
|
// Prevent forceFlushDeletedBB() from erasing DomTree or PostDomTree nodes.
|
|
IsRecalculatingDomTree = IsRecalculatingPostDomTree = true;
|
|
|
|
// Because all trees are going to be up-to-date after recalculation,
|
|
// flush awaiting deleted BasicBlocks.
|
|
forceFlushDeletedBB();
|
|
if (DT)
|
|
DT->recalculate(F);
|
|
if (PDT)
|
|
PDT->recalculate(F);
|
|
|
|
// Resume forceFlushDeletedBB() to erase DomTree or PostDomTree nodes.
|
|
IsRecalculatingDomTree = IsRecalculatingPostDomTree = false;
|
|
PendDTUpdateIndex = PendPDTUpdateIndex = PendUpdates.size();
|
|
dropOutOfDateUpdates();
|
|
}
|
|
|
|
bool DomTreeUpdater::hasPendingUpdates() const {
|
|
return hasPendingDomTreeUpdates() || hasPendingPostDomTreeUpdates();
|
|
}
|
|
|
|
bool DomTreeUpdater::hasPendingDomTreeUpdates() const {
|
|
if (!DT)
|
|
return false;
|
|
return PendUpdates.size() != PendDTUpdateIndex;
|
|
}
|
|
|
|
bool DomTreeUpdater::hasPendingPostDomTreeUpdates() const {
|
|
if (!PDT)
|
|
return false;
|
|
return PendUpdates.size() != PendPDTUpdateIndex;
|
|
}
|
|
|
|
bool DomTreeUpdater::isBBPendingDeletion(llvm::BasicBlock *DelBB) const {
|
|
if (Strategy == UpdateStrategy::Eager || DeletedBBs.empty())
|
|
return false;
|
|
return DeletedBBs.count(DelBB) != 0;
|
|
}
|
|
|
|
// The DT and PDT require the nodes related to updates
|
|
// are not deleted when update functions are called.
|
|
// So BasicBlock deletions must be pended when the
|
|
// UpdateStrategy is Lazy. When the UpdateStrategy is
|
|
// Eager, the BasicBlock will be deleted immediately.
|
|
void DomTreeUpdater::deleteBB(BasicBlock *DelBB) {
|
|
validateDeleteBB(DelBB);
|
|
if (Strategy == UpdateStrategy::Lazy) {
|
|
DeletedBBs.insert(DelBB);
|
|
return;
|
|
}
|
|
|
|
DelBB->removeFromParent();
|
|
eraseDelBBNode(DelBB);
|
|
delete DelBB;
|
|
}
|
|
|
|
void DomTreeUpdater::callbackDeleteBB(
|
|
BasicBlock *DelBB, std::function<void(BasicBlock *)> Callback) {
|
|
validateDeleteBB(DelBB);
|
|
if (Strategy == UpdateStrategy::Lazy) {
|
|
Callbacks.push_back(CallBackOnDeletion(DelBB, Callback));
|
|
DeletedBBs.insert(DelBB);
|
|
return;
|
|
}
|
|
|
|
DelBB->removeFromParent();
|
|
eraseDelBBNode(DelBB);
|
|
Callback(DelBB);
|
|
delete DelBB;
|
|
}
|
|
|
|
void DomTreeUpdater::eraseDelBBNode(BasicBlock *DelBB) {
|
|
if (DT && !IsRecalculatingDomTree)
|
|
if (DT->getNode(DelBB))
|
|
DT->eraseNode(DelBB);
|
|
|
|
if (PDT && !IsRecalculatingPostDomTree)
|
|
if (PDT->getNode(DelBB))
|
|
PDT->eraseNode(DelBB);
|
|
}
|
|
|
|
void DomTreeUpdater::validateDeleteBB(BasicBlock *DelBB) {
|
|
assert(DelBB && "Invalid push_back of nullptr DelBB.");
|
|
assert(pred_empty(DelBB) && "DelBB has one or more predecessors.");
|
|
// DelBB is unreachable and all its instructions are dead.
|
|
while (!DelBB->empty()) {
|
|
Instruction &I = DelBB->back();
|
|
// Replace used instructions with an arbitrary value (undef).
|
|
if (!I.use_empty())
|
|
I.replaceAllUsesWith(llvm::UndefValue::get(I.getType()));
|
|
DelBB->getInstList().pop_back();
|
|
}
|
|
// Make sure DelBB has a valid terminator instruction. As long as DelBB is a
|
|
// Child of Function F it must contain valid IR.
|
|
new UnreachableInst(DelBB->getContext(), DelBB);
|
|
}
|
|
|
|
void DomTreeUpdater::applyUpdates(ArrayRef<DominatorTree::UpdateType> Updates) {
|
|
if (!DT && !PDT)
|
|
return;
|
|
|
|
if (Strategy == UpdateStrategy::Lazy) {
|
|
for (const auto U : Updates)
|
|
if (!isSelfDominance(U))
|
|
PendUpdates.push_back(U);
|
|
|
|
return;
|
|
}
|
|
|
|
if (DT)
|
|
DT->applyUpdates(Updates);
|
|
if (PDT)
|
|
PDT->applyUpdates(Updates);
|
|
}
|
|
|
|
void DomTreeUpdater::applyUpdatesPermissive(
|
|
ArrayRef<DominatorTree::UpdateType> Updates) {
|
|
if (!DT && !PDT)
|
|
return;
|
|
|
|
SmallSet<std::pair<BasicBlock *, BasicBlock *>, 8> Seen;
|
|
SmallVector<DominatorTree::UpdateType, 8> DeduplicatedUpdates;
|
|
for (const auto U : Updates) {
|
|
auto Edge = std::make_pair(U.getFrom(), U.getTo());
|
|
// Because it is illegal to submit updates that have already been applied
|
|
// and updates to an edge need to be strictly ordered,
|
|
// it is safe to infer the existence of an edge from the first update
|
|
// to this edge.
|
|
// If the first update to an edge is "Delete", it means that the edge
|
|
// existed before. If the first update to an edge is "Insert", it means
|
|
// that the edge didn't exist before.
|
|
//
|
|
// For example, if the user submits {{Delete, A, B}, {Insert, A, B}},
|
|
// because
|
|
// 1. it is illegal to submit updates that have already been applied,
|
|
// i.e., user cannot delete an nonexistent edge,
|
|
// 2. updates to an edge need to be strictly ordered,
|
|
// So, initially edge A -> B existed.
|
|
// We can then safely ignore future updates to this edge and directly
|
|
// inspect the current CFG:
|
|
// a. If the edge still exists, because the user cannot insert an existent
|
|
// edge, so both {Delete, A, B}, {Insert, A, B} actually happened and
|
|
// resulted in a no-op. DTU won't submit any update in this case.
|
|
// b. If the edge doesn't exist, we can then infer that {Delete, A, B}
|
|
// actually happened but {Insert, A, B} was an invalid update which never
|
|
// happened. DTU will submit {Delete, A, B} in this case.
|
|
if (!isSelfDominance(U) && Seen.count(Edge) == 0) {
|
|
Seen.insert(Edge);
|
|
// If the update doesn't appear in the CFG, it means that
|
|
// either the change isn't made or relevant operations
|
|
// result in a no-op.
|
|
if (isUpdateValid(U)) {
|
|
if (isLazy())
|
|
PendUpdates.push_back(U);
|
|
else
|
|
DeduplicatedUpdates.push_back(U);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Strategy == UpdateStrategy::Lazy)
|
|
return;
|
|
|
|
if (DT)
|
|
DT->applyUpdates(DeduplicatedUpdates);
|
|
if (PDT)
|
|
PDT->applyUpdates(DeduplicatedUpdates);
|
|
}
|
|
|
|
DominatorTree &DomTreeUpdater::getDomTree() {
|
|
assert(DT && "Invalid acquisition of a null DomTree");
|
|
applyDomTreeUpdates();
|
|
dropOutOfDateUpdates();
|
|
return *DT;
|
|
}
|
|
|
|
PostDominatorTree &DomTreeUpdater::getPostDomTree() {
|
|
assert(PDT && "Invalid acquisition of a null PostDomTree");
|
|
applyPostDomTreeUpdates();
|
|
dropOutOfDateUpdates();
|
|
return *PDT;
|
|
}
|
|
|
|
void DomTreeUpdater::insertEdge(BasicBlock *From, BasicBlock *To) {
|
|
|
|
#ifndef NDEBUG
|
|
assert(isUpdateValid({DominatorTree::Insert, From, To}) &&
|
|
"Inserted edge does not appear in the CFG");
|
|
#endif
|
|
|
|
if (!DT && !PDT)
|
|
return;
|
|
|
|
// Won't affect DomTree and PostDomTree; discard update.
|
|
if (From == To)
|
|
return;
|
|
|
|
if (Strategy == UpdateStrategy::Eager) {
|
|
if (DT)
|
|
DT->insertEdge(From, To);
|
|
if (PDT)
|
|
PDT->insertEdge(From, To);
|
|
return;
|
|
}
|
|
|
|
PendUpdates.push_back({DominatorTree::Insert, From, To});
|
|
}
|
|
|
|
void DomTreeUpdater::insertEdgeRelaxed(BasicBlock *From, BasicBlock *To) {
|
|
if (From == To)
|
|
return;
|
|
|
|
if (!DT && !PDT)
|
|
return;
|
|
|
|
if (!isUpdateValid({DominatorTree::Insert, From, To}))
|
|
return;
|
|
|
|
if (Strategy == UpdateStrategy::Eager) {
|
|
if (DT)
|
|
DT->insertEdge(From, To);
|
|
if (PDT)
|
|
PDT->insertEdge(From, To);
|
|
return;
|
|
}
|
|
|
|
PendUpdates.push_back({DominatorTree::Insert, From, To});
|
|
}
|
|
|
|
void DomTreeUpdater::deleteEdge(BasicBlock *From, BasicBlock *To) {
|
|
|
|
#ifndef NDEBUG
|
|
assert(isUpdateValid({DominatorTree::Delete, From, To}) &&
|
|
"Deleted edge still exists in the CFG!");
|
|
#endif
|
|
|
|
if (!DT && !PDT)
|
|
return;
|
|
|
|
// Won't affect DomTree and PostDomTree; discard update.
|
|
if (From == To)
|
|
return;
|
|
|
|
if (Strategy == UpdateStrategy::Eager) {
|
|
if (DT)
|
|
DT->deleteEdge(From, To);
|
|
if (PDT)
|
|
PDT->deleteEdge(From, To);
|
|
return;
|
|
}
|
|
|
|
PendUpdates.push_back({DominatorTree::Delete, From, To});
|
|
}
|
|
|
|
void DomTreeUpdater::deleteEdgeRelaxed(BasicBlock *From, BasicBlock *To) {
|
|
if (From == To)
|
|
return;
|
|
|
|
if (!DT && !PDT)
|
|
return;
|
|
|
|
if (!isUpdateValid({DominatorTree::Delete, From, To}))
|
|
return;
|
|
|
|
if (Strategy == UpdateStrategy::Eager) {
|
|
if (DT)
|
|
DT->deleteEdge(From, To);
|
|
if (PDT)
|
|
PDT->deleteEdge(From, To);
|
|
return;
|
|
}
|
|
|
|
PendUpdates.push_back({DominatorTree::Delete, From, To});
|
|
}
|
|
|
|
void DomTreeUpdater::dropOutOfDateUpdates() {
|
|
if (Strategy == DomTreeUpdater::UpdateStrategy::Eager)
|
|
return;
|
|
|
|
tryFlushDeletedBB();
|
|
|
|
// Drop all updates applied by both trees.
|
|
if (!DT)
|
|
PendDTUpdateIndex = PendUpdates.size();
|
|
if (!PDT)
|
|
PendPDTUpdateIndex = PendUpdates.size();
|
|
|
|
const size_t dropIndex = std::min(PendDTUpdateIndex, PendPDTUpdateIndex);
|
|
const auto B = PendUpdates.begin();
|
|
const auto E = PendUpdates.begin() + dropIndex;
|
|
assert(B <= E && "Iterator out of range.");
|
|
PendUpdates.erase(B, E);
|
|
// Calculate current index.
|
|
PendDTUpdateIndex -= dropIndex;
|
|
PendPDTUpdateIndex -= dropIndex;
|
|
}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void DomTreeUpdater::dump() const {
|
|
raw_ostream &OS = llvm::dbgs();
|
|
|
|
OS << "Available Trees: ";
|
|
if (DT || PDT) {
|
|
if (DT)
|
|
OS << "DomTree ";
|
|
if (PDT)
|
|
OS << "PostDomTree ";
|
|
OS << "\n";
|
|
} else
|
|
OS << "None\n";
|
|
|
|
OS << "UpdateStrategy: ";
|
|
if (Strategy == UpdateStrategy::Eager) {
|
|
OS << "Eager\n";
|
|
return;
|
|
} else
|
|
OS << "Lazy\n";
|
|
int Index = 0;
|
|
|
|
auto printUpdates =
|
|
[&](ArrayRef<DominatorTree::UpdateType>::const_iterator begin,
|
|
ArrayRef<DominatorTree::UpdateType>::const_iterator end) {
|
|
if (begin == end)
|
|
OS << " None\n";
|
|
Index = 0;
|
|
for (auto It = begin, ItEnd = end; It != ItEnd; ++It) {
|
|
auto U = *It;
|
|
OS << " " << Index << " : ";
|
|
++Index;
|
|
if (U.getKind() == DominatorTree::Insert)
|
|
OS << "Insert, ";
|
|
else
|
|
OS << "Delete, ";
|
|
BasicBlock *From = U.getFrom();
|
|
if (From) {
|
|
auto S = From->getName();
|
|
if (!From->hasName())
|
|
S = "(no name)";
|
|
OS << S << "(" << From << "), ";
|
|
} else {
|
|
OS << "(badref), ";
|
|
}
|
|
BasicBlock *To = U.getTo();
|
|
if (To) {
|
|
auto S = To->getName();
|
|
if (!To->hasName())
|
|
S = "(no_name)";
|
|
OS << S << "(" << To << ")\n";
|
|
} else {
|
|
OS << "(badref)\n";
|
|
}
|
|
}
|
|
};
|
|
|
|
if (DT) {
|
|
const auto I = PendUpdates.begin() + PendDTUpdateIndex;
|
|
assert(PendUpdates.begin() <= I && I <= PendUpdates.end() &&
|
|
"Iterator out of range.");
|
|
OS << "Applied but not cleared DomTreeUpdates:\n";
|
|
printUpdates(PendUpdates.begin(), I);
|
|
OS << "Pending DomTreeUpdates:\n";
|
|
printUpdates(I, PendUpdates.end());
|
|
}
|
|
|
|
if (PDT) {
|
|
const auto I = PendUpdates.begin() + PendPDTUpdateIndex;
|
|
assert(PendUpdates.begin() <= I && I <= PendUpdates.end() &&
|
|
"Iterator out of range.");
|
|
OS << "Applied but not cleared PostDomTreeUpdates:\n";
|
|
printUpdates(PendUpdates.begin(), I);
|
|
OS << "Pending PostDomTreeUpdates:\n";
|
|
printUpdates(I, PendUpdates.end());
|
|
}
|
|
|
|
OS << "Pending DeletedBBs:\n";
|
|
Index = 0;
|
|
for (auto BB : DeletedBBs) {
|
|
OS << " " << Index << " : ";
|
|
++Index;
|
|
if (BB->hasName())
|
|
OS << BB->getName() << "(";
|
|
else
|
|
OS << "(no_name)(";
|
|
OS << BB << ")\n";
|
|
}
|
|
|
|
OS << "Pending Callbacks:\n";
|
|
Index = 0;
|
|
for (auto BB : Callbacks) {
|
|
OS << " " << Index << " : ";
|
|
++Index;
|
|
if (BB->hasName())
|
|
OS << BB->getName() << "(";
|
|
else
|
|
OS << "(no_name)(";
|
|
OS << BB << ")\n";
|
|
}
|
|
}
|
|
#endif
|
|
} // namespace llvm
|