Basic support for name mangling of C++11 lambda expressions. Because

name mangling in the Itanium C++ ABI for lambda expressions is so
dependent on context, we encode the number used to encode each lambda
as part of the lambda closure type, and maintain this value within
Sema.

Note that there are a several pieces still missing:
  - We still get the linkage of lambda expressions wrong
  - We aren't properly numbering or mangling lambda expressions that
  occur in default function arguments or in data member initializers.
  - We aren't (de-)serializing the lambda numbering tables

llvm-svn: 150982
This commit is contained in:
Douglas Gregor 2012-02-20 19:44:39 +00:00
parent a00c5c451a
commit 6379854457
17 changed files with 206 additions and 29 deletions

View File

@ -21,6 +21,7 @@
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/VersionTuple.h"
#include "clang/AST/Decl.h"
#include "clang/AST/LambdaMangleContext.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TemplateName.h"
@ -326,6 +327,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// expression used to copy the lambda object.
llvm::DenseMap<const CXXConversionDecl *, Expr *> LambdaBlockPointerInits;
/// \brief Mapping from each declaration context to its corresponding lambda
/// mangling context.
llvm::DenseMap<const DeclContext *, LambdaMangleContext> LambdaMangleContexts;
friend class CXXConversionDecl;
/// \brief Mapping that stores parameterIndex values for ParmVarDecls
@ -1765,6 +1770,8 @@ public:
/// it is not used.
bool DeclMustBeEmitted(const Decl *D);
/// \brief Retrieve the lambda mangling number for a lambda expression.
unsigned getLambdaManglingNumber(CXXMethodDecl *CallOperator);
/// \brief Used by ParmVarDecl to store on the side the
/// index of the parameter when it exceeds the size of the normal bitfield.

View File

@ -569,8 +569,12 @@ class CXXRecordDecl : public RecordDecl {
unsigned NumCaptures : 16;
/// \brief The number of explicit captures in this lambda.
unsigned NumExplicitCaptures : 15;
unsigned NumExplicitCaptures : 16;
/// \brief The number used to indicate this lambda expression for name
/// mangling in the Itanium C++ ABI.
unsigned ManglingNumber;
/// \brief The "extra" data associated with the lambda, including
/// captures, capture initializers, the body of the lambda, and the
/// array-index variables for array captures.
@ -1442,6 +1446,17 @@ public:
/// actually abstract.
bool mayBeAbstract() const;
/// \brief If this is the closure type of a lambda expression, retrieve the
/// number to be used for name mangling in the Itanium C++ ABI.
///
/// Zero indicates that this closure type has internal linkage, so the
/// mangling number does not matter, while a non-zero value indicates which
/// lambda expression this is in this particular context.
unsigned getLambdaManglingNumber() const {
assert(isLambda() && "Not a lambda closure type!");
return getLambdaData().ManglingNumber;
}
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) {
return K >= firstCXXRecord && K <= lastCXXRecord;

View File

@ -1166,7 +1166,8 @@ private:
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
SourceLocation ClosingBrace);
SourceLocation ClosingBrace,
unsigned ManglingNumber);
/// \brief Construct an empty lambda expression.
LambdaExpr(EmptyShell Empty, unsigned NumCaptures, bool HasArrayIndexVars)
@ -1204,7 +1205,8 @@ public:
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
SourceLocation ClosingBrace);
SourceLocation ClosingBrace,
unsigned ManglingNumber);
/// \brief Construct a new lambda expression that will be deserialized from
/// an external source.
@ -1296,7 +1298,7 @@ public:
/// \brief Whether this lambda had its result type explicitly specified.
bool hasExplicitResultType() const { return ExplicitResultType; }
static bool classof(const Stmt *T) {
return T->getStmtClass() == LambdaExprClass;
}

View File

@ -0,0 +1,36 @@
//===--- 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 "llvm/ADT/DenseMap.h"
namespace clang {
class CXXMethodDecl;
class FunctionProtoType;
/// \brief Keeps track of the mangled names of lambda expressions within a
/// particular context.
class 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

View File

@ -3555,7 +3555,10 @@ public:
/// ActOnLambdaExpr - This is called when the body of a lambda expression
/// was successfully completed.
ExprResult ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
Scope *CurScope, bool IsInstantiation = false);
Scope *CurScope,
llvm::Optional<unsigned> ManglingNumber
= llvm::Optional<unsigned>(),
bool IsInstantiation = false);
/// \brief Define the "body" of the conversion from a lambda object to a
/// function pointer.

