diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -26,8 +26,10 @@ #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Metadata.h" +#include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/SanitizerStats.h" using namespace clang; @@ -3044,3 +3046,38 @@ EmitLambdaDelegatingInvokeBody(MD); } + +void CodeGenFunction::EmitClonedLambdaStaticInvoke(const CXXMethodDecl *MD) { + llvm::Function *Fn = + cast(CGM.GetAddrOfFunction(GlobalDecl(MD))); + + const CXXMethodDecl *CallOp = MD->getParent()->getLambdaCallOperator(); + llvm::Function *CallOpFn = + cast(CGM.GetAddrOfFunction(GlobalDecl(CallOp))); + + // Clone from call operator, which we've made sure is already emitted. + llvm::ValueToValueMapTy VMap; + + for (llvm::Argument &I : CallOpFn->args()) { + // Don't copy the %this argument. + if (I.getName().equals("this")) { + VMap[&I] = llvm::Constant::getNullValue(I.getType()); + } else { + // Try to map the inalloca arg from the call op fn to the invoker fn. + for (llvm::Argument &DestI : Fn->args()) { + if (DestI.getType() == I.getType()) { + VMap[&I] = &DestI; + break; + } + } + } + } + + Fn->setAttributes(CallOpFn->getAttributes()); + Fn->setCallingConv(CallOpFn->getCallingConv()); + Fn->setLinkage(CallOpFn->getLinkage()); + + SmallVector Returns; + llvm::CloneFunctionInto(Fn, CallOpFn, VMap, + llvm::CloneFunctionChangeType::LocalChangesOnly, Returns, "", nullptr); +} diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2223,6 +2223,8 @@ void EmitLambdaBlockInvokeBody(); void EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD); void EmitLambdaStaticInvokeBody(const CXXMethodDecl *MD); + void EmitClonedLambdaStaticInvoke(const CXXMethodDecl *MD); + void EmitLambdaVLACapture(const VariableArrayType *VAT, LValue LV) { EmitStoreThroughLValue(RValue::get(VLASizeMap[VAT->getSizeExpr()]), LV); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3554,6 +3554,18 @@ EmitGlobalFunctionDefinition(GD, GV); } +static bool isInAllocaArg(CGCXXABI &ABI, QualType T) { + const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + return RD && ABI.getRecordArgABI(RD) == CGCXXABI::RAA_DirectInMemory; +} + +static bool hasInAllocaArg(const TargetInfo &TI, CGCXXABI &CGABI, const CXXMethodDecl *MD) { + return TI.getCXXABI().isMicrosoft() && + llvm::any_of(MD->parameters(), [&](ParmVarDecl *P) { + return isInAllocaArg(CGABI, P->getType()); + }); +} + void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { const auto *D = cast(GD.getDecl()); @@ -3580,6 +3592,14 @@ // This is necessary for the generation of certain thunks. if (isa(Method) || isa(Method)) ABI->emitCXXStructor(GD); + // Special path for emitting lambda static invokers with inalloca parameters. + else if (Method->isLambdaStaticInvoker() && + hasInAllocaArg(getTarget(), getCXXABI(), Method)) { + // Emit the call operator definition before emitting a static invoker. + const CXXMethodDecl *CallOp = Method->getParent()->getLambdaCallOperator(); + EmitGlobalFunctionDefinition(GlobalDecl(CallOp), nullptr); + CodeGenFunction(*this).EmitClonedLambdaStaticInvoke(Method); + } else if (FD->isMultiVersion()) EmitMultiVersionFunctionDefinition(GD, GV); else diff --git a/clang/test/CodeGenCXX/inalloca-lambda.cpp b/clang/test/CodeGenCXX/inalloca-lambda.cpp --- a/clang/test/CodeGenCXX/inalloca-lambda.cpp +++ b/clang/test/CodeGenCXX/inalloca-lambda.cpp @@ -1,7 +1,4 @@ -// RUN: not %clang_cc1 -triple i686-windows-msvc -emit-llvm -o /dev/null %s 2>&1 | FileCheck %s - -// PR28299 -// CHECK: error: cannot compile this forwarded non-trivially copyable parameter yet +// RUN: %clang_cc1 -triple i686-windows-msvc -emit-llvm -o - %s 2>&1 | FileCheck %s class A { A(const A &); @@ -9,3 +6,7 @@ typedef void (*fptr_t)(A); fptr_t fn1() { return [](A) {}; } +// CHECK: define internal x86_thiscallcc void @"?__invoke@@?0??fn1@@YAP6AXVA@@@ZXZ@CA?A?@@0@Z" +// CHECK-SAME: (ptr inalloca(<{ %class.A, [3 x i8] }>) %0) +// CHECK: %1 = getelementptr inbounds <{ %class.A, [3 x i8] }>, ptr %0, i32 0, i32 0 +// CHECK: ret void