From 95f12f04f17f0085f068fab5fcb269bcf1b88dbe Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Sat, 7 Mar 2009 19:38:00 +0100 Subject: [PATCH] Add '-singleobj' command line switch that will tell LDC to link LLVM modules internally and only emit a single object file. The switch allows the optimizer and inliner to run on all modules at once and opens the door for template instantiation improvements that should lower compile time and executable size. --- dmd/module.h | 3 ++- gen/main.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- gen/toobj.cpp | 45 +++++++++++++++++++++++---------------------- gen/toobj.h | 6 ++++++ 4 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 gen/toobj.h diff --git a/dmd/module.h b/dmd/module.h index ac8f103a..cdf6feb5 100644 --- a/dmd/module.h +++ b/dmd/module.h @@ -30,6 +30,7 @@ struct Library; #if IN_LLVM struct DValue; typedef DValue elem; +namespace llvm { class Module; } #else #ifdef IN_GCC union tree_node; typedef union tree_node elem; @@ -133,7 +134,6 @@ struct Module : Package #ifdef _DH void genhdrfile(); // generate D import file #endif - void genobjfile(int multiobj); // void gensymfile(); void gendocfile(); int needModuleInfo(); @@ -171,6 +171,7 @@ struct Module : Package void genmoduleinfo(); // LDC + llvm::Module* genLLVMModule(int multiobj); void buildTargetFiles(); File* buildFilePath(char* forcename, char* path, char* ext); Module *isModule() { return this; } diff --git a/gen/main.cpp b/gen/main.cpp index 63152101..6a5495d7 100644 --- a/gen/main.cpp +++ b/gen/main.cpp @@ -4,6 +4,7 @@ // which uses the llvm license #include "gen/llvm.h" +#include "llvm/Linker.h" #include "llvm/Target/SubtargetFeature.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetMachineRegistry.h" @@ -32,6 +33,7 @@ #include "gen/logger.h" #include "gen/linker.h" #include "gen/irstate.h" +#include "gen/toobj.h" #include "gen/cl_options.h" #include "gen/cl_helpers.h" @@ -41,6 +43,10 @@ extern void getenv_setargv(const char *envvar, int *pargc, char** *pargv); extern void backend_init(); extern void backend_term(); +static cl::opt singleObj("singleobj", + cl::desc("Create only a single output object file"), + cl::ZeroOrMore); + static cl::opt noDefaultLib("nodefaultlib", cl::desc("Don't add a default library for linking implicitly"), cl::ZeroOrMore); @@ -796,6 +802,9 @@ int main(int argc, char** argv) if (global.errors) fatal(); + // collects llvm modules to be linked if singleobj is passed + std::vector llvmModules; + // Generate output files for (int i = 0; i < modules.dim; i++) { @@ -804,8 +813,16 @@ int main(int argc, char** argv) printf("code %s\n", m->toChars()); if (global.params.obj) { - m->genobjfile(0); - global.params.objfiles->push(m->objfile->name->str); + llvm::Module* lm = m->genLLVMModule(0); + if (!singleObj) + { + m->deleteObjFile(); + writeModule(lm, m->objfile->name->str); + global.params.objfiles->push(m->objfile->name->str); + delete lm; + } + else + llvmModules.push_back(lm); } if (global.errors) m->deleteObjFile(); @@ -815,7 +832,34 @@ int main(int argc, char** argv) m->gendocfile(); } } - + + // internal linking for singleobj + if (singleObj && llvmModules.size() > 0) + { + Module* m = (Module*)modules.data[0]; + char* name = m->toChars(); + char* filename = m->objfile->name->str; + + llvm::Linker linker(name, name); + std::string errormsg; + for (int i = 0; i < llvmModules.size(); i++) + { + if(linker.LinkInModule(llvmModules[i], &errormsg)) + error(errormsg.c_str()); + delete llvmModules[i]; + } + + // workaround for llvm::Linker bug, see llvm #3749 + llvm::GlobalVariable* ctors = linker.getModule()->getGlobalVariable("llvm.global_ctors"); + if (ctors) + while (ctors->getNumUses() > 0) + delete *ctors->use_begin(); + + m->deleteObjFile(); + writeModule(linker.getModule(), filename); + global.params.objfiles->push(filename); + } + backend_term(); if (global.errors) fatal(); diff --git a/gen/toobj.cpp b/gen/toobj.cpp index 4b58d7b6..0284f3bb 100644 --- a/gen/toobj.cpp +++ b/gen/toobj.cpp @@ -70,7 +70,7 @@ void assemble(const llvm::sys::Path& asmpath, const llvm::sys::Path& objpath); ////////////////////////////////////////////////////////////////////////////////////////// -void Module::genobjfile(int multiobj) +llvm::Module* Module::genLLVMModule(int multiobj) { bool logenabled = Logger::enabled(); if (llvmForceLogging && !logenabled) @@ -85,9 +85,6 @@ void Module::genobjfile(int multiobj) assert(!global.errors); - // start by deleting the old object file - deleteObjFile(); - // name the module std::string mname(toChars()); if (md != 0) @@ -174,17 +171,29 @@ void Module::genobjfile(int multiobj) } } + gIR = NULL; + + if (llvmForceLogging && !logenabled) + { + Logger::disable(); + } + + return ir.module; +} + +void writeModule(llvm::Module* m, std::string filename) +{ // run optimizer - ldc_optimize_module(ir.module, global.params.optimizeLevel, global.params.llvmInline); + ldc_optimize_module(m, global.params.optimizeLevel, global.params.llvmInline); // verify the llvm if (!noVerify && (global.params.optimizeLevel >= 0 || global.params.llvmInline)) { std::string verifyErr; Logger::println("Verifying module... again..."); LOG_SCOPE; - if (llvm::verifyModule(*ir.module,llvm::ReturnStatusAction,&verifyErr)) + if (llvm::verifyModule(*m,llvm::ReturnStatusAction,&verifyErr)) { - error("%s", verifyErr.c_str()); + //error("%s", verifyErr.c_str()); fatal(); } else { @@ -197,27 +206,27 @@ void Module::genobjfile(int multiobj) // write LLVM bitcode if (global.params.output_bc) { - LLPath bcpath = LLPath(objfile->name->toChars()); + LLPath bcpath = LLPath(filename); bcpath.eraseSuffix(); bcpath.appendSuffix(std::string(global.bc_ext)); Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str()); std::ofstream bos(bcpath.c_str(), std::ios::binary); - llvm::WriteBitcodeToFile(ir.module, bos); + llvm::WriteBitcodeToFile(m, bos); } // write LLVM IR if (global.params.output_ll) { - LLPath llpath = LLPath(objfile->name->toChars()); + LLPath llpath = LLPath(filename); llpath.eraseSuffix(); llpath.appendSuffix(std::string(global.ll_ext)); Logger::println("Writing LLVM asm to: %s\n", llpath.c_str()); std::ofstream aos(llpath.c_str()); - ir.module->print(aos, NULL); + m->print(aos, NULL); } // write native assembly if (global.params.output_s || global.params.output_o) { - LLPath spath = LLPath(objfile->name->toChars()); + LLPath spath = LLPath(filename); spath.eraseSuffix(); spath.appendSuffix(std::string(global.s_ext)); if (!global.params.output_s) { @@ -227,12 +236,12 @@ void Module::genobjfile(int multiobj) std::string err; { llvm::raw_fd_ostream out(spath.c_str(), false, err); - write_asm_to_file(*gTargetMachine, *ir.module, out); + write_asm_to_file(*gTargetMachine, *m, out); } // call gcc to convert assembly to object file if (global.params.output_o) { - LLPath objpath = LLPath(objfile->name->toChars()); + LLPath objpath = LLPath(filename); assemble(spath, objpath); } @@ -240,14 +249,6 @@ void Module::genobjfile(int multiobj) spath.eraseFromDisk(); } } - - delete ir.module; - gIR = NULL; - - if (llvmForceLogging && !logenabled) - { - Logger::disable(); - } } /* ================================================================== */ diff --git a/gen/toobj.h b/gen/toobj.h new file mode 100644 index 00000000..ecc4c0a5 --- /dev/null +++ b/gen/toobj.h @@ -0,0 +1,6 @@ +#ifndef LDC_GEN_TOOBJ_H +#define LDC_GEN_TOOBJ_H + +void writeModule(llvm::Module* m, std::string filename); + +#endif