WIP Patch: Introduce a proper mangling scheme for block literals

Eli Friedman eli.friedman at gmail.com
Wed Jun 12 16:42:11 PDT 2013


Take a C++ function like the following:

inline void f() {
  ^{

    static int i = 0;

    printf("%d\n", ++i);

  }();
}

Looks pretty simple, right?  Unfortunately, trunk clang doesn't handle this
correctly.  The primary issue here is we haven't defined the correct way to
mangle the name of "i", and clang's current scheme is unusable because it
depends on the details of IRGen.  (There's also the matter that we don't
compute the linkage correctly, but that's a simple IRGen bug.)

I'm attaching a patch which expands the scheme we use for mangling lambda
expressions in this sort of context to also work for block literals.  The
patch is still a bit of a mess: there's a bunch of copy-paste code I still
need to clean up, I haven't included tests, and it would probably be nice
to include the parameter types of the block in the mangling.  That said, it
essentially implements everything necessary.

Note that this doesn't touch the mangling scheme for the actual function
definitions for blocks; they don't need to be externally visible, so we
don't need to change the current mangling.

I'd like some feedback to make sure my patch is actually implementing
something sane.  Any suggestions for a better name
than LambdaBlockMangleContext are also welcome.

-Eli
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130612/09cd929c/attachment.html>
-------------- next part --------------
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h	(revision 183846)
+++ include/clang/AST/ASTContext.h	(working copy)
@@ -19,7 +19,7 @@
 #include "clang/AST/CanonicalType.h"
 #include "clang/AST/CommentCommandTraits.h"
 #include "clang/AST/Decl.h"
-#include "clang/AST/LambdaMangleContext.h"
+#include "clang/AST/LambdaBlockMangleContext.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/AST/RawCommentList.h"
@@ -334,9 +334,10 @@
   typedef llvm::TinyPtrVector<const CXXMethodDecl*> CXXMethodVector;
   llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector> OverriddenMethods;
 
-  /// \brief Mapping from each declaration context to its corresponding lambda 
-  /// mangling context.
-  llvm::DenseMap<const DeclContext *, LambdaMangleContext> LambdaMangleContexts;
+  /// \brief Mapping from each declaration context to its corresponding lambda
+  /// expression and block literal mangling context.
+  llvm::DenseMap<const DeclContext *, LambdaBlockMangleContext>
+      LambdaBlockMangleContexts;
 
   llvm::DenseMap<const DeclContext *, unsigned> UnnamedMangleContexts;
   llvm::DenseMap<const TagDecl *, unsigned> UnnamedMangleNumbers;
@@ -2116,6 +2117,12 @@
   /// \brief Retrieve the lambda mangling number for a lambda expression.
   unsigned getLambdaManglingNumber(CXXMethodDecl *CallOperator);
   
+  /// \brief Retrieve the block mangling number for a block literal.
+  ///
+  /// Note that this is not used to mangle the block literal itself; it
+  /// is only used for children of the block literal which need to be mangled.
+  unsigned getBlockManglingNumber(BlockDecl *BD);
+
   /// \brief Used by ParmVarDecl to store on the side the
   /// index of the parameter when it exceeds the size of the normal bitfield.
   void setParameterIndex(const ParmVarDecl *D, unsigned index);
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h	(revision 183846)
+++ include/clang/AST/Decl.h	(working copy)
@@ -3129,13 +3129,17 @@
   Capture *Captures;
   unsigned NumCaptures;
 
+  unsigned ManglingNumber;
+  Decl *ManglingContextDecl;
+
 protected:
   BlockDecl(DeclContext *DC, SourceLocation CaretLoc)
     : Decl(Block, DC, CaretLoc), DeclContext(Block),
       IsVariadic(false), CapturesCXXThis(false),
       BlockMissingReturnType(true), IsConversionFromLambda(false),
       ParamInfo(0), NumParams(0), Body(0),
-      SignatureAsWritten(0), Captures(0), NumCaptures(0) {}
+      SignatureAsWritten(0), Captures(0), NumCaptures(0),
+      ManglingNumber(0), ManglingContextDecl(0) {}
 
 public:
   static BlockDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L); 
@@ -3205,6 +3209,18 @@
                    const Capture *end,
                    bool capturesCXXThis);
 
