Skip to content

Commit

Permalink
gen: Use single ldc.register_dso function for both module ctor/dtor
Browse files Browse the repository at this point in the history
This reduces the size of a statically linked Phobos-based
Hello World by 11 kB on Linux x86_64.

Also creates a header file for gen/module.cpp, which has been
renamed to "modules" such as not to conflict with the frontend
header file.
  • Loading branch information
dnadlinger committed Aug 18, 2016
1 parent e7c95db commit d47bdb2
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 84 deletions.
4 changes: 0 additions & 4 deletions ddmd/module.h
Expand Up @@ -207,10 +207,6 @@ class Module : public Package
void accept(Visitor *v) { v->visit(this); }
};

#if IN_LLVM
void buildTargetFiles(Module *m, bool singleObj, bool library);
#endif

struct ModuleDeclaration
{
Loc loc;
Expand Down
13 changes: 3 additions & 10 deletions driver/codegenerator.cpp
Expand Up @@ -16,10 +16,9 @@
#include "driver/linker.h"
#include "driver/toobj.h"
#include "gen/logger.h"
#include "gen/modules.h"
#include "gen/runtime.h"

void codegenModule(IRState *irs, Module *m, bool emitFullModuleInfo);

/// The module with the frontend-generated C main() definition.
extern Module *g_entrypointModule;

Expand Down Expand Up @@ -219,15 +218,9 @@ void CodeGenerator::emit(Module *m) {

prepareLLModule(m);

// If we are compiling to a single object file then only the first module
// needs to generate a call to _d_dso_registry(). All other modules only add
// a module reference.
// FIXME Find better name.
const bool emitFullModuleInfo =
!singleObj_ || (singleObj_ && moduleCount_ == 1);
codegenModule(ir_, m, emitFullModuleInfo);
codegenModule(ir_, m);
if (m == g_dMainModule) {
codegenModule(ir_, g_entrypointModule, emitFullModuleInfo);
codegenModule(ir_, g_entrypointModule);

if (global.params.targetTriple->getEnvironment() == llvm::Triple::Android) {
// On Android, bracket TLS data with the symbols _tlsstart and _tlsend, as
Expand Down
1 change: 1 addition & 0 deletions driver/main.cpp
Expand Up @@ -33,6 +33,7 @@
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
#include "gen/metadata.h"
#include "gen/modules.h"
#include "gen/objcgen.h"
#include "gen/optimizer.h"
#include "gen/passes/Passes.h"
Expand Down
168 changes: 98 additions & 70 deletions gen/module.cpp → gen/modules.cpp
@@ -1,4 +1,4 @@
//===-- module.cpp --------------------------------------------------------===//
//===-- modules.cpp -------------------------------------------------------===//
//
// LDC – the LLVM D compiler
//
Expand All @@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//

#include "module.h"
#include "aggregate.h"
#include "attrib.h"
#include "declaration.h"
Expand Down Expand Up @@ -169,10 +170,12 @@ void buildTargetFiles(Module *m, bool singleObj, bool library) {
// check_and_add_output_file(m, hdrfile->name->str);
}

namespace {

// build ModuleReference and register function, to register the module info in
// the global linked list
static LLFunction *build_module_reference_and_ctor(const char *moduleMangle,
LLConstant *moduleinfo) {
LLFunction *build_module_reference_and_ctor(const char *moduleMangle,
LLConstant *moduleinfo) {
// build ctor type
LLFunctionType *fty = LLFunctionType::get(LLType::getVoidTy(gIR->context()),
std::vector<LLType *>(), false);
Expand Down Expand Up @@ -246,38 +249,54 @@ static LLFunction *build_module_reference_and_ctor(const char *moduleMangle,
/// Builds the body for the ldc.dso_ctor and ldc.dso_dtor functions.
///
/// Pseudocode:
/// if (dsoInitialized == executeWhenInitialized) {
/// dsoInitialized = !executeWhenInitialized;
/// void ldc.register_dso(bool isShutdown, void* minfoUsedPointer) {
/// if (dsoInitialized == isShutdown) {
/// dsoInitialized = !isShutdown;
/// auto record = {1, dsoSlot, minfoBeg, minfoEnd, minfoUsedPointer};
/// _d_dso_registry(cast(CompilerDSOData*)&record);
/// }
/// }
static void build_dso_ctor_dtor_body(
llvm::Function *targetFunc, llvm::Value *dsoInitialized,
llvm::Value *dsoSlot, llvm::Value *minfoBeg, llvm::Value *minfoEnd,
llvm::Value *minfoUsedPointer, bool executeWhenInitialized) {
llvm::Function *const dsoRegistry =
llvm::Function *buildRegisterDSO(llvm::Value *dsoInitialized,
llvm::Value *dsoSlot, llvm::Value *minfoBeg,
llvm::Value *minfoEnd) {
llvm::Type *argTypes[] = {llvm::Type::getInt1Ty(gIR->context()),
llvm::Type::getInt8PtrTy(gIR->context())};
const auto fnType = llvm::FunctionType::get(
llvm::Type::getVoidTy(gIR->context()), argTypes, false);
const auto fn =
llvm::Function::Create(fnType, llvm::GlobalValue::LinkOnceODRLinkage,
"ldc.register_dso", &gIR->module);
fn->setVisibility(llvm::GlobalValue::HiddenVisibility);
auto argIt = fn->arg_begin();
const auto isShutdown = &*argIt;
isShutdown->setName("isShutdown");
++argIt;
const auto minfoUsedPointer = &*argIt;
minfoUsedPointer->setName("minfoUsedPointer");

// Never inline – the functions is only called on startup/shutdown, hence
// it isn't worth the increase in code size.
fn->addFnAttr(llvm::Attribute::NoInline);

const auto dsoRegistry =
getRuntimeFunction(Loc(), gIR->module, "_d_dso_registry");
llvm::Type *const recordPtrTy =
dsoRegistry->getFunctionType()->getContainedType(1);
const auto recordPtrTy = dsoRegistry->getFunctionType()->getContainedType(1);

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);
const auto entryBB = llvm::BasicBlock::Create(gIR->context(), "", fn);
const auto initBB = llvm::BasicBlock::Create(gIR->context(), "init", fn);
const auto endBB = llvm::BasicBlock::Create(gIR->context(), "end", fn);

{
IRBuilder<> b(entryBB);
llvm::Value *condEval =
b.CreateICmp(executeWhenInitialized ? llvm::ICmpInst::ICMP_NE
: llvm::ICmpInst::ICMP_EQ,
b.CreateLoad(dsoInitialized), b.getInt8(0));
const auto loadedFlag = b.CreateTrunc(b.CreateLoad(dsoInitialized), b.getInt1Ty());
const auto condEval =
b.CreateICmp(llvm::ICmpInst::ICMP_EQ, loadedFlag, isShutdown);
b.CreateCondBr(condEval, initBB, endBB);
}
{
IRBuilder<> b(initBB);
b.CreateStore(b.getInt8(!executeWhenInitialized), dsoInitialized);
const auto newFlag = b.CreateXor(isShutdown, b.getTrue());
b.CreateStore(b.CreateZExt(newFlag, b.getInt8Ty()), dsoInitialized);

llvm::Constant *version = DtoConstSize_t(1);
llvm::Type *memberTypes[] = {version->getType(), dsoSlot->getType(),
Expand Down Expand Up @@ -307,40 +326,34 @@ static void build_dso_ctor_dtor_body(
IRBuilder<> b(endBB);
b.CreateRetVoid();
}
}

static void build_module_ref(std::string moduleMangle,
llvm::Constant *thisModuleInfo) {
// Build the ModuleInfo reference and bracketing symbols.
llvm::Type *const moduleInfoPtrTy = DtoPtrToType(Module::moduleinfo->type);

std::string thismrefname = "_D";
thismrefname += moduleMangle;
thismrefname += "11__moduleRefZ";
auto thismref = new llvm::GlobalVariable(
gIR->module, moduleInfoPtrTy,
false, // FIXME: mRelocModel != llvm::Reloc::PIC_
llvm::GlobalValue::LinkOnceODRLinkage,
DtoBitCast(thisModuleInfo, moduleInfoPtrTy), thismrefname);
thismref->setSection(".minfo");
gIR->usedArray.push_back(thismref);
return fn;
}

static void build_dso_registry_calls(std::string moduleMangle,
llvm::Constant *thisModuleInfo) {
// Build the ModuleInfo reference and bracketing symbols.
void build_module_ref(std::string moduleMangle,
llvm::Constant *thisModuleInfo) {
// Only for the first D module to be emitted into this llvm::Module we need to
// create the _minfo_beg/_minfo_end symbols and the global ctors/dtors. For
// all subsequent ones, we just need to emit an additional reference into the
// .minfo section (even with --gc-sections, the section is already kept alive
// by the first module's reference being used in the ctor/dtor functions).
const bool isFirst = !gIR->module.getGlobalVariable("ldc.dso_slot");

llvm::Type *const moduleInfoPtrTy = DtoPtrToType(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.
auto minfoBeg =
new llvm::GlobalVariable(gIR->module, moduleInfoPtrTy,
false, // FIXME: mRelocModel != llvm::Reloc::PIC_
llvm::GlobalValue::LinkOnceODRLinkage,
getNullPtr(moduleInfoPtrTy), "_minfo_beg");
minfoBeg->setSection(".minfo_beg");
minfoBeg->setVisibility(llvm::GlobalValue::HiddenVisibility);
llvm::GlobalVariable *minfoBeg;
if (isFirst) {
minfoBeg = new llvm::GlobalVariable(
gIR->module, moduleInfoPtrTy,
false, // FIXME: mRelocModel != llvm::Reloc::PIC_
llvm::GlobalValue::LinkOnceODRLinkage, getNullPtr(moduleInfoPtrTy),
"_minfo_beg");
minfoBeg->setSection(".minfo_beg");
minfoBeg->setVisibility(llvm::GlobalValue::HiddenVisibility);
}

std::string thismrefname = "_D";
thismrefname += moduleMangle;
Expand All @@ -353,6 +366,11 @@ static void build_dso_registry_calls(std::string moduleMangle,
thismref->setSection(".minfo");
gIR->usedArray.push_back(thismref);

if (!isFirst) {
// Nothing left to do.
return;
}

auto minfoEnd =
new llvm::GlobalVariable(gIR->module, moduleInfoPtrTy,
false, // FIXME: mRelocModel != llvm::Reloc::PIC_
Expand Down Expand Up @@ -413,28 +431,41 @@ static void build_dso_registry_calls(std::string moduleMangle,
// minfoUsedPointer store in the ctor as soon as the optimizer runs.
llvm::Value *minfoRefPtr = DtoBitCast(thismref, getVoidPtrType());

const auto registerDSO =
buildRegisterDSO(dsoInitialized, dsoSlot, minfoBeg, minfoEnd);

std::string ctorName = "ldc.dso_ctor.";
ctorName += moduleMangle;
llvm::Function *dsoCtor = llvm::Function::Create(
const auto dsoCtor = llvm::Function::Create(
llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false),
llvm::GlobalValue::LinkOnceODRLinkage, ctorName, &gIR->module);
dsoCtor->setVisibility(llvm::GlobalValue::HiddenVisibility);
build_dso_ctor_dtor_body(dsoCtor, dsoInitialized, dsoSlot, minfoBeg, minfoEnd,
minfoRefPtr, false);
{
const auto bb = llvm::BasicBlock::Create(gIR->context(), "", dsoCtor);
IRBuilder<> b{bb};
LLValue *params[] = {b.getFalse(), minfoRefPtr};
b.CreateCall(registerDSO, params);
b.CreateRetVoid();
}
llvm::appendToGlobalCtors(gIR->module, dsoCtor, 65535);

std::string dtorName = "ldc.dso_dtor.";
dtorName += moduleMangle;
llvm::Function *dsoDtor = llvm::Function::Create(
const auto dsoDtor = llvm::Function::Create(
llvm::FunctionType::get(llvm::Type::getVoidTy(gIR->context()), false),
llvm::GlobalValue::LinkOnceODRLinkage, dtorName, &gIR->module);
dsoDtor->setVisibility(llvm::GlobalValue::HiddenVisibility);
build_dso_ctor_dtor_body(dsoDtor, dsoInitialized, dsoSlot, minfoBeg, minfoEnd,
minfoRefPtr, true);
{
const auto bb = llvm::BasicBlock::Create(gIR->context(), "", dsoDtor);
IRBuilder<> b{bb};
LLValue *params[] = {b.getTrue(), minfoRefPtr};
b.CreateCall(registerDSO, params);
b.CreateRetVoid();
}
llvm::appendToGlobalDtors(gIR->module, dsoDtor, 65535);
}

static void build_llvm_used_array(IRState *p) {
void build_llvm_used_array(IRState *p) {
if (p->usedArray.empty()) {
return;
}
Expand All @@ -455,7 +486,7 @@ static void build_llvm_used_array(IRState *p) {
}

// Add module-private variables and functions for coverage analysis.
static void addCoverageAnalysis(Module *m) {
void addCoverageAnalysis(Module *m) {
IF_LOG {
Logger::println("Adding coverage analysis for module %s (%d lines)",
m->srcfile->toChars(), m->numlines);
Expand Down Expand Up @@ -580,7 +611,7 @@ static void addCoverageAnalysis(Module *m) {
}

// Initialize _d_cover_valid for coverage analysis
static void addCoverageAnalysisInitializer(Module *m) {
void addCoverageAnalysisInitializer(Module *m) {
IF_LOG Logger::println("Adding coverage analysis _d_cover_valid initializer");

size_t array_size = m->d_cover_valid_init.size();
Expand All @@ -596,7 +627,7 @@ static void addCoverageAnalysisInitializer(Module *m) {
// Load InstrProf data from file and store in it IrState
// TODO: This is probably not the right place, we should load it once for all
// modules?
static void loadInstrProfileData(IRState *irs) {
void loadInstrProfileData(IRState *irs) {
#if LDC_WITH_PGO
// Only load from datafileInstrProf if we are not generating instrumented
// code.
Expand Down Expand Up @@ -647,7 +678,7 @@ static void loadInstrProfileData(IRState *irs) {
#endif
}

static void registerModuleInfo(Module *m, bool emitFullModuleInfo) {
void registerModuleInfo(Module *m) {
const auto moduleInfoSym = genModuleInfo(m);

if ((global.params.targetTriple->isOSLinux() &&
Expand All @@ -663,20 +694,17 @@ static void registerModuleInfo(Module *m, bool emitFullModuleInfo) {
global.params.targetTriple->getOS() == llvm::Triple::DragonFly
#endif
) {
if (emitFullModuleInfo) {
build_dso_registry_calls(mangle(m), moduleInfoSym);
} else {
build_module_ref(mangle(m), moduleInfoSym);
}
build_module_ref(mangle(m), moduleInfoSym);
} else {
// build the modulereference and ctor for registering it
LLFunction *mictor =
build_module_reference_and_ctor(mangle(m), moduleInfoSym);
AppendFunctionToLLVMGlobalCtorsDtors(mictor, 65535, true);
}
}
}

void codegenModule(IRState *irs, Module *m, bool emitFullModuleInfo) {
void codegenModule(IRState *irs, Module *m) {
assert(!irs->dmodule &&
"irs->module not null, codegen already in progress?!");
irs->dmodule = m;
Expand All @@ -687,12 +715,12 @@ void codegenModule(IRState *irs, Module *m, bool emitFullModuleInfo) {

// Skip pseudo-modules for coverage analysis
std::string name = m->toChars();
bool pseudo_modules = (name == "__entrypoint") || (name == "__main");
if (global.params.cov && !pseudo_modules) {
const bool isPseudoModule = (name == "__entrypoint") || (name == "__main");
if (global.params.cov && !isPseudoModule) {
addCoverageAnalysis(m);
}

if (!pseudo_modules) {
if (!isPseudoModule) {
loadInstrProfileData(gIR);
}

Expand All @@ -711,7 +739,7 @@ void codegenModule(IRState *irs, Module *m, bool emitFullModuleInfo) {
// user.
if (!m->noModuleInfo) {
// generate ModuleInfo
registerModuleInfo(m, emitFullModuleInfo);
registerModuleInfo(m);

build_llvm_used_array(irs);
}
Expand Down
20 changes: 20 additions & 0 deletions gen/modules.h
@@ -0,0 +1,20 @@
//===-- gen/modules.h - Entry points for D module codegen -------*- C++ -*-===//
//
// LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//

struct IRState;
class Module;

/// Sets the m->*file members to the output file names, as derived from platform
/// and command line switches, making sure that they are valid paths and don't
/// conflict with the source file.
void buildTargetFiles(Module *m, bool singleObj, bool library);

/// Generates code for the contents of module m into the LLVM module associated
/// with irs.
void codegenModule(IRState *irs, Module *m);

0 comments on commit d47bdb2

Please sign in to comment.