mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-19 04:32:19 +00:00
All DSGraphs keep a reference to the targetdata they are created with. This is
used to eliminate the hard coded, hacked in, sparc specific, global TargetData. Changing the TargetData used to actually match the code fixes problems, and eliminates a crash. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@9659 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
dbfe36e51e
commit
15869aa2c7
@ -62,12 +62,19 @@ private:
|
|||||||
//
|
//
|
||||||
GlobalSetTy InlinedGlobals;
|
GlobalSetTy InlinedGlobals;
|
||||||
|
|
||||||
|
/// TD - This is the target data object for the machine this graph is
|
||||||
|
/// constructed for.
|
||||||
|
const TargetData &TD;
|
||||||
|
|
||||||
void operator=(const DSGraph &); // DO NOT IMPLEMENT
|
void operator=(const DSGraph &); // DO NOT IMPLEMENT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Create a new, empty, DSGraph.
|
// Create a new, empty, DSGraph.
|
||||||
DSGraph() : GlobalsGraph(0), PrintAuxCalls(false) {}
|
DSGraph(const TargetData &td)
|
||||||
DSGraph(Function &F, DSGraph *GlobalsGraph); // Compute the local DSGraph
|
: GlobalsGraph(0), PrintAuxCalls(false), TD(td) {}
|
||||||
|
|
||||||
|
// Compute the local DSGraph
|
||||||
|
DSGraph(const TargetData &td, Function &F, DSGraph *GlobalsGraph);
|
||||||
|
|
||||||
// Copy ctor - If you want to capture the node mapping between the source and
|
// Copy ctor - If you want to capture the node mapping between the source and
|
||||||
// destination graph, you may optionally do this by specifying a map to record
|
// destination graph, you may optionally do this by specifying a map to record
|
||||||
@ -84,9 +91,13 @@ public:
|
|||||||
DSGraph *getGlobalsGraph() const { return GlobalsGraph; }
|
DSGraph *getGlobalsGraph() const { return GlobalsGraph; }
|
||||||
void setGlobalsGraph(DSGraph *G) { GlobalsGraph = G; }
|
void setGlobalsGraph(DSGraph *G) { GlobalsGraph = G; }
|
||||||
|
|
||||||
// setPrintAuxCalls - If you call this method, the auxillary call vector will
|
/// getTargetData - Return the TargetData object for the current target.
|
||||||
// be printed instead of the standard call vector to the dot file.
|
///
|
||||||
//
|
const TargetData &getTargetData() const { return TD; }
|
||||||
|
|
||||||
|
/// setPrintAuxCalls - If you call this method, the auxillary call vector will
|
||||||
|
/// be printed instead of the standard call vector to the dot file.
|
||||||
|
///
|
||||||
void setPrintAuxCalls() { PrintAuxCalls = true; }
|
void setPrintAuxCalls() { PrintAuxCalls = true; }
|
||||||
bool shouldPrintAuxCalls() const { return PrintAuxCalls; }
|
bool shouldPrintAuxCalls() const { return PrintAuxCalls; }
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
template<typename BaseType>
|
template<typename BaseType>
|
||||||
class DSNodeIterator; // Data structure graph traversal iterator
|
class DSNodeIterator; // Data structure graph traversal iterator
|
||||||
|
class TargetData;
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
/// DSNode - Data structure node class
|
/// DSNode - Data structure node class
|
||||||
@ -134,6 +135,11 @@ public:
|
|||||||
DSGraph *getParentGraph() const { return ParentGraph; }
|
DSGraph *getParentGraph() const { return ParentGraph; }
|
||||||
void setParentGraph(DSGraph *G) { ParentGraph = G; }
|
void setParentGraph(DSGraph *G) { ParentGraph = G; }
|
||||||
|
|
||||||
|
|
||||||
|
/// getTargetData - Get the target data object used to construct this node.
|
||||||
|
///
|
||||||
|
const TargetData &getTargetData() const;
|
||||||
|
|
||||||
/// getForwardNode - This method returns the node that this node is forwarded
|
/// getForwardNode - This method returns the node that this node is forwarded
|
||||||
/// to, if any.
|
/// to, if any.
|
||||||
DSNode *getForwardNode() const { return ForwardNH.getNode(); }
|
DSNode *getForwardNode() const { return ForwardNH.getNode(); }
|
||||||
|
@ -28,7 +28,7 @@ class DSNode; // Each node in the graph
|
|||||||
class DSGraph; // A graph for a function
|
class DSGraph; // A graph for a function
|
||||||
|
|
||||||
namespace DS { // FIXME: After the paper, this should get cleaned up
|
namespace DS { // FIXME: After the paper, this should get cleaned up
|
||||||
enum { PointerShift = 3, // 64bit ptrs = 3, 32 bit ptrs = 2
|
enum { PointerShift = 2, // 64bit ptrs = 3, 32 bit ptrs = 2
|
||||||
PointerSize = 1 << PointerShift
|
PointerSize = 1 << PointerShift
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#define LLVM_ANALYSIS_DATA_STRUCTURE_H
|
#define LLVM_ANALYSIS_DATA_STRUCTURE_H
|
||||||
|
|
||||||
#include "llvm/Pass.h"
|
#include "llvm/Pass.h"
|
||||||
|
#include "llvm/Target/TargetData.h"
|
||||||
#include "Support/hash_set"
|
#include "Support/hash_set"
|
||||||
|
|
||||||
class Type;
|
class Type;
|
||||||
@ -69,6 +70,7 @@ public:
|
|||||||
// getAnalysisUsage - This obviously provides a data structure graph.
|
// getAnalysisUsage - This obviously provides a data structure graph.
|
||||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
|
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
|
||||||
AU.setPreservesAll();
|
AU.setPreservesAll();
|
||||||
|
AU.addRequired<TargetData>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -62,12 +62,19 @@ private:
|
|||||||
//
|
//
|
||||||
GlobalSetTy InlinedGlobals;
|
GlobalSetTy InlinedGlobals;
|
||||||
|
|
||||||
|
/// TD - This is the target data object for the machine this graph is
|
||||||
|
/// constructed for.
|
||||||
|
const TargetData &TD;
|
||||||
|
|
||||||
void operator=(const DSGraph &); // DO NOT IMPLEMENT
|
void operator=(const DSGraph &); // DO NOT IMPLEMENT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Create a new, empty, DSGraph.
|
// Create a new, empty, DSGraph.
|
||||||
DSGraph() : GlobalsGraph(0), PrintAuxCalls(false) {}
|
DSGraph(const TargetData &td)
|
||||||
DSGraph(Function &F, DSGraph *GlobalsGraph); // Compute the local DSGraph
|
: GlobalsGraph(0), PrintAuxCalls(false), TD(td) {}
|
||||||
|
|
||||||
|
// Compute the local DSGraph
|
||||||
|
DSGraph(const TargetData &td, Function &F, DSGraph *GlobalsGraph);
|
||||||
|
|
||||||
// Copy ctor - If you want to capture the node mapping between the source and
|
// Copy ctor - If you want to capture the node mapping between the source and
|
||||||
// destination graph, you may optionally do this by specifying a map to record
|
// destination graph, you may optionally do this by specifying a map to record
|
||||||
@ -84,9 +91,13 @@ public:
|
|||||||
DSGraph *getGlobalsGraph() const { return GlobalsGraph; }
|
DSGraph *getGlobalsGraph() const { return GlobalsGraph; }
|
||||||
void setGlobalsGraph(DSGraph *G) { GlobalsGraph = G; }
|
void setGlobalsGraph(DSGraph *G) { GlobalsGraph = G; }
|
||||||
|
|
||||||
// setPrintAuxCalls - If you call this method, the auxillary call vector will
|
/// getTargetData - Return the TargetData object for the current target.
|
||||||
// be printed instead of the standard call vector to the dot file.
|
///
|
||||||
//
|
const TargetData &getTargetData() const { return TD; }
|
||||||
|
|
||||||
|
/// setPrintAuxCalls - If you call this method, the auxillary call vector will
|
||||||
|
/// be printed instead of the standard call vector to the dot file.
|
||||||
|
///
|
||||||
void setPrintAuxCalls() { PrintAuxCalls = true; }
|
void setPrintAuxCalls() { PrintAuxCalls = true; }
|
||||||
bool shouldPrintAuxCalls() const { return PrintAuxCalls; }
|
bool shouldPrintAuxCalls() const { return PrintAuxCalls; }
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
template<typename BaseType>
|
template<typename BaseType>
|
||||||
class DSNodeIterator; // Data structure graph traversal iterator
|
class DSNodeIterator; // Data structure graph traversal iterator
|
||||||
|
class TargetData;
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
/// DSNode - Data structure node class
|
/// DSNode - Data structure node class
|
||||||
@ -134,6 +135,11 @@ public:
|
|||||||
DSGraph *getParentGraph() const { return ParentGraph; }
|
DSGraph *getParentGraph() const { return ParentGraph; }
|
||||||
void setParentGraph(DSGraph *G) { ParentGraph = G; }
|
void setParentGraph(DSGraph *G) { ParentGraph = G; }
|
||||||
|
|
||||||
|
|
||||||
|
/// getTargetData - Get the target data object used to construct this node.
|
||||||
|
///
|
||||||
|
const TargetData &getTargetData() const;
|
||||||
|
|
||||||
/// getForwardNode - This method returns the node that this node is forwarded
|
/// getForwardNode - This method returns the node that this node is forwarded
|
||||||
/// to, if any.
|
/// to, if any.
|
||||||
DSNode *getForwardNode() const { return ForwardNH.getNode(); }
|
DSNode *getForwardNode() const { return ForwardNH.getNode(); }
|
||||||
|
@ -28,7 +28,7 @@ class DSNode; // Each node in the graph
|
|||||||
class DSGraph; // A graph for a function
|
class DSGraph; // A graph for a function
|
||||||
|
|
||||||
namespace DS { // FIXME: After the paper, this should get cleaned up
|
namespace DS { // FIXME: After the paper, this should get cleaned up
|
||||||
enum { PointerShift = 3, // 64bit ptrs = 3, 32 bit ptrs = 2
|
enum { PointerShift = 2, // 64bit ptrs = 3, 32 bit ptrs = 2
|
||||||
PointerSize = 1 << PointerShift
|
PointerSize = 1 << PointerShift
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#define LLVM_ANALYSIS_DATA_STRUCTURE_H
|
#define LLVM_ANALYSIS_DATA_STRUCTURE_H
|
||||||
|
|
||||||
#include "llvm/Pass.h"
|
#include "llvm/Pass.h"
|
||||||
|
#include "llvm/Target/TargetData.h"
|
||||||
#include "Support/hash_set"
|
#include "Support/hash_set"
|
||||||
|
|
||||||
class Type;
|
class Type;
|
||||||
@ -69,6 +70,7 @@ public:
|
|||||||
// getAnalysisUsage - This obviously provides a data structure graph.
|
// getAnalysisUsage - This obviously provides a data structure graph.
|
||||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
|
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
|
||||||
AU.setPreservesAll();
|
AU.setPreservesAll();
|
||||||
|
AU.addRequired<TargetData>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,9 +28,6 @@ namespace {
|
|||||||
Statistic<> NumCallNodesMerged("dsnode", "Number of call nodes merged");
|
Statistic<> NumCallNodesMerged("dsnode", "Number of call nodes merged");
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace DS { // TODO: FIXME
|
|
||||||
extern TargetData TD;
|
|
||||||
}
|
|
||||||
using namespace DS;
|
using namespace DS;
|
||||||
|
|
||||||
DSNode *DSNodeHandle::HandleForwarding() const {
|
DSNode *DSNodeHandle::HandleForwarding() const {
|
||||||
@ -72,6 +69,12 @@ DSNode::DSNode(const DSNode &N, DSGraph *G)
|
|||||||
G->getNodes().push_back(this);
|
G->getNodes().push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// getTargetData - Get the target data object used to construct this node.
|
||||||
|
///
|
||||||
|
const TargetData &DSNode::getTargetData() const {
|
||||||
|
return ParentGraph->getTargetData();
|
||||||
|
}
|
||||||
|
|
||||||
void DSNode::assertOK() const {
|
void DSNode::assertOK() const {
|
||||||
assert((Ty != Type::VoidTy ||
|
assert((Ty != Type::VoidTy ||
|
||||||
Ty == Type::VoidTy && (Size == 0 ||
|
Ty == Type::VoidTy && (Size == 0 ||
|
||||||
@ -172,8 +175,9 @@ namespace {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::vector<StackState> Stack;
|
std::vector<StackState> Stack;
|
||||||
|
const TargetData &TD;
|
||||||
public:
|
public:
|
||||||
TypeElementWalker(const Type *T) {
|
TypeElementWalker(const Type *T, const TargetData &td) : TD(td) {
|
||||||
Stack.push_back(T);
|
Stack.push_back(T);
|
||||||
StepToLeaf();
|
StepToLeaf();
|
||||||
}
|
}
|
||||||
@ -256,8 +260,8 @@ namespace {
|
|||||||
/// is true, then we also allow a larger T1.
|
/// is true, then we also allow a larger T1.
|
||||||
///
|
///
|
||||||
static bool ElementTypesAreCompatible(const Type *T1, const Type *T2,
|
static bool ElementTypesAreCompatible(const Type *T1, const Type *T2,
|
||||||
bool AllowLargerT1) {
|
bool AllowLargerT1, const TargetData &TD){
|
||||||
TypeElementWalker T1W(T1), T2W(T2);
|
TypeElementWalker T1W(T1, TD), T2W(T2, TD);
|
||||||
|
|
||||||
while (!T1W.isDone() && !T2W.isDone()) {
|
while (!T1W.isDone() && !T2W.isDone()) {
|
||||||
if (T1W.getCurrentOffset() != T2W.getCurrentOffset())
|
if (T1W.getCurrentOffset() != T2W.getCurrentOffset())
|
||||||
@ -286,6 +290,7 @@ static bool ElementTypesAreCompatible(const Type *T1, const Type *T2,
|
|||||||
///
|
///
|
||||||
bool DSNode::mergeTypeInfo(const Type *NewTy, unsigned Offset,
|
bool DSNode::mergeTypeInfo(const Type *NewTy, unsigned Offset,
|
||||||
bool FoldIfIncompatible) {
|
bool FoldIfIncompatible) {
|
||||||
|
const TargetData &TD = getTargetData();
|
||||||
// Check to make sure the Size member is up-to-date. Size can be one of the
|
// Check to make sure the Size member is up-to-date. Size can be one of the
|
||||||
// following:
|
// following:
|
||||||
// Size = 0, Ty = Void: Nothing is known about this node.
|
// Size = 0, Ty = Void: Nothing is known about this node.
|
||||||
@ -426,7 +431,7 @@ bool DSNode::mergeTypeInfo(const Type *NewTy, unsigned Offset,
|
|||||||
// just require each element in the node to be compatible.
|
// just require each element in the node to be compatible.
|
||||||
if (NewTySize <= SubTypeSize && NewTySize && NewTySize < 256 &&
|
if (NewTySize <= SubTypeSize && NewTySize && NewTySize < 256 &&
|
||||||
SubTypeSize && SubTypeSize < 256 &&
|
SubTypeSize && SubTypeSize < 256 &&
|
||||||
ElementTypesAreCompatible(NewTy, SubType, !isArray()))
|
ElementTypesAreCompatible(NewTy, SubType, !isArray(), TD))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Okay, so we found the leader type at the offset requested. Search the list
|
// Okay, so we found the leader type at the offset requested. Search the list
|
||||||
@ -743,7 +748,7 @@ std::string DSGraph::getFunctionNames() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DSGraph::DSGraph(const DSGraph &G) : GlobalsGraph(0) {
|
DSGraph::DSGraph(const DSGraph &G) : GlobalsGraph(0), TD(G.TD) {
|
||||||
PrintAuxCalls = false;
|
PrintAuxCalls = false;
|
||||||
NodeMapTy NodeMap;
|
NodeMapTy NodeMap;
|
||||||
cloneInto(G, ScalarMap, ReturnNodes, NodeMap);
|
cloneInto(G, ScalarMap, ReturnNodes, NodeMap);
|
||||||
@ -751,7 +756,7 @@ DSGraph::DSGraph(const DSGraph &G) : GlobalsGraph(0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DSGraph::DSGraph(const DSGraph &G, NodeMapTy &NodeMap)
|
DSGraph::DSGraph(const DSGraph &G, NodeMapTy &NodeMap)
|
||||||
: GlobalsGraph(0) {
|
: GlobalsGraph(0), TD(G.TD) {
|
||||||
PrintAuxCalls = false;
|
PrintAuxCalls = false;
|
||||||
cloneInto(G, ScalarMap, ReturnNodes, NodeMap);
|
cloneInto(G, ScalarMap, ReturnNodes, NodeMap);
|
||||||
InlinedGlobals.clear(); // clear set of "up-to-date" globals
|
InlinedGlobals.clear(); // clear set of "up-to-date" globals
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
#include "llvm/Analysis/DSGraph.h"
|
#include "llvm/Analysis/DSGraph.h"
|
||||||
#include "llvm/Constants.h"
|
#include "llvm/Constants.h"
|
||||||
#include "llvm/DerivedTypes.h"
|
#include "llvm/DerivedTypes.h"
|
||||||
#include "llvm/Function.h"
|
|
||||||
#include "llvm/GlobalVariable.h"
|
|
||||||
#include "llvm/Instructions.h"
|
#include "llvm/Instructions.h"
|
||||||
#include "llvm/Support/InstVisitor.h"
|
#include "llvm/Support/InstVisitor.h"
|
||||||
#include "llvm/Target/TargetData.h"
|
#include "llvm/Target/TargetData.h"
|
||||||
@ -34,9 +32,6 @@ static RegisterAnalysis<LocalDataStructures>
|
|||||||
X("datastructure", "Local Data Structure Analysis");
|
X("datastructure", "Local Data Structure Analysis");
|
||||||
|
|
||||||
namespace DS {
|
namespace DS {
|
||||||
// FIXME: Do something smarter with target data!
|
|
||||||
TargetData TD("temp-td");
|
|
||||||
|
|
||||||
// isPointerType - Return true if this type is big enough to hold a pointer.
|
// isPointerType - Return true if this type is big enough to hold a pointer.
|
||||||
bool isPointerType(const Type *Ty) {
|
bool isPointerType(const Type *Ty) {
|
||||||
if (isa<PointerType>(Ty))
|
if (isa<PointerType>(Ty))
|
||||||
@ -152,7 +147,8 @@ namespace {
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// DSGraph constructor - Simply use the GraphBuilder to construct the local
|
// DSGraph constructor - Simply use the GraphBuilder to construct the local
|
||||||
// graph.
|
// graph.
|
||||||
DSGraph::DSGraph(Function &F, DSGraph *GG) : GlobalsGraph(GG) {
|
DSGraph::DSGraph(const TargetData &td, Function &F, DSGraph *GG)
|
||||||
|
: GlobalsGraph(GG), TD(td) {
|
||||||
PrintAuxCalls = false;
|
PrintAuxCalls = false;
|
||||||
|
|
||||||
DEBUG(std::cerr << " [Loc] Calculating graph for: " << F.getName() << "\n");
|
DEBUG(std::cerr << " [Loc] Calculating graph for: " << F.getName() << "\n");
|
||||||
@ -311,6 +307,8 @@ void GraphBuilder::visitGetElementPtrInst(User &GEP) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TargetData &TD = Value.getNode()->getTargetData();
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Handle the pointer index specially...
|
// Handle the pointer index specially...
|
||||||
if (GEP.getNumOperands() > 1 &&
|
if (GEP.getNumOperands() > 1 &&
|
||||||
@ -532,6 +530,8 @@ void GraphBuilder::MergeConstantInitIntoNode(DSNodeHandle &NH, Constant *C) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TargetData &TD = NH.getNode()->getTargetData();
|
||||||
|
|
||||||
if (ConstantArray *CA = dyn_cast<ConstantArray>(C)) {
|
if (ConstantArray *CA = dyn_cast<ConstantArray>(C)) {
|
||||||
for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i)
|
for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i)
|
||||||
// We don't currently do any indexing for arrays...
|
// We don't currently do any indexing for arrays...
|
||||||
@ -556,12 +556,14 @@ void GraphBuilder::mergeInGlobalInitializer(GlobalVariable *GV) {
|
|||||||
|
|
||||||
|
|
||||||
bool LocalDataStructures::run(Module &M) {
|
bool LocalDataStructures::run(Module &M) {
|
||||||
GlobalsGraph = new DSGraph();
|
GlobalsGraph = new DSGraph(getAnalysis<TargetData>());
|
||||||
|
|
||||||
|
const TargetData &TD = getAnalysis<TargetData>();
|
||||||
|
|
||||||
// Calculate all of the graphs...
|
// Calculate all of the graphs...
|
||||||
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
|
for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
|
||||||
if (!I->isExternal())
|
if (!I->isExternal())
|
||||||
DSInfo.insert(std::make_pair(I, new DSGraph(*I, GlobalsGraph)));
|
DSInfo.insert(std::make_pair(I, new DSGraph(TD, *I, GlobalsGraph)));
|
||||||
|
|
||||||
GraphBuilder GGB(*GlobalsGraph);
|
GraphBuilder GGB(*GlobalsGraph);
|
||||||
|
|
||||||
|
@ -110,8 +110,8 @@ bool Steens::run(Module &M) {
|
|||||||
LocalDataStructures &LDS = getAnalysis<LocalDataStructures>();
|
LocalDataStructures &LDS = getAnalysis<LocalDataStructures>();
|
||||||
|
|
||||||
// Create a new, empty, graph...
|
// Create a new, empty, graph...
|
||||||
ResultGraph = new DSGraph();
|
ResultGraph = new DSGraph(getTargetData());
|
||||||
GlobalsGraph = new DSGraph();
|
GlobalsGraph = new DSGraph(getTargetData());
|
||||||
ResultGraph->setGlobalsGraph(GlobalsGraph);
|
ResultGraph->setGlobalsGraph(GlobalsGraph);
|
||||||
ResultGraph->setPrintAuxCalls();
|
ResultGraph->setPrintAuxCalls();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user