View File

@ -6748,6 +6748,13 @@ size_t ASTContext::getSideTableAllocatedMemory() const {
+ llvm::capacity_in_bytes(ClassScopeSpecializationPattern);
}
unsigned ASTContext::getLambdaManglingNumber(CXXMethodDecl *CallOperator) {
CXXRecordDecl *Lambda = CallOperator->getParent();
return LambdaMangleContexts[Lambda->getDeclContext()]
.getManglingNumber(CallOperator);
}
void ASTContext::setParameterIndex(const ParmVarDecl *D, unsigned int index) {
ParamIndices[D] = index;
}

View File

@ -28,6 +28,7 @@ add_clang_library(clangAST
InheritViz.cpp
ItaniumCXXABI.cpp
ItaniumMangle.cpp
LambdaMangleContext.cpp
Mangle.cpp
MicrosoftCXXABI.cpp
MicrosoftMangle.cpp

View File

@ -764,7 +764,8 @@ LambdaExpr::LambdaExpr(QualType T,
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
SourceLocation ClosingBrace)
SourceLocation ClosingBrace,
unsigned ManglingNumber)
: Expr(LambdaExprClass, T, VK_RValue, OK_Ordinary,
T->isDependentType(), T->isDependentType(), T->isDependentType(),
/*ContainsUnexpandedParameterPack=*/false),
@ -785,6 +786,7 @@ LambdaExpr::LambdaExpr(QualType T,
ASTContext &Context = Class->getASTContext();
Data.NumCaptures = NumCaptures;
Data.NumExplicitCaptures = 0;
Data.ManglingNumber = ManglingNumber;
Data.Captures = (Capture *)Context.Allocate(sizeof(Capture) * NumCaptures);
Capture *ToCapture = Data.Captures;
for (unsigned I = 0, N = Captures.size(); I != N; ++I) {
@ -824,7 +826,8 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context,
ArrayRef<Expr *> CaptureInits,
ArrayRef<VarDecl *> ArrayIndexVars,
ArrayRef<unsigned> ArrayIndexStarts,
SourceLocation ClosingBrace) {
SourceLocation ClosingBrace,
unsigned ManglingNumber) {
// Determine the type of the expression (i.e., the type of the
// function object we're creating).
QualType T = Context.getTypeDeclType(Class);
@ -837,7 +840,7 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context,
return new (Mem) LambdaExpr(T, IntroducerRange, CaptureDefault,
Captures, ExplicitParams, ExplicitResultType,
CaptureInits, ArrayIndexVars, ArrayIndexStarts,
ClosingBrace);
ClosingBrace, ManglingNumber);
}
LambdaExpr *LambdaExpr::CreateDeserialized(ASTContext &C, unsigned NumCaptures,

View File

@ -122,6 +122,13 @@ public:
}
bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) {
// Lambda closure types with external linkage (indicated by a
// non-zero lambda mangling number) have their own numbering scheme, so
// they do not need a discriminator.
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(ND))
if (RD->isLambda() && RD->getLambdaManglingNumber() > 0)
return false;
unsigned &discriminator = Uniquifier[ND];
if (!discriminator)
discriminator = ++Discriminator;
@ -1076,6 +1083,38 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
break;
}
// <unnamed-type-name> ::= <closure-type-name>
//
// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
// <lambda-sig> ::= <parameter-type>+ # Parameter types or 'v' for 'void'.
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(TD)) {
if (Record->isLambda()) {
// FIXME: Figure out if we're in a function body, default argument,
// or initializer for a class member.
Out << "Ul";
DeclarationName Name
= getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
const FunctionProtoType *Proto
= cast<CXXMethodDecl>(*Record->lookup(Name).first)->getType()->
getAs<FunctionProtoType>();
mangleBareFunctionType(Proto, /*MangleReturnType=*/false);
Out << "E";
// The number is omitted for the first closure type with a given
// <lambda-sig> in a given context; it is n-2 for the nth closure type
// (in lexical order) with that same <lambda-sig> and context.
//
// The AST keeps track of the number for us.
if (unsigned Number = Record->getLambdaManglingNumber()) {
if (Number > 1)
mangleNumber(Number - 2);
}
Out << '_';
break;
}
}
// Get a unique id for the anonymous struct.
uint64_t AnonStructId = Context.getAnonymousStructId(TD);

