diff options
author | Sebastian Redl <sebastian.redl@getdesigned.at> | 2012-02-25 20:51:20 +0000 |
---|---|---|
committer | Sebastian Redl <sebastian.redl@getdesigned.at> | 2012-02-25 20:51:20 +0000 |
commit | 19b1a6eb2c90fab7cefe74bea5b6de490b65ac9d (patch) | |
tree | 1a0f6ea234bd4d6a3d4e8a523d0aad37d967ebba /lib/CodeGen/CodeGenModule.cpp | |
parent | babcf9d04f4ed9d7ac96812e42c9e8fc0f1ae2c2 (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
Diffstat (limited to 'lib/CodeGen/CodeGenModule.cpp')
-rw-r--r-- | lib/CodeGen/CodeGenModule.cpp | 119 |
1 files changed, 117 insertions, 2 deletions
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()) |