Index: llvm/trunk/docs/LangRef.rst =================================================================== --- llvm/trunk/docs/LangRef.rst +++ llvm/trunk/docs/LangRef.rst @@ -3152,14 +3152,18 @@ .. code-block:: llvm - %0 = type { i32, void ()* } - @llvm.global_ctors = appending global [1 x %0] [%0 { i32 65535, void ()* @ctor }] + %0 = type { i32, void ()*, i8* } + @llvm.global_ctors = appending global [1 x %0] [%0 { i32 65535, void ()* @ctor, i8* @data }] The ``@llvm.global_ctors`` array contains a list of constructor -functions and associated priorities. The functions referenced by this -array will be called in ascending order of priority (i.e. lowest first) -when the module is loaded. The order of functions with the same priority -is not defined. +functions, priorities, and an optional associated global or function. +The functions referenced by this array will be called in ascending order +of priority (i.e. lowest first) when the module is loaded. The order of +functions with the same priority is not defined. + +If the third field is present, non-null, and points to a global variable +or function, the initializer function will only run if the associated +data from the current module is not discarded. .. _llvmglobaldtors: @@ -3168,14 +3172,18 @@ .. code-block:: llvm - %0 = type { i32, void ()* } - @llvm.global_dtors = appending global [1 x %0] [%0 { i32 65535, void ()* @dtor }] + %0 = type { i32, void ()*, i8* } + @llvm.global_dtors = appending global [1 x %0] [%0 { i32 65535, void ()* @dtor, i8* @data }] -The ``@llvm.global_dtors`` array contains a list of destructor functions -and associated priorities. The functions referenced by this array will -be called in descending order of priority (i.e. highest first) when the -module is loaded. The order of functions with the same priority is not -defined. +The ``@llvm.global_dtors`` array contains a list of destructor +functions, priorities, and an optional associated global or function. +The functions referenced by this array will be called in descending +order of priority (i.e. highest first) when the module is loaded. The +order of functions with the same priority is not defined. + +If the third field is present, non-null, and points to a global variable +or function, the destructor function will only run if the associated +data from the current module is not discarded. Instruction Reference ===================== Index: llvm/trunk/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ llvm/trunk/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -67,10 +67,12 @@ MachineModuleInfo *MMI) const override; void InitializeELF(bool UseInitArray_); - const MCSection * - getStaticCtorSection(unsigned Priority = 65535) const override; - const MCSection * - getStaticDtorSection(unsigned Priority = 65535) const override; + const MCSection *getStaticCtorSection(unsigned Priority, + const MCSymbol *KeySym, + const MCSection *KeySec) const override; + const MCSection *getStaticDtorSection(unsigned Priority, + const MCSymbol *KeySym, + const MCSection *KeySec) const override; }; @@ -140,6 +142,13 @@ void emitModuleFlags(MCStreamer &Streamer, ArrayRef ModuleFlags, Mangler &Mang, const TargetMachine &TM) const override; + + const MCSection *getStaticCtorSection(unsigned Priority, + const MCSymbol *KeySym, + const MCSection *KeySec) const override; + const MCSection *getStaticDtorSection(unsigned Priority, + const MCSymbol *KeySym, + const MCSection *KeySec) const override; }; } // end namespace llvm Index: llvm/trunk/include/llvm/Target/TargetLoweringObjectFile.h =================================================================== --- llvm/trunk/include/llvm/Target/TargetLoweringObjectFile.h +++ llvm/trunk/include/llvm/Target/TargetLoweringObjectFile.h @@ -130,14 +130,15 @@ getTTypeReference(const MCSymbolRefExpr *Sym, unsigned Encoding, MCStreamer &Streamer) const; - virtual const MCSection * - getStaticCtorSection(unsigned Priority = 65535) const { - (void)Priority; + virtual const MCSection *getStaticCtorSection(unsigned Priority, + const MCSymbol *KeySym, + const MCSection *KeySec) const { return StaticCtorSection; } - virtual const MCSection * - getStaticDtorSection(unsigned Priority = 65535) const { - (void)Priority; + + virtual const MCSection *getStaticDtorSection(unsigned Priority, + const MCSymbol *KeySym, + const MCSection *KeySec) const { return StaticDtorSection; } Index: llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1700,8 +1700,11 @@ // Look for global variables which need to be renamed. for (Module::global_iterator GI = TheModule->global_begin(), GE = TheModule->global_end(); - GI != GE; ++GI) - UpgradeGlobalVariable(GI); + GI != GE;) { + GlobalVariable *GV = GI++; + UpgradeGlobalVariable(GV); + } + // Force deallocation of memory for these vectors to favor the client that // want lazy deserialization. std::vector >().swap(GlobalInits); Index: llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ llvm/trunk/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -1296,6 +1296,15 @@ } } +namespace { +struct Structor { + Structor() : Priority(0), Func(nullptr), ComdatKey(nullptr) {} + int Priority; + llvm::Constant *Func; + llvm::GlobalValue *ComdatKey; +}; +} // end namespace + /// EmitXXStructorList - Emit the ctor or dtor list taking into account the init /// priority. void AsmPrinter::EmitXXStructorList(const Constant *List, bool isCtor) { @@ -1307,37 +1316,52 @@ const ConstantArray *InitList = dyn_cast(List); if (!InitList) return; // Not an array! StructType *ETy = dyn_cast(InitList->getType()->getElementType()); - if (!ETy || ETy->getNumElements() != 2) return; // Not an array of pairs! + // FIXME: Only allow the 3-field form in LLVM 4.0. + if (!ETy || ETy->getNumElements() < 2 || ETy->getNumElements() > 3) + return; // Not an array of two or three elements! if (!isa(ETy->getTypeAtIndex(0U)) || !isa(ETy->getTypeAtIndex(1U))) return; // Not (int, ptr). + if (ETy->getNumElements() == 3 && !isa(ETy->getTypeAtIndex(2U))) + return; // Not (int, ptr, ptr). // Gather the structors in a form that's convenient for sorting by priority. - typedef std::pair Structor; SmallVector Structors; - for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) { - ConstantStruct *CS = dyn_cast(InitList->getOperand(i)); + for (Value *O : InitList->operands()) { + ConstantStruct *CS = dyn_cast(O); if (!CS) continue; // Malformed. if (CS->getOperand(1)->isNullValue()) break; // Found a null terminator, skip the rest. ConstantInt *Priority = dyn_cast(CS->getOperand(0)); if (!Priority) continue; // Malformed. - Structors.push_back(std::make_pair(Priority->getLimitedValue(65535), - CS->getOperand(1))); + Structors.push_back(Structor()); + Structor &S = Structors.back(); + S.Priority = Priority->getLimitedValue(65535); + S.Func = CS->getOperand(1); + if (ETy->getNumElements() == 3 && !CS->getOperand(2)->isNullValue()) + S.ComdatKey = dyn_cast(CS->getOperand(2)->stripPointerCasts()); } // Emit the function pointers in the target-specific order const DataLayout *DL = TM.getDataLayout(); unsigned Align = Log2_32(DL->getPointerPrefAlignment()); - std::stable_sort(Structors.begin(), Structors.end(), less_first()); - for (unsigned i = 0, e = Structors.size(); i != e; ++i) { + std::stable_sort(Structors.begin(), Structors.end(), + [](const Structor &L, + const Structor &R) { return L.Priority < R.Priority; }); + for (Structor &S : Structors) { + const TargetLoweringObjectFile &Obj = getObjFileLowering(); + const MCSymbol *KeySym = nullptr; + const MCSection *KeySec = nullptr; + if (S.ComdatKey) { + KeySym = getSymbol(S.ComdatKey); + KeySec = getObjFileLowering().SectionForGlobal(S.ComdatKey, *Mang, TM); + } const MCSection *OutputSection = - (isCtor ? - getObjFileLowering().getStaticCtorSection(Structors[i].first) : - getObjFileLowering().getStaticDtorSection(Structors[i].first)); + (isCtor ? Obj.getStaticCtorSection(S.Priority, KeySym, KeySec) + : Obj.getStaticDtorSection(S.Priority, KeySym, KeySec)); OutStreamer.SwitchSection(OutputSection); if (OutStreamer.getCurrentSection() != OutStreamer.getPreviousSection()) EmitAlignment(Align); - EmitXXStructor(Structors[i].second); + EmitXXStructor(S.Func); } } Index: llvm/trunk/lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- llvm/trunk/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ llvm/trunk/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -339,8 +339,8 @@ return DataRelROSection; } -const MCSection * -TargetLoweringObjectFileELF::getStaticCtorSection(unsigned Priority) const { +const MCSection *TargetLoweringObjectFileELF::getStaticCtorSection( + unsigned Priority, const MCSymbol *KeySym, const MCSection *KeySec) const { // The default scheme is .ctor / .dtor, so we have to invert the priority // numbering. if (Priority == 65535) @@ -359,8 +359,8 @@ } } -const MCSection * -TargetLoweringObjectFileELF::getStaticDtorSection(unsigned Priority) const { +const MCSection *TargetLoweringObjectFileELF::getStaticDtorSection( + unsigned Priority, const MCSymbol *KeySym, const MCSection *KeySec) const { // The default scheme is .ctor / .dtor, so we have to invert the priority // numbering. if (Priority == 65535) @@ -865,3 +865,32 @@ } } } + +static const MCSection *getAssociativeCOFFSection(MCContext &Ctx, + const MCSection *Sec, + const MCSymbol *KeySym, + const MCSection *KeySec) { + // Return the normal section if we don't have to be associative. + if (!KeySym) + return Sec; + + // Make an associative section with the same name and kind as the normal + // section. + const MCSectionCOFF *SecCOFF = cast(Sec); + const MCSectionCOFF *KeySecCOFF = cast(KeySec); + unsigned Characteristics = + SecCOFF->getCharacteristics() | COFF::IMAGE_SCN_LNK_COMDAT; + return Ctx.getCOFFSection(SecCOFF->getSectionName(), Characteristics, + SecCOFF->getKind(), KeySym->getName(), + COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE, KeySecCOFF); +} + +const MCSection *TargetLoweringObjectFileCOFF::getStaticCtorSection( + unsigned Priority, const MCSymbol *KeySym, const MCSection *KeySec) const { + return getAssociativeCOFFSection(getContext(), StaticCtorSection, KeySym, KeySec); +} + +const MCSection *TargetLoweringObjectFileCOFF::getStaticDtorSection( + unsigned Priority, const MCSymbol *KeySym, const MCSection *KeySec) const { + return getAssociativeCOFFSection(getContext(), StaticDtorSection, KeySym, KeySec); +} Index: llvm/trunk/lib/IR/AutoUpgrade.cpp =================================================================== --- llvm/trunk/lib/IR/AutoUpgrade.cpp +++ llvm/trunk/lib/IR/AutoUpgrade.cpp @@ -170,7 +170,59 @@ return Upgraded; } +static bool UpgradeGlobalStructors(GlobalVariable *GV) { + ArrayType *ATy = dyn_cast(GV->getType()->getElementType()); + StructType *OldTy = + ATy ? dyn_cast(ATy->getElementType()) : nullptr; + + // Only upgrade an array of a two field struct with the appropriate field + // types. + if (!OldTy || OldTy->getNumElements() != 2) + return false; + + // Get the upgraded 3 element type. + PointerType *VoidPtrTy = Type::getInt8Ty(GV->getContext())->getPointerTo(); + Type *Tys[3] = { + OldTy->getElementType(0), + OldTy->getElementType(1), + VoidPtrTy + }; + StructType *NewTy = + StructType::get(GV->getContext(), Tys, /*isPacked=*/false); + + // Build new constants with a null third field filled in. + ConstantArray *OldInit = dyn_cast(GV->getInitializer()); + if (!OldInit) + return false; + std::vector Initializers; + for (Use &U : OldInit->operands()) { + ConstantStruct *Init = cast(&U); + Constant *NewInit = + ConstantStruct::get(NewTy, Init->getOperand(0), Init->getOperand(1), + Constant::getNullValue(VoidPtrTy), nullptr); + Initializers.push_back(NewInit); + } + assert(Initializers.size() == ATy->getNumElements()); + + // Replace the old GV with a new one. + ATy = ArrayType::get(NewTy, Initializers.size()); + Constant *NewInit = ConstantArray::get(ATy, Initializers); + GlobalVariable *NewGV = new GlobalVariable( + *GV->getParent(), ATy, GV->isConstant(), GV->getLinkage(), NewInit, "", + GV, GV->getThreadLocalMode(), GV->getType()->getAddressSpace(), + GV->isExternallyInitialized()); + NewGV->copyAttributesFrom(GV); + NewGV->takeName(GV); + assert(GV->use_empty() && "program cannot use initializer list"); + GV->eraseFromParent(); + return true; +} + bool llvm::UpgradeGlobalVariable(GlobalVariable *GV) { + if (GV->getName() == "llvm.global_ctors" || + GV->getName() == "llvm.global_dtors") + return UpgradeGlobalStructors(GV); + // Nothing to do yet. return false; } Index: llvm/trunk/lib/IR/Verifier.cpp =================================================================== --- llvm/trunk/lib/IR/Verifier.cpp +++ llvm/trunk/lib/IR/Verifier.cpp @@ -396,14 +396,22 @@ "invalid linkage for intrinsic global variable", &GV); // Don't worry about emitting an error for it not being an array, // visitGlobalValue will complain on appending non-array. - if (ArrayType *ATy = dyn_cast(GV.getType())) { + if (ArrayType *ATy = dyn_cast(GV.getType()->getElementType())) { StructType *STy = dyn_cast(ATy->getElementType()); PointerType *FuncPtrTy = FunctionType::get(Type::getVoidTy(*Context), false)->getPointerTo(); - Assert1(STy && STy->getNumElements() == 2 && + // FIXME: Reject the 2-field form in LLVM 4.0. + Assert1(STy && (STy->getNumElements() == 2 || + STy->getNumElements() == 3) && STy->getTypeAtIndex(0u)->isIntegerTy(32) && STy->getTypeAtIndex(1) == FuncPtrTy, "wrong type for intrinsic global variable", &GV); + if (STy->getNumElements() == 3) { + Type *ETy = STy->getTypeAtIndex(2); + Assert1(ETy->isPointerTy() && + cast(ETy)->getElementType()->isIntegerTy(8), + "wrong type for intrinsic global variable", &GV); + } } } Index: llvm/trunk/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp =================================================================== --- llvm/trunk/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp +++ llvm/trunk/lib/Transforms/ObjCARC/ObjCARCAPElim.cpp @@ -155,8 +155,8 @@ for (User::op_iterator OI = Init->op_begin(), OE = Init->op_end(); OI != OE; ++OI) { Value *Op = *OI; - // llvm.global_ctors is an array of pairs where the second members - // are constructor functions. + // llvm.global_ctors is an array of three-field structs where the second + // members are constructor functions. Function *F = dyn_cast(cast(Op)->getOperand(1)); // If the user used a constructor function with the wrong signature and // it got bitcasted or whatever, look the other way. Index: llvm/trunk/lib/Transforms/Utils/CtorUtils.cpp =================================================================== --- llvm/trunk/lib/Transforms/Utils/CtorUtils.cpp +++ llvm/trunk/lib/Transforms/Utils/CtorUtils.cpp @@ -29,26 +29,28 @@ void installGlobalCtors(GlobalVariable *GCL, const std::vector &Ctors) { // If we made a change, reassemble the initializer list. - Constant *CSVals[2]; - CSVals[0] = ConstantInt::get(Type::getInt32Ty(GCL->getContext()), 65535); - CSVals[1] = nullptr; + Constant *CSVals[3]; StructType *StructTy = cast(GCL->getType()->getElementType()->getArrayElementType()); // Create the new init list. std::vector CAList; - for (unsigned i = 0, e = Ctors.size(); i != e; ++i) { - if (Ctors[i]) { - CSVals[1] = Ctors[i]; + for (Function *F : Ctors) { + Type *Int32Ty = Type::getInt32Ty(GCL->getContext()); + if (F) { + CSVals[0] = ConstantInt::get(Int32Ty, 65535); + CSVals[1] = F; } else { - Type *FTy = FunctionType::get(Type::getVoidTy(GCL->getContext()), false); - PointerType *PFTy = PointerType::getUnqual(FTy); - CSVals[1] = Constant::getNullValue(PFTy); - CSVals[0] = - ConstantInt::get(Type::getInt32Ty(GCL->getContext()), 0x7fffffff); + CSVals[0] = ConstantInt::get(Int32Ty, 0x7fffffff); + CSVals[1] = Constant::getNullValue(StructTy->getElementType(1)); } - CAList.push_back(ConstantStruct::get(StructTy, CSVals)); + // FIXME: Only allow the 3-field form in LLVM 4.0. + size_t NumElts = StructTy->getNumElements(); + if (NumElts > 2) + CSVals[2] = Constant::getNullValue(StructTy->getElementType(2)); + CAList.push_back( + ConstantStruct::get(StructTy, makeArrayRef(CSVals, NumElts))); } // Create the array initializer. Index: llvm/trunk/lib/Transforms/Utils/ModuleUtils.cpp =================================================================== --- llvm/trunk/lib/Transforms/Utils/ModuleUtils.cpp +++ llvm/trunk/lib/Transforms/Utils/ModuleUtils.cpp @@ -24,16 +24,16 @@ Module &M, Function *F, int Priority) { IRBuilder<> IRB(M.getContext()); FunctionType *FnTy = FunctionType::get(IRB.getVoidTy(), false); - StructType *Ty = StructType::get( - IRB.getInt32Ty(), PointerType::getUnqual(FnTy), NULL); - - Constant *RuntimeCtorInit = ConstantStruct::get( - Ty, IRB.getInt32(Priority), F, NULL); // Get the current set of static global constructors and add the new ctor // to the list. SmallVector CurrentCtors; - if (GlobalVariable * GVCtor = M.getNamedGlobal(Array)) { + StructType *EltTy; + if (GlobalVariable *GVCtor = M.getNamedGlobal(Array)) { + // If there is a global_ctors array, use the existing struct type, which can + // have 2 or 3 fields. + ArrayType *ATy = cast(GVCtor->getType()->getElementType()); + EltTy = cast(ATy->getElementType()); if (Constant *Init = GVCtor->getInitializer()) { unsigned n = Init->getNumOperands(); CurrentCtors.reserve(n + 1); @@ -41,13 +41,26 @@ CurrentCtors.push_back(cast(Init->getOperand(i))); } GVCtor->eraseFromParent(); + } else { + // Use a simple two-field struct if there isn't one already. + EltTy = StructType::get(IRB.getInt32Ty(), PointerType::getUnqual(FnTy), + nullptr); } + // Build a 2 or 3 field global_ctor entry. We don't take a comdat key. + Constant *CSVals[3]; + CSVals[0] = IRB.getInt32(Priority); + CSVals[1] = F; + // FIXME: Drop support for the two element form in LLVM 4.0. + if (EltTy->getNumElements() >= 3) + CSVals[2] = llvm::Constant::getNullValue(IRB.getInt8PtrTy()); + Constant *RuntimeCtorInit = + ConstantStruct::get(EltTy, makeArrayRef(CSVals, EltTy->getNumElements())); + CurrentCtors.push_back(RuntimeCtorInit); // Create a new initializer. - ArrayType *AT = ArrayType::get(RuntimeCtorInit->getType(), - CurrentCtors.size()); + ArrayType *AT = ArrayType::get(EltTy, CurrentCtors.size()); Constant *NewInit = ConstantArray::get(AT, CurrentCtors); // Create the new global variable and replace all uses of Index: llvm/trunk/test/Linker/global_ctors.ll =================================================================== --- llvm/trunk/test/Linker/global_ctors.ll +++ llvm/trunk/test/Linker/global_ctors.ll @@ -0,0 +1,29 @@ +; RUN: llvm-as %s -o %t.new.bc +; RUN: llvm-link %t.new.bc %S/Inputs/old_global_ctors.3.4.bc | llvm-dis | FileCheck %s + +; old_global_ctors.3.4.bc contains the following LLVM IL, assembled into +; bitcode by llvm-as from 3.4. It uses a two element @llvm.global_ctors array. +; --- +; declare void @a_global_ctor() +; declare void @b_global_ctor() +; +; @llvm.global_ctors = appending global [2 x { i32, void ()* } ] [ +; { i32, void ()* } { i32 65535, void ()* @a_global_ctor }, +; { i32, void ()* } { i32 65535, void ()* @b_global_ctor } +; ] +; --- + +declare void @c_global_ctor() +declare void @d_global_ctor() + +@llvm.global_ctors = appending global [2 x { i32, void ()*, i8* } ] [ + { i32, void ()*, i8* } { i32 65535, void ()* @c_global_ctor, i8* null }, + { i32, void ()*, i8* } { i32 65535, void ()* @d_global_ctor, i8* null } +] + +; CHECK: @llvm.global_ctors = appending global [4 x { i32, void ()*, i8* }] [ +; CHECK-DAG: { i32, void ()*, i8* } { i32 65535, void ()* @a_global_ctor, i8* null } +; CHECK-DAG: { i32, void ()*, i8* } { i32 65535, void ()* @b_global_ctor, i8* null } +; CHECK-DAG: { i32, void ()*, i8* } { i32 65535, void ()* @c_global_ctor, i8* null } +; CHECK-DAG: { i32, void ()*, i8* } { i32 65535, void ()* @d_global_ctor, i8* null } +; CHECK: ] Index: llvm/trunk/test/MC/COFF/global_ctors_dtors.ll =================================================================== --- llvm/trunk/test/MC/COFF/global_ctors_dtors.ll +++ llvm/trunk/test/MC/COFF/global_ctors_dtors.ll @@ -9,8 +9,13 @@ @.str2 = private unnamed_addr constant [12 x i8] c"destructing\00", align 1 @.str3 = private unnamed_addr constant [5 x i8] c"main\00", align 1 -@llvm.global_ctors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @a_global_ctor }] -@llvm.global_dtors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @a_global_dtor }] +%ini = type { i32, void()*, i8* } + +@llvm.global_ctors = appending global [2 x %ini ] [ + %ini { i32 65535, void ()* @a_global_ctor, i8* null }, + %ini { i32 65535, void ()* @b_global_ctor, i8* bitcast (i32* @b to i8*) } +] +@llvm.global_dtors = appending global [1 x %ini ] [%ini { i32 65535, void ()* @a_global_dtor, i8* null }] declare i32 @puts(i8*) @@ -19,6 +24,13 @@ ret void } +@b = global i32 zeroinitializer + +define void @b_global_ctor() nounwind { + store i32 42, i32* @b + ret void +} + define void @a_global_dtor() nounwind { %1 = call i32 @puts(i8* getelementptr inbounds ([12 x i8]* @.str2, i32 0, i32 0)) ret void @@ -31,9 +43,13 @@ ; WIN32: .section .CRT$XCU,"rd" ; WIN32: a_global_ctor +; WIN32: .section .CRT$XCU,"rd",associative .bss,{{_?}}b +; WIN32: b_global_ctor ; WIN32: .section .CRT$XTX,"rd" ; WIN32: a_global_dtor ; MINGW32: .section .ctors,"wd" ; MINGW32: a_global_ctor +; MINGW32: .section .ctors,"wd",associative .bss,{{_?}}b +; MINGW32: b_global_ctor ; MINGW32: .section .dtors,"wd" ; MINGW32: a_global_dtor Index: llvm/trunk/test/Transforms/GlobalOpt/ctor-list-opt.ll =================================================================== --- llvm/trunk/test/Transforms/GlobalOpt/ctor-list-opt.ll +++ llvm/trunk/test/Transforms/GlobalOpt/ctor-list-opt.ll @@ -1,5 +1,20 @@ -; RUN: opt < %s -globalopt -S | not grep CTOR -@llvm.global_ctors = appending global [11 x { i32, void ()* }] [ { i32, void ()* } { i32 65535, void ()* @CTOR1 }, { i32, void ()* } { i32 65535, void ()* @CTOR1 }, { i32, void ()* } { i32 65535, void ()* @CTOR2 }, { i32, void ()* } { i32 65535, void ()* @CTOR3 }, { i32, void ()* } { i32 65535, void ()* @CTOR4 }, { i32, void ()* } { i32 65535, void ()* @CTOR5 }, { i32, void ()* } { i32 65535, void ()* @CTOR6 }, { i32, void ()* } { i32 65535, void ()* @CTOR7 }, { i32, void ()* } { i32 65535, void ()* @CTOR8 }, { i32, void ()* } { i32 65535, void ()* @CTOR9 }, { i32, void ()* } { i32 2147483647, void ()* null } ] ; <[10 x { i32, void ()* }]*> [#uses=0] +; RUN: opt < %s -globalopt -S | FileCheck %s +; CHECK-NOT: CTOR +%ini = type { i32, void()*, i8* } +@llvm.global_ctors = appending global [11 x %ini] [ + %ini { i32 65535, void ()* @CTOR1, i8* null }, + %ini { i32 65535, void ()* @CTOR1, i8* null }, + %ini { i32 65535, void ()* @CTOR2, i8* null }, + %ini { i32 65535, void ()* @CTOR3, i8* null }, + %ini { i32 65535, void ()* @CTOR4, i8* null }, + %ini { i32 65535, void ()* @CTOR5, i8* null }, + %ini { i32 65535, void ()* @CTOR6, i8* null }, + %ini { i32 65535, void ()* @CTOR7, i8* null }, + %ini { i32 65535, void ()* @CTOR8, i8* null }, + %ini { i32 65535, void ()* @CTOR9, i8* null }, + %ini { i32 2147483647, void ()* null, i8* null } +] + @G = global i32 0 ; [#uses=1] @G2 = global i32 0 ; [#uses=1] @G3 = global i32 -123 ; [#uses=2] Index: llvm/trunk/test/Verifier/global-ctors.ll =================================================================== --- llvm/trunk/test/Verifier/global-ctors.ll +++ llvm/trunk/test/Verifier/global-ctors.ll @@ -0,0 +1,11 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +@llvm.global_ctors = appending global [1 x { i32, void()*, i8 } ] [ + { i32, void()*, i8 } { i32 65535, void ()* null, i8 0 } +] +; CHECK: wrong type for intrinsic global variable + +@llvm.global_dtors = appending global [1 x { i32, void()*, i8*, i8 } ] [ + { i32, void()*, i8*, i8 } { i32 65535, void ()* null, i8* null, i8 0} +] +; CHECK: wrong type for intrinsic global variable