+   unsigned getBlockManglingNumber() const {
+     return ManglingNumber;
+   }
+   Decl *getBlockManglingContextDecl() const {
+     return ManglingContextDecl;    
+   }
+
+  void setBlockMangling(unsigned Number, Decl *Ctx) {
+    ManglingNumber = Number;
+    ManglingContextDecl = Ctx;
+  }
+
   virtual SourceRange getSourceRange() const LLVM_READONLY;
 
   // Implement isa/cast/dyncast/etc.
Index: include/clang/AST/LambdaBlockMangleContext.h
===================================================================
--- include/clang/AST/LambdaBlockMangleContext.h	(working copy)
+++ include/clang/AST/LambdaBlockMangleContext.h	(working copy)
@@ -1,4 +1,4 @@
-//===--- LambdaMangleContext.h - Context for mangling lambdas ---*- C++ -*-===//
+//=== LambdaBlockMangleContext.h - Context for mangling ---------*- C++ -*-===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -7,8 +7,9 @@
 //
 //===----------------------------------------------------------------------===//
 //
-//  This file defines the LambdaMangleContext interface, which keeps track of
-//  the Itanium C++ ABI mangling numbers for lambda expressions.
+//  This file defines the LambdaBlockMangleContext interface, which keeps track
+//  of the Itanium C++ ABI mangling numbers for lambda expressions and block
+//  literals.
 //
 //===----------------------------------------------------------------------===//
 #ifndef LLVM_CLANG_LAMBDAMANGLECONTEXT_H
@@ -20,18 +21,24 @@
 
 namespace clang {
 
+class BlockDecl;
 class CXXMethodDecl;
-class FunctionProtoType;
+class Type;
 
-/// \brief Keeps track of the mangled names of lambda expressions within a
-/// particular context.
-class LambdaMangleContext : public RefCountedBase<LambdaMangleContext> {
-  llvm::DenseMap<const FunctionProtoType *, unsigned> ManglingNumbers;
+/// \brief Keeps track of the mangled names of lambda expressions and block
+/// literals within a particular context.
+class LambdaBlockMangleContext 
+    : public RefCountedBase<LambdaBlockMangleContext> {
+  llvm::DenseMap<const Type *, unsigned> ManglingNumbers;
   
 public:
   /// \brief Retrieve the mangling number of a new lambda expression with the
-  /// given call operator within this lambda context.
+  /// given call operator within this context.
   unsigned getManglingNumber(CXXMethodDecl *CallOperator);
+
+  /// \brief Retrieve the mangling number of a new block literal within this
+  /// context.
+  unsigned getManglingNumber(BlockDecl *BD);
 };
   
 } // end namespace clang
Index: include/clang/AST/LambdaMangleContext.h
===================================================================
--- include/clang/AST/LambdaMangleContext.h	(revision 183846)
+++ include/clang/AST/LambdaMangleContext.h	(working copy)
@@ -1,38 +0,0 @@
-//===--- LambdaMangleContext.h - Context for mangling lambdas ---*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-//  This file defines the LambdaMangleContext interface, which keeps track of
-//  the Itanium C++ ABI mangling numbers for lambda expressions.
-//
-//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_LAMBDAMANGLECONTEXT_H
-#define LLVM_CLANG_LAMBDAMANGLECONTEXT_H
-
-#include "clang/Basic/LLVM.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/IntrusiveRefCntPtr.h"
-
-namespace clang {
-
-class CXXMethodDecl;
-class FunctionProtoType;
-
-/// \brief Keeps track of the mangled names of lambda expressions within a
-/// particular context.
-class LambdaMangleContext : public RefCountedBase<LambdaMangleContext> {
-  llvm::DenseMap<const FunctionProtoType *, unsigned> ManglingNumbers;
-  
-public:
-  /// \brief Retrieve the mangling number of a new lambda expression with the
-  /// given call operator within this lambda context.
-  unsigned getManglingNumber(CXXMethodDecl *CallOperator);
-};
-  
-} // end namespace clang
-#endif
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 183846)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -20,7 +20,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/ExternalASTSource.h"
-#include "clang/AST/LambdaMangleContext.h"
+#include "clang/AST/LambdaBlockMangleContext.h"
 #include "clang/AST/NSAPI.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/AST/TypeLoc.h"
@@ -663,17 +663,17 @@
     /// is indeed an unevaluated context.
     SmallVector<LambdaExpr *, 2> Lambdas;
 
