# HG changeset patch # User lindquist # Date 1213976713 -7200 # Node ID 21f85bac0b1ab1d580d050ae798cf53b2981547d # Parent 665b81613475d878904eb98409cb5e7a424800b4 [svn r297] Fixed: rewrote linker code to use LLVM's Program facilities instead of DMD's oldschool broken "native" approach. diff -r 665b81613475 -r 21f85bac0b1a dmd/link.c --- a/dmd/link.c Thu Jun 19 17:30:32 2008 +0200 +++ b/dmd/link.c Fri Jun 20 17:45:13 2008 +0200 @@ -37,299 +37,6 @@ int executecmd(char *cmd, char *args, int useenv); int executearg0(char *cmd, char *args); -/***************************** - * Run the linker. Return status of execution. - */ - -int runLINK() -{ - Logger::println("*** Linking executable ***"); - -#if _WIN32 - assert(0 && "linking not done for win32"); - - char *p; - int i; - int status; - OutBuffer cmdbuf; - - global.params.libfiles->push((void *) "user32"); - global.params.libfiles->push((void *) "kernel32"); - - for (i = 0; i < global.params.objfiles->dim; i++) - { - if (i) - cmdbuf.writeByte('+'); - p = (char *)global.params.objfiles->data[i]; - char *ext = FileName::ext(p); - if (ext) - cmdbuf.write(p, ext - p - 1); - else - cmdbuf.writestring(p); - } - cmdbuf.writeByte(','); - if (global.params.exefile) - cmdbuf.writestring(global.params.exefile); - else - { // Generate exe file name from first obj name - char *n = (char *)global.params.objfiles->data[0]; - char *ex; - - n = FileName::name(n); - FileName *fn = FileName::forceExt(n, "exe"); - global.params.exefile = fn->toChars(); - } - - // Make sure path to exe file exists - { char *p = FileName::path(global.params.exefile); - FileName::ensurePathExists(p); - mem.free(p); - } - - cmdbuf.writeByte(','); - if (global.params.run) - cmdbuf.writestring("nul"); -// if (mapfile) -// cmdbuf.writestring(output); - cmdbuf.writeByte(','); - - for (i = 0; i < global.params.libfiles->dim; i++) - { - if (i) - cmdbuf.writeByte('+'); - cmdbuf.writestring((char *) global.params.libfiles->data[i]); - } - - if (global.params.deffile) - { - cmdbuf.writeByte(','); - cmdbuf.writestring(global.params.deffile); - } - - /* Eliminate unnecessary trailing commas */ - while (1) - { i = cmdbuf.offset; - if (!i || cmdbuf.data[i - 1] != ',') - break; - cmdbuf.offset--; - } - - if (global.params.resfile) - { - cmdbuf.writestring("/RC:"); - cmdbuf.writestring(global.params.resfile); - } - -#if 0 - if (mapfile) - cmdbuf.writestring("/m"); - if (debuginfo) - cmdbuf.writestring("/li"); - if (codeview) - { - cmdbuf.writestring("/co"); - if (codeview3) - cmdbuf.writestring(":3"); - } -#else - if (global.params.symdebug) - cmdbuf.writestring("/co"); -#endif - - cmdbuf.writestring("/noi"); - for (i = 0; i < global.params.linkswitches->dim; i++) - { - cmdbuf.writestring((char *) global.params.linkswitches->data[i]); - } - cmdbuf.writeByte(';'); - - p = cmdbuf.toChars(); - - FileName *lnkfilename = NULL; - size_t plen = strlen(p); - if (plen > 7000) - { - lnkfilename = FileName::forceExt(global.params.exefile, "lnk"); - File flnk(lnkfilename); - flnk.setbuffer(p, plen); - flnk.ref = 1; - if (flnk.write()) - error("error writing file %s", lnkfilename); - if (lnkfilename->len() < plen) - sprintf(p, "@%s", lnkfilename->toChars()); - } - - char *linkcmd = getenv("LINKCMD"); - if (!linkcmd) - linkcmd = "link"; - status = executecmd(linkcmd, p, 1); - if (lnkfilename) - { - remove(lnkfilename->toChars()); - delete lnkfilename; - } - return status; -#elif linux - pid_t childpid; - int i; - int status; - - // Build argv[] - Array argv; - - //char *cc = getenv("CC"); - //if (!cc) - //cc = "gcc"; - char *cc = "llvm-ld"; - argv.push((void *)cc); - - // None of that a.out stuff. Use explicit exe file name, or - // generate one from name of first source file. - OutBuffer* exestr = new OutBuffer; - if (global.params.exefile) - { - exestr->printf("-o=%s", global.params.exefile); - argv.push(exestr->toChars()); - } - else - { // Generate exe file name from first obj name - char *n = (char *)global.params.objfiles->data[0]; - char *e; - char *ex; - - n = FileName::name(n); - e = FileName::ext(n); - if (e) - { - e--; // back up over '.' - ex = (char *)mem.malloc(e - n + 1); - memcpy(ex, n, e - n); - ex[e - n] = 0; - } - else - ex = (char *)"a.out"; // no extension, so give up - exestr->printf("-o=%s", ex); - ex = exestr->toChars(); - argv.push(ex); - global.params.exefile = ex; - } - - // Make sure path to exe file exists - { char *p = FileName::path(global.params.exefile); - FileName::ensurePathExists(p); - mem.free(p); - } - - argv.insert(argv.dim, global.params.libfiles); - - if (!global.params.symdebug) - argv.push((void *)"-strip-debug"); - - //argv.push((void *)"-m32"); - - - if (!global.params.optimize) - argv.push((void *)"-disable-opt"); - else { - const char* s = 0; - switch(global.params.optimizeLevel) { - case 0: - s = "-O0"; break; - case 1: - s = "-O1"; break; - case 2: - s = "-O2"; break; - case 3: - s = "-O3"; break; - case 4: - s = "-O4"; break; - case 5: - s = "-O5"; break; - default: - assert(0); - } - argv.push((void*)s); - } - - if (!(global.params.useInline || global.params.llvmInline)) { - argv.push((void *)"-disable-inlining"); - } - -#if 0 - if (0 && global.params.exefile) - { - /* This switch enables what is known as 'smart linking' - * in the Windows world, where unreferenced sections - * are removed from the executable. It eliminates unreferenced - * functions, essentially making a 'library' out of a module. - * Although it is documented to work with ld version 2.13, - * in practice it does not, but just seems to be ignored. - * Thomas Kuehne has verified that it works with ld 2.16.1. - * BUG: disabled because it causes exception handling to fail - */ - argv.push((void *)"-Xlinker"); - argv.push((void *)"--gc-sections"); - } -#endif - - for (i = 0; i < global.params.linkswitches->dim; i++) - { char *p = (char *)global.params.linkswitches->data[i]; - //if (!p || !p[0] || !(p[0] == '-' && p[1] == 'l')) - //{ // Don't need -Xlinker if switch starts with -l - // argv.push((void *)"-Xlinker"); - //} - argv.push((void *) p); - } - - argv.push((void*)"-native"); - - /* Standard libraries must go after user specified libraries - * passed with -l. - */ - - argv.push((void*)"-ltango-base-c-llvmdc"); - argv.push((void*)"-lpthread"); - argv.push((void*)"-ldl"); - argv.push((void*)"-lm"); - - argv.append(global.params.objfiles); - - std::string runtime_path(global.params.runtimePath); - if (*runtime_path.rbegin() != '/') - runtime_path.append("/"); - runtime_path.append("libtango-base-llvmdc.a"); - argv.push((void*)runtime_path.c_str()); - - if (!global.params.quiet || global.params.verbose) - { - // Print it - for (i = 0; i < argv.dim; i++) - printf("%s ", (char *)argv.data[i]); - printf("\n"); - fflush(stdout); - } - - argv.push(NULL); - childpid = fork(); - if (childpid == 0) - { - execvp((char *)argv.data[0], (char **)argv.data); - perror((char *)argv.data[0]); // failed to execute - return -1; - } - - waitpid(childpid, &status, 0); - - status=WEXITSTATUS(status); - if (status) - printf("--- errorlevel %d\n", status); - return status; -#else - printf ("Linking is not yet supported for this version of LLVMDC.\n"); - return -1; -#endif -} - /********************************** * Delete generated EXE file. */ diff -r 665b81613475 -r 21f85bac0b1a dmd/mars.c --- a/dmd/mars.c Thu Jun 19 17:30:32 2008 +0200 +++ b/dmd/mars.c Fri Jun 20 17:45:13 2008 +0200 @@ -1103,7 +1103,8 @@ else { if (global.params.link) - status = runLINK(); + //status = runLINK(); + linkExecutable(); if (global.params.run) { diff -r 665b81613475 -r 21f85bac0b1a dmd/mars.h --- a/dmd/mars.h Thu Jun 19 17:30:32 2008 +0200 +++ b/dmd/mars.h Fri Jun 20 17:45:13 2008 +0200 @@ -304,12 +304,15 @@ void verror(Loc loc, const char *format, va_list); void fatal(); void err_nomem(); -int runLINK(); +//int runLINK(); // no longer used void deleteExeFile(); int runProgram(); void inifile(char *argv0, char *inifile); void halt(); +// LLVMDC +int linkExecutable(); + /*** Where to send error messages ***/ #if IN_GCC #define stdmsg stderr diff -r 665b81613475 -r 21f85bac0b1a gen/linker.cpp --- a/gen/linker.cpp Thu Jun 19 17:30:32 2008 +0200 +++ b/gen/linker.cpp Fri Jun 20 17:45:13 2008 +0200 @@ -1,8 +1,15 @@ #include "gen/llvm.h" #include "llvm/Linker.h" +#include "llvm/System/Program.h" #include "root.h" #include "mars.h" +#include "module.h" + +#define NO_COUT_LOGGER +#include "gen/logger.h" + +////////////////////////////////////////////////////////////////////////////// typedef std::vector Module_vector; @@ -23,3 +30,154 @@ } } } + +////////////////////////////////////////////////////////////////////////////// + +int linkExecutable() +{ + Logger::println("*** Linking executable ***"); + + // error string + std::string errstr; + + // find the llvm-ld program + llvm::sys::Path ldpath = llvm::sys::Program::FindProgramByName("llvm-ld"); + if (ldpath.isEmpty()) + { + error("linker program not found"); + fatal(); + } + + // build arguments + std::vector args; + + // first the program name ?? + args.push_back("llvm-ld"); + + // output filename + std::string exestr; + if (global.params.exefile) + { // explicit + exestr = global.params.exefile; + } + else + { // inferred + // try root module name + if (Module::rootModule) + exestr = Module::rootModule->toChars(); + else + exestr = "a.out"; + } + if (global.params.isWindows) + exestr.append(".exe"); + + std::string outopt = "-o=" + exestr; + args.push_back(outopt.c_str()); + + // create path to exe + llvm::sys::Path exepath(exestr); + exepath.set(exepath.getDirname()); + exepath.createDirectoryOnDisk(true, &errstr); + if (!errstr.empty()) + { + error("failed to create path to linking output\n%s", errstr.c_str()); + fatal(); + } + + // strip debug info + if (!global.params.symdebug) + args.push_back("-strip-debug"); + + // optimization level + if (!global.params.optimize) + args.push_back("-disable-opt"); + else + { + const char* s = 0; + switch(global.params.optimizeLevel) + { + case 0: + s = "-O0"; break; + case 1: + s = "-O1"; break; + case 2: + s = "-O2"; break; + case 3: + s = "-O3"; break; + case 4: + s = "-O4"; break; + case 5: + s = "-O5"; break; + default: + assert(0); + } + args.push_back(s); + } + + // inlining + if (!(global.params.useInline || global.params.llvmInline)) + { + args.push_back("-disable-inlining"); + } + + // additional linker switches + for (int i = 0; i < global.params.linkswitches->dim; i++) + { + char *p = (char *)global.params.linkswitches->data[i]; + args.push_back(p); + } + + // native please + args.push_back("-native"); + + + // user libs + for (int i = 0; i < global.params.libfiles->dim; i++) + { + char *p = (char *)global.params.libfiles->data[i]; + args.push_back(p); + } + + // default libs + args.push_back("-ltango-base-c-llvmdc"); + args.push_back("-lpthread"); + args.push_back("-ldl"); + args.push_back("-lm"); + + // object files + for (int i = 0; i < global.params.objfiles->dim; i++) + { + char *p = (char *)global.params.objfiles->data[i]; + args.push_back(p); + } + + // runtime library + // must be linked in last to null terminate the moduleinfo appending list + std::string runtime_path(global.params.runtimePath); + if (*runtime_path.rbegin() != '/') + runtime_path.append("/"); + runtime_path.append("libtango-base-llvmdc.a"); + args.push_back(runtime_path.c_str()); + + // print link command? + if (!global.params.quiet || global.params.verbose) + { + // Print it + for (int i = 0; i < args.size(); i++) + printf("%s ", args[i]); + printf("\n"); + fflush(stdout); + } + + // terminate args list + args.push_back(NULL); + + // try to call linker!!! + if (int status = llvm::sys::Program::ExecuteAndWait(ldpath, &args[0], NULL, NULL, 0,0, &errstr)) + { + error("linking failed:\nstatus: %d", status); + if (!errstr.empty()) + error("message: %s", errstr.c_str()); + fatal(); + } +} diff -r 665b81613475 -r 21f85bac0b1a gen/linker.h --- a/gen/linker.h Thu Jun 19 17:30:32 2008 +0200 +++ b/gen/linker.h Fri Jun 20 17:45:13 2008 +0200 @@ -8,4 +8,10 @@ */ void linkModules(llvm::Module* dst, const std::vector& MV); +/** + * Link an executable. + * @return 0 on success. + */ +int linkExecutable(); + #endif // LLVMDC_GEN_LINKER_H diff -r 665b81613475 -r 21f85bac0b1a test/asm1.d --- a/test/asm1.d Thu Jun 19 17:30:32 2008 +0200 +++ b/test/asm1.d Fri Jun 20 17:45:13 2008 +0200 @@ -1,8 +1,20 @@ module asm1; +extern(C) int printf(char*, ...); + void main() { - version(LLVM_InlineAsm_X86_64) + version(D_InlineAsm_X86) + { + int x; + asm + { + mov EAX, 42; + mov x, EAX; + } + printf("x = %d\n", x); + } + else version(D_InlineAsm_X86_64) { long x; asm @@ -14,6 +26,6 @@ } else { - static assert(0, "no llvm inline asm for this platform yet"); + static assert(0, "no inline asm for this platform yet"); } }