View File

@ -0,0 +1,30 @@
//===--- 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/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->arg_type_begin(),
Proto->getNumArgs(),
FunctionProtoType::ExtProtoInfo());
Key = Context.getCanonicalType(Key);
return ++ManglingNumbers[Key->castAs<FunctionProtoType>()];
}

View File

@ -886,8 +886,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
BodyScope.Exit();
if (!Stmt.isInvalid())
return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.take(),
getCurScope());
return Actions.ActOnLambdaExpr(LambdaBeginLoc, Stmt.take(), getCurScope());
Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
return ExprError();

View File

@ -483,7 +483,9 @@ static void addBlockPointerConversion(Sema &S,
}
ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
Scope *CurScope, bool IsInstantiation) {
Scope *CurScope,
llvm::Optional<unsigned> ManglingNumber,
bool IsInstantiation) {
// Leave the expression-evaluation context.
DiscardCleanupsInEvaluationContext();
PopExpressionEvaluationContext();
@ -633,11 +635,19 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
if (LambdaExprNeedsCleanups)
ExprNeedsCleanups = true;
// If we don't already have a mangling number for this lambda expression,
// allocate one now.
if (!ManglingNumber) {
// FIXME: Default arguments, data member initializers are special.
ManglingNumber = Context.getLambdaManglingNumber(CallOperator);
}
LambdaExpr *Lambda = LambdaExpr::Create(Context, Class, IntroducerRange,
CaptureDefault, Captures,
ExplicitParams, ExplicitResultType,
CaptureInits, ArrayIndexVars,
ArrayIndexStarts, Body->getLocEnd());
ArrayIndexStarts, Body->getLocEnd(),
*ManglingNumber);
// C++11 [expr.prim.lambda]p2:
// A lambda-expression shall not appear in an unevaluated operand

View File

@ -7769,8 +7769,9 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
return ExprError();
}
unsigned ManglingNumber = E->getLambdaClass()->getLambdaManglingNumber();
return getSema().ActOnLambdaExpr(E->getLocStart(), Body.take(),
/*CurScope=*/0,
/*CurScope=*/0, ManglingNumber,
/*IsInstantiation=*/true);
}

View File

@ -1105,6 +1105,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
= static_cast<CXXRecordDecl::LambdaDefinitionData &>(Data);
Lambda.NumCaptures = Record[Idx++];
Lambda.NumExplicitCaptures = Record[Idx++];
Lambda.ManglingNumber = Record[Idx++];
Lambda.Captures
= (Capture*)Reader.Context.Allocate(sizeof(Capture)*Lambda.NumCaptures);
Capture *ToCapture = Lambda.Captures;

View File