-    /// \brief The declaration that provides context for the lambda expression
-    /// if the normal declaration context does not suffice, e.g., in a
-    /// default function argument.
-    Decl *LambdaContextDecl;
+    /// \brief The declaration that provides context for lambda expressions
+    /// and block literals if the normal declaration context does not
+    /// suffice, e.g., in a default function argument.
+    Decl *LambdaBlockContextDecl;
 
     /// \brief The context information used to mangle lambda expressions
-    /// within this context.
+    /// and block literals within this context.
     ///
     /// This mangling information is allocated lazily, since most contexts
-    /// do not have lambda expressions.
-    IntrusiveRefCntPtr<LambdaMangleContext> LambdaMangle;
+    /// do not have lambda expressions or block literals.
+    IntrusiveRefCntPtr<LambdaBlockMangleContext> LambdaBlockMangle;
 
     /// \brief If we are processing a decltype type, a set of call expressions
     /// for which we have deferred checking the completeness of the return type.
@@ -686,18 +686,18 @@
     ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
                                       unsigned NumCleanupObjects,
                                       bool ParentNeedsCleanups,
-                                      Decl *LambdaContextDecl,
+                                      Decl *LambdaBlockContextDecl,
                                       bool IsDecltype)
       : Context(Context), ParentNeedsCleanups(ParentNeedsCleanups),
         IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects),
-        LambdaContextDecl(LambdaContextDecl), LambdaMangle() { }
+        LambdaBlockContextDecl(LambdaBlockContextDecl), LambdaBlockMangle() { }
 
     /// \brief Retrieve the mangling context for lambdas.
