mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-06 09:28:50 +00:00
Reland "Try to implement lambdas with inalloca parameters by forwarding without use of inallocas."t
This reverts commit 8ed7aa59f489715d39d32e72a787b8e75cfda151. Differential Revision: https://reviews.llvm.org/D154007
This commit is contained in:
parent
63458d92e5
commit
27dab4d305
@ -567,6 +567,10 @@ class CGFunctionInfo final
|
||||
/// Whether this is a chain call.
|
||||
unsigned ChainCall : 1;
|
||||
|
||||
/// Whether this function is called by forwarding arguments.
|
||||
/// This doesn't support inalloca or varargs.
|
||||
unsigned DelegateCall : 1;
|
||||
|
||||
/// Whether this function is a CMSE nonsecure call
|
||||
unsigned CmseNSCall : 1;
|
||||
|
||||
@ -616,14 +620,11 @@ class CGFunctionInfo final
|
||||
CGFunctionInfo() : Required(RequiredArgs::All) {}
|
||||
|
||||
public:
|
||||
static CGFunctionInfo *create(unsigned llvmCC,
|
||||
bool instanceMethod,
|
||||
bool chainCall,
|
||||
const FunctionType::ExtInfo &extInfo,
|
||||
ArrayRef<ExtParameterInfo> paramInfos,
|
||||
CanQualType resultType,
|
||||
ArrayRef<CanQualType> argTypes,
|
||||
RequiredArgs required);
|
||||
static CGFunctionInfo *
|
||||
create(unsigned llvmCC, bool instanceMethod, bool chainCall,
|
||||
bool delegateCall, const FunctionType::ExtInfo &extInfo,
|
||||
ArrayRef<ExtParameterInfo> paramInfos, CanQualType resultType,
|
||||
ArrayRef<CanQualType> argTypes, RequiredArgs required);
|
||||
void operator delete(void *p) { ::operator delete(p); }
|
||||
|
||||
// Friending class TrailingObjects is apparently not good enough for MSVC,
|
||||
@ -663,6 +664,8 @@ public:
|
||||
|
||||
bool isChainCall() const { return ChainCall; }
|
||||
|
||||
bool isDelegateCall() const { return DelegateCall; }
|
||||
|
||||
bool isCmseNSCall() const { return CmseNSCall; }
|
||||
|
||||
bool isNoReturn() const { return NoReturn; }
|
||||
@ -749,6 +752,7 @@ public:
|
||||
ID.AddInteger(getASTCallingConvention());
|
||||
ID.AddBoolean(InstanceMethod);
|
||||
ID.AddBoolean(ChainCall);
|
||||
ID.AddBoolean(DelegateCall);
|
||||
ID.AddBoolean(NoReturn);
|
||||
ID.AddBoolean(ReturnsRetained);
|
||||
ID.AddBoolean(NoCallerSavedRegs);
|
||||
@ -766,17 +770,16 @@ public:
|
||||
for (const auto &I : arguments())
|
||||
I.type.Profile(ID);
|
||||
}
|
||||
static void Profile(llvm::FoldingSetNodeID &ID,
|
||||
bool InstanceMethod,
|
||||
bool ChainCall,
|
||||
static void Profile(llvm::FoldingSetNodeID &ID, bool InstanceMethod,
|
||||
bool ChainCall, bool IsDelegateCall,
|
||||
const FunctionType::ExtInfo &info,
|
||||
ArrayRef<ExtParameterInfo> paramInfos,
|
||||
RequiredArgs required,
|
||||
CanQualType resultType,
|
||||
RequiredArgs required, CanQualType resultType,
|
||||
ArrayRef<CanQualType> argTypes) {
|
||||
ID.AddInteger(info.getCC());
|
||||
ID.AddBoolean(InstanceMethod);
|
||||
ID.AddBoolean(ChainCall);
|
||||
ID.AddBoolean(IsDelegateCall);
|
||||
ID.AddBoolean(info.getNoReturn());
|
||||
ID.AddBoolean(info.getProducesResult());
|
||||
ID.AddBoolean(info.getNoCallerSavedRegs());
|
||||
|
@ -112,8 +112,7 @@ CodeGenTypes::arrangeFreeFunctionType(CanQual<FunctionNoProtoType> FTNP) {
|
||||
// When translating an unprototyped function type, always use a
|
||||
// variadic type.
|
||||
return arrangeLLVMFunctionInfo(FTNP->getReturnType().getUnqualifiedType(),
|
||||
/*instanceMethod=*/false,
|
||||
/*chainCall=*/false, std::nullopt,
|
||||
FnInfoOpts::None, std::nullopt,
|
||||
FTNP->getExtInfo(), {}, RequiredArgs(0));
|
||||
}
|
||||
|
||||
@ -189,10 +188,10 @@ arrangeLLVMFunctionInfo(CodeGenTypes &CGT, bool instanceMethod,
|
||||
appendParameterTypes(CGT, prefix, paramInfos, FTP);
|
||||
CanQualType resultType = FTP->getReturnType().getUnqualifiedType();
|
||||
|
||||
return CGT.arrangeLLVMFunctionInfo(resultType, instanceMethod,
|
||||
/*chainCall=*/false, prefix,
|
||||
FTP->getExtInfo(), paramInfos,
|
||||
Required);
|
||||
FnInfoOpts opts =
|
||||
instanceMethod ? FnInfoOpts::IsInstanceMethod : FnInfoOpts::None;
|
||||
return CGT.arrangeLLVMFunctionInfo(resultType, opts, prefix,
|
||||
FTP->getExtInfo(), paramInfos, Required);
|
||||
}
|
||||
|
||||
/// Arrange the argument and result information for a value of the
|
||||
@ -271,7 +270,7 @@ CodeGenTypes::arrangeCXXMethodType(const CXXRecordDecl *RD,
|
||||
argTypes.push_back(DeriveThisType(RD, MD));
|
||||
|
||||
return ::arrangeLLVMFunctionInfo(
|
||||
*this, true, argTypes,
|
||||
*this, /*instanceMethod=*/true, argTypes,
|
||||
FTP->getCanonicalTypeUnqualified().getAs<FunctionProtoType>());
|
||||
}
|
||||
|
||||
@ -363,9 +362,8 @@ CodeGenTypes::arrangeCXXStructorDeclaration(GlobalDecl GD) {
|
||||
: TheCXXABI.hasMostDerivedReturn(GD)
|
||||
? CGM.getContext().VoidPtrTy
|
||||
: Context.VoidTy;
|
||||
return arrangeLLVMFunctionInfo(resultType, /*instanceMethod=*/true,
|
||||
/*chainCall=*/false, argTypes, extInfo,
|
||||
paramInfos, required);
|
||||
return arrangeLLVMFunctionInfo(resultType, FnInfoOpts::IsInstanceMethod,
|
||||
argTypes, extInfo, paramInfos, required);
|
||||
}
|
||||
|
||||
static SmallVector<CanQualType, 16>
|
||||
@ -439,9 +437,9 @@ CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args,
|
||||
addExtParameterInfosForCall(ParamInfos, FPT.getTypePtr(), TotalPrefixArgs,
|
||||
ArgTypes.size());
|
||||
}
|
||||
return arrangeLLVMFunctionInfo(ResultType, /*instanceMethod=*/true,
|
||||
/*chainCall=*/false, ArgTypes, Info,
|
||||
ParamInfos, Required);
|
||||
|
||||
return arrangeLLVMFunctionInfo(ResultType, FnInfoOpts::IsInstanceMethod,
|
||||
ArgTypes, Info, ParamInfos, Required);
|
||||
}
|
||||
|
||||
/// Arrange the argument and result information for the declaration or
|
||||
@ -460,10 +458,9 @@ CodeGenTypes::arrangeFunctionDeclaration(const FunctionDecl *FD) {
|
||||
// When declaring a function without a prototype, always use a
|
||||
// non-variadic type.
|
||||
if (CanQual<FunctionNoProtoType> noProto = FTy.getAs<FunctionNoProtoType>()) {
|
||||
return arrangeLLVMFunctionInfo(
|
||||
noProto->getReturnType(), /*instanceMethod=*/false,
|
||||
/*chainCall=*/false, std::nullopt, noProto->getExtInfo(), {},
|
||||
RequiredArgs::All);
|
||||
return arrangeLLVMFunctionInfo(noProto->getReturnType(), FnInfoOpts::None,
|
||||
std::nullopt, noProto->getExtInfo(), {},
|
||||
RequiredArgs::All);
|
||||
}
|
||||
|
||||
return arrangeFreeFunctionType(FTy.castAs<FunctionProtoType>());
|
||||
@ -512,9 +509,9 @@ CodeGenTypes::arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD,
|
||||
RequiredArgs required =
|
||||
(MD->isVariadic() ? RequiredArgs(argTys.size()) : RequiredArgs::All);
|
||||
|
||||
return arrangeLLVMFunctionInfo(
|
||||
GetReturnType(MD->getReturnType()), /*instanceMethod=*/false,
|
||||
/*chainCall=*/false, argTys, einfo, extParamInfos, required);
|
||||
return arrangeLLVMFunctionInfo(GetReturnType(MD->getReturnType()),
|
||||
FnInfoOpts::None, argTys, einfo, extParamInfos,
|
||||
required);
|
||||
}
|
||||
|
||||
const CGFunctionInfo &
|
||||
@ -523,9 +520,8 @@ CodeGenTypes::arrangeUnprototypedObjCMessageSend(QualType returnType,
|
||||
auto argTypes = getArgTypesForCall(Context, args);
|
||||
FunctionType::ExtInfo einfo;
|
||||
|
||||
return arrangeLLVMFunctionInfo(
|
||||
GetReturnType(returnType), /*instanceMethod=*/false,
|
||||
/*chainCall=*/false, argTypes, einfo, {}, RequiredArgs::All);
|
||||
return arrangeLLVMFunctionInfo(GetReturnType(returnType), FnInfoOpts::None,
|
||||
argTypes, einfo, {}, RequiredArgs::All);
|
||||
}
|
||||
|
||||
const CGFunctionInfo &
|
||||
@ -550,8 +546,7 @@ CodeGenTypes::arrangeUnprototypedMustTailThunk(const CXXMethodDecl *MD) {
|
||||
assert(MD->isVirtual() && "only methods have thunks");
|
||||
CanQual<FunctionProtoType> FTP = GetFormalType(MD);
|
||||
CanQualType ArgTys[] = {DeriveThisType(MD->getParent(), MD)};
|
||||
return arrangeLLVMFunctionInfo(Context.VoidTy, /*instanceMethod=*/false,
|
||||
/*chainCall=*/false, ArgTys,
|
||||
return arrangeLLVMFunctionInfo(Context.VoidTy, FnInfoOpts::None, ArgTys,
|
||||
FTP->getExtInfo(), {}, RequiredArgs(1));
|
||||
}
|
||||
|
||||
@ -570,9 +565,8 @@ CodeGenTypes::arrangeMSCtorClosure(const CXXConstructorDecl *CD,
|
||||
ArgTys.push_back(Context.IntTy);
|
||||
CallingConv CC = Context.getDefaultCallingConvention(
|
||||
/*IsVariadic=*/false, /*IsCXXMethod=*/true);
|
||||
return arrangeLLVMFunctionInfo(Context.VoidTy, /*instanceMethod=*/true,
|
||||
/*chainCall=*/false, ArgTys,
|
||||
FunctionType::ExtInfo(CC), {},
|
||||
return arrangeLLVMFunctionInfo(Context.VoidTy, FnInfoOpts::IsInstanceMethod,
|
||||
ArgTys, FunctionType::ExtInfo(CC), {},
|
||||
RequiredArgs::All);
|
||||
}
|
||||
|
||||
@ -616,10 +610,10 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT,
|
||||
SmallVector<CanQualType, 16> argTypes;
|
||||
for (const auto &arg : args)
|
||||
argTypes.push_back(CGT.getContext().getCanonicalParamType(arg.Ty));
|
||||
FnInfoOpts opts = chainCall ? FnInfoOpts::IsChainCall : FnInfoOpts::None;
|
||||
return CGT.arrangeLLVMFunctionInfo(GetReturnType(fnType->getReturnType()),
|
||||
/*instanceMethod=*/false, chainCall,
|
||||
argTypes, fnType->getExtInfo(), paramInfos,
|
||||
required);
|
||||
opts, argTypes, fnType->getExtInfo(),
|
||||
paramInfos, required);
|
||||
}
|
||||
|
||||
/// Figure out the rules for calling a function with the given formal
|
||||
@ -650,8 +644,8 @@ CodeGenTypes::arrangeBlockFunctionDeclaration(const FunctionProtoType *proto,
|
||||
auto argTypes = getArgTypesForDeclaration(Context, params);
|
||||
|
||||
return arrangeLLVMFunctionInfo(GetReturnType(proto->getReturnType()),
|
||||
/*instanceMethod*/ false, /*chainCall*/ false,
|
||||
argTypes, proto->getExtInfo(), paramInfos,
|
||||
FnInfoOpts::None, argTypes,
|
||||
proto->getExtInfo(), paramInfos,
|
||||
RequiredArgs::forPrototypePlus(proto, 1));
|
||||
}
|
||||
|
||||
@ -662,10 +656,9 @@ CodeGenTypes::arrangeBuiltinFunctionCall(QualType resultType,
|
||||
SmallVector<CanQualType, 16> argTypes;
|
||||
for (const auto &Arg : args)
|
||||
argTypes.push_back(Context.getCanonicalParamType(Arg.Ty));
|
||||
return arrangeLLVMFunctionInfo(
|
||||
GetReturnType(resultType), /*instanceMethod=*/false,
|
||||
/*chainCall=*/false, argTypes, FunctionType::ExtInfo(),
|
||||
/*paramInfos=*/ {}, RequiredArgs::All);
|
||||
return arrangeLLVMFunctionInfo(GetReturnType(resultType), FnInfoOpts::None,
|
||||
argTypes, FunctionType::ExtInfo(),
|
||||
/*paramInfos=*/{}, RequiredArgs::All);
|
||||
}
|
||||
|
||||
const CGFunctionInfo &
|
||||
@ -673,17 +666,17 @@ CodeGenTypes::arrangeBuiltinFunctionDeclaration(QualType resultType,
|
||||
const FunctionArgList &args) {
|
||||
auto argTypes = getArgTypesForDeclaration(Context, args);
|
||||
|
||||
return arrangeLLVMFunctionInfo(
|
||||
GetReturnType(resultType), /*instanceMethod=*/false, /*chainCall=*/false,
|
||||
argTypes, FunctionType::ExtInfo(), {}, RequiredArgs::All);
|
||||
return arrangeLLVMFunctionInfo(GetReturnType(resultType), FnInfoOpts::None,
|
||||
argTypes, FunctionType::ExtInfo(), {},
|
||||
RequiredArgs::All);
|
||||
}
|
||||
|
||||
const CGFunctionInfo &
|
||||
CodeGenTypes::arrangeBuiltinFunctionDeclaration(CanQualType resultType,
|
||||
ArrayRef<CanQualType> argTypes) {
|
||||
return arrangeLLVMFunctionInfo(
|
||||
resultType, /*instanceMethod=*/false, /*chainCall=*/false,
|
||||
argTypes, FunctionType::ExtInfo(), {}, RequiredArgs::All);
|
||||
return arrangeLLVMFunctionInfo(resultType, FnInfoOpts::None, argTypes,
|
||||
FunctionType::ExtInfo(), {},
|
||||
RequiredArgs::All);
|
||||
}
|
||||
|
||||
/// Arrange a call to a C++ method, passing the given arguments.
|
||||
@ -706,15 +699,15 @@ CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args,
|
||||
auto argTypes = getArgTypesForCall(Context, args);
|
||||
|
||||
FunctionType::ExtInfo info = proto->getExtInfo();
|
||||
return arrangeLLVMFunctionInfo(
|
||||
GetReturnType(proto->getReturnType()), /*instanceMethod=*/true,
|
||||
/*chainCall=*/false, argTypes, info, paramInfos, required);
|
||||
return arrangeLLVMFunctionInfo(GetReturnType(proto->getReturnType()),
|
||||
FnInfoOpts::IsInstanceMethod, argTypes, info,
|
||||
paramInfos, required);
|
||||
}
|
||||
|
||||
const CGFunctionInfo &CodeGenTypes::arrangeNullaryFunction() {
|
||||
return arrangeLLVMFunctionInfo(
|
||||
getContext().VoidTy, /*instanceMethod=*/false, /*chainCall=*/false,
|
||||
std::nullopt, FunctionType::ExtInfo(), {}, RequiredArgs::All);
|
||||
return arrangeLLVMFunctionInfo(getContext().VoidTy, FnInfoOpts::None,
|
||||
std::nullopt, FunctionType::ExtInfo(), {},
|
||||
RequiredArgs::All);
|
||||
}
|
||||
|
||||
const CGFunctionInfo &
|
||||
@ -734,12 +727,15 @@ CodeGenTypes::arrangeCall(const CGFunctionInfo &signature,
|
||||
auto argTypes = getArgTypesForCall(Context, args);
|
||||
|
||||
assert(signature.getRequiredArgs().allowsOptionalArgs());
|
||||
return arrangeLLVMFunctionInfo(signature.getReturnType(),
|
||||
signature.isInstanceMethod(),
|
||||
signature.isChainCall(),
|
||||
argTypes,
|
||||
signature.getExtInfo(),
|
||||
paramInfos,
|
||||
FnInfoOpts opts = FnInfoOpts::None;
|
||||
if (signature.isInstanceMethod())
|
||||
opts |= FnInfoOpts::IsInstanceMethod;
|
||||
if (signature.isChainCall())
|
||||
opts |= FnInfoOpts::IsChainCall;
|
||||
if (signature.isDelegateCall())
|
||||
opts |= FnInfoOpts::IsDelegateCall;
|
||||
return arrangeLLVMFunctionInfo(signature.getReturnType(), opts, argTypes,
|
||||
signature.getExtInfo(), paramInfos,
|
||||
signature.getRequiredArgs());
|
||||
}
|
||||
|
||||
@ -752,21 +748,24 @@ void computeSPIRKernelABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI);
|
||||
/// Arrange the argument and result information for an abstract value
|
||||
/// of a given function type. This is the method which all of the
|
||||
/// above functions ultimately defer to.
|
||||
const CGFunctionInfo &
|
||||
CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType,
|
||||
bool instanceMethod,
|
||||
bool chainCall,
|
||||
ArrayRef<CanQualType> argTypes,
|
||||
FunctionType::ExtInfo info,
|
||||
ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
|
||||
RequiredArgs required) {
|
||||
const CGFunctionInfo &CodeGenTypes::arrangeLLVMFunctionInfo(
|
||||
CanQualType resultType, FnInfoOpts opts, ArrayRef<CanQualType> argTypes,
|
||||
FunctionType::ExtInfo info,
|
||||
ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
|
||||
RequiredArgs required) {
|
||||
assert(llvm::all_of(argTypes,
|
||||
[](CanQualType T) { return T.isCanonicalAsParam(); }));
|
||||
|
||||
// Lookup or create unique function info.
|
||||
llvm::FoldingSetNodeID ID;
|
||||
CGFunctionInfo::Profile(ID, instanceMethod, chainCall, info, paramInfos,
|
||||
required, resultType, argTypes);
|
||||
bool isInstanceMethod =
|
||||
(opts & FnInfoOpts::IsInstanceMethod) == FnInfoOpts::IsInstanceMethod;
|
||||
bool isChainCall =
|
||||
(opts & FnInfoOpts::IsChainCall) == FnInfoOpts::IsChainCall;
|
||||
bool isDelegateCall =
|
||||
(opts & FnInfoOpts::IsDelegateCall) == FnInfoOpts::IsDelegateCall;
|
||||
CGFunctionInfo::Profile(ID, isInstanceMethod, isChainCall, isDelegateCall,
|
||||
info, paramInfos, required, resultType, argTypes);
|
||||
|
||||
void *insertPos = nullptr;
|
||||
CGFunctionInfo *FI = FunctionInfos.FindNodeOrInsertPos(ID, insertPos);
|
||||
@ -776,8 +775,8 @@ CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType,
|
||||
unsigned CC = ClangCallConvToLLVMCallConv(info.getCC());
|
||||
|
||||
// Construct the function info. We co-allocate the ArgInfos.
|
||||
FI = CGFunctionInfo::create(CC, instanceMethod, chainCall, info,
|
||||
paramInfos, resultType, argTypes, required);
|
||||
FI = CGFunctionInfo::create(CC, isInstanceMethod, isChainCall, isDelegateCall,
|
||||
info, paramInfos, resultType, argTypes, required);
|
||||
FunctionInfos.InsertNode(FI, insertPos);
|
||||
|
||||
bool inserted = FunctionsBeingProcessed.insert(FI).second;
|
||||
@ -812,9 +811,8 @@ CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType,
|
||||
return *FI;
|
||||
}
|
||||
|
||||
CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC,
|
||||
bool instanceMethod,
|
||||
bool chainCall,
|
||||
CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, bool instanceMethod,
|
||||
bool chainCall, bool delegateCall,
|
||||
const FunctionType::ExtInfo &info,
|
||||
ArrayRef<ExtParameterInfo> paramInfos,
|
||||
CanQualType resultType,
|
||||
@ -834,6 +832,7 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC,
|
||||
FI->ASTCallingConvention = info.getCC();
|
||||
FI->InstanceMethod = instanceMethod;
|
||||
FI->ChainCall = chainCall;
|
||||
FI->DelegateCall = delegateCall;
|
||||
FI->CmseNSCall = info.getCmseNSCall();
|
||||
FI->NoReturn = info.getNoReturn();
|
||||
FI->ReturnsRetained = info.getProducesResult();
|
||||
@ -3989,10 +3988,6 @@ void CodeGenFunction::EmitDelegateCallArg(CallArgList &args,
|
||||
|
||||
QualType type = param->getType();
|
||||
|
||||
if (isInAllocaArgument(CGM.getCXXABI(), type)) {
|
||||
CGM.ErrorUnsupported(param, "forwarded non-trivially copyable parameter");
|
||||
}
|
||||
|
||||
// GetAddrOfLocalVar returns a pointer-to-pointer for references,
|
||||
// but the argument needs to be the original pointer.
|
||||
if (type->isReferenceType()) {
|
||||
@ -5105,7 +5100,6 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
|
||||
"indirect argument must be in alloca address space");
|
||||
|
||||
bool NeedCopy = false;
|
||||
|
||||
if (Addr.getAlignment() < Align &&
|
||||
llvm::getOrEnforceKnownAlignment(V, Align.getAsAlign(), *TD) <
|
||||
Align.getAsAlign()) {
|
||||
|
@ -383,6 +383,35 @@ void mergeDefaultFunctionDefinitionAttributes(llvm::Function &F,
|
||||
const TargetOptions &TargetOpts,
|
||||
bool WillInternalize);
|
||||
|
||||
enum class FnInfoOpts {
|
||||
None = 0,
|
||||
IsInstanceMethod = 1 << 0,
|
||||
IsChainCall = 1 << 1,
|
||||
IsDelegateCall = 1 << 2,
|
||||
};
|
||||
|
||||
inline FnInfoOpts operator|(FnInfoOpts A, FnInfoOpts B) {
|
||||
return static_cast<FnInfoOpts>(
|
||||
static_cast<std::underlying_type_t<FnInfoOpts>>(A) |
|
||||
static_cast<std::underlying_type_t<FnInfoOpts>>(B));
|
||||
}
|
||||
|
||||
inline FnInfoOpts operator&(FnInfoOpts A, FnInfoOpts B) {
|
||||
return static_cast<FnInfoOpts>(
|
||||
static_cast<std::underlying_type_t<FnInfoOpts>>(A) &
|
||||
static_cast<std::underlying_type_t<FnInfoOpts>>(B));
|
||||
}
|
||||
|
||||
inline FnInfoOpts operator|=(FnInfoOpts A, FnInfoOpts B) {
|
||||
A = A | B;
|
||||
return A;
|
||||
}
|
||||
|
||||
inline FnInfoOpts operator&=(FnInfoOpts A, FnInfoOpts B) {
|
||||
A = A & B;
|
||||
return A;
|
||||
}
|
||||
|
||||
} // end namespace CodeGen
|
||||
} // end namespace clang
|
||||
|
||||
|
@ -2927,14 +2927,16 @@ llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad(
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitForwardingCallToLambda(
|
||||
const CXXMethodDecl *callOperator,
|
||||
CallArgList &callArgs) {
|
||||
const CXXMethodDecl *callOperator, CallArgList &callArgs,
|
||||
const CGFunctionInfo *calleeFnInfo, llvm::Constant *calleePtr) {
|
||||
// Get the address of the call operator.
|
||||
const CGFunctionInfo &calleeFnInfo =
|
||||
CGM.getTypes().arrangeCXXMethodDeclaration(callOperator);
|
||||
llvm::Constant *calleePtr =
|
||||
CGM.GetAddrOfFunction(GlobalDecl(callOperator),
|
||||
CGM.getTypes().GetFunctionType(calleeFnInfo));
|
||||
if (!calleeFnInfo)
|
||||
calleeFnInfo = &CGM.getTypes().arrangeCXXMethodDeclaration(callOperator);
|
||||
|
||||
if (!calleePtr)
|
||||
calleePtr =
|
||||
CGM.GetAddrOfFunction(GlobalDecl(callOperator),
|
||||
CGM.getTypes().GetFunctionType(*calleeFnInfo));
|
||||
|
||||
// Prepare the return slot.
|
||||
const FunctionProtoType *FPT =
|
||||
@ -2942,8 +2944,8 @@ void CodeGenFunction::EmitForwardingCallToLambda(
|
||||
QualType resultType = FPT->getReturnType();
|
||||
ReturnValueSlot returnSlot;
|
||||
if (!resultType->isVoidType() &&
|
||||
calleeFnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect &&
|
||||
!hasScalarEvaluationKind(calleeFnInfo.getReturnType()))
|
||||
calleeFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect &&
|
||||
!hasScalarEvaluationKind(calleeFnInfo->getReturnType()))
|
||||
returnSlot =
|
||||
ReturnValueSlot(ReturnValue, resultType.isVolatileQualified(),
|
||||
/*IsUnused=*/false, /*IsExternallyDestructed=*/true);
|
||||
@ -2954,7 +2956,7 @@ void CodeGenFunction::EmitForwardingCallToLambda(
|
||||
|
||||
// Now emit our call.
|
||||
auto callee = CGCallee::forDirect(calleePtr, GlobalDecl(callOperator));
|
||||
RValue RV = EmitCall(calleeFnInfo, callee, returnSlot, callArgs);
|
||||
RValue RV = EmitCall(*calleeFnInfo, callee, returnSlot, callArgs);
|
||||
|
||||
// If necessary, copy the returned value into the slot.
|
||||
if (!resultType->isVoidType() && returnSlot.isNull()) {
|
||||
@ -2996,7 +2998,15 @@ void CodeGenFunction::EmitLambdaBlockInvokeBody() {
|
||||
EmitForwardingCallToLambda(CallOp, CallArgs);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD) {
|
||||
void CodeGenFunction::EmitLambdaStaticInvokeBody(const CXXMethodDecl *MD) {
|
||||
if (MD->isVariadic()) {
|
||||
// FIXME: Making this work correctly is nasty because it requires either
|
||||
// cloning the body of the call operator or making the call operator
|
||||
// forward.
|
||||
CGM.ErrorUnsupported(MD, "lambda conversion to variadic function");
|
||||
return;
|
||||
}
|
||||
|
||||
const CXXRecordDecl *Lambda = MD->getParent();
|
||||
|
||||
// Start building arguments for forwarding call
|
||||
@ -3007,10 +3017,16 @@ void CodeGenFunction::EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD) {
|
||||
Address ThisPtr = CreateMemTemp(LambdaType, "unused.capture");
|
||||
CallArgs.add(RValue::get(ThisPtr.getPointer()), ThisType);
|
||||
|
||||
// Add the rest of the parameters.
|
||||
EmitLambdaDelegatingInvokeBody(MD, CallArgs);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD,
|
||||
CallArgList &CallArgs) {
|
||||
// Add the rest of the forwarded parameters.
|
||||
for (auto *Param : MD->parameters())
|
||||
EmitDelegateCallArg(CallArgs, Param, Param->getBeginLoc());
|
||||
|
||||
const CXXRecordDecl *Lambda = MD->getParent();
|
||||
const CXXMethodDecl *CallOp = Lambda->getLambdaCallOperator();
|
||||
// For a generic lambda, find the corresponding call operator specialization
|
||||
// to which the call to the static-invoker shall be forwarded.
|
||||
@ -3024,10 +3040,21 @@ void CodeGenFunction::EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD) {
|
||||
assert(CorrespondingCallOpSpecialization);
|
||||
CallOp = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);
|
||||
}
|
||||
|
||||
// Special lambda forwarding when there are inalloca parameters.
|
||||
if (hasInAllocaArg(MD)) {
|
||||
const CGFunctionInfo *ImplFnInfo = nullptr;
|
||||
llvm::Function *ImplFn = nullptr;
|
||||
EmitLambdaInAllocaImplFn(CallOp, &ImplFnInfo, &ImplFn);
|
||||
|
||||
EmitForwardingCallToLambda(CallOp, CallArgs, ImplFnInfo, ImplFn);
|
||||
return;
|
||||
}
|
||||
|
||||
EmitForwardingCallToLambda(CallOp, CallArgs);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitLambdaStaticInvokeBody(const CXXMethodDecl *MD) {
|
||||
void CodeGenFunction::EmitLambdaInAllocaCallOpBody(const CXXMethodDecl *MD) {
|
||||
if (MD->isVariadic()) {
|
||||
// FIXME: Making this work correctly is nasty because it requires either
|
||||
// cloning the body of the call operator or making the call operator forward.
|
||||
@ -3035,5 +3062,56 @@ void CodeGenFunction::EmitLambdaStaticInvokeBody(const CXXMethodDecl *MD) {
|
||||
return;
|
||||
}
|
||||
|
||||
EmitLambdaDelegatingInvokeBody(MD);
|
||||
// Forward %this argument.
|
||||
CallArgList CallArgs;
|
||||
QualType LambdaType = getContext().getRecordType(MD->getParent());
|
||||
QualType ThisType = getContext().getPointerType(LambdaType);
|
||||
llvm::Value *ThisArg = CurFn->getArg(0);
|
||||
CallArgs.add(RValue::get(ThisArg), ThisType);
|
||||
|
||||
EmitLambdaDelegatingInvokeBody(MD, CallArgs);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitLambdaInAllocaImplFn(
|
||||
const CXXMethodDecl *CallOp, const CGFunctionInfo **ImplFnInfo,
|
||||
llvm::Function **ImplFn) {
|
||||
const CGFunctionInfo &FnInfo =
|
||||
CGM.getTypes().arrangeCXXMethodDeclaration(CallOp);
|
||||
llvm::Function *CallOpFn =
|
||||
cast<llvm::Function>(CGM.GetAddrOfFunction(GlobalDecl(CallOp)));
|
||||
|
||||
// Emit function containing the original call op body. __invoke will delegate
|
||||
// to this function.
|
||||
SmallVector<CanQualType, 4> ArgTypes;
|
||||
for (auto I = FnInfo.arg_begin(); I != FnInfo.arg_end(); ++I)
|
||||
ArgTypes.push_back(I->type);
|
||||
*ImplFnInfo = &CGM.getTypes().arrangeLLVMFunctionInfo(
|
||||
FnInfo.getReturnType(), FnInfoOpts::IsDelegateCall, ArgTypes,
|
||||
FnInfo.getExtInfo(), {}, FnInfo.getRequiredArgs());
|
||||
|
||||
// Create mangled name as if this was a method named __impl. If for some
|
||||
// reason the name doesn't look as expected then just tack __impl to the
|
||||
// front.
|
||||
// TODO: Use the name mangler to produce the right name instead of using
|
||||
// string replacement.
|
||||
StringRef CallOpName = CallOpFn->getName();
|
||||
std::string ImplName;
|
||||
if (size_t Pos = CallOpName.find_first_of("<lambda"))
|
||||
ImplName = ("?__impl@" + CallOpName.drop_front(Pos)).str();
|
||||
else
|
||||
ImplName = ("__impl" + CallOpName).str();
|
||||
|
||||
llvm::Function *Fn = CallOpFn->getParent()->getFunction(ImplName);
|
||||
if (!Fn) {
|
||||
Fn = llvm::Function::Create(CGM.getTypes().GetFunctionType(**ImplFnInfo),
|
||||
llvm::GlobalValue::InternalLinkage, ImplName,
|
||||
CGM.getModule());
|
||||
CGM.SetInternalFunctionAttributes(CallOp, Fn, **ImplFnInfo);
|
||||
|
||||
const GlobalDecl &GD = GlobalDecl(CallOp);
|
||||
const auto *D = cast<FunctionDecl>(GD.getDecl());
|
||||
CodeGenFunction(CGM).GenerateCode(GD, Fn, **ImplFnInfo);
|
||||
CGM.SetLLVMFunctionAttributesForDefinition(D, Fn);
|
||||
}
|
||||
*ImplFn = Fn;
|
||||
}
|
||||
|
@ -279,8 +279,8 @@ llvm::Function *CodeGenFunction::createTLSAtExitStub(
|
||||
}
|
||||
|
||||
const CGFunctionInfo &FI = CGM.getTypes().arrangeLLVMFunctionInfo(
|
||||
getContext().IntTy, /*instanceMethod=*/false, /*chainCall=*/false,
|
||||
{getContext().IntTy}, FunctionType::ExtInfo(), {}, RequiredArgs::All);
|
||||
getContext().IntTy, FnInfoOpts::None, {getContext().IntTy},
|
||||
FunctionType::ExtInfo(), {}, RequiredArgs::All);
|
||||
|
||||
// Get the stub function type, int(*)(int,...).
|
||||
llvm::FunctionType *StubTy =
|
||||
|
@ -65,9 +65,8 @@ CodeGen::arrangeFreeFunctionCall(CodeGenModule &CGM,
|
||||
ArrayRef<CanQualType> argTypes,
|
||||
FunctionType::ExtInfo info,
|
||||
RequiredArgs args) {
|
||||
return CGM.getTypes().arrangeLLVMFunctionInfo(
|
||||
returnType, /*instanceMethod=*/false, /*chainCall=*/false, argTypes,
|
||||
info, {}, args);
|
||||
return CGM.getTypes().arrangeLLVMFunctionInfo(returnType, FnInfoOpts::None,
|
||||
argTypes, info, {}, args);
|
||||
}
|
||||
|
||||
ImplicitCXXConstructorArgs
|
||||
|
@ -683,6 +683,19 @@ static bool matchesStlAllocatorFn(const Decl *D, const ASTContext &Ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CodeGenFunction::isInAllocaArgument(CGCXXABI &ABI, QualType Ty) {
|
||||
const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
|
||||
return RD && ABI.getRecordArgABI(RD) == CGCXXABI::RAA_DirectInMemory;
|
||||
}
|
||||
|
||||
bool CodeGenFunction::hasInAllocaArg(const CXXMethodDecl *MD) {
|
||||
return getTarget().getTriple().getArch() == llvm::Triple::x86 &&
|
||||
getTarget().getCXXABI().isMicrosoft() &&
|
||||
llvm::any_of(MD->parameters(), [&](ParmVarDecl *P) {
|
||||
return isInAllocaArgument(CGM.getCXXABI(), P->getType());
|
||||
});
|
||||
}
|
||||
|
||||
/// Return the UBSan prologue signature for \p FD if one is available.
|
||||
static llvm::Constant *getPrologueSignature(CodeGenModule &CGM,
|
||||
const FunctionDecl *FD) {
|
||||
@ -1447,6 +1460,17 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
|
||||
// The lambda static invoker function is special, because it forwards or
|
||||
// clones the body of the function call operator (but is actually static).
|
||||
EmitLambdaStaticInvokeBody(cast<CXXMethodDecl>(FD));
|
||||
} else if (isa<CXXMethodDecl>(FD) &&
|
||||
isLambdaCallOperator(cast<CXXMethodDecl>(FD)) &&
|
||||
!FnInfo.isDelegateCall() &&
|
||||
cast<CXXMethodDecl>(FD)->getParent()->getLambdaStaticInvoker() &&
|
||||
hasInAllocaArg(cast<CXXMethodDecl>(FD))) {
|
||||
// If emitting a lambda with static invoker on X86 Windows, change
|
||||
// the call operator body.
|
||||
// Make sure that this is a call operator with an inalloca arg and check
|
||||
// for delegate call to make sure this is the original call op and not the
|
||||
// new forwarding function for the static invoker.
|
||||
EmitLambdaInAllocaCallOpBody(cast<CXXMethodDecl>(FD));
|
||||
} else if (FD->isDefaulted() && isa<CXXMethodDecl>(FD) &&
|
||||
(cast<CXXMethodDecl>(FD)->isCopyAssignmentOperator() ||
|
||||
cast<CXXMethodDecl>(FD)->isMoveAssignmentOperator())) {
|
||||
|
@ -1963,6 +1963,9 @@ private:
|
||||
/// Check if the return value of this function requires sanitization.
|
||||
bool requiresReturnValueCheck() const;
|
||||
|
||||
bool isInAllocaArgument(CGCXXABI &ABI, QualType Ty);
|
||||
bool hasInAllocaArg(const CXXMethodDecl *MD);
|
||||
|
||||
llvm::BasicBlock *TerminateLandingPad = nullptr;
|
||||
llvm::BasicBlock *TerminateHandler = nullptr;
|
||||
llvm::SmallVector<llvm::BasicBlock *, 2> TrapBBs;
|
||||
@ -2227,10 +2230,17 @@ public:
|
||||
void EmitBlockWithFallThrough(llvm::BasicBlock *BB, const Stmt *S);
|
||||
|
||||
void EmitForwardingCallToLambda(const CXXMethodDecl *LambdaCallOperator,
|
||||
CallArgList &CallArgs);
|
||||
CallArgList &CallArgs,
|
||||
const CGFunctionInfo *CallOpFnInfo = nullptr,
|
||||
llvm::Constant *CallOpFn = nullptr);
|
||||
void EmitLambdaBlockInvokeBody();
|
||||
void EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD);
|
||||
void EmitLambdaStaticInvokeBody(const CXXMethodDecl *MD);
|
||||
void EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD,
|
||||
CallArgList &CallArgs);
|
||||
void EmitLambdaInAllocaImplFn(const CXXMethodDecl *CallOp,
|
||||
const CGFunctionInfo **ImplFnInfo,
|
||||
llvm::Function **ImplFn);
|
||||
void EmitLambdaInAllocaCallOpBody(const CXXMethodDecl *MD);
|
||||
void EmitLambdaVLACapture(const VariableArrayType *VAT, LValue LV) {
|
||||
EmitStoreThroughLValue(RValue::get(VLASizeMap[VAT->getSizeExpr()]), LV);
|
||||
}
|
||||
|
@ -252,13 +252,11 @@ public:
|
||||
/// this.
|
||||
///
|
||||
/// \param argTypes - must all actually be canonical as params
|
||||
const CGFunctionInfo &arrangeLLVMFunctionInfo(CanQualType returnType,
|
||||
bool instanceMethod,
|
||||
bool chainCall,
|
||||
ArrayRef<CanQualType> argTypes,
|
||||
FunctionType::ExtInfo info,
|
||||
ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
|
||||
RequiredArgs args);
|
||||
const CGFunctionInfo &arrangeLLVMFunctionInfo(
|
||||
CanQualType returnType, FnInfoOpts opts, ArrayRef<CanQualType> argTypes,
|
||||
FunctionType::ExtInfo info,
|
||||
ArrayRef<FunctionProtoType::ExtParameterInfo> paramInfos,
|
||||
RequiredArgs args);
|
||||
|
||||
/// Compute a new LLVM record layout object for the given record.
|
||||
std::unique_ptr<CGRecordLayout> ComputeRecordLayout(const RecordDecl *D,
|
||||
|
@ -140,7 +140,8 @@ class X86_32ABIInfo : public ABIInfo {
|
||||
|
||||
Class classify(QualType Ty) const;
|
||||
ABIArgInfo classifyReturnType(QualType RetTy, CCState &State) const;
|
||||
ABIArgInfo classifyArgumentType(QualType RetTy, CCState &State) const;
|
||||
ABIArgInfo classifyArgumentType(QualType RetTy, CCState &State,
|
||||
bool isDelegateCall) const;
|
||||
|
||||
/// Updates the number of available free registers, returns
|
||||
/// true if any registers were allocated.
|
||||
@ -737,8 +738,8 @@ void X86_32ABIInfo::runVectorCallFirstPass(CGFunctionInfo &FI, CCState &State) c
|
||||
}
|
||||
}
|
||||
|
||||
ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty,
|
||||
CCState &State) const {
|
||||
ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty, CCState &State,
|
||||
bool isDelegateCall) const {
|
||||
// FIXME: Set alignment on indirect arguments.
|
||||
bool IsFastCall = State.CC == llvm::CallingConv::X86_FastCall;
|
||||
bool IsRegCall = State.CC == llvm::CallingConv::X86_RegCall;
|
||||
@ -753,6 +754,12 @@ ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty,
|
||||
CGCXXABI::RecordArgABI RAA = getRecordArgABI(RT, getCXXABI());
|
||||
if (RAA == CGCXXABI::RAA_Indirect) {
|
||||
return getIndirectResult(Ty, false, State);
|
||||
} else if (isDelegateCall) {
|
||||
// Avoid having different alignments on delegate call args by always
|
||||
// setting the alignment to 4, which is what we do for inallocas.
|
||||
ABIArgInfo Res = getIndirectResult(Ty, false, State);
|
||||
Res.setIndirectAlign(CharUnits::fromQuantity(4));
|
||||
return Res;
|
||||
} else if (RAA == CGCXXABI::RAA_DirectInMemory) {
|
||||
// The field index doesn't matter, we'll fix it up later.
|
||||
return ABIArgInfo::getInAlloca(/*FieldIndex=*/0);
|
||||
@ -940,7 +947,8 @@ void X86_32ABIInfo::computeInfo(CGFunctionInfo &FI) const {
|
||||
if (State.IsPreassigned.test(I))
|
||||
continue;
|
||||
|
||||
Args[I].info = classifyArgumentType(Args[I].type, State);
|
||||
Args[I].info =
|
||||
classifyArgumentType(Args[I].type, State, FI.isDelegateCall());
|
||||
UsedInAlloca |= (Args[I].info.getKind() == ABIArgInfo::InAlloca);
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,63 @@
|
||||
// RUN: not %clang_cc1 -triple i686-windows-msvc -emit-llvm -o /dev/null %s 2>&1 | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple i686-windows-msvc -emit-llvm -o - %s 2>&1 | FileCheck %s
|
||||
|
||||
// PR28299
|
||||
// CHECK: error: cannot compile this forwarded non-trivially copyable parameter yet
|
||||
|
||||
class A {
|
||||
struct A {
|
||||
A();
|
||||
A(const A &);
|
||||
int x;
|
||||
};
|
||||
typedef void (*fptr_t)(A);
|
||||
fptr_t fn1() { return [](A) {}; }
|
||||
void decayToFp(int (*f)(A));
|
||||
void test() {
|
||||
auto ld = [](A a) {
|
||||
static int calls = 0;
|
||||
++calls;
|
||||
return a.x + calls;
|
||||
};
|
||||
decayToFp(ld);
|
||||
ld(A{});
|
||||
}
|
||||
|
||||
// CHECK: define internal x86_thiscallcc noundef i32
|
||||
// CHECK-SAME: @"??R<lambda_0>@?0??test@@YAXXZ@QBE?A?<auto>@@UA@@@Z"
|
||||
// CHECK-SAME: (ptr noundef %this, ptr inalloca(<{ %struct.A }>) %[[ARG:.*]])
|
||||
// CHECK: %[[V:.*]] = getelementptr inbounds <{ %struct.A }>, ptr %[[ARG]], i32 0, i32 0
|
||||
// CHECK: %call = call x86_thiscallcc noundef i32
|
||||
// CHECK-SAME: @"?__impl@<lambda_0>@?0??test@@YAXXZ@QBE?A?<auto>@@UA@@@Z"
|
||||
// CHECK-SAME: (ptr noundef %this, ptr noundef %[[V]])
|
||||
|
||||
// CHECK: define internal noundef i32
|
||||
// CHECK-SAME: @"?__invoke@<lambda_0>@?0??test@@YAXXZ@CA?A?<auto>@@UA@@@Z"
|
||||
// CHECK-SAME: (ptr inalloca(<{ %struct.A }>) %[[ARG:.*]])
|
||||
// CHECK: %unused.capture = alloca %class.anon, align 1
|
||||
// CHECK: %[[VAR:.*]] = getelementptr inbounds <{ %struct.A }>, ptr %[[ARG]], i32 0, i32 0
|
||||
// CHECK: %call = call x86_thiscallcc noundef i32
|
||||
// CHECK-SAME: @"?__impl@<lambda_0>@?0??test@@YAXXZ@QBE?A?<auto>@@UA@@@Z"
|
||||
// CHECK-SAME: (ptr noundef %unused.capture, ptr noundef %[[VAR]])
|
||||
// CHECK: ret i32 %call
|
||||
|
||||
// CHECK: define internal x86_thiscallcc noundef i32
|
||||
// CHECK-SAME: @"?__impl@<lambda_0>@?0??test@@YAXXZ@QBE?A?<auto>@@UA@@@Z"
|
||||
// CHECK-SAME: (ptr noundef %this, ptr noundef %[[ARG:.*]])
|
||||
// CHECK: %this.addr = alloca ptr, align 4
|
||||
// CHECK: store ptr %this, ptr %this.addr, align 4
|
||||
// CHECK: %this1 = load ptr, ptr %this.addr, align 4
|
||||
// CHECK: %{{.*}} = load i32, ptr @"?calls@?1???R<lambda_0>
|
||||
// CHECK: %inc = add nsw i32 %{{.*}}, 1
|
||||
// CHECK: store i32 %inc, ptr @"?calls@?1???R<lambda_0>
|
||||
// CHECK: %{{.*}} = getelementptr inbounds %struct.A, ptr %{{.*}}, i32 0, i32 0
|
||||
// CHECK: %{{.*}} = load i32, ptr %{{.*}}, align 4
|
||||
// CHECK: %{{.*}} = load i32, ptr @"?calls@?1???R<lambda_0>
|
||||
// CHECK: %add = add nsw i32 %{{.*}}, %{{.*}}
|
||||
// CHECK: ret i32 %add
|
||||
|
||||
// Make sure we don't try to copy an uncopyable type.
|
||||
struct B {
|
||||
B();
|
||||
B(B &);
|
||||
void operator=(B);
|
||||
long long x;
|
||||
} b;
|
||||
|
||||
void f() {
|
||||
[](B) {}(b);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user