Skip to content

Commit

Permalink
Implement new ModuleInfo discovery/druntime startup scheme on Linux.
Browse files Browse the repository at this point in the history
This is the same implementation that is used for DMD and
supports shared libraries (not yet tested).
  • Loading branch information
dnadlinger authored and redstar committed Oct 29, 2013
1 parent 82c202b commit d9b137b
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 7 deletions.
181 changes: 177 additions & 4 deletions gen/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#if LDC_LLVM_VER >= 303
#include "llvm/IR/Module.h"
#include "llvm/IR/DataLayout.h"
Expand Down Expand Up @@ -326,6 +327,173 @@ static LLFunction* build_module_reference_and_ctor(LLConstant* moduleinfo)
return ctor;
}

/// Builds the body for the ldc.dso_ctor and ldc.dso_dtor functions.
///
/// Pseudocode:
/// if (dsoInitialized == executeWhenInitialized) {
/// dsoInitiaized = !executeWhenInitialized;
/// auto record = CompilerDSOData(1, dsoSlot, minfoBeg, minfoEnd);
/// _d_dso_registry(&record);
/// }
static void build_dso_ctor_dtor_body(
llvm::Function* targetFunc,
llvm::GlobalVariable* dsoInitiaized,
llvm::GlobalVariable* dsoSlot,
llvm::GlobalVariable* minfoBeg,
llvm::GlobalVariable* minfoEnd,
bool executeWhenInitialized
) {
llvm::Function* const dsoRegistry = LLVM_D_GetRuntimeFunction(
gIR->module, "_d_dso_registry");
llvm::Type* const recordPtrTy = dsoRegistry->getFunctionType()->getContainedType(1);
llvm::Type* const recordTy = recordPtrTy->getContainedType(0);

llvm::BasicBlock* const entryBB =
llvm::BasicBlock::Create(gIR->context(), "", targetFunc);
llvm::BasicBlock* const initBB =
llvm::BasicBlock::Create(gIR->context(), "init", targetFunc);
llvm::BasicBlock* const endBB =
llvm::BasicBlock::Create(gIR->context(), "end", targetFunc);

{
IRBuilder<> b(entryBB);
llvm::Value* initialized = b.CreateLoad(dsoInitiaized);
if (executeWhenInitialized)
b.CreateCondBr(initialized, initBB, endBB);
else
b.CreateCondBr(initialized, endBB, initBB);
}
{
IRBuilder<> b(initBB);
b.CreateStore(b.getInt1(!executeWhenInitialized), dsoInitiaized);

llvm::Value* record = b.CreateAlloca(recordTy);
b.CreateStore(DtoConstSize_t(1), b.CreateStructGEP(record, 0)); // version
b.CreateStore(dsoSlot, b.CreateStructGEP(record, 1)); // slot
b.CreateStore(minfoBeg, b.CreateStructGEP(record, 2));
b.CreateStore(minfoEnd, b.CreateStructGEP(record, 3));

b.CreateCall(dsoRegistry, record);
b.CreateBr(endBB);
}
{
IRBuilder<> b(endBB);
b.CreateRetVoid();
}
}

