mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-15 07:34:33 +00:00
move a bunch of predicates up into their own section
in this file, no other changes. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@52303 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
e7275794d3
commit
defa1c8034
@ -32,110 +32,48 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
namespace {
|
//===----------------------------------------------------------------------===//
|
||||||
/// NoAA - This class implements the -no-aa pass, which always returns "I
|
// Useful predicates
|
||||||
/// don't know" for alias queries. NoAA is unlike other alias analysis
|
//===----------------------------------------------------------------------===//
|
||||||
/// implementations, in that it does not chain to a previous analysis. As
|
|
||||||
/// such it doesn't follow many of the rules that other alias analyses must.
|
|
||||||
///
|
|
||||||
struct VISIBILITY_HIDDEN NoAA : public ImmutablePass, public AliasAnalysis {
|
|
||||||
static char ID; // Class identification, replacement for typeinfo
|
|
||||||
NoAA() : ImmutablePass((intptr_t)&ID) {}
|
|
||||||
explicit NoAA(intptr_t PID) : ImmutablePass(PID) { }
|
|
||||||
|
|
||||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
|
// Determine if an AllocationInst instruction escapes from the function it is
|
||||||
AU.addRequired<TargetData>();
|
// contained in. If it does not escape, there is no way for another function to
|
||||||
|
// mod/ref it. We do this by looking at its uses and determining if the uses
|
||||||
|
// can escape (recursively).
|
||||||
|
static bool AddressMightEscape(const Value *V) {
|
||||||
|
for (Value::use_const_iterator UI = V->use_begin(), E = V->use_end();
|
||||||
|
UI != E; ++UI) {
|
||||||
|
const Instruction *I = cast<Instruction>(*UI);
|
||||||
|
switch (I->getOpcode()) {
|
||||||
|
case Instruction::Load:
|
||||||
|
break; //next use.
|
||||||
|
case Instruction::Store:
|
||||||
|
if (I->getOperand(0) == V)
|
||||||
|
return true; // Escapes if the pointer is stored.
|
||||||
|
break; // next use.
|
||||||
|
case Instruction::GetElementPtr:
|
||||||
|
if (AddressMightEscape(I))
|
||||||
|
return true;
|
||||||
|
break; // next use.
|
||||||
|
case Instruction::BitCast:
|
||||||
|
if (AddressMightEscape(I))
|
||||||
|
return true;
|
||||||
|
break; // next use
|
||||||
|
case Instruction::Ret:
|
||||||
|
// If returned, the address will escape to calling functions, but no
|
||||||
|
// callees could modify it.
|
||||||
|
break; // next use
|
||||||
|
case Instruction::Call:
|
||||||
|
// If the call is to a few known safe intrinsics, we know that it does
|
||||||
|
// not escape
|
||||||
|
if (!isa<MemIntrinsic>(I))
|
||||||
|
return true;
|
||||||
|
break; // next use
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
virtual void initializePass() {
|
return false;
|
||||||
TD = &getAnalysis<TargetData>();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual AliasResult alias(const Value *V1, unsigned V1Size,
|
|
||||||
const Value *V2, unsigned V2Size) {
|
|
||||||
return MayAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ModRefBehavior getModRefBehavior(Function *F, CallSite CS,
|
|
||||||
std::vector<PointerAccessInfo> *Info) {
|
|
||||||
return UnknownModRefBehavior;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void getArgumentAccesses(Function *F, CallSite CS,
|
|
||||||
std::vector<PointerAccessInfo> &Info) {
|
|
||||||
assert(0 && "This method may not be called on this function!");
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void getMustAliases(Value *P, std::vector<Value*> &RetVals) { }
|
|
||||||
virtual bool pointsToConstantMemory(const Value *P) { return false; }
|
|
||||||
virtual ModRefResult getModRefInfo(CallSite CS, Value *P, unsigned Size) {
|
|
||||||
return ModRef;
|
|
||||||
}
|
|
||||||
virtual ModRefResult getModRefInfo(CallSite CS1, CallSite CS2) {
|
|
||||||
return ModRef;
|
|
||||||
}
|
|
||||||
virtual bool hasNoModRefInfoForCalls() const { return true; }
|
|
||||||
|
|
||||||
virtual void deleteValue(Value *V) {}
|
|
||||||
virtual void copyValue(Value *From, Value *To) {}
|
|
||||||
};
|
|
||||||
} // End of anonymous namespace
|
|
||||||
|
|
||||||
// Register this pass...
|
|
||||||
char NoAA::ID = 0;
|
|
||||||
static RegisterPass<NoAA>
|
|
||||||
U("no-aa", "No Alias Analysis (always returns 'may' alias)", true, true);
|
|
||||||
|
|
||||||
// Declare that we implement the AliasAnalysis interface
|
|
||||||
static RegisterAnalysisGroup<AliasAnalysis> V(U);
|
|
||||||
|
|
||||||
ImmutablePass *llvm::createNoAAPass() { return new NoAA(); }
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
/// BasicAliasAnalysis - This is the default alias analysis implementation.
|
|
||||||
/// Because it doesn't chain to a previous alias analysis (like -no-aa), it
|
|
||||||
/// derives from the NoAA class.
|
|
||||||
struct VISIBILITY_HIDDEN BasicAliasAnalysis : public NoAA {
|
|
||||||
static char ID; // Class identification, replacement for typeinfo
|
|
||||||
BasicAliasAnalysis() : NoAA((intptr_t)&ID) { }
|
|
||||||
AliasResult alias(const Value *V1, unsigned V1Size,
|
|
||||||
const Value *V2, unsigned V2Size);
|
|
||||||
|
|
||||||
ModRefResult getModRefInfo(CallSite CS, Value *P, unsigned Size);
|
|
||||||
ModRefResult getModRefInfo(CallSite CS1, CallSite CS2) {
|
|
||||||
return NoAA::getModRefInfo(CS1,CS2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// hasNoModRefInfoForCalls - We can provide mod/ref information against
|
|
||||||
/// non-escaping allocations.
|
|
||||||
virtual bool hasNoModRefInfoForCalls() const { return false; }
|
|
||||||
|
|
||||||
/// pointsToConstantMemory - Chase pointers until we find a (constant
|
|
||||||
/// global) or not.
|
|
||||||
bool pointsToConstantMemory(const Value *P);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// CheckGEPInstructions - Check two GEP instructions with known
|
|
||||||
// must-aliasing base pointers. This checks to see if the index expressions
|
|
||||||
// preclude the pointers from aliasing...
|
|
||||||
AliasResult
|
|
||||||
CheckGEPInstructions(const Type* BasePtr1Ty,
|
|
||||||
Value **GEP1Ops, unsigned NumGEP1Ops, unsigned G1Size,
|
|
||||||
const Type *BasePtr2Ty,
|
|
||||||
Value **GEP2Ops, unsigned NumGEP2Ops, unsigned G2Size);
|
|
||||||
};
|
|
||||||
} // End of anonymous namespace
|
|
||||||
|
|
||||||
// Register this pass...
|
|
||||||
char BasicAliasAnalysis::ID = 0;
|
|
||||||
static RegisterPass<BasicAliasAnalysis>
|
|
||||||
X("basicaa", "Basic Alias Analysis (default AA impl)", false, true);
|
|
||||||
|
|
||||||
// Declare that we implement the AliasAnalysis interface
|
|
||||||
static RegisterAnalysisGroup<AliasAnalysis, true> Y(X);
|
|
||||||
|
|
||||||
ImmutablePass *llvm::createBasicAliasAnalysisPass() {
|
|
||||||
return new BasicAliasAnalysis();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// getUnderlyingObject - This traverses the use chain to figure out what object
|
/// getUnderlyingObject - This traverses the use chain to figure out what object
|
||||||
@ -190,103 +128,6 @@ static const Value *GetGEPOperands(const Value *V,
|
|||||||
return V;
|
return V;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// pointsToConstantMemory - Chase pointers until we find a (constant
|
|
||||||
/// global) or not.
|
|
||||||
bool BasicAliasAnalysis::pointsToConstantMemory(const Value *P) {
|
|
||||||
if (const GlobalVariable *GV =
|
|
||||||
dyn_cast<GlobalVariable>(getUnderlyingObject(P)))
|
|
||||||
return GV->isConstant();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if an AllocationInst instruction escapes from the function it is
|
|
||||||
// contained in. If it does not escape, there is no way for another function to
|
|
||||||
// mod/ref it. We do this by looking at its uses and determining if the uses
|
|
||||||
// can escape (recursively).
|
|
||||||
static bool AddressMightEscape(const Value *V) {
|
|
||||||
for (Value::use_const_iterator UI = V->use_begin(), E = V->use_end();
|
|
||||||
UI != E; ++UI) {
|
|
||||||
const Instruction *I = cast<Instruction>(*UI);
|
|
||||||
switch (I->getOpcode()) {
|
|
||||||
case Instruction::Load:
|
|
||||||
break; //next use.
|
|
||||||
case Instruction::Store:
|
|
||||||
if (I->getOperand(0) == V)
|
|
||||||
return true; // Escapes if the pointer is stored.
|
|
||||||
break; // next use.
|
|
||||||
case Instruction::GetElementPtr:
|
|
||||||
if (AddressMightEscape(I))
|
|
||||||
return true;
|
|
||||||
break; // next use.
|
|
||||||
case Instruction::BitCast:
|
|
||||||
if (AddressMightEscape(I))
|
|
||||||
return true;
|
|
||||||
break; // next use
|
|
||||||
case Instruction::Ret:
|
|
||||||
// If returned, the address will escape to calling functions, but no
|
|
||||||
// callees could modify it.
|
|
||||||
break; // next use
|
|
||||||
case Instruction::Call:
|
|
||||||
// If the call is to a few known safe intrinsics, we know that it does
|
|
||||||
// not escape
|
|
||||||
if (!isa<MemIntrinsic>(I))
|
|
||||||
return true;
|
|
||||||
break; // next use
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// getModRefInfo - Check to see if the specified callsite can clobber the
|
|
||||||
// specified memory object. Since we only look at local properties of this
|
|
||||||
// function, we really can't say much about this query. We do, however, use
|
|
||||||
// simple "address taken" analysis on local objects.
|
|
||||||
//
|
|
||||||
AliasAnalysis::ModRefResult
|
|
||||||
BasicAliasAnalysis::getModRefInfo(CallSite CS, Value *P, unsigned Size) {
|
|
||||||
if (!isa<Constant>(P)) {
|
|
||||||
const Value *Object = getUnderlyingObject(P);
|
|
||||||
|
|
||||||
// If this is a tail call and P points to a stack location, we know that
|
|
||||||
// the tail call cannot access or modify the local stack.
|
|
||||||
// We cannot exclude byval arguments here; these belong to the caller of
|
|
||||||
// the current function not to the current function, and a tail callee
|
|
||||||
// may reference them.
|
|
||||||
if (isa<AllocaInst>(Object))
|
|
||||||
if (CallInst *CI = dyn_cast<CallInst>(CS.getInstruction()))
|
|
||||||
if (CI->isTailCall())
|
|
||||||
return NoModRef;
|
|
||||||
|
|
||||||
// Allocations and byval arguments are "new" objects.
|
|
||||||
if (isa<AllocationInst>(Object) || isa<Argument>(Object)) {
|
|
||||||
// Okay, the pointer is to a stack allocated (or effectively so, for
|
|
||||||
// for noalias parameters) object. If the address of this object doesn't
|
|
||||||
// escape from this function body to a callee, then we know that no
|
|
||||||
// callees can mod/ref it unless they are actually passed it.
|
|
||||||
if (isa<AllocationInst>(Object) ||
|
|
||||||
cast<Argument>(Object)->hasByValAttr() ||
|
|
||||||
cast<Argument>(Object)->hasNoAliasAttr())
|
|
||||||
if (!AddressMightEscape(Object)) {
|
|
||||||
bool passedAsArg = false;
|
|
||||||
for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end();
|
|
||||||
CI != CE; ++CI)
|
|
||||||
if (isa<PointerType>((*CI)->getType()) &&
|
|
||||||
(getUnderlyingObject(*CI) == P ||
|
|
||||||
alias(cast<Value>(CI), ~0U, P, ~0U) != NoAlias))
|
|
||||||
passedAsArg = true;
|
|
||||||
|
|
||||||
if (!passedAsArg)
|
|
||||||
return NoModRef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The AliasAnalysis base class has some smarts, lets use them.
|
|
||||||
return AliasAnalysis::getModRefInfo(CS, P, Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// isIdentifiedObject - Return true if this pointer refers to a distinct and
|
/// isIdentifiedObject - Return true if this pointer refers to a distinct and
|
||||||
/// identifiable object. This returns true for:
|
/// identifiable object. This returns true for:
|
||||||
/// Global Variables and Functions
|
/// Global Variables and Functions
|
||||||
@ -354,6 +195,179 @@ static bool isObjectSmallerThan(const Value *V, unsigned Size,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// NoAA Pass
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/// NoAA - This class implements the -no-aa pass, which always returns "I
|
||||||
|
/// don't know" for alias queries. NoAA is unlike other alias analysis
|
||||||
|
/// implementations, in that it does not chain to a previous analysis. As
|
||||||
|
/// such it doesn't follow many of the rules that other alias analyses must.
|
||||||
|
///
|
||||||
|
struct VISIBILITY_HIDDEN NoAA : public ImmutablePass, public AliasAnalysis {
|
||||||
|
static char ID; // Class identification, replacement for typeinfo
|
||||||
|
NoAA() : ImmutablePass((intptr_t)&ID) {}
|
||||||
|
explicit NoAA(intptr_t PID) : ImmutablePass(PID) { }
|
||||||
|
|
||||||
|
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
|
||||||
|
AU.addRequired<TargetData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void initializePass() {
|
||||||
|
TD = &getAnalysis<TargetData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual AliasResult alias(const Value *V1, unsigned V1Size,
|
||||||
|
const Value *V2, unsigned V2Size) {
|
||||||
|
return MayAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ModRefBehavior getModRefBehavior(Function *F, CallSite CS,
|
||||||
|
std::vector<PointerAccessInfo> *Info) {
|
||||||
|
return UnknownModRefBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void getArgumentAccesses(Function *F, CallSite CS,
|
||||||
|
std::vector<PointerAccessInfo> &Info) {
|
||||||
|
assert(0 && "This method may not be called on this function!");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void getMustAliases(Value *P, std::vector<Value*> &RetVals) { }
|
||||||
|
virtual bool pointsToConstantMemory(const Value *P) { return false; }
|
||||||
|
virtual ModRefResult getModRefInfo(CallSite CS, Value *P, unsigned Size) {
|
||||||
|
return ModRef;
|
||||||
|
}
|
||||||
|
virtual ModRefResult getModRefInfo(CallSite CS1, CallSite CS2) {
|
||||||
|
return ModRef;
|
||||||
|
}
|
||||||
|
virtual bool hasNoModRefInfoForCalls() const { return true; }
|
||||||
|
|
||||||
|
virtual void deleteValue(Value *V) {}
|
||||||
|
virtual void copyValue(Value *From, Value *To) {}
|
||||||
|
};
|
||||||
|
} // End of anonymous namespace
|
||||||
|
|
||||||
|
// Register this pass...
|
||||||
|
char NoAA::ID = 0;
|
||||||
|
static RegisterPass<NoAA>
|
||||||
|
U("no-aa", "No Alias Analysis (always returns 'may' alias)", true, true);
|
||||||
|
|
||||||
|
// Declare that we implement the AliasAnalysis interface
|
||||||
|
static RegisterAnalysisGroup<AliasAnalysis> V(U);
|
||||||
|
|
||||||
|
ImmutablePass *llvm::createNoAAPass() { return new NoAA(); }
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// BasicAA Pass
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
/// BasicAliasAnalysis - This is the default alias analysis implementation.
|
||||||
|
/// Because it doesn't chain to a previous alias analysis (like -no-aa), it
|
||||||
|
/// derives from the NoAA class.
|
||||||
|
struct VISIBILITY_HIDDEN BasicAliasAnalysis : public NoAA {
|
||||||
|
static char ID; // Class identification, replacement for typeinfo
|
||||||
|
BasicAliasAnalysis() : NoAA((intptr_t)&ID) { }
|
||||||
|
AliasResult alias(const Value *V1, unsigned V1Size,
|
||||||
|
const Value *V2, unsigned V2Size);
|
||||||
|
|
||||||
|
ModRefResult getModRefInfo(CallSite CS, Value *P, unsigned Size);
|
||||||
|
ModRefResult getModRefInfo(CallSite CS1, CallSite CS2) {
|
||||||
|
return NoAA::getModRefInfo(CS1,CS2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// hasNoModRefInfoForCalls - We can provide mod/ref information against
|
||||||
|
/// non-escaping allocations.
|
||||||
|
virtual bool hasNoModRefInfoForCalls() const { return false; }
|
||||||
|
|
||||||
|
/// pointsToConstantMemory - Chase pointers until we find a (constant
|
||||||
|
/// global) or not.
|
||||||
|
bool pointsToConstantMemory(const Value *P);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// CheckGEPInstructions - Check two GEP instructions with known
|
||||||
|
// must-aliasing base pointers. This checks to see if the index expressions
|
||||||
|
// preclude the pointers from aliasing...
|
||||||
|
AliasResult
|
||||||
|
CheckGEPInstructions(const Type* BasePtr1Ty,
|
||||||
|
Value **GEP1Ops, unsigned NumGEP1Ops, unsigned G1Size,
|
||||||
|
const Type *BasePtr2Ty,
|
||||||
|
Value **GEP2Ops, unsigned NumGEP2Ops, unsigned G2Size);
|
||||||
|
};
|
||||||
|
} // End of anonymous namespace
|
||||||
|
|
||||||
|
// Register this pass...
|
||||||
|
char BasicAliasAnalysis::ID = 0;
|
||||||
|
static RegisterPass<BasicAliasAnalysis>
|
||||||
|
X("basicaa", "Basic Alias Analysis (default AA impl)", false, true);
|
||||||
|
|
||||||
|
// Declare that we implement the AliasAnalysis interface
|
||||||
|
static RegisterAnalysisGroup<AliasAnalysis, true> Y(X);
|
||||||
|
|
||||||
|
ImmutablePass *llvm::createBasicAliasAnalysisPass() {
|
||||||
|
return new BasicAliasAnalysis();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// pointsToConstantMemory - Chase pointers until we find a (constant
|
||||||
|
/// global) or not.
|
||||||
|
bool BasicAliasAnalysis::pointsToConstantMemory(const Value *P) {
|
||||||
|
if (const GlobalVariable *GV =
|
||||||
|
dyn_cast<GlobalVariable>(getUnderlyingObject(P)))
|
||||||
|
return GV->isConstant();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getModRefInfo - Check to see if the specified callsite can clobber the
|
||||||
|
// specified memory object. Since we only look at local properties of this
|
||||||
|
// function, we really can't say much about this query. We do, however, use
|
||||||
|
// simple "address taken" analysis on local objects.
|
||||||
|
//
|
||||||
|
AliasAnalysis::ModRefResult
|
||||||
|
BasicAliasAnalysis::getModRefInfo(CallSite CS, Value *P, unsigned Size) {
|
||||||
|
if (!isa<Constant>(P)) {
|
||||||
|
const Value *Object = getUnderlyingObject(P);
|
||||||
|
|
||||||
|
// If this is a tail call and P points to a stack location, we know that
|
||||||
|
// the tail call cannot access or modify the local stack.
|
||||||
|
// We cannot exclude byval arguments here; these belong to the caller of
|
||||||
|
// the current function not to the current function, and a tail callee
|
||||||
|
// may reference them.
|
||||||
|
if (isa<AllocaInst>(Object))
|
||||||
|
if (CallInst *CI = dyn_cast<CallInst>(CS.getInstruction()))
|
||||||
|
if (CI->isTailCall())
|
||||||
|
return NoModRef;
|
||||||
|
|
||||||
|
// Allocations and byval arguments are "new" objects.
|
||||||
|
if (isa<AllocationInst>(Object) || isa<Argument>(Object)) {
|
||||||
|
// Okay, the pointer is to a stack allocated (or effectively so, for
|
||||||
|
// for noalias parameters) object. If the address of this object doesn't
|
||||||
|
// escape from this function body to a callee, then we know that no
|
||||||
|
// callees can mod/ref it unless they are actually passed it.
|
||||||
|
if (isa<AllocationInst>(Object) ||
|
||||||
|
cast<Argument>(Object)->hasByValAttr() ||
|
||||||
|
cast<Argument>(Object)->hasNoAliasAttr())
|
||||||
|
if (!AddressMightEscape(Object)) {
|
||||||
|
bool passedAsArg = false;
|
||||||
|
for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end();
|
||||||
|
CI != CE; ++CI)
|
||||||
|
if (isa<PointerType>((*CI)->getType()) &&
|
||||||
|
(getUnderlyingObject(*CI) == P ||
|
||||||
|
alias(cast<Value>(CI), ~0U, P, ~0U) != NoAlias))
|
||||||
|
passedAsArg = true;
|
||||||
|
|
||||||
|
if (!passedAsArg)
|
||||||
|
return NoModRef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AliasAnalysis base class has some smarts, lets use them.
|
||||||
|
return AliasAnalysis::getModRefInfo(CS, P, Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// alias - Provide a bunch of ad-hoc rules to disambiguate in common cases, such
|
// alias - Provide a bunch of ad-hoc rules to disambiguate in common cases, such
|
||||||
// as array references. Note that this function is heavily tail recursive.
|
// as array references. Note that this function is heavily tail recursive.
|
||||||
// Hopefully we have a smart C++ compiler. :)
|
// Hopefully we have a smart C++ compiler. :)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user