aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Redl <sebastian.redl@getdesigned.at>2012-02-25 20:51:20 +0000
committerSebastian Redl <sebastian.redl@getdesigned.at>2012-02-25 20:51:20 +0000
commit19b1a6eb2c90fab7cefe74bea5b6de490b65ac9d (patch)
tree1a0f6ea234bd4d6a3d4e8a523d0aad37d967ebba
parentbabcf9d04f4ed9d7ac96812e42c9e8fc0f1ae2c2 (diff)
CodeGen support for global variables of type std::initializer_list<X>.
This emits a backing array with internal linkage and fills it with data, then has the initializer_list point at the array. Dynamic initialization and global destructors are correctly supported. What doesn't work is nested initializer_lists. I have no idea how to get them to work, either. However, these should be very rare, and so I'll just call it a known bug and declare generalized initializers DONE! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151457 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/CodeGen/CGExprConstant.cpp2
-rw-r--r--lib/CodeGen/CodeGenModule.cpp119
-rw-r--r--lib/CodeGen/CodeGenModule.h3
-rw-r--r--test/CodeGenCXX/cxx0x-initializer-stdinitializerlist-startend.cpp4
-rw-r--r--test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp47
5 files changed, 157 insertions, 18 deletions
diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp
index a074466ec5..2803640bda 100644
--- a/lib/CodeGen/CGExprConstant.cpp
+++ b/lib/CodeGen/CGExprConstant.cpp
@@ -369,7 +369,7 @@ void ConstStructBuilder::ConvertStructToPacked() {
bool ConstStructBuilder::Build(InitListExpr *ILE) {
if (ILE->initializesStdInitializerList()) {
- CGM.ErrorUnsupported(ILE, "global std::initializer_list");
+ //CGM.ErrorUnsupported(ILE, "global std::initializer_list");
return false;
}
diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp
index 20efd85956..ddd04a427c 100644
--- a/lib/CodeGen/CodeGenModule.cpp
+++ b/lib/CodeGen/CodeGenModule.cpp
@@ -39,6 +39,7 @@
#include "llvm/Module.h"
#include "llvm/Intrinsics.h"
#include "llvm/LLVMContext.h"
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Target/Mangler.h"
#include "llvm/Target/TargetData.h"
@@ -1359,6 +1360,112 @@ CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const {
TheTargetData.getTypeStoreSizeInBits(Ty));
}
+llvm::Constant *
+CodeGenModule::MaybeEmitGlobalStdInitializerListInitializer(const VarDecl *D,
+ const Expr *rawInit) {
+ ArrayRef<ExprWithCleanups::CleanupObject> cleanups;
+ if (const ExprWithCleanups *withCleanups =
+ dyn_cast<ExprWithCleanups>(rawInit)) {
+ cleanups = withCleanups->getObjects();
+ rawInit = withCleanups->getSubExpr();
+ }
+
+ const InitListExpr *init = dyn_cast<InitListExpr>(rawInit);
+ if (!init || !init->initializesStdInitializerList() ||
+ init->getNumInits() == 0)
+ return 0;
+
+ ASTContext &ctx = getContext();
+ // Synthesize a fake VarDecl for the array and initialize that.
+ unsigned numInits = init->getNumInits();
+ QualType elementType = init->getInit(0)->getType();
+ llvm::APInt numElements(ctx.getTypeSize(ctx.getSizeType()), numInits);
+ QualType arrayType = ctx.getConstantArrayType(elementType, numElements,
+ ArrayType::Normal, 0);
+
+ IdentifierInfo *name = &ctx.Idents.get(D->getNameAsString() + "__initlist");
+ TypeSourceInfo *sourceInfo = ctx.getTrivialTypeSourceInfo(
+ arrayType, D->getLocation());
+ VarDecl *backingArray = VarDecl::Create(ctx, const_cast<DeclContext*>(
+ D->getDeclContext()),
+ D->getLocStart(), D->getLocation(),
+ name, arrayType, sourceInfo,
+ SC_Static, SC_Static);
+
+ // Now clone the InitListExpr to initialize the array instead.
+ // Incredible hack: we want to use the existing InitListExpr here, so we need
+ // to tell it that it no longer initializes a std::initializer_list.
+ Expr *arrayInit = new (ctx) InitListExpr(ctx, init->getLBraceLoc(),
+ const_cast<InitListExpr*>(init)->getInits(),
+ init->getNumInits(),
+ init->getRBraceLoc());
+ arrayInit->setType(arrayType);
+
+ if (!cleanups.empty())
+ arrayInit = ExprWithCleanups::Create(ctx, arrayInit, cleanups);
+
+ backingArray->setInit(arrayInit);
+
+ // Emit the definition of the array.
+ EmitGlobalVarDefinition(backingArray);
+
+ // Inspect the initializer list to validate it and determine its type.
+ // FIXME: doing this every time is probably inefficient; caching would be nice
+ RecordDecl *record = init->getType()->castAs<RecordType>()->getDecl();
+ RecordDecl::field_iterator field = record->field_begin();
+ if (field == record->field_end()) {
+ ErrorUnsupported(D, "weird std::initializer_list");
+ return 0;
+ }
+ QualType elementPtr = ctx.getPointerType(elementType.withConst());
+ // Start pointer.
+ if (!ctx.hasSameType(field->getType(), elementPtr)) {
+ ErrorUnsupported(D, "weird std::initializer_list");
+ return 0;
+ }
+ ++field;
+ if (field == record->field_end()) {
+ ErrorUnsupported(D, "weird std::initializer_list");
+ return 0;
+ }
+ bool isStartEnd = false;
+ if (ctx.hasSameType(field->getType(), elementPtr)) {
+ // End pointer.
+ isStartEnd = true;
+ } else if(!ctx.hasSameType(field->getType(), ctx.getSizeType())) {
+ ErrorUnsupported(D, "weird std::initializer_list");
+ return 0;
+ }
+
+ // Now build an APValue representing the std::initializer_list.
+ APValue initListValue(APValue::UninitStruct(), 0, 2);
+ APValue &startField = initListValue.getStructField(0);
+ APValue::LValuePathEntry startOffsetPathEntry;
+ startOffsetPathEntry.ArrayIndex = 0;
+ startField = APValue(APValue::LValueBase(backingArray),
+ CharUnits::fromQuantity(0),
+ llvm::makeArrayRef(startOffsetPathEntry),
+ /*IsOnePastTheEnd=*/false, 0);
+
+ if (isStartEnd) {
+ APValue &endField = initListValue.getStructField(1);
+ APValue::LValuePathEntry endOffsetPathEntry;
+ endOffsetPathEntry.ArrayIndex = numInits;
+ endField = APValue(APValue::LValueBase(backingArray),
+ ctx.getTypeSizeInChars(elementType) * numInits,
+ llvm::makeArrayRef(endOffsetPathEntry),
+ /*IsOnePastTheEnd=*/true, 0);
+ } else {
+ APValue &sizeField = initListValue.getStructField(1);
+ sizeField = APValue(llvm::APSInt(numElements));
+ }
+
+ // Emit the constant for the initializer_list.
+ llvm::Constant *llvmInit = EmitConstantValue(initListValue, D->getType());
+ assert(llvmInit && "failed to initialize as constant");
+ return llvmInit;
+}
+
void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
llvm::Constant *Init = 0;
QualType ASTTy = D->getType();
@@ -1368,7 +1475,7 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
const VarDecl *InitDecl;
const Expr *InitExpr = D->getAnyInitializer(InitDecl);
-
+
if (!InitExpr) {
// This is a tentative definition; tentative definitions are
// implicitly initialized with { 0 }.
@@ -1382,7 +1489,15 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D) {
assert(!ASTTy->isIncompleteType() && "Unexpected incomplete type");
Init = EmitNullConstant(D->getType());
} else {
- Init = EmitConstantInit(*InitDecl);
+ // If this is a std::initializer_list, emit the special initializer.
+ Init = MaybeEmitGlobalStdInitializerListInitializer(D, InitExpr);
+ // An empty init list will perform zero-initialization, which happens
+ // to be exactly what we want.
+ // FIXME: It does so in a global constructor, which is *not* what we
+ // want.
+
+ if (!Init)
+ Init = EmitConstantInit(*InitDecl);
if (!Init) {
QualType T = InitExpr->getType();
if (D->getType()->isReferenceType())
diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h
index fa1830a386..8bbee6a284 100644
--- a/lib/CodeGen/CodeGenModule.h
+++ b/lib/CodeGen/CodeGenModule.h
@@ -55,6 +55,7 @@ namespace clang {
class Decl;
class Expr;
class Stmt;
+ class InitListExpr;
class StringLiteral;
class NamedDecl;
class ValueDecl;
@@ -877,6 +878,8 @@ private:
void EmitGlobalFunctionDefinition(GlobalDecl GD);
void EmitGlobalVarDefinition(const VarDecl *D);
+ llvm::Constant *MaybeEmitGlobalStdInitializerListInitializer(const VarDecl *D,
+ const Expr *init);
void EmitAliasDefinition(GlobalDecl GD);
void EmitObjCPropertyImplementations(const ObjCImplementationDecl *D);
void EmitObjCIvarInitializations(ObjCImplementationDecl *D);
diff --git a/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist-startend.cpp b/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist-startend.cpp
index 3ab44e5712..c533e453a5 100644
--- a/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist-startend.cpp
+++ b/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist-startend.cpp
@@ -32,6 +32,10 @@ namespace std {
};
}
+// CHECK: @_ZL25globalInitList1__initlist = internal global [3 x i32] [i32 1, i32 2, i32 3]
+// CHECK: @globalInitList1 = global {{[^ ]+}} { i32* getelementptr inbounds ([3 x i32]* @_ZL25globalInitList1__initlist, {{[^)]*}}), i32*
+std::initializer_list<int> globalInitList1 = {1, 2, 3};
+
void fn1(int i) {
// CHECK: define void @_Z3fn1i
// temporary array
diff --git a/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp b/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp
index 68754f436a..775060bfac 100644
--- a/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp
+++ b/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp
@@ -32,6 +32,38 @@ namespace std {
};
}
+struct destroyme1 {
+ ~destroyme1();
+};
+struct destroyme2 {
+ ~destroyme2();
+};
+struct witharg1 {
+ witharg1(const destroyme1&);
+ ~witharg1();
+};
+struct wantslist1 {
+ wantslist1(std::initializer_list<destroyme1>);
+ ~wantslist1();
+};
+
+// CHECK: @_ZL25globalInitList1__initlist = internal global [3 x i32] [i32 1, i32 2, i32 3]
+// CHECK: @globalInitList1 = global %{{[^ ]+}} { i32* getelementptr inbounds ([3 x i32]* @_ZL25globalInitList1__initlist, i32 0, i32 0), i{{32|64}} 3 }
+std::initializer_list<int> globalInitList1 = {1, 2, 3};
+
+// CHECK: @_ZL25globalInitList2__initlist = internal global [2 x %{{[^ ]*}}] zeroinitializer
+// CHECK: @globalInitList2 = global %{{[^ ]+}} { %[[WITHARG:[^ *]+]]* getelementptr inbounds ([2 x
+// CHECK: appending global
+// CHECK: define internal void
+// CHECK: call void @_ZN8witharg1C1ERK10destroyme1(%[[WITHARG]]* getelementptr inbounds ([2 x %[[WITHARG]]]* @_ZL25globalInitList2__initlist, i{{32|64}} 0, i{{32|64}} 0
+// CHECK: call void @_ZN8witharg1C1ERK10destroyme1(%[[WITHARG]]* getelementptr inbounds ([2 x %[[WITHARG]]]* @_ZL25globalInitList2__initlist, i{{32|64}} 0, i{{32|64}} 1
+// CHECK: __cxa_atexit
+// CHECK: call void @_ZN10destroyme1D1Ev
+// CHECK: call void @_ZN10destroyme1D1Ev
+std::initializer_list<witharg1> globalInitList2 = {
+ witharg1(destroyme1()), witharg1(destroyme1())
+};
+
void fn1(int i) {
// CHECK: define void @_Z3fn1i
// temporary array
@@ -52,21 +84,6 @@ void fn1(int i) {
std::initializer_list<int> intlist{1, 2, i};
}
-struct destroyme1 {
- ~destroyme1();
-};
-struct destroyme2 {
- ~destroyme2();
-};
-struct witharg1 {
- witharg1(const destroyme1&);
- ~witharg1();
-};
-struct wantslist1 {
- wantslist1(std::initializer_list<destroyme1>);
- ~wantslist1();
-};
-
void fn2() {
// CHECK: define void @_Z3fn2v
void target(std::initializer_list<destroyme1>);