From: Sebastian Redl Date: Sat, 25 Feb 2012 20:51:20 +0000 (+0000) Subject: CodeGen support for global variables of type std::initializer_list. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=19b1a6eb2c90fab7cefe74bea5b6de490b65ac9d;p=clang CodeGen support for global variables of type std::initializer_list. 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 --- 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 cleanups; + if (const ExprWithCleanups *withCleanups = + dyn_cast(rawInit)) { + cleanups = withCleanups->getObjects(); + rawInit = withCleanups->getSubExpr(); + } + + const InitListExpr *init = dyn_cast(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( + 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(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()->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 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); + ~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 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 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 intlist{1, 2, i}; } -struct destroyme1 { - ~destroyme1(); -}; -struct destroyme2 { - ~destroyme2(); -}; -struct witharg1 { - witharg1(const destroyme1&); - ~witharg1(); -}; -struct wantslist1 { - wantslist1(std::initializer_list); - ~wantslist1(); -}; - void fn2() { // CHECK: define void @_Z3fn2v void target(std::initializer_list);