static void build_dso_registry_calls(llvm::Constant* thisModuleInfo)
{
// Build the ModuleInfo reference and bracketing symbols.
llvm::Type* const moduleInfoPtrTy =
getPtrToType(DtoType(Module::moduleinfo->type));

// Order is important here: We must create the symbols in the
// bracketing sections right before/after the ModuleInfo reference
// so that they end up in the correct order in the object file.
llvm::GlobalVariable* minfoBeg = new llvm::GlobalVariable(
*gIR->module,
moduleInfoPtrTy,
true,
llvm::GlobalValue::LinkOnceODRLinkage,
getNullPtr(moduleInfoPtrTy),
"_minfo_beg"
);
minfoBeg->setSection(".minfo_beg");
minfoBeg->setVisibility(llvm::GlobalValue::HiddenVisibility);

std::string thismrefname = "_D";
thismrefname += gIR->dmodule->mangle();
thismrefname += "11__moduleRefZ";
llvm::GlobalVariable* thismref = new llvm::GlobalVariable(
*gIR->module,
moduleInfoPtrTy,
true,
llvm::GlobalValue::LinkOnceODRLinkage,
DtoBitCast(thisModuleInfo, moduleInfoPtrTy),
thismrefname
);
thismref->setSection(".minfo");
gIR->usedArray.push_back(thismref);

llvm::GlobalVariable* minfoEnd = new llvm::GlobalVariable(
*gIR->module,
moduleInfoPtrTy,
true,
llvm::GlobalValue::LinkOnceODRLinkage,
getNullPtr(moduleInfoPtrTy),
"_minfo_end"
);
minfoEnd->setSection(".minfo_end");
minfoEnd->setVisibility(llvm::GlobalValue::HiddenVisibility);

// Build the ctor to invoke _d_dso_registry.
llvm::GlobalVariable* dsoSlot = new llvm::GlobalVariable(
*gIR->module,
getVoidPtrType(),
false,
llvm::GlobalValue::LinkOnceODRLinkage,
getNullPtr(getVoidPtrType()),
"ldc.dso_slot"
);
dsoSlot->setVisibility(llvm::GlobalValue::HiddenVisibility);
llvm::GlobalVariable* dsoInitiaized = new llvm::GlobalVariable(
*gIR->module,
llvm::Type::getInt1Ty(gIR->context()),
false,
llvm::GlobalValue::LinkOnceODRLinkage,
llvm::ConstantInt::getFalse(gIR->context()),
"ldc.dso_initialized"
);
dsoInitiaized->setVisibility(llvm::GlobalValue::HiddenVisibility);

llvm::Function* dsoCtor = llvm::Function::Create(
llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false),
llvm::GlobalValue::LinkOnceODRLinkage,
"ldc.dso_ctor",
gIR->module
);
dsoCtor->setVisibility(llvm::GlobalValue::HiddenVisibility);
build_dso_ctor_dtor_body(dsoCtor, dsoInitiaized, dsoSlot, minfoBeg, minfoEnd, false);
llvm::appendToGlobalCtors(*gIR->module, dsoCtor, 65535);

llvm::Function* dsoDtor = llvm::Function::Create(
llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false),
llvm::GlobalValue::LinkOnceODRLinkage,
"ldc.dso_dtor",
gIR->module
);
dsoDtor->setVisibility(llvm::GlobalValue::HiddenVisibility);
build_dso_ctor_dtor_body(dsoDtor, dsoInitiaized, dsoSlot, minfoBeg, minfoEnd, true);
llvm::appendToGlobalDtors(*gIR->module, dsoDtor, 65535);

This comment has been minimized.

Copy link
@MartinNowak

MartinNowak Dec 2, 2013

Contributor

You should actually be able to just emit one function and a reference into .ctors/.dtors each.
The _d_dso_registry implementation can tag the shared library using the dsoSlot to detect whether it should run ctor or dtor.
Also you could save the dsoInitiaized guard.

This comment has been minimized.

Copy link
@dnadlinger

dnadlinger Dec 2, 2013

Author Member

I should be able, yes, but I didn't find a way to get this to work on the LLVM IR level. Module-level inline assembly would be an option in theory, but then, we can't do LTO anymore, as the LLVM linker wouldn't know how to coalesce them, and I couldn't find a way to make the MC asm parsers just ignore the subsequent instances. The proper solution would be to use linkonce_odr globals with the section attribute set to .ctors.<something>, but unfortunately, MC doesn't currently handle this case properly (specifying a custom section causes the weak linkage part to be disregarded).

And once the constructor is called multiple times, just using dsoSlot to distinguish between initialization/destruction doesn't work anymore.

Thinking of it, I should add the above as a source comment, or at least write that blog post on the topic I have been meaning do to for ages. -.-

This comment has been minimized.

Copy link
@MartinNowak

MartinNowak Dec 2, 2013

Contributor

And once the constructor is called multiple times, just using dsoSlot to distinguish between initialization/destruction doesn't work anymore.

Why is it called multiple times? Can't you mark the .ctors entries as weak symbols too?

This comment has been minimized.

Copy link
@dnadlinger

dnadlinger Dec 2, 2013

Author Member

With current LLVM MC, you can either mark a symbol as weak, or specify a custom section, but not both. It's likely an unintended restriction/bug, but I wanted a solution that works now, with stock LLVM.

This comment has been minimized.

Copy link
@MartinNowak

MartinNowak Dec 2, 2013

Contributor

With current LLVM MC, you can either mark a symbol as weak, or specify a custom section, but not both.

I see, using a boolean gate won't have noticeable performance implications.

}

static void build_llvm_used_array(IRState* p)
{
if (p->usedArray.empty()) return;

std::vector<llvm::Constant*> usedVoidPtrs;
usedVoidPtrs.reserve(p->usedArray.size());

for (std::vector<llvm::Constant*>::iterator it = p->usedArray.begin(),
end = p->usedArray.end(); it != end; ++it)
{
usedVoidPtrs.push_back(DtoBitCast(*it, getVoidPtrType()));
}

llvm::ArrayType *arrayType = llvm::ArrayType::get(
getVoidPtrType(), usedVoidPtrs.size());
llvm::GlobalVariable* llvmUsed = new llvm::GlobalVariable(
*p->module,
arrayType,
false,
llvm::GlobalValue::AppendingLinkage,
llvm::ConstantArray::get(arrayType, usedVoidPtrs),
"llvm.used"
);
llvmUsed->setSection("llvm.metadata");
}

