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:
Amy Huang 2023-06-23 11:41:44 -07:00
parent 63458d92e5
commit 27dab4d305
11 changed files with 322 additions and 127 deletions

View File

@ -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());

View File

@ -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()) {

View File

@ -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

View File

@ -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;
}

View File

@ -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 =

View File

@ -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

View File

@ -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())) {

View File

@ -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);
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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);
}