diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3801,7 +3801,8 @@ QualType type = param->getType(); if (isInAllocaArgument(CGM.getCXXABI(), type)) { - CGM.ErrorUnsupported(param, "forwarded non-trivially copyable parameter"); + // There should be an error here but instead we try to get rid of these + // calls later in codegen. } // GetAddrOfLocalVar returns a pointer-to-pointer for references, 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,48 @@ EmitLambdaDelegatingInvokeBody(MD); } + +void CodeGenFunction::EmitClonedLambdaStaticInvoke(const CXXMethodDecl *MD) { + const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeCXXMethodDeclaration(MD); + llvm::Type *Ty = CGM.getTypes().GetFunctionType(FnInfo); + llvm::Function *Fn = cast(CGM.GetAddrOfFunction(GlobalDecl(MD), Ty)); + + // Loop through all the __invoke functions. + const CXXRecordDecl *Lambda = MD->getParent(); + + CallingConv CCs[] = + { CC_C, CC_X86StdCall, CC_X86FastCall, CC_X86ThisCall, CC_X86VectorCall }; + for (CallingConv CC : CCs) { + const CXXMethodDecl *Invoker = Lambda->getLambdaStaticInvoker(CC); + if (!Invoker) + continue; + + const CGFunctionInfo &InvokerFnInfo = + CGM.getTypes().arrangeCXXMethodDeclaration(Invoker); + llvm::Function *InvokerFn = cast( + CGM.GetAddrOfFunction(GlobalDecl(Invoker), + CGM.getTypes().GetFunctionType(InvokerFnInfo))); + + if (InvokerFn->isDeclaration()) + continue; + + // Clone from call operator. + llvm::ValueToValueMapTy VMap; + + // Don't copy the %this argument. + for (llvm::Argument &I : Fn->args()) { + if (I.getName().equals("this")) { + VMap[&I] = llvm::UndefValue::get(I.getType()); + } + } + + llvm::Function *NewFn = llvm::CloneFunction(Fn, VMap); + InvokerFn->replaceAllUsesWith(NewFn); + NewFn->takeName(InvokerFn); + NewFn->setCallingConv(InvokerFn->getCallingConv()); + NewFn->setAttributes(InvokerFn->getAttributes()); + + InvokerFn->eraseFromParent(); + InvokerFn = NewFn; + } +} 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/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1327,6 +1327,11 @@ return ResTy; } +static bool isInAllocaArgument(CGCXXABI &ABI, QualType T) { + const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); + return RD && ABI.getRecordArgABI(RD) == CGCXXABI::RAA_DirectInMemory; +} + void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, const CGFunctionInfo &FnInfo) { assert(Fn && "generating code for null Function"); @@ -1482,6 +1487,17 @@ // a quick pass now to see if we can. if (!CurFn->doesNotThrow()) TryMarkNoThrow(CurFn); + + // If 32-bit Windows, and there's an inalloca parameter, don't use the + // emitted lambda static invoker function because we can't forward inalloca + // parameters. Instead copy the body from the call operator. + if (isa(FD) && isLambdaCallOperator(cast(FD)) && + CGM.getTarget().getCXXABI().isMicrosoft() && + llvm::any_of(cast(FD)->parameters(), [&](ParmVarDecl *P) { + return isInAllocaArgument(CGM.getCXXABI(), P->getType()); + })) { + EmitClonedLambdaStaticInvoke(cast(FD)); + } } /// ContainsLabel - Return true if the statement contains a label in it. If 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 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