@ -4332,6 +4332,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
CXXRecordDecl::LambdaDefinitionData &Lambda = D->getLambdaData();
Record.push_back(Lambda.NumCaptures);
Record.push_back(Lambda.NumExplicitCaptures);
Record.push_back(Lambda.ManglingNumber);
for (unsigned I = 0, N = Lambda.NumCaptures; I != N; ++I) {
LambdaExpr::Capture &Capture = Lambda.Captures[I];
AddSourceLocation(Capture.getLocation(), Record);

View File

@ -2,8 +2,8 @@
int a() { return []{ return 1; }(); }
// CHECK: define i32 @_Z1av
// CHECK: call i32 @"_ZZ1avENK3$_0clEv"
// CHECK: define internal i32 @"_ZZ1avENK3$_0clEv"
// CHECK: call i32 @_ZZ1avENKUlvE_clEv
// CHECK: define internal i32 @_ZZ1avENKUlvE_clEv
// CHECK: ret i32 1
int b(int x) { return [x]{return x;}(); }
@ -11,8 +11,8 @@ int b(int x) { return [x]{return x;}(); }
// CHECK: store i32
// CHECK: load i32*
// CHECK: store i32
// CHECK: call i32 @"_ZZ1biENK3$_1clEv"
// CHECK: define internal i32 @"_ZZ1biENK3$_1clEv"
// CHECK: call i32 @_ZZ1biENKUlvE_clEv
// CHECK: define internal i32 @_ZZ1biENKUlvE_clEv
// CHECK: load i32*
// CHECK: ret i32
@ -20,8 +20,8 @@ int c(int x) { return [&x]{return x;}(); }
// CHECK: define i32 @_Z1ci
// CHECK: store i32
// CHECK: store i32*
// CHECK: call i32 @"_ZZ1ciENK3$_2clEv"
// CHECK: define internal i32 @"_ZZ1ciENK3$_2clEv"
// CHECK: call i32 @_ZZ1ciENKUlvE_clEv
// CHECK: define internal i32 @_ZZ1ciENKUlvE_clEv
// CHECK: load i32**
// CHECK: load i32*
// CHECK: ret i32
@ -33,8 +33,8 @@ int d(int x) { D y[10]; [x,y] { return y[x].x; }(); }
// CHECK: call void @_ZN1DC1Ev
// CHECK: icmp ult i64 %{{.*}}, 10
// CHECK: call void @_ZN1DC1ERKS_
// CHECK: call i32 @"_ZZ1diENK3$_3clEv"
// CHECK: define internal i32 @"_ZZ1diENK3$_3clEv"
// CHECK: call i32 @_ZZ1diENKUlvE_clEv
// CHECK: define internal i32 @_ZZ1diENKUlvE_clEv
// CHECK: load i32*
// CHECK: load i32*
// CHECK: ret i32
@ -44,29 +44,29 @@ int e(E a, E b, bool cond) { [a,b,cond](){ return (cond ? a : b).x; }(); }
// CHECK: define i32 @_Z1e1ES_b
// CHECK: call void @_ZN1EC1ERKS_
// CHECK: invoke void @_ZN1EC1ERKS_
// CHECK: invoke i32 @"_ZZ1e1ES_bENK3$_4clEv"
// CHECK: call void @"_ZZ1e1ES_bEN3$_4D1Ev"
// CHECK: call void @"_ZZ1e1ES_bEN3$_4D1Ev"
// CHECK: invoke i32 @_ZZ1e1ES_bENKUlvE_clEv
// CHECK: call void @_ZZ1e1ES_bENUlvE_D1Ev
// CHECK: call void @_ZZ1e1ES_bENUlvE_D1Ev
// CHECK: define internal i32 @"_ZZ1e1ES_bENK3$_4clEv"
// CHECK: define internal i32 @_ZZ1e1ES_bENKUlvE_clEv
// CHECK: trunc i8
// CHECK: load i32*
// CHECK: ret i32
void f() {
// CHECK: define void @_Z1fv()
// CHECK: {{call.*_5cvPFiiiEEv}}
// CHECK: {{call.*@_ZZ1fvENKUliiE_cvPFiiiEEv}}
// 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: define internal i32 @_ZZ1fvENUliiE_8__invokeEii
// CHECK: store i32
// CHECK-NEXT: store i32
// CHECK-NEXT: load i32*
// CHECK-NEXT: load i32*
// CHECK-NEXT: call i32 @"_ZZ1fvENK3$_5clEii"
// CHECK-NEXT: call i32 @_ZZ1fvENKUliiE_clEii
// CHECK-NEXT: ret i32
// CHECK: define internal void @"_ZZ1e1ES_bEN3$_4D2Ev"
// CHECK: define internal void @_ZZ1e1ES_bENUlvE_D2Ev

View File

@ -0,0 +1,22 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s | FileCheck %s
// CHECK: define linkonce_odr void @_Z11inline_funci
inline void inline_func(int n) {
// CHECK: call i32 @_ZZ11inline_funciENKUlvE_clEv
int i = []{ return 1; }();
// CHECK: call i32 @_ZZ11inline_funciENKUlvE0_clEv
int j = [=] { return n + i; }();
// CHECK: call double @_ZZ11inline_funciENKUlvE1_clEv
int k = [=] () -> double { return n + i; }();
// CHECK: call i32 @_ZZ11inline_funciENKUliE_clEi
int l = [=] (int x) -> int { return x + i; }(n);
// CHECK: ret void
}
void call_inline_func() {
inline_func(17);
}