Skip to content

Commit

Permalink
Add comdat key field to llvm.global_ctors and llvm.global_dtors
Browse files Browse the repository at this point in the history
This allows us to put dynamic initializers for weak data into the same
comdat group as the data being initialized.  This is necessary for MSVC
ABI compatibility.  Once we have comdats for guard variables, we can use
the combination to help GlobalOpt fire more often for weak data with
guarded initialization on other platforms.

Reviewers: nlewycky

Differential Revision: http://reviews.llvm.org/D3499

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@209015 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
rnk committed May 16, 2014
1 parent dedd620 commit 1ce3088
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 69 deletions.
34 changes: 21 additions & 13 deletions docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3152,14 +3152,18 @@ The '``llvm.global_ctors``' Global Variable

.. 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:

Expand All @@ -3168,14 +3172,18 @@ The '``llvm.global_dtors``' Global Variable

.. 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, 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.

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.
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
=====================
Expand Down
17 changes: 13 additions & 4 deletions include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ class TargetLoweringObjectFileELF : public TargetLoweringObjectFile {
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;
};


Expand Down Expand Up @@ -140,6 +142,13 @@ class TargetLoweringObjectFileCOFF : public TargetLoweringObjectFile {
void emitModuleFlags(MCStreamer &Streamer,
ArrayRef<Module::ModuleFlagEntry> 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
Expand Down
13 changes: 7 additions & 6 deletions include/llvm/Target/TargetLoweringObjectFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,15 @@ class TargetLoweringObjectFile : public MCObjectFileInfo {
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;
}

Expand Down
7 changes: 5 additions & 2 deletions lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1700,8 +1700,11 @@ error_code BitcodeReader::GlobalCleanup() {
// 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<std::pair<GlobalVariable*, unsigned> >().swap(GlobalInits);
Expand Down
48 changes: 36 additions & 12 deletions lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,15 @@ void AsmPrinter::EmitLLVMUsedList(const ConstantArray *InitList) {
}
}

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) {
Expand All @@ -1307,37 +1316,52 @@ void AsmPrinter::EmitXXStructorList(const Constant *List, bool isCtor) {
const ConstantArray *InitList = dyn_cast<ConstantArray>(List);
if (!InitList) return; // Not an array!
StructType *ETy = dyn_cast<StructType>(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<IntegerType>(ETy->getTypeAtIndex(0U)) ||
!isa<PointerType>(ETy->getTypeAtIndex(1U))) return; // Not (int, ptr).
if (ETy->getNumElements() == 3 && !isa<PointerType>(ETy->getTypeAtIndex(2U)))
return; // Not (int, ptr, ptr).

// Gather the structors in a form that's convenient for sorting by priority.
typedef std::pair<unsigned, Constant *> Structor;
SmallVector<Structor, 8> Structors;
for (unsigned i = 0, e = InitList->getNumOperands(); i != e; ++i) {
ConstantStruct *CS = dyn_cast<ConstantStruct>(InitList->getOperand(i));
for (Value *O : InitList->operands()) {
ConstantStruct *CS = dyn_cast<ConstantStruct>(O);
if (!CS) continue; // Malformed.
if (CS->getOperand(1)->isNullValue())
break; // Found a null terminator, skip the rest.
ConstantInt *Priority = dyn_cast<ConstantInt>(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<GlobalValue>(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);
}
}

Expand Down
37 changes: 33 additions & 4 deletions lib/CodeGen/TargetLoweringObjectFileImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,8 @@ getSectionForConstant(SectionKind Kind) const {
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)
Expand All @@ -359,8 +359,8 @@ TargetLoweringObjectFileELF::getStaticCtorSection(unsigned Priority) const {
}
}

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)
Expand Down Expand Up @@ -865,3 +865,32 @@ emitModuleFlags(MCStreamer &Streamer,
}
}
}

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<MCSectionCOFF>(Sec);
const MCSectionCOFF *KeySecCOFF = cast<MCSectionCOFF>(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);
}
52 changes: 52 additions & 0 deletions lib/IR/AutoUpgrade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,59 @@ bool llvm::UpgradeIntrinsicFunction(Function *F, Function *&NewFn) {
return Upgraded;
}

static bool UpgradeGlobalStructors(GlobalVariable *GV) {
ArrayType *ATy = dyn_cast<ArrayType>(GV->getType()->getElementType());
StructType *OldTy =
ATy ? dyn_cast<StructType>(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<ConstantArray>(GV->getInitializer());
if (!OldInit)
return false;
std::vector<Constant *> Initializers;
for (Use &U : OldInit->operands()) {
ConstantStruct *Init = cast<ConstantStruct>(&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;
}
Expand Down
12 changes: 10 additions & 2 deletions lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,14 +396,22 @@ void Verifier::visitGlobalVariable(const GlobalVariable &GV) {
"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<ArrayType>(GV.getType())) {
if (ArrayType *ATy = dyn_cast<ArrayType>(GV.getType()->getElementType())) {
StructType *STy = dyn_cast<StructType>(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<PointerType>(ETy)->getElementType()->isIntegerTy(8),
"wrong type for intrinsic global variable", &GV);
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/Transforms/ObjCARC/ObjCARCAPElim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ bool ObjCARCAPElim::runOnModule(Module &M) {
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<Function>(cast<ConstantStruct>(Op)->getOperand(1));
// If the user used a constructor function with the wrong signature and
// it got bitcasted or whatever, look the other way.
Expand Down
26 changes: 14 additions & 12 deletions lib/Transforms/Utils/CtorUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,28 @@ namespace {
void installGlobalCtors(GlobalVariable *GCL,
const std::vector<Function *> &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<StructType>(GCL->getType()->getElementType()->getArrayElementType());

// Create the new init list.
std::vector<Constant *> 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.
Expand Down
Loading

0 comments on commit 1ce3088

Please sign in to comment.