llvm::Module* Module::genLLVMModule(llvm::LLVMContext& context)
{
bool logenabled = Logger::enabled();
Expand Down Expand Up @@ -410,6 +578,8 @@ llvm::Module* Module::genLLVMModule(llvm::LLVMContext& context)
// generate ModuleInfo
genmoduleinfo();

build_llvm_used_array(&ir);

#if LDC_LLVM_VER >= 303
// Add the linker options metadata flag.
ir.module->addModuleFlag(llvm::Module::AppendUnique, "Linker Options",
Expand Down Expand Up @@ -632,8 +802,11 @@ void Module::genmoduleinfo()
b.finalize(moduleInfoSym->getType()->getPointerElementType(), moduleInfoSym);
moduleInfoSym->setLinkage(llvm::GlobalValue::ExternalLinkage);

// build the modulereference and ctor for registering it
LLFunction* mictor = build_module_reference_and_ctor(moduleInfoSym);

AppendFunctionToLLVMGlobalCtorsDtors(mictor, 65535, true);
if (global.params.isLinux) {
build_dso_registry_calls(moduleInfoSym);
} else {
// build the modulereference and ctor for registering it
LLFunction* mictor = build_module_reference_and_ctor(moduleInfoSym);
AppendFunctionToLLVMGlobalCtorsDtors(mictor, 65535, true);
}
}
24 changes: 22 additions & 2 deletions gen/runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ static void LLVM_D_BuildRuntimeModule()
LLType* classInfoTy = DtoType(Type::typeinfoclass->type);
LLType* typeInfoTy = DtoType(Type::dtypeinfo->type);
LLType* aaTypeInfoTy = DtoType(Type::typeinfoassociativearray->type);
LLType* moduleInfoPtrTy = getPtrToType(DtoType(Module::moduleinfo->type));

LLType* aaTy = rt_ptr(LLStructType::get(gIR->context()));

Expand Down Expand Up @@ -306,7 +307,7 @@ static void LLVM_D_BuildRuntimeModule()
llvm::StringRef fname("_d_array_bounds");
llvm::StringRef fname2("_d_switch_error");
LLType *types[] = {
getPtrToType(DtoType(Module::moduleinfo->type)),
moduleInfoPtrTy,
intTy
};
LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false);
Expand Down Expand Up @@ -777,7 +778,7 @@ static void LLVM_D_BuildRuntimeModule()
->setAttributes(Attr_1_NoCapture);
}

// int _aaEqual(TypeInfo_AssociativeArray ti, AA e1, AA e2)
// int _aaEqual(in TypeInfo tiRaw, in AA e1, in AA e2)
{
llvm::StringRef fname("_aaEqual");
LLType *types[] = { typeInfoTy, aaTy, aaTy };
Expand Down Expand Up @@ -941,4 +942,23 @@ static void LLVM_D_BuildRuntimeModule()
LLFunctionType* fty = llvm::FunctionType::get(voidTy, false);
llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M);
}

// void _d_dso_registry(CompilerDSOData* data)
if (global.params.isLinux) {
llvm::StringRef fname("_d_dso_registry");

llvm::StructType* dsoDataTy = llvm::StructType::get(
sizeTy, // version
getPtrToType(voidPtrTy), // slot
getPtrToType(moduleInfoPtrTy), // _minfo_beg
getPtrToType(moduleInfoPtrTy), // _minfo_end
NULL
);

llvm::Type* params[] = {
getPtrToType(dsoDataTy)
};
llvm::FunctionType* fty = llvm::FunctionType::get(voidTy, params, false);
llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M);
}
}
2 changes: 1 addition & 1 deletion runtime/druntime
Submodule druntime updated 2 files
+1 −8 src/rt/sections_ldc.d
+11 −7 src/rt/sections_linux.d

2 comments on commit d9b137b

@MartinNowak
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to see, that the hidden global weak symbols seem to work as section brackets.
At some point I want to switch dmd's backend implementation to something similar to this.

@dnadlinger
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it works in theory, although the current implementation is not too pretty due to the LLVM limitations mentioned in the above (the custom section/weak linkage bug also causes the _minfo_beg/_minfo_end to be duplicated for each module right now, which is rather ugly, even though the amount of bloat caused by that is not too bad).

Please sign in to comment.