-    LambdaMangleContext &getLambdaMangleContext() {
-      assert(LambdaContextDecl && "Need to have a lambda context declaration");
-      if (!LambdaMangle)
-        LambdaMangle = new LambdaMangleContext;
-      return *LambdaMangle;
+    LambdaBlockMangleContext &getLambdaBlockMangleContext() {
+      assert(LambdaBlockContextDecl && "Need to have a context declaration");
+      if (!LambdaBlockMangle)
+        LambdaBlockMangle = new LambdaBlockMangleContext;
+      return *LambdaBlockMangle;
     }
 
     bool isUnevaluated() const {
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp	(revision 183846)
+++ lib/AST/ASTContext.cpp	(working copy)
@@ -8004,10 +8004,13 @@
 
 unsigned ASTContext::getLambdaManglingNumber(CXXMethodDecl *CallOperator) {
   CXXRecordDecl *Lambda = CallOperator->getParent();
-  return LambdaMangleContexts[Lambda->getDeclContext()]
+  return LambdaBlockMangleContexts[Lambda->getDeclContext()]
            .getManglingNumber(CallOperator);
 }
 
+unsigned ASTContext::getBlockManglingNumber(BlockDecl *BD) {
+  return LambdaBlockMangleContexts[BD->getDeclContext()].getManglingNumber(BD);
+}
 
 void ASTContext::setParameterIndex(const ParmVarDecl *D, unsigned int index) {
   ParamIndices[D] = index;
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp	(revision 183846)
+++ lib/AST/ItaniumMangle.cpp	(working copy)
@@ -1412,12 +1412,17 @@
     return;
 
   if (const BlockDecl *Block = dyn_cast<BlockDecl>(DC)) {
-    manglePrefix(getEffectiveParentContext(DC), NoFunction);    
-    SmallString<64> Name;
-    llvm::raw_svector_ostream NameStream(Name);
-    Context.mangleBlock(Block, NameStream);
-    NameStream.flush();
-    Out << Name.size() << Name;
+    manglePrefix(getEffectiveParentContext(DC), NoFunction);
+    if (unsigned Number = Block->getBlockManglingNumber()) {
+      Out << "14__block_prefix";
+      if (Number > 1)
+        mangleNumber(Number - 2);
+      Out << '_';
+      return;
+    }
+    Out << "23__block_prefix_internal";
+    Out << Context.getPrefixBlockId(Block);
+    Out << '_';
     return;
   } else if (isa<CapturedDecl>(DC)) {
     // Skip CapturedDecl context.
Index: lib/AST/LambdaBlockMangleContext.cpp
===================================================================
--- lib/AST/LambdaBlockMangleContext.cpp	(working copy)
+++ lib/AST/LambdaBlockMangleContext.cpp	(working copy)
@@ -1,4 +1,4 @@
-//===--- LambdaMangleContext.cpp - Context for mangling lambdas -*- C++ -*-===//
+//===--- LambdaBlockMangleContext.cpp - Context for mangling --------------===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -12,13 +12,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/AST/LambdaMangleContext.h"
+#include "clang/AST/LambdaBlockMangleContext.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclCXX.h"
 
 using namespace clang;
 
-unsigned LambdaMangleContext::getManglingNumber(CXXMethodDecl *CallOperator) {
+unsigned
+LambdaBlockMangleContext::getManglingNumber(CXXMethodDecl *CallOperator) {
   const FunctionProtoType *Proto
     = CallOperator->getType()->getAs<FunctionProtoType>();
   ASTContext &Context = CallOperator->getASTContext();
@@ -28,3 +29,10 @@
   Key = Context.getCanonicalType(Key);
   return ++ManglingNumbers[Key->castAs<FunctionProtoType>()];
 }
+
+unsigned
+LambdaBlockMangleContext::getManglingNumber(BlockDecl *BD) {
+  // FIXME: Compute a BlockPointerType?  Not obvious how.
+  const Type *Ty = 0;
+  return ++ManglingNumbers[Ty];
+}
Index: lib/AST/LambdaMangleContext.cpp
===================================================================
--- lib/AST/LambdaMangleContext.cpp	(revision 183846)
+++ lib/AST/LambdaMangleContext.cpp	(working copy)
@@ -1,30 +0,0 @@
-//===--- LambdaMangleContext.cpp - Context for mangling lambdas -*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-//  This file defines the LambdaMangleContext class, which keeps track of
-//  the Itanium C++ ABI mangling numbers for lambda expressions.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/AST/LambdaMangleContext.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/DeclCXX.h"
-
-using namespace clang;
-
-unsigned LambdaMangleContext::getManglingNumber(CXXMethodDecl *CallOperator) {
-  const FunctionProtoType *Proto
-    = CallOperator->getType()->getAs<FunctionProtoType>();
-  ASTContext &Context = CallOperator->getASTContext();
-
-  QualType Key = Context.getFunctionType(Context.VoidTy, Proto->getArgTypes(),
-                                         FunctionProtoType::ExtProtoInfo());
-  Key = Context.getCanonicalType(Key);
-  return ++ManglingNumbers[Key->castAs<FunctionProtoType>()];
-}
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp	(revision 183846)
+++ lib/CodeGen/CGDecl.cpp	(working copy)
@@ -130,8 +130,8 @@
     // standard way to agree on which variables are the same (i.e.
     // there's no mangling).
     if (getLangOpts().CPlusPlus)
-      if (llvm::GlobalValue::isWeakForLinker(CurFn->getLinkage()))
-        Linkage = CurFn->getLinkage();
+      if (llvm::GlobalValue::isWeakForLinker(CGM.getFunctionLinkage(CurGD)))
+        Linkage = CGM.getFunctionLinkage(CurGD);
 
     return EmitStaticVarDecl(D, Linkage);
   }
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp	(revision 183846)
+++ lib/Sema/SemaExpr.cpp	(working copy)
@@ -9809,6 +9809,20 @@
 // Clang Extensions.
 //===----------------------------------------------------------------------===//
 
+/// \brief Determine whether the given context is or is enclosed in an inline
+/// function.
+static bool isInInlineFunction(const DeclContext *DC) {
+  while (!DC->isFileContext()) {
+    if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DC))
+      if (FD->isInlined())
+        return true;
+    
+    DC = DC->getLexicalParent();
+  }
+  
+  return false;
+}
+
 /// ActOnBlockStart - This callback is invoked when a block literal is started.
 void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope) {
   BlockDecl *Block = BlockDecl::Create(Context, CurContext, CaretLoc);
@@ -9938,11 +9952,7 @@
   // Finally we can process decl attributes.
   ProcessDeclAttributes(CurScope, CurBlock->TheDecl, ParamInfo);
 
-  // Put the parameter variables in scope.  We can bail out immediately
-  // if we don't have any.
-  if (Params.empty())
-    return;
-
+  // Put the parameter variables in scope.
   for (BlockDecl::param_iterator AI = CurBlock->TheDecl->param_begin(),
          E = CurBlock->TheDecl->param_end(); AI != E; ++AI) {
     (*AI)->setOwningFunction(CurBlock->TheDecl);
@@ -9954,6 +9964,76 @@
       PushOnScopeChains(*AI, CurBlock->TheScope);
     }
   }
+
+  // Allocate a mangling number for this lambda expression, if the ABI
+  // requires one.
+  Decl *ContextDecl = ExprEvalContexts.back().LambdaBlockContextDecl;
+
+  enum ContextKind {
+    Normal,
+    DefaultArgument,
+    DataMember,
+    StaticDataMember
+  } Kind = Normal;
+
+  // Default arguments of member function parameters that appear in a class
+  // definition, as well as the initializers of data members, receive special
+  // treatment. Identify them.
+  if (ContextDecl) {
+    if (ParmVarDecl *Param = dyn_cast<ParmVarDecl>(ContextDecl)) {
+      if (const DeclContext *LexicalDC
+          = Param->getDeclContext()->getLexicalParent())
+        if (LexicalDC->isRecord())
+          Kind = DefaultArgument;
+    } else if (VarDecl *Var = dyn_cast<VarDecl>(ContextDecl)) {
+      if (Var->getDeclContext()->isRecord())
+        Kind = StaticDataMember;
+    } else if (isa<FieldDecl>(ContextDecl)) {
+      Kind = DataMember;
+    }
+  }
+
+  // Itanium ABI [5.1.7]:
+  //   In the following contexts [...] the one-definition rule requires closure
+  //   types in different translation units to "correspond":
+  bool IsInNonspecializedTemplate =
+    !ActiveTemplateInstantiations.empty() || CurContext->isDependentContext();
+  unsigned ManglingNumber;
+  switch (Kind) {
+  case Normal:
+    //  -- the bodies of non-exported nonspecialized template functions
+    //  -- the bodies of inline functions
+    if ((IsInNonspecializedTemplate &&
+         !(ContextDecl && isa<ParmVarDecl>(ContextDecl))) ||
+        isInInlineFunction(CurContext))
+      ManglingNumber = Context.getBlockManglingNumber(CurBlock->TheDecl);
+    else
+      ManglingNumber = 0;
+
+    // There is no special context for this lambda.
+    ContextDecl = 0;
+    break;
+
+  case StaticDataMember:
+    //  -- the initializers of nonspecialized static members of template classes
+    if (!IsInNonspecializedTemplate) {
+      ManglingNumber = 0;
+      ContextDecl = 0;
+      break;
+    }
+    // Fall through to assign a mangling number.
+
+  case DataMember:
+    //  -- the in-class initializers of class members
+  case DefaultArgument:
+    //  -- default arguments appearing in class definitions
+    ManglingNumber = ExprEvalContexts.back().getLambdaBlockMangleContext()
+                       .getManglingNumber(CurBlock->TheDecl);
+    break;
+  }
+
+  CurBlock->TheDecl->setBlockMangling(ManglingNumber, ContextDecl);
+  
 }
 
 /// ActOnBlockError - If there is an error parsing a block, this callback
