[cfe-commits] r150783 - in /cfe/trunk: include/clang/AST/DeclCXX.h lib/AST/DeclCXX.cpp lib/CodeGen/CGClass.cpp lib/CodeGen/CodeGenFunction.cpp lib/CodeGen/CodeGenFunction.h lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaLambda.cpp test/CodeGenCXX/lambda-expressions.cpp

Douglas Gregor dgregor at apple.com
Thu Feb 16 19:02:34 PST 2012


Author: dgregor
Date: Thu Feb 16 21:02:34 2012
New Revision: 150783

URL: http://llvm.org/viewvc/llvm-project?rev=150783&view=rev
Log:
Rework the Sema/AST/IRgen dance for the lambda closure type's
conversion to function pointer. Rather than having IRgen synthesize
the body of this function, we instead introduce a static member
function "__invoke" with the same signature as the lambda's
operator() in the AST. Sema then generates a body for the conversion
to function pointer which simply returns the address of __invoke. This
approach makes it easier to evaluate a call to the conversion function
as a constant, makes the linkage of the __invoke function follow the
normal rules for member functions, and may make life easier down the
road if we ever want to constexpr'ify some of lambdas.

Note that IR generation is responsible for filling in the body of
__invoke (Sema just adds a dummy body), because the body can't
generally be expressed in C++.

Eli, please review!

Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaLambda.cpp
    cfe/trunk/test/CodeGenCXX/lambda-expressions.cpp

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=150783&r1=150782&r2=150783&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Thu Feb 16 21:02:34 2012
@@ -1565,6 +1565,15 @@
 
   bool hasInlineBody() const;
 
+  /// \brief Determine whether this is a lambda closure type's static member
+  /// function that is used for the result of the lambda's conversion to
+  /// function pointer (for a lambda with no captures).
+  ///
+  /// The function itself, if used, will have a placeholder body that will be
+  /// supplied by IR generation to either forward to the function call operator
+  /// or clone the function call operator.
+  bool isLambdaStaticInvoker() const;
+  
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classof(const CXXMethodDecl *D) { return true; }

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=150783&r1=150782&r2=150783&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Thu Feb 16 21:02:34 2012
@@ -1455,6 +1455,12 @@
   return CheckFn->hasBody(fn) && !fn->isOutOfLine();
 }
 
+bool CXXMethodDecl::isLambdaStaticInvoker() const {
+  return getParent()->isLambda() && 
+         getIdentifier() && getIdentifier()->getName() == "__invoke";
+}
+
+
 CXXCtorInitializer::CXXCtorInitializer(ASTContext &Context,
                                        TypeSourceInfo *TInfo, bool IsVirtual,
                                        SourceLocation L, Expr *Init,

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=150783&r1=150782&r2=150783&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Thu Feb 16 21:02:34 2012
@@ -1725,26 +1725,15 @@
   CGM.ErrorUnsupported(CurFuncDecl, "lambda conversion to block");
 }
 
-void CodeGenFunction::EmitLambdaThunkBody(llvm::Function *Fn,
-                                          const CGFunctionInfo &FnInfo,
-                                          const CXXRecordDecl *Lambda) {
+void CodeGenFunction::EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD) {
+  const CXXRecordDecl *Lambda = MD->getParent();
   DeclarationName Name
     = getContext().DeclarationNames.getCXXOperatorName(OO_Call);
   DeclContext::lookup_const_result Calls = Lambda->lookup(Name);
-  CXXMethodDecl *MD = cast<CXXMethodDecl>(*Calls.first++);
+  CXXMethodDecl *CallOperator = cast<CXXMethodDecl>(*Calls.first++);
   const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
   QualType ResultType = FPT->getResultType();
 
-  // Begin function
-  FunctionArgList FunctionArgs;
-  for (FunctionDecl::param_const_iterator I = MD->param_begin(),
-       E = MD->param_end(); I != E; ++I) {
-    ParmVarDecl *Param = *I;
-    FunctionArgs.push_back(Param);
-  }
-  StartFunction(GlobalDecl(), ResultType, Fn, FnInfo, FunctionArgs,
-                SourceLocation());
-
   // Start building arguments for forwarding call
   CallArgList CallArgs;
 
@@ -1760,7 +1749,7 @@
   }
 
   // Get the address of the call operator.
