mirror of
https://github.com/RPCSX/llvm.git
synced 2024-12-03 09:21:13 +00:00
Template DominatorTreeBase by node type. This is the next major step towards
having dominator information on MBB's. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@43036 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
dcd8f78f8a
commit
49b653aa6a
@ -35,8 +35,8 @@
|
||||
namespace llvm {
|
||||
|
||||
template<class GraphT>
|
||||
unsigned DFSPass(DominatorTreeBase& DT, typename GraphT::NodeType* V,
|
||||
unsigned N) {
|
||||
unsigned DFSPass(DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
typename GraphT::NodeType* V, unsigned N) {
|
||||
// This is more understandable as a recursive algorithm, but we can't use the
|
||||
// recursive algorithm due to stack depth issues. Keep it here for
|
||||
// documentation purposes.
|
||||
@ -67,7 +67,8 @@ unsigned DFSPass(DominatorTreeBase& DT, typename GraphT::NodeType* V,
|
||||
|
||||
// First time we visited this BB?
|
||||
if (NextSucc == GraphT::child_begin(BB)) {
|
||||
DominatorTree::InfoRec &BBInfo = DT.Info[BB];
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &BBInfo =
|
||||
DT.Info[BB];
|
||||
BBInfo.Semi = ++N;
|
||||
BBInfo.Label = BB;
|
||||
|
||||
@ -89,7 +90,8 @@ unsigned DFSPass(DominatorTreeBase& DT, typename GraphT::NodeType* V,
|
||||
// Visit the successor next, if it isn't already visited.
|
||||
typename GraphT::NodeType* Succ = *NextSucc;
|
||||
|
||||
DominatorTree::InfoRec &SuccVInfo = DT.Info[Succ];
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &SuccVInfo =
|
||||
DT.Info[Succ];
|
||||
if (SuccVInfo.Semi == 0) {
|
||||
SuccVInfo.Parent = BB;
|
||||
Worklist.push_back(std::make_pair(Succ, GraphT::child_begin(Succ)));
|
||||
@ -100,20 +102,24 @@ unsigned DFSPass(DominatorTreeBase& DT, typename GraphT::NodeType* V,
|
||||
}
|
||||
|
||||
template<class GraphT>
|
||||
void Compress(DominatorTreeBase& DT, typename GraphT::NodeType *VIn) {
|
||||
void Compress(DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
typename GraphT::NodeType *VIn) {
|
||||
std::vector<typename GraphT::NodeType*> Work;
|
||||
SmallPtrSet<typename GraphT::NodeType*, 32> Visited;
|
||||
typename GraphT::NodeType* VInAncestor = DT.Info[VIn].Ancestor;
|
||||
DominatorTreeBase::InfoRec &VInVAInfo = DT.Info[VInAncestor];
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &VInVAInfo =
|
||||
DT.Info[VInAncestor];
|
||||
|
||||
if (VInVAInfo.Ancestor != 0)
|
||||
Work.push_back(VIn);
|
||||
|
||||
while (!Work.empty()) {
|
||||
typename GraphT::NodeType* V = Work.back();
|
||||
DominatorTree::InfoRec &VInfo = DT.Info[V];
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &VInfo =
|
||||
DT.Info[V];
|
||||
typename GraphT::NodeType* VAncestor = VInfo.Ancestor;
|
||||
DominatorTreeBase::InfoRec &VAInfo = DT.Info[VAncestor];
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &VAInfo =
|
||||
DT.Info[VAncestor];
|
||||
|
||||
// Process Ancestor first
|
||||
if (Visited.insert(VAncestor) &&
|
||||
@ -135,9 +141,10 @@ void Compress(DominatorTreeBase& DT, typename GraphT::NodeType *VIn) {
|
||||
}
|
||||
|
||||
template<class GraphT>
|
||||
typename GraphT::NodeType* Eval(DominatorTreeBase& DT,
|
||||
typename GraphT::NodeType* Eval(DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
typename GraphT::NodeType *V) {
|
||||
DominatorTreeBase::InfoRec &VInfo = DT.Info[V];
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &VInfo =
|
||||
DT.Info[V];
|
||||
#if !BALANCE_IDOM_TREE
|
||||
// Higher-complexity but faster implementation
|
||||
if (VInfo.Ancestor == 0)
|
||||
@ -160,8 +167,9 @@ typename GraphT::NodeType* Eval(DominatorTreeBase& DT,
|
||||
}
|
||||
|
||||
template<class GraphT>
|
||||
void Link(DominatorTreeBase& DT, typename GraphT::NodeType* V,
|
||||
typename GraphT::NodeType* W, DominatorTreeBase::InfoRec &WInfo) {
|
||||
void Link(DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
typename GraphT::NodeType* V, typename GraphT::NodeType* W,
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &WInfo) {
|
||||
#if !BALANCE_IDOM_TREE
|
||||
// Higher-complexity but faster implementation
|
||||
WInfo.Ancestor = V;
|
||||
@ -208,49 +216,49 @@ void Link(DominatorTreeBase& DT, typename GraphT::NodeType* V,
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class NodeT>
|
||||
void Calculate(DominatorTreeBase& DT, Function& F) {
|
||||
template<class NodeT, class GraphT>
|
||||
void Calculate(DominatorTreeBase<typename GraphT::NodeType>& DT, Function& F) {
|
||||
// Step #1: Number blocks in depth-first order and initialize variables used
|
||||
// in later stages of the algorithm.
|
||||
unsigned N = 0;
|
||||
for (unsigned i = 0, e = DT.Roots.size(); i != e; ++i)
|
||||
N = DFSPass<GraphTraits<NodeT> >(DT, DT.Roots[i], N);
|
||||
N = DFSPass<GraphT>(DT, DT.Roots[i], N);
|
||||
|
||||
for (unsigned i = N; i >= 2; --i) {
|
||||
typename GraphTraits<NodeT>::NodeType* W = DT.Vertex[i];
|
||||
DominatorTree::InfoRec &WInfo = DT.Info[W];
|
||||
typename GraphT::NodeType* W = DT.Vertex[i];
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &WInfo =
|
||||
DT.Info[W];
|
||||
|
||||
// Step #2: Calculate the semidominators of all vertices
|
||||
for (typename GraphTraits<Inverse<NodeT> >::ChildIteratorType CI =
|
||||
GraphTraits<Inverse<NodeT> >::child_begin(W),
|
||||
E = GraphTraits<Inverse<NodeT> >::child_end(W); CI != E; ++CI)
|
||||
if (DT.Info.count(*CI)) { // Only if this predecessor is reachable!
|
||||
unsigned SemiU = DT.Info[Eval<GraphTraits<NodeT> >(DT, *CI)].Semi;
|
||||
unsigned SemiU = DT.Info[Eval<GraphT>(DT, *CI)].Semi;
|
||||
if (SemiU < WInfo.Semi)
|
||||
WInfo.Semi = SemiU;
|
||||
}
|
||||
|
||||
DT.Info[DT.Vertex[WInfo.Semi]].Bucket.push_back(W);
|
||||
|
||||
typename GraphTraits<NodeT>::NodeType* WParent = WInfo.Parent;
|
||||
Link<GraphTraits<NodeT> >(DT, WParent, W, WInfo);
|
||||
typename GraphT::NodeType* WParent = WInfo.Parent;
|
||||
Link<GraphT>(DT, WParent, W, WInfo);
|
||||
|
||||
// Step #3: Implicitly define the immediate dominator of vertices
|
||||
std::vector<typename GraphTraits<NodeT>::NodeType*> &WParentBucket =
|
||||
std::vector<typename GraphT::NodeType*> &WParentBucket =
|
||||
DT.Info[WParent].Bucket;
|
||||
while (!WParentBucket.empty()) {
|
||||
typename GraphTraits<NodeT>::NodeType* V = WParentBucket.back();
|
||||
typename GraphT::NodeType* V = WParentBucket.back();
|
||||
WParentBucket.pop_back();
|
||||
typename GraphTraits<NodeT>::NodeType* U =
|
||||
Eval<GraphTraits<NodeT> >(DT, V);
|
||||
typename GraphT::NodeType* U = Eval<GraphT>(DT, V);
|
||||
DT.IDoms[V] = DT.Info[U].Semi < DT.Info[V].Semi ? U : WParent;
|
||||
}
|
||||
}
|
||||
|
||||
// Step #4: Explicitly define the immediate dominator of each vertex
|
||||
for (unsigned i = 2; i <= N; ++i) {
|
||||
typename GraphTraits<NodeT>::NodeType* W = DT.Vertex[i];
|
||||
typename GraphTraits<NodeT>::NodeType*& WIDom = DT.IDoms[W];
|
||||
typename GraphT::NodeType* W = DT.Vertex[i];
|
||||
typename GraphT::NodeType*& WIDom = DT.IDoms[W];
|
||||
if (WIDom != DT.Vertex[DT.Info[W].Semi])
|
||||
WIDom = DT.IDoms[WIDom];
|
||||
}
|
||||
@ -260,13 +268,13 @@ void Calculate(DominatorTreeBase& DT, Function& F) {
|
||||
// Add a node for the root. This node might be the actual root, if there is
|
||||
// one exit block, or it may be the virtual exit (denoted by (BasicBlock *)0)
|
||||
// which postdominates all real exits if there are multiple exit blocks.
|
||||
typename GraphTraits<NodeT>::NodeType* Root = DT.Roots.size() == 1 ? DT.Roots[0]
|
||||
: 0;
|
||||
typename GraphT::NodeType* Root = DT.Roots.size() == 1 ? DT.Roots[0]
|
||||
: 0;
|
||||
DT.DomTreeNodes[Root] = DT.RootNode = new DomTreeNode(Root, 0);
|
||||
|
||||
// Loop over all of the reachable blocks in the function...
|
||||
for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I)
|
||||
if (typename GraphTraits<NodeT>::NodeType* ImmDom = DT.getIDom(I)) {
|
||||
if (typename GraphT::NodeType* ImmDom = DT.getIDom(I)) {
|
||||
// Reachable block.
|
||||
DomTreeNode *BBNode = DT.DomTreeNodes[I];
|
||||
if (BBNode) continue; // Haven't calculated this node yet?
|
||||
@ -283,7 +291,7 @@ void Calculate(DominatorTreeBase& DT, Function& F) {
|
||||
// Free temporary memory used to construct idom's
|
||||
DT.IDoms.clear();
|
||||
DT.Info.clear();
|
||||
std::vector<typename GraphTraits<NodeT>::NodeType*>().swap(DT.Vertex);
|
||||
std::vector<typename GraphT::NodeType*>().swap(DT.Vertex);
|
||||
|
||||
// FIXME: This does not work on PostDomTrees. It seems likely that this is
|
||||
// due to an error in the algorithm for post-dominators. This really should
|
||||
|
@ -22,23 +22,28 @@
|
||||
#define LLVM_ANALYSIS_DOMINATORS_H
|
||||
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Instruction.h"
|
||||
#include "llvm/Instructions.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class Instruction;
|
||||
|
||||
template <typename GraphType> struct GraphTraits;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// DominatorBase - Base class that other, more interesting dominator analyses
|
||||
/// inherit from.
|
||||
///
|
||||
template <class NodeT>
|
||||
class DominatorBase : public FunctionPass {
|
||||
protected:
|
||||
std::vector<BasicBlock*> Roots;
|
||||
std::vector<NodeT*> Roots;
|
||||
const bool IsPostDominators;
|
||||
inline DominatorBase(intptr_t ID, bool isPostDom) :
|
||||
FunctionPass(ID), Roots(), IsPostDominators(isPostDom) {}
|
||||
@ -48,7 +53,7 @@ public:
|
||||
/// multiple blocks if we are computing post dominators. For forward
|
||||
/// dominators, this will always be a single block (the entry node).
|
||||
///
|
||||
inline const std::vector<BasicBlock*> &getRoots() const { return Roots; }
|
||||
inline const std::vector<NodeT*> &getRoots() const { return Roots; }
|
||||
|
||||
/// isPostDominator - Returns true if analysis based of postdoms
|
||||
///
|
||||
@ -58,7 +63,7 @@ public:
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// DomTreeNode - Dominator Tree Node
|
||||
class DominatorTreeBase;
|
||||
template<class NodeT> class DominatorTreeBase;
|
||||
class PostDominatorTree;
|
||||
class MachineBasicBlock;
|
||||
|
||||
@ -69,7 +74,7 @@ class DomTreeNodeBase {
|
||||
std::vector<DomTreeNodeBase<NodeT> *> Children;
|
||||
int DFSNumIn, DFSNumOut;
|
||||
|
||||
friend class DominatorTreeBase;
|
||||
template<class N> friend class DominatorTreeBase;
|
||||
friend class PostDominatorTree;
|
||||
public:
|
||||
typedef typename std::vector<DomTreeNodeBase<NodeT> *>::iterator iterator;
|
||||
@ -124,18 +129,43 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
EXTERN_TEMPLATE_INSTANTIATION(class DomTreeNodeBase<BasicBlock>);
|
||||
|
||||
template<class NodeT>
|
||||
static std::ostream &operator<<(std::ostream &o,
|
||||
const DomTreeNodeBase<NodeT> *Node) {
|
||||
if (Node->getBlock())
|
||||
WriteAsOperand(o, Node->getBlock(), false);
|
||||
else
|
||||
o << " <<exit node>>";
|
||||
|
||||
o << " {" << Node->getDFSNumIn() << "," << Node->getDFSNumOut() << "}";
|
||||
|
||||
return o << "\n";
|
||||
}
|
||||
|
||||
template<class NodeT>
|
||||
static void PrintDomTree(const DomTreeNodeBase<NodeT> *N, std::ostream &o,
|
||||
unsigned Lev) {
|
||||
o << std::string(2*Lev, ' ') << "[" << Lev << "] " << N;
|
||||
for (typename DomTreeNodeBase<NodeT>::const_iterator I = N->begin(),
|
||||
E = N->end(); I != E; ++I)
|
||||
PrintDomTree<NodeT>(*I, o, Lev+1);
|
||||
}
|
||||
|
||||
typedef DomTreeNodeBase<BasicBlock> DomTreeNode;
|
||||
typedef DomTreeNodeBase<MachineBasicBlock> MachineDomTreeNode;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// DominatorTree - Calculate the immediate dominator tree for a function.
|
||||
///
|
||||
class DominatorTreeBase : public DominatorBase {
|
||||
|
||||
template<class NodeT>
|
||||
class DominatorTreeBase : public DominatorBase<NodeT> {
|
||||
protected:
|
||||
void reset();
|
||||
typedef DenseMap<BasicBlock*, DomTreeNode*> DomTreeNodeMapType;
|
||||
typedef DenseMap<NodeT*, DomTreeNodeBase<NodeT>*> DomTreeNodeMapType;
|
||||
DomTreeNodeMapType DomTreeNodes;
|
||||
DomTreeNode *RootNode;
|
||||
DomTreeNodeBase<NodeT> *RootNode;
|
||||
|
||||
bool DFSInfoValid;
|
||||
unsigned int SlowQueries;
|
||||
@ -143,24 +173,35 @@ protected:
|
||||
struct InfoRec {
|
||||
unsigned Semi;
|
||||
unsigned Size;
|
||||
BasicBlock *Label, *Parent, *Child, *Ancestor;
|
||||
NodeT *Label, *Parent, *Child, *Ancestor;
|
||||
|
||||
std::vector<BasicBlock*> Bucket;
|
||||
std::vector<NodeT*> Bucket;
|
||||
|
||||
InfoRec() : Semi(0), Size(0), Label(0), Parent(0), Child(0), Ancestor(0) {}
|
||||
};
|
||||
|
||||
DenseMap<BasicBlock*, BasicBlock*> IDoms;
|
||||
DenseMap<NodeT*, NodeT*> IDoms;
|
||||
|
||||
// Vertex - Map the DFS number to the BasicBlock*
|
||||
std::vector<BasicBlock*> Vertex;
|
||||
std::vector<NodeT*> Vertex;
|
||||
|
||||
// Info - Collection of information used during the computation of idoms.
|
||||
DenseMap<BasicBlock*, InfoRec> Info;
|
||||
DenseMap<NodeT*, InfoRec> Info;
|
||||
|
||||
void reset() {
|
||||
for (typename DomTreeNodeMapType::iterator I = this->DomTreeNodes.begin(),
|
||||
E = DomTreeNodes.end(); I != E; ++I)
|
||||
delete I->second;
|
||||
DomTreeNodes.clear();
|
||||
IDoms.clear();
|
||||
this->Roots.clear();
|
||||
Vertex.clear();
|
||||
RootNode = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
DominatorTreeBase(intptr_t ID, bool isPostDom)
|
||||
: DominatorBase(ID, isPostDom), DFSInfoValid(false), SlowQueries(0) {}
|
||||
: DominatorBase<NodeT>(ID, isPostDom), DFSInfoValid(false), SlowQueries(0) {}
|
||||
~DominatorTreeBase() { reset(); }
|
||||
|
||||
virtual void releaseMemory() { reset(); }
|
||||
@ -168,12 +209,12 @@ public:
|
||||
/// getNode - return the (Post)DominatorTree node for the specified basic
|
||||
/// block. This is the same as using operator[] on this class.
|
||||
///
|
||||
inline DomTreeNode *getNode(BasicBlock *BB) const {
|
||||
DomTreeNodeMapType::const_iterator I = DomTreeNodes.find(BB);
|
||||
inline DomTreeNodeBase<NodeT> *getNode(NodeT *BB) const {
|
||||
typename DomTreeNodeMapType::const_iterator I = DomTreeNodes.find(BB);
|
||||
return I != DomTreeNodes.end() ? I->second : 0;
|
||||
}
|
||||
|
||||
inline DomTreeNode *operator[](BasicBlock *BB) const {
|
||||
inline DomTreeNodeBase<NodeT> *operator[](NodeT *BB) const {
|
||||
return getNode(BB);
|
||||
}
|
||||
|
||||
@ -184,25 +225,25 @@ public:
|
||||
/// post-dominance information must be capable of dealing with this
|
||||
/// possibility.
|
||||
///
|
||||
DomTreeNode *getRootNode() { return RootNode; }
|
||||
const DomTreeNode *getRootNode() const { return RootNode; }
|
||||
DomTreeNodeBase<NodeT> *getRootNode() { return RootNode; }
|
||||
const DomTreeNodeBase<NodeT> *getRootNode() const { return RootNode; }
|
||||
|
||||
/// properlyDominates - Returns true iff this dominates N and this != N.
|
||||
/// Note that this is not a constant time operation!
|
||||
///
|
||||
bool properlyDominates(const DomTreeNode *A,
|
||||
DomTreeNode *B) const {
|
||||
bool properlyDominates(const DomTreeNodeBase<NodeT> *A,
|
||||
DomTreeNodeBase<NodeT> *B) const {
|
||||
if (A == 0 || B == 0) return false;
|
||||
return dominatedBySlowTreeWalk(A, B);
|
||||
}
|
||||
|
||||
inline bool properlyDominates(BasicBlock *A, BasicBlock *B) {
|
||||
inline bool properlyDominates(NodeT *A, NodeT *B) {
|
||||
return properlyDominates(getNode(A), getNode(B));
|
||||
}
|
||||
|
||||
bool dominatedBySlowTreeWalk(const DomTreeNode *A,
|
||||
const DomTreeNode *B) const {
|
||||
const DomTreeNode *IDom;
|
||||
bool dominatedBySlowTreeWalk(const DomTreeNodeBase<NodeT> *A,
|
||||
const DomTreeNodeBase<NodeT> *B) const {
|
||||
const DomTreeNodeBase<NodeT> *IDom;
|
||||
if (A == 0 || B == 0) return false;
|
||||
while ((IDom = B->getIDom()) != 0 && IDom != A && IDom != B)
|
||||
B = IDom; // Walk up the tree
|
||||
@ -212,13 +253,17 @@ public:
|
||||
|
||||
/// isReachableFromEntry - Return true if A is dominated by the entry
|
||||
/// block of the function containing it.
|
||||
const bool isReachableFromEntry(BasicBlock* A);
|
||||
const bool isReachableFromEntry(NodeT* A) {
|
||||
assert (!this->isPostDominator()
|
||||
&& "This is not implemented for post dominators");
|
||||
return dominates(&A->getParent()->getEntryBlock(), A);
|
||||
}
|
||||
|
||||
/// dominates - Returns true iff A dominates B. Note that this is not a
|
||||
/// constant time operation!
|
||||
///
|
||||
inline bool dominates(const DomTreeNode *A,
|
||||
DomTreeNode *B) {
|
||||
inline bool dominates(const DomTreeNodeBase<NodeT> *A,
|
||||
DomTreeNodeBase<NodeT> *B) {
|
||||
if (B == A)
|
||||
return true; // A node trivially dominates itself.
|
||||
|
||||
@ -239,7 +284,7 @@ public:
|
||||
return dominatedBySlowTreeWalk(A, B);
|
||||
}
|
||||
|
||||
inline bool dominates(BasicBlock *A, BasicBlock *B) {
|
||||
inline bool dominates(NodeT *A, NodeT *B) {
|
||||
if (A == B)
|
||||
return true;
|
||||
|
||||
@ -248,11 +293,73 @@ public:
|
||||
|
||||
/// findNearestCommonDominator - Find nearest common dominator basic block
|
||||
/// for basic block A and B. If there is no such block then return NULL.
|
||||
BasicBlock *findNearestCommonDominator(BasicBlock *A, BasicBlock *B);
|
||||
NodeT *findNearestCommonDominator(NodeT *A, NodeT *B) {
|
||||
|
||||
assert (!this->isPostDominator()
|
||||
&& "This is not implemented for post dominators");
|
||||
assert (A->getParent() == B->getParent()
|
||||
&& "Two blocks are not in same function");
|
||||
|
||||
// If either A or B is a entry block then it is nearest common dominator.
|
||||
NodeT &Entry = A->getParent()->getEntryBlock();
|
||||
if (A == &Entry || B == &Entry)
|
||||
return &Entry;
|
||||
|
||||
// If B dominates A then B is nearest common dominator.
|
||||
if (dominates(B, A))
|
||||
return B;
|
||||
|
||||
// If A dominates B then A is nearest common dominator.
|
||||
if (dominates(A, B))
|
||||
return A;
|
||||
|
||||
DomTreeNodeBase<NodeT> *NodeA = getNode(A);
|
||||
DomTreeNodeBase<NodeT> *NodeB = getNode(B);
|
||||
|
||||
// Collect NodeA dominators set.
|
||||
SmallPtrSet<DomTreeNodeBase<NodeT>*, 16> NodeADoms;
|
||||
NodeADoms.insert(NodeA);
|
||||
DomTreeNodeBase<NodeT> *IDomA = NodeA->getIDom();
|
||||
while (IDomA) {
|
||||
NodeADoms.insert(IDomA);
|
||||
IDomA = IDomA->getIDom();
|
||||
}
|
||||
|
||||
// Walk NodeB immediate dominators chain and find common dominator node.
|
||||
DomTreeNodeBase<NodeT> *IDomB = NodeB->getIDom();
|
||||
while(IDomB) {
|
||||
if (NodeADoms.count(IDomB) != 0)
|
||||
return IDomB->getBlock();
|
||||
|
||||
IDomB = IDomB->getIDom();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// dominates - Return true if A dominates B. This performs the
|
||||
// special checks necessary if A and B are in the same basic block.
|
||||
bool dominates(Instruction *A, Instruction *B);
|
||||
bool dominates(Instruction *A, Instruction *B) {
|
||||
NodeT *BBA = A->getParent(), *BBB = B->getParent();
|
||||
if (BBA != BBB) return this->dominates(BBA, BBB);
|
||||
|
||||
// It is not possible to determine dominance between two PHI nodes
|
||||
// based on their ordering.
|
||||
if (isa<PHINode>(A) && isa<PHINode>(B))
|
||||
return false;
|
||||
|
||||
// Loop through the basic block until we find A or B.
|
||||
typename NodeT::iterator I = BBA->begin();
|
||||
for (; &*I != A && &*I != B; ++I) /*empty*/;
|
||||
|
||||
if(!this->IsPostDominators) {
|
||||
// A dominates B if it is found first in the basic block.
|
||||
return &*I == A;
|
||||
} else {
|
||||
// A post-dominates B if B is found first in the basic block.
|
||||
return &*I == B;
|
||||
}
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// API to update (Post)DominatorTree information based on modifications to
|
||||
@ -261,9 +368,9 @@ public:
|
||||
/// addNewBlock - Add a new node to the dominator tree information. This
|
||||
/// creates a new node as a child of DomBB dominator node,linking it into
|
||||
/// the children list of the immediate dominator.
|
||||
DomTreeNode *addNewBlock(BasicBlock *BB, BasicBlock *DomBB) {
|
||||
DomTreeNodeBase<NodeT> *addNewBlock(NodeT *BB, NodeT *DomBB) {
|
||||
assert(getNode(BB) == 0 && "Block already in dominator tree!");
|
||||
DomTreeNode *IDomNode = getNode(DomBB);
|
||||
DomTreeNodeBase<NodeT> *IDomNode = getNode(DomBB);
|
||||
assert(IDomNode && "Not immediate dominator specified for block!");
|
||||
DFSInfoValid = false;
|
||||
return DomTreeNodes[BB] =
|
||||
@ -273,76 +380,156 @@ public:
|
||||
/// changeImmediateDominator - This method is used to update the dominator
|
||||
/// tree information when a node's immediate dominator changes.
|
||||
///
|
||||
void changeImmediateDominator(DomTreeNode *N,
|
||||
DomTreeNode *NewIDom) {
|
||||
void changeImmediateDominator(DomTreeNodeBase<NodeT> *N,
|
||||
DomTreeNodeBase<NodeT> *NewIDom) {
|
||||
assert(N && NewIDom && "Cannot change null node pointers!");
|
||||
DFSInfoValid = false;
|
||||
N->setIDom(NewIDom);
|
||||
}
|
||||
|
||||
void changeImmediateDominator(BasicBlock *BB, BasicBlock *NewBB) {
|
||||
void changeImmediateDominator(NodeT *BB, NodeT *NewBB) {
|
||||
changeImmediateDominator(getNode(BB), getNode(NewBB));
|
||||
}
|
||||
|
||||
/// eraseNode - Removes a node from the dominator tree. Block must not
|
||||
/// domiante any other blocks. Removes node from its immediate dominator's
|
||||
/// children list. Deletes dominator node associated with basic block BB.
|
||||
void eraseNode(BasicBlock *BB);
|
||||
void eraseNode(NodeT *BB) {
|
||||
DomTreeNodeBase<NodeT> *Node = getNode(BB);
|
||||
assert (Node && "Removing node that isn't in dominator tree.");
|
||||
assert (Node->getChildren().empty() && "Node is not a leaf node.");
|
||||
|
||||
// Remove node from immediate dominator's children list.
|
||||
DomTreeNodeBase<NodeT> *IDom = Node->getIDom();
|
||||
if (IDom) {
|
||||
typename std::vector<DomTreeNodeBase<NodeT>*>::iterator I =
|
||||
std::find(IDom->Children.begin(), IDom->Children.end(), Node);
|
||||
assert(I != IDom->Children.end() &&
|
||||
"Not in immediate dominator children set!");
|
||||
// I am no longer your child...
|
||||
IDom->Children.erase(I);
|
||||
}
|
||||
|
||||
DomTreeNodes.erase(BB);
|
||||
delete Node;
|
||||
}
|
||||
|
||||
/// removeNode - Removes a node from the dominator tree. Block must not
|
||||
/// dominate any other blocks. Invalidates any node pointing to removed
|
||||
/// block.
|
||||
void removeNode(BasicBlock *BB) {
|
||||
void removeNode(NodeT *BB) {
|
||||
assert(getNode(BB) && "Removing node that isn't in dominator tree.");
|
||||
DomTreeNodes.erase(BB);
|
||||
}
|
||||
|
||||
/// print - Convert to human readable form
|
||||
///
|
||||
virtual void print(std::ostream &OS, const Module* = 0) const;
|
||||
virtual void print(std::ostream &o, const Module* ) const {
|
||||
o << "=============================--------------------------------\n";
|
||||
o << "Inorder Dominator Tree: ";
|
||||
if (this->DFSInfoValid)
|
||||
o << "DFSNumbers invalid: " << SlowQueries << " slow queries.";
|
||||
o << "\n";
|
||||
|
||||
PrintDomTree<NodeT>(getRootNode(), o, 1);
|
||||
}
|
||||
|
||||
void print(std::ostream *OS, const Module* M = 0) const {
|
||||
if (OS) print(*OS, M);
|
||||
}
|
||||
virtual void dump();
|
||||
|
||||
virtual void dump() {
|
||||
print(llvm::cerr);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<class GraphT> friend void Compress(DominatorTreeBase& DT,
|
||||
template<class GraphT> friend void Compress(DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
typename GraphT::NodeType* VIn);
|
||||
template<class GraphT> friend typename GraphT::NodeType* Eval(
|
||||
DominatorTreeBase& DT,
|
||||
DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
typename GraphT::NodeType* V);
|
||||
template<class GraphT> friend void Link(DominatorTreeBase& DT,
|
||||
template<class GraphT> friend void Link(DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
typename GraphT::NodeType* V,
|
||||
typename GraphT::NodeType* W,
|
||||
InfoRec &WInfo);
|
||||
typename DominatorTreeBase<typename GraphT::NodeType>::InfoRec &WInfo);
|
||||
|
||||
template<class GraphT> friend unsigned DFSPass(DominatorTreeBase& DT,
|
||||
template<class GraphT> friend unsigned DFSPass(
|
||||
DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
typename GraphT::NodeType* V,
|
||||
unsigned N);
|
||||
|
||||
template<class NodeT> friend void Calculate(DominatorTreeBase& DT,
|
||||
Function& F);
|
||||
template<class N, class GraphT> friend void Calculate(DominatorTreeBase<typename GraphT::NodeType>& DT,
|
||||
Function& F);
|
||||
|
||||
/// updateDFSNumbers - Assign In and Out numbers to the nodes while walking
|
||||
/// dominator tree in dfs order.
|
||||
void updateDFSNumbers();
|
||||
void updateDFSNumbers() {
|
||||
unsigned DFSNum = 0;
|
||||
|
||||
SmallVector<std::pair<DomTreeNodeBase<NodeT>*,
|
||||
typename DomTreeNodeBase<NodeT>::iterator>, 32> WorkStack;
|
||||
|
||||
for (unsigned i = 0, e = this->Roots.size(); i != e; ++i) {
|
||||
DomTreeNodeBase<NodeT> *ThisRoot = getNode(this->Roots[i]);
|
||||
WorkStack.push_back(std::make_pair(ThisRoot, ThisRoot->begin()));
|
||||
ThisRoot->DFSNumIn = DFSNum++;
|
||||
|
||||
while (!WorkStack.empty()) {
|
||||
DomTreeNodeBase<NodeT> *Node = WorkStack.back().first;
|
||||
typename DomTreeNodeBase<NodeT>::iterator ChildIt =
|
||||
WorkStack.back().second;
|
||||
|
||||
// If we visited all of the children of this node, "recurse" back up the
|
||||
// stack setting the DFOutNum.
|
||||
if (ChildIt == Node->end()) {
|
||||
Node->DFSNumOut = DFSNum++;
|
||||
WorkStack.pop_back();
|
||||
} else {
|
||||
// Otherwise, recursively visit this child.
|
||||
DomTreeNodeBase<NodeT> *Child = *ChildIt;
|
||||
++WorkStack.back().second;
|
||||
|
||||
WorkStack.push_back(std::make_pair(Child, Child->begin()));
|
||||
Child->DFSNumIn = DFSNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SlowQueries = 0;
|
||||
DFSInfoValid = true;
|
||||
}
|
||||
|
||||
DomTreeNode *getNodeForBlock(BasicBlock *BB);
|
||||
DomTreeNodeBase<NodeT> *getNodeForBlock(NodeT *BB) {
|
||||
if (DomTreeNodeBase<NodeT> *BBNode = this->DomTreeNodes[BB])
|
||||
return BBNode;
|
||||
|
||||
// Haven't calculated this node yet? Get or calculate the node for the
|
||||
// immediate dominator.
|
||||
NodeT *IDom = getIDom(BB);
|
||||
DomTreeNodeBase<NodeT> *IDomNode = getNodeForBlock(IDom);
|
||||
|
||||
// Add a new tree node for this BasicBlock, and link it as a child of
|
||||
// IDomNode
|
||||
DomTreeNodeBase<NodeT> *C = new DomTreeNodeBase<NodeT>(BB, IDomNode);
|
||||
return this->DomTreeNodes[BB] = IDomNode->addChild(C);
|
||||
}
|
||||
|
||||
inline BasicBlock *getIDom(BasicBlock *BB) const {
|
||||
DenseMap<BasicBlock*, BasicBlock*>::const_iterator I = IDoms.find(BB);
|
||||
inline NodeT *getIDom(NodeT *BB) const {
|
||||
typename DenseMap<NodeT*, NodeT*>::const_iterator I = IDoms.find(BB);
|
||||
return I != IDoms.end() ? I->second : 0;
|
||||
}
|
||||
};
|
||||
|
||||
EXTERN_TEMPLATE_INSTANTIATION(class DominatorTreeBase<BasicBlock>);
|
||||
|
||||
//===-------------------------------------
|
||||
/// DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to
|
||||
/// compute a normal dominator tree.
|
||||
///
|
||||
class DominatorTree : public DominatorTreeBase {
|
||||
class DominatorTree : public DominatorTreeBase<BasicBlock> {
|
||||
public:
|
||||
static char ID; // Pass ID, replacement for typeid
|
||||
DominatorTree() : DominatorTreeBase(intptr_t(&ID), false) {}
|
||||
DominatorTree() : DominatorTreeBase<BasicBlock>(intptr_t(&ID), false) {}
|
||||
|
||||
BasicBlock *getRoot() const {
|
||||
assert(Roots.size() == 1 && "Should always have entry node!");
|
||||
@ -392,7 +579,7 @@ template <> struct GraphTraits<DominatorTree*>
|
||||
/// DominanceFrontierBase - Common base class for computing forward and inverse
|
||||
/// dominance frontiers for a function.
|
||||
///
|
||||
class DominanceFrontierBase : public DominatorBase {
|
||||
class DominanceFrontierBase : public DominatorBase<BasicBlock> {
|
||||
public:
|
||||
typedef std::set<BasicBlock*> DomSetType; // Dom set for a bb
|
||||
typedef std::map<BasicBlock*, DomSetType> DomSetMapType; // Dom set map
|
||||
@ -400,7 +587,7 @@ protected:
|
||||
DomSetMapType Frontiers;
|
||||
public:
|
||||
DominanceFrontierBase(intptr_t ID, bool isPostDom)
|
||||
: DominatorBase(ID, isPostDom) {}
|
||||
: DominatorBase<BasicBlock>(ID, isPostDom) {}
|
||||
|
||||
virtual void releaseMemory() { Frontiers.clear(); }
|
||||
|
||||
|
@ -21,11 +21,11 @@ namespace llvm {
|
||||
/// PostDominatorTree Class - Concrete subclass of DominatorTree that is used to
|
||||
/// compute the a post-dominator tree.
|
||||
///
|
||||
struct PostDominatorTree : public DominatorTreeBase {
|
||||
struct PostDominatorTree : public DominatorTreeBase<BasicBlock> {
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
|
||||
PostDominatorTree() :
|
||||
DominatorTreeBase((intptr_t)&ID, true) {}
|
||||
DominatorTreeBase<BasicBlock>((intptr_t)&ID, true) {}
|
||||
|
||||
virtual bool runOnFunction(Function &F);
|
||||
|
||||
|
@ -47,7 +47,7 @@ bool PostDominatorTree::runOnFunction(Function &F) {
|
||||
|
||||
Vertex.push_back(0);
|
||||
|
||||
Calculate<Inverse<BasicBlock*> >(*this, F);
|
||||
Calculate<Inverse<BasicBlock*>, GraphTraits<Inverse<BasicBlock*> > >(*this, F);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
#include "llvm/Analysis/Dominators.h"
|
||||
#include "llvm/Support/CFG.h"
|
||||
#include "llvm/Assembly/Writer.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/ADT/DepthFirstIterator.h"
|
||||
#include "llvm/ADT/SetOperations.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
@ -49,6 +49,9 @@ static std::ostream &operator<<(std::ostream &o,
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
TEMPLATE_INSTANTIATION(class DomTreeNodeBase<BasicBlock>);
|
||||
TEMPLATE_INSTANTIATION(class DominatorTreeBase<BasicBlock>);
|
||||
|
||||
char DominatorTree::ID = 0;
|
||||
static RegisterPass<DominatorTree>
|
||||
E("domtree", "Dominator Tree Construction", true);
|
||||
@ -135,203 +138,6 @@ void DominatorTree::splitBlock(BasicBlock *NewBB) {
|
||||
}
|
||||
}
|
||||
|
||||
void DominatorTreeBase::updateDFSNumbers() {
|
||||
unsigned DFSNum = 0;
|
||||
|
||||
SmallVector<std::pair<DomTreeNode*, DomTreeNode::iterator>, 32> WorkStack;
|
||||
|
||||
for (unsigned i = 0, e = Roots.size(); i != e; ++i) {
|
||||
DomTreeNode *ThisRoot = getNode(Roots[i]);
|
||||
WorkStack.push_back(std::make_pair(ThisRoot, ThisRoot->begin()));
|
||||
ThisRoot->DFSNumIn = DFSNum++;
|
||||
|
||||
while (!WorkStack.empty()) {
|
||||
DomTreeNode *Node = WorkStack.back().first;
|
||||
DomTreeNode::iterator ChildIt = WorkStack.back().second;
|
||||
|
||||
// If we visited all of the children of this node, "recurse" back up the
|
||||
// stack setting the DFOutNum.
|
||||
if (ChildIt == Node->end()) {
|
||||
Node->DFSNumOut = DFSNum++;
|
||||
WorkStack.pop_back();
|
||||
} else {
|
||||
// Otherwise, recursively visit this child.
|
||||
DomTreeNode *Child = *ChildIt;
|
||||
++WorkStack.back().second;
|
||||
|
||||
WorkStack.push_back(std::make_pair(Child, Child->begin()));
|
||||
Child->DFSNumIn = DFSNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SlowQueries = 0;
|
||||
DFSInfoValid = true;
|
||||
}
|
||||
|
||||
/// isReachableFromEntry - Return true if A is dominated by the entry
|
||||
/// block of the function containing it.
|
||||
const bool DominatorTreeBase::isReachableFromEntry(BasicBlock* A) {
|
||||
assert (!isPostDominator()
|
||||
&& "This is not implemented for post dominators");
|
||||
return dominates(&A->getParent()->getEntryBlock(), A);
|
||||
}
|
||||
|
||||
// dominates - Return true if A dominates B. THis performs the
|
||||
// special checks necessary if A and B are in the same basic block.
|
||||
bool DominatorTreeBase::dominates(Instruction *A, Instruction *B) {
|
||||
BasicBlock *BBA = A->getParent(), *BBB = B->getParent();
|
||||
if (BBA != BBB) return dominates(BBA, BBB);
|
||||
|
||||
// It is not possible to determine dominance between two PHI nodes
|
||||
// based on their ordering.
|
||||
if (isa<PHINode>(A) && isa<PHINode>(B))
|
||||
return false;
|
||||
|
||||
// Loop through the basic block until we find A or B.
|
||||
BasicBlock::iterator I = BBA->begin();
|
||||
for (; &*I != A && &*I != B; ++I) /*empty*/;
|
||||
|
||||
if(!IsPostDominators) {
|
||||
// A dominates B if it is found first in the basic block.
|
||||
return &*I == A;
|
||||
} else {
|
||||
// A post-dominates B if B is found first in the basic block.
|
||||
return &*I == B;
|
||||
}
|
||||
}
|
||||
|
||||
// DominatorTreeBase::reset - Free all of the tree node memory.
|
||||
//
|
||||
void DominatorTreeBase::reset() {
|
||||
for (DomTreeNodeMapType::iterator I = DomTreeNodes.begin(),
|
||||
E = DomTreeNodes.end(); I != E; ++I)
|
||||
delete I->second;
|
||||
DomTreeNodes.clear();
|
||||
IDoms.clear();
|
||||
Roots.clear();
|
||||
Vertex.clear();
|
||||
RootNode = 0;
|
||||
}
|
||||
|
||||
DomTreeNode *DominatorTreeBase::getNodeForBlock(BasicBlock *BB) {
|
||||
if (DomTreeNode *BBNode = DomTreeNodes[BB])
|
||||
return BBNode;
|
||||
|
||||
// Haven't calculated this node yet? Get or calculate the node for the
|
||||
// immediate dominator.
|
||||
BasicBlock *IDom = getIDom(BB);
|
||||
DomTreeNode *IDomNode = getNodeForBlock(IDom);
|
||||
|
||||
// Add a new tree node for this BasicBlock, and link it as a child of
|
||||
// IDomNode
|
||||
DomTreeNode *C = new DomTreeNode(BB, IDomNode);
|
||||
return DomTreeNodes[BB] = IDomNode->addChild(C);
|
||||
}
|
||||
|
||||
/// findNearestCommonDominator - Find nearest common dominator basic block
|
||||
/// for basic block A and B. If there is no such block then return NULL.
|
||||
BasicBlock *DominatorTreeBase::findNearestCommonDominator(BasicBlock *A,
|
||||
BasicBlock *B) {
|
||||
|
||||
assert (!isPostDominator()
|
||||
&& "This is not implemented for post dominators");
|
||||
assert (A->getParent() == B->getParent()
|
||||
&& "Two blocks are not in same function");
|
||||
|
||||
// If either A or B is a entry block then it is nearest common dominator.
|
||||
BasicBlock &Entry = A->getParent()->getEntryBlock();
|
||||
if (A == &Entry || B == &Entry)
|
||||
return &Entry;
|
||||
|
||||
// If B dominates A then B is nearest common dominator.
|
||||
if (dominates(B, A))
|
||||
return B;
|
||||
|
||||
// If A dominates B then A is nearest common dominator.
|
||||
if (dominates(A, B))
|
||||
return A;
|
||||
|
||||
DomTreeNode *NodeA = getNode(A);
|
||||
DomTreeNode *NodeB = getNode(B);
|
||||
|
||||
// Collect NodeA dominators set.
|
||||
SmallPtrSet<DomTreeNode*, 16> NodeADoms;
|
||||
NodeADoms.insert(NodeA);
|
||||
DomTreeNode *IDomA = NodeA->getIDom();
|
||||
while (IDomA) {
|
||||
NodeADoms.insert(IDomA);
|
||||
IDomA = IDomA->getIDom();
|
||||
}
|
||||
|
||||
// Walk NodeB immediate dominators chain and find common dominator node.
|
||||
DomTreeNode *IDomB = NodeB->getIDom();
|
||||
while(IDomB) {
|
||||
if (NodeADoms.count(IDomB) != 0)
|
||||
return IDomB->getBlock();
|
||||
|
||||
IDomB = IDomB->getIDom();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static std::ostream &operator<<(std::ostream &o, const DomTreeNode *Node) {
|
||||
if (Node->getBlock())
|
||||
WriteAsOperand(o, Node->getBlock(), false);
|
||||
else
|
||||
o << " <<exit node>>";
|
||||
|
||||
o << " {" << Node->getDFSNumIn() << "," << Node->getDFSNumOut() << "}";
|
||||
|
||||
return o << "\n";
|
||||
}
|
||||
|
||||
static void PrintDomTree(const DomTreeNode *N, std::ostream &o,
|
||||
unsigned Lev) {
|
||||
o << std::string(2*Lev, ' ') << "[" << Lev << "] " << N;
|
||||
for (DomTreeNode::const_iterator I = N->begin(), E = N->end();
|
||||
I != E; ++I)
|
||||
PrintDomTree(*I, o, Lev+1);
|
||||
}
|
||||
|
||||
/// eraseNode - Removes a node from the domiantor tree. Block must not
|
||||
/// domiante any other blocks. Removes node from its immediate dominator's
|
||||
/// children list. Deletes dominator node associated with basic block BB.
|
||||
void DominatorTreeBase::eraseNode(BasicBlock *BB) {
|
||||
DomTreeNode *Node = getNode(BB);
|
||||
assert (Node && "Removing node that isn't in dominator tree.");
|
||||
assert (Node->getChildren().empty() && "Node is not a leaf node.");
|
||||
|
||||
// Remove node from immediate dominator's children list.
|
||||
DomTreeNode *IDom = Node->getIDom();
|
||||
if (IDom) {
|
||||
std::vector<DomTreeNode*>::iterator I =
|
||||
std::find(IDom->Children.begin(), IDom->Children.end(), Node);
|
||||
assert(I != IDom->Children.end() &&
|
||||
"Not in immediate dominator children set!");
|
||||
// I am no longer your child...
|
||||
IDom->Children.erase(I);
|
||||
}
|
||||
|
||||
DomTreeNodes.erase(BB);
|
||||
delete Node;
|
||||
}
|
||||
|
||||
void DominatorTreeBase::print(std::ostream &o, const Module* ) const {
|
||||
o << "=============================--------------------------------\n";
|
||||
o << "Inorder Dominator Tree: ";
|
||||
if (DFSInfoValid)
|
||||
o << "DFSNumbers invalid: " << SlowQueries << " slow queries.";
|
||||
o << "\n";
|
||||
|
||||
PrintDomTree(getRootNode(), o, 1);
|
||||
}
|
||||
|
||||
void DominatorTreeBase::dump() {
|
||||
print(llvm::cerr);
|
||||
}
|
||||
|
||||
bool DominatorTree::runOnFunction(Function &F) {
|
||||
reset(); // Reset from the last time we were run...
|
||||
|
||||
@ -341,7 +147,7 @@ bool DominatorTree::runOnFunction(Function &F) {
|
||||
DomTreeNodes[&F.getEntryBlock()] = 0;
|
||||
Vertex.push_back(0);
|
||||
|
||||
Calculate<BasicBlock*>(*this, F);
|
||||
Calculate<BasicBlock*, GraphTraits<BasicBlock*> >(*this, F);
|
||||
|
||||
updateDFSNumbers();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user