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,46 @@ EmitLambdaDelegatingInvokeBody(MD); } + +void CodeGenFunction::EmitClonedLambdaStaticInvoke(const CXXMethodDecl *MD) { + const CGFunctionInfo &InvokerFnInfo = CGM.getTypes().arrangeCXXMethodDeclaration(MD); + llvm::Type *InvokerTy = CGM.getTypes().GetFunctionType(InvokerFnInfo); + GlobalDecl GD = GlobalDecl(MD); + llvm::Function *InvokerFn = cast(CGM.GetAddrOfFunction(GD, InvokerTy)); + + const CXXRecordDecl *Lambda = MD->getParent(); + const CXXMethodDecl *CallOp = Lambda->getLambdaCallOperator(); + const CGFunctionInfo &CallOpFnInfo = + CGM.getTypes().arrangeCXXMethodDeclaration(CallOp); + llvm::Function *CallOpFn = cast( + CGM.GetAddrOfFunction(GlobalDecl(CallOp), + CGM.getTypes().GetFunctionType(CallOpFnInfo))); + + // Generate code for call op. + if (CallOpFn->isDeclaration()) { + CodeGenFunction(CGM).GenerateCode(GlobalDecl(CallOp), CallOpFn, CallOpFnInfo); + } + + // Clone from call operator. + llvm::ValueToValueMapTy VMap; + + // Don't copy the %this argument. + // Assume CallOp is going to have two parameters?: this and inalloca. + for (llvm::Argument &I : CallOpFn->args()) { + if (I.getName().equals("this")) { + VMap[&I] = llvm::PoisonValue::get(I.getType()); + } else { + for (llvm::Argument &DestI : InvokerFn->args()) { + if (DestI.getType() == I.getType()) { + VMap[&I] = &DestI; + break; + } + } + } + } + + InvokerFn->dropAllReferences(); // Erase function body. + SmallVector Returns; + llvm::CloneFunctionInto(InvokerFn, 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,11 @@ EmitGlobalFunctionDefinition(GD, GV); } +static bool isInAllocaArgument(CGCXXABI &ABI, QualType T) { + const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + return RD && ABI.getRecordArgABI(RD) == CGCXXABI::RAA_DirectInMemory; +} + void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { const auto *D = cast(GD.getDecl()); @@ -3580,6 +3585,25 @@ // This is necessary for the generation of certain thunks. if (isa(Method) || isa(Method)) ABI->emitCXXStructor(GD); + // Hack for lambda forwarding calls with inalloca parameters. + else if (isa(FD) && cast(FD)->isLambdaStaticInvoker() && + getTarget().getCXXABI().isMicrosoft() && + llvm::any_of(cast(FD)->parameters(), [&](ParmVarDecl *P) { + return isInAllocaArgument(getCXXABI(), P->getType()); + })) { + // If the call op is not yet emitted, emit that. + const CXXRecordDecl *Lambda = cast(FD)->getParent(); + const CXXMethodDecl *CallOp = Lambda->getLambdaCallOperator(); + const CGFunctionInfo &CallOpFnInfo = + getTypes().arrangeCXXMethodDeclaration(CallOp); + llvm::Function *CallOpFn = cast( + GetAddrOfFunction(GlobalDecl(CallOp), + getTypes().GetFunctionType(CallOpFnInfo))); + if (CallOpFn->isDeclaration()) + EmitGlobalFunctionDefinition(GlobalDecl(CallOp), nullptr); + + CodeGenFunction(*this).EmitClonedLambdaStaticInvoke(cast(FD)); + } 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