-  GlobalDecl GD(MD);
+  GlobalDecl GD(CallOperator);
   const CGFunctionInfo &CalleeFnInfo = CGM.getTypes().getFunctionInfo(GD);
   llvm::Type *Ty =
     CGM.getTypes().GetFunctionType(CalleeFnInfo, FPT->isVariadic());
@@ -1769,54 +1758,24 @@
   // Determine whether we have a return value slot to use.
   ReturnValueSlot Slot;
   if (!ResultType->isVoidType() &&
-      FnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect &&
+      CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect &&
       hasAggregateLLVMType(CurFnInfo->getReturnType()))
     Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified());
   
   // Now emit our call.
-  RValue RV = EmitCall(CalleeFnInfo, Callee, Slot, CallArgs, MD);
+  RValue RV = EmitCall(CalleeFnInfo, Callee, Slot, CallArgs, CallOperator);
 
   // Forward the returned value
   if (!ResultType->isVoidType() && Slot.isNull())
     EmitReturnOfRValue(RV, ResultType);
-
-  // End the function.
-  FinishFunction();
 }
 
-llvm::Constant *
-CodeGenFunction::EmitLambdaConvertedFnPtr(const CXXMethodDecl *MD) {
-  QualType FnTy = MD->getResultType()->getPointeeType();
-  CanQual<FunctionProtoType> CanFnTy =
-      CGM.getContext().getCanonicalType(FnTy)->getAs<FunctionProtoType>();
-  llvm::FunctionType *FnLLVMTy = cast<llvm::FunctionType>(CGM.getTypes().ConvertType(FnTy));
-  const CXXRecordDecl *Lambda = MD->getParent();
-  const CGFunctionInfo &FnInfo = CGM.getTypes().getFunctionInfo(CanFnTy);
-
-  if (CanFnTy->isVariadic()) {
+void CodeGenFunction::EmitLambdaStaticInvokeFunction(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 llvm::UndefValue::get(FnLLVMTy->getPointerTo());
   }
 
-  // Build a declaration for the function which this function will
-  // return a pointer to.
-  // FIXME: Should the "thunk" actually be part of the AST?  That would allow
-  // the conversion to function pointer to be constexpr...
-  std::string MangledName =
-      (llvm::Twine(CurFn->getName()) + "_lambdacallthunk").str();
-  llvm::Function *Fn =
-      llvm::Function::Create(FnLLVMTy, llvm::Function::InternalLinkage,
-                            MangledName, &CGM.getModule());
-
-  // Emit the definition of the new function.
-  CodeGenFunction(CGM).EmitLambdaThunkBody(Fn, FnInfo, Lambda);
-  return Fn;
-}
-
-void CodeGenFunction::EmitLambdaToFunctionPointerBody(FunctionArgList &Args) {
-  const CXXMethodDecl *MD = cast<CXXMethodDecl>(CurFuncDecl);
-  EmitReturnOfRValue(RValue::get(EmitLambdaConvertedFnPtr(MD)),
-                     MD->getResultType());
+  EmitLambdaDelegatingInvokeBody(MD);
 }

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=150783&r1=150782&r2=150783&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Thu Feb 16 21:02:34 2012
@@ -448,13 +448,15 @@
            FD->hasAttr<CUDAGlobalAttr>())
     CGM.getCUDARuntime().EmitDeviceStubBody(*this, Args);
   else if (isa<CXXConversionDecl>(FD) &&
-           cast<CXXConversionDecl>(FD)->getParent()->isLambda()) {
-    // The lambda conversion operators are special; the semantics can't be
-    // expressed in the AST, so IRGen needs to special-case them.
-    if (cast<CXXConversionDecl>(FD)->isLambdaToBlockPointerConversion())
-      EmitLambdaToBlockPointerBody(Args);
-    else
-      EmitLambdaToFunctionPointerBody(Args);
+           cast<CXXConversionDecl>(FD)->isLambdaToBlockPointerConversion()) {
+    // The lambda conversion to block pointer is special; the semantics can't be
+    // expressed in the AST, so IRGen needs to special-case it.
+    EmitLambdaToBlockPointerBody(Args);
+  } else if (isa<CXXMethodDecl>(FD) &&
+             cast<CXXMethodDecl>(FD)->isLambdaStaticInvoker()) {
+    // The lambda "__invoke" function is special, because it forwards or
+    // clones the body of the function call operator (but is actually static).
+    EmitLambdaStaticInvokeFunction(cast<CXXMethodDecl>(FD));
   }
   else
     EmitFunctionBody(Args);

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=150783&r1=150782&r2=150783&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Thu Feb 16 21:02:34 2012
@@ -1377,11 +1377,8 @@
   void EmitFunctionBody(FunctionArgList &Args);
 
   void EmitLambdaToBlockPointerBody(FunctionArgList &Args);