@@ -10650,8 +10730,10 @@
 Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
                                       ReuseLambdaContextDecl_t,
                                       bool IsDecltype) {
-  Decl *LambdaContextDecl = ExprEvalContexts.back().LambdaContextDecl;
-  PushExpressionEvaluationContext(NewContext, LambdaContextDecl, IsDecltype);
+  Decl *LambdaBlockContextDecl =
+      ExprEvalContexts.back().LambdaBlockContextDecl;
+  PushExpressionEvaluationContext(NewContext, LambdaBlockContextDecl,
+                                  IsDecltype);
 }
 
 void Sema::PopExpressionEvaluationContext() {
Index: lib/Sema/SemaLambda.cpp
===================================================================
--- lib/Sema/SemaLambda.cpp	(revision 183846)
+++ lib/Sema/SemaLambda.cpp	(working copy)
@@ -100,7 +100,7 @@
 
   // Allocate a mangling number for this lambda expression, if the ABI
   // requires one.
-  Decl *ContextDecl = ExprEvalContexts.back().LambdaContextDecl;
+  Decl *ContextDecl = ExprEvalContexts.back().LambdaBlockContextDecl;
 
   enum ContextKind {
     Normal,
@@ -160,7 +160,7 @@
     //  -- the in-class initializers of class members
   case DefaultArgument:
     //  -- default arguments appearing in class definitions
-    ManglingNumber = ExprEvalContexts.back().getLambdaMangleContext()
+    ManglingNumber = ExprEvalContexts.back().getLambdaBlockMangleContext()
                        .getManglingNumber(Method);
     break;
   }


More information about the cfe-commits mailing list