-  void EmitLambdaToFunctionPointerBody(FunctionArgList &Args);
-  llvm::Constant *EmitLambdaConvertedFnPtr(const CXXMethodDecl *MD);
-  void EmitLambdaThunkBody(llvm::Function *Fn,
-                           const CGFunctionInfo &FnInfo,
-                           const CXXRecordDecl *Lambda);
+  void EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD);
+  void EmitLambdaStaticInvokeFunction(const CXXMethodDecl *MD);
 
   /// EmitReturnBlock - Emit the unified return block, trying to avoid its
   /// emission when possible.

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=150783&r1=150782&r2=150783&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Thu Feb 16 21:02:34 2012
@@ -9051,21 +9051,52 @@
          isa<CXXMethodDecl>(FD);
 }
 
+/// \brief Mark the call operator of the given lambda closure type as "used".
+static void markLambdaCallOperatorUsed(Sema &S, CXXRecordDecl *Lambda) {
+  CXXMethodDecl *CallOperator 
+  = cast<CXXMethodDecl>(
+      *Lambda->lookup(
+        S.Context.DeclarationNames.getCXXOperatorName(OO_Call)).first);
+  CallOperator->setReferenced();
+  CallOperator->setUsed();
+}
+
 void Sema::DefineImplicitLambdaToFunctionPointerConversion(
        SourceLocation CurrentLocation,
        CXXConversionDecl *Conv) 
 {
+  CXXRecordDecl *Lambda = Conv->getParent();
+  
+  // Make sure that the lambda call operator is marked used.
+  markLambdaCallOperatorUsed(*this, Lambda);
+  
   Conv->setUsed();
   
   ImplicitlyDefinedFunctionScope Scope(*this, Conv);
   DiagnosticErrorTrap Trap(Diags);
   
-  // Introduce a bogus body, which IR generation will override anyway.
-  Conv->setBody(new (Context) CompoundStmt(Context, 0, 0, Conv->getLocation(),
+  // Return the address of the __invoke function.
+  DeclarationName InvokeName = &Context.Idents.get("__invoke");
+  CXXMethodDecl *Invoke 
+    = cast<CXXMethodDecl>(*Lambda->lookup(InvokeName).first);
+  Expr *FunctionRef = BuildDeclRefExpr(Invoke, Invoke->getType(),
+                                       VK_LValue, Conv->getLocation()).take();
+  assert(FunctionRef && "Can't refer to __invoke function?");
+  Stmt *Return = ActOnReturnStmt(Conv->getLocation(), FunctionRef).take();
+  Conv->setBody(new (Context) CompoundStmt(Context, &Return, 1, 
+                                           Conv->getLocation(),
                                            Conv->getLocation()));
+    
+  // Fill in the __invoke function with a dummy implementation. IR generation
+  // will fill in the actual details.
+  Invoke->setUsed();
+  Invoke->setReferenced();
+  Invoke->setBody(new (Context) CompoundStmt(Context, 0, 0, Conv->getLocation(),
+                                             Conv->getLocation()));
   
   if (ASTMutationListener *L = getASTMutationListener()) {
     L->CompletedImplicitDefinition(Conv);
+    L->CompletedImplicitDefinition(Invoke);
   }
 }
 
@@ -9073,6 +9104,8 @@
        SourceLocation CurrentLocation,
        CXXConversionDecl *Conv) 
 {
+  // Make sure that the lambda call operator is marked used.
+  markLambdaCallOperatorUsed(*this, Conv->getParent());
   Conv->setUsed();
   
   ImplicitlyDefinedFunctionScope Scope(*this, Conv);

Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=150783&r1=150782&r2=150783&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLambda.cpp Thu Feb 16 21:02:34 2012
@@ -372,17 +372,18 @@
                                          SourceRange IntroducerRange,
                                          CXXRecordDecl *Class,
                                          CXXMethodDecl *CallOperator) {
+  // Add the conversion to function pointer.
   const FunctionProtoType *Proto
     = CallOperator->getType()->getAs<FunctionProtoType>(); 
   QualType FunctionPtrTy;
+  QualType FunctionTy;
   {
     FunctionProtoType::ExtProtoInfo ExtInfo = Proto->getExtProtoInfo();
     ExtInfo.TypeQuals = 0;
-    QualType FunctionTy
-      = S.Context.getFunctionType(Proto->getResultType(),
-                                  Proto->arg_type_begin(),
-                                  Proto->getNumArgs(),
-                                  ExtInfo);
+    FunctionTy = S.Context.getFunctionType(Proto->getResultType(),
+                                           Proto->arg_type_begin(),
+                                           Proto->getNumArgs(),
+                                           ExtInfo);
     FunctionPtrTy = S.Context.getPointerType(FunctionTy);
   }
   
@@ -409,6 +410,34 @@
   Conversion->setAccess(AS_public);
   Conversion->setImplicit(true);
   Class->addDecl(Conversion);
+  
+  // Add a non-static member function "__invoke" that will be the result of
+  // the conversion.
+  Name = &S.Context.Idents.get("__invoke");
+  CXXMethodDecl *Invoke
+    = CXXMethodDecl::Create(S.Context, Class, Loc, 
+                            DeclarationNameInfo(Name, Loc), FunctionTy, 
+                            CallOperator->getTypeSourceInfo(),
+                            /*IsStatic=*/true, SC_Static, /*IsInline=*/true,
+                            /*IsConstexpr=*/false, 
+                            CallOperator->getBody()->getLocEnd());
+  SmallVector<ParmVarDecl *, 4> InvokeParams;
+  for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) {
+    ParmVarDecl *From = CallOperator->getParamDecl(I);
+    InvokeParams.push_back(ParmVarDecl::Create(S.Context, Invoke,
+                                               From->getLocStart(),
+                                               From->getLocation(),
+                                               From->getIdentifier(),
+                                               From->getType(),
+                                               From->getTypeSourceInfo(),
+                                               From->getStorageClass(),
+                                               From->getStorageClassAsWritten(),
+                                               /*DefaultArg=*/0));
+  }
+  Invoke->setParams(InvokeParams);
+  Invoke->setAccess(AS_private);
+  Invoke->setImplicit(true);
+  Class->addDecl(Invoke);
 }
 
 /// \brief Add a lambda's conversion to block pointer.

Modified: cfe/trunk/test/CodeGenCXX/lambda-expressions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/lambda-expressions.cpp?rev=150783&r1=150782&r2=150783&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/lambda-expressions.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/lambda-expressions.cpp Thu Feb 16 21:02:34 2012
@@ -47,8 +47,26 @@
 // CHECK: invoke i32 @"_ZZ1e1ES_bENK3$_4clEv"
 // CHECK: call void @"_ZZ1e1ES_bEN3$_4D1Ev"
 // CHECK: call void @"_ZZ1e1ES_bEN3$_4D1Ev"
+
 // CHECK: define internal i32 @"_ZZ1e1ES_bENK3$_4clEv"
 // CHECK: trunc i8
 // CHECK: load i32*
 // CHECK: ret i32
+
+void f() {
+  // CHECK: define void @_Z1fv()
+  // CHECK: {{call.*_5cvPFiiiEEv}}
+  // CHECK-NEXT: store i32 (i32, i32)*
+  // CHECK-NEXT: ret void
+  int (*fp)(int, int) = [](int x, int y){ return x + y; };
+}
+
+// CHECK: define internal i32 @"_ZZ1fvEN3$_58__invokeEii"
+// CHECK: store i32
+// CHECK-NEXT: store i32
+// CHECK-NEXT: load i32*
+// CHECK-NEXT: load i32*
+// CHECK-NEXT: call i32 @"_ZZ1fvENK3$_5clEii"
+// CHECK-NEXT: ret i32
+
 // CHECK: define internal void @"_ZZ1e1ES_bEN3$_4D2Ev"





More information about the cfe-commits mailing list