changeset 276:21f85bac0b1a trunk

[svn r297] Fixed: rewrote linker code to use LLVM's Program facilities instead of DMD's oldschool broken "native" approach.
author lindquist
date Fri, 20 Jun 2008 17:45:13 +0200
parents 665b81613475
children 90a8c798b0db
files dmd/link.c dmd/mars.c dmd/mars.h gen/linker.cpp gen/linker.h test/asm1.d
diffstat 6 files changed, 184 insertions(+), 297 deletions(-) [+]
line wrap: on
line diff
--- 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.
  */
--- 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)
 	{
--- 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
--- 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<llvm::Module*> 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<const char*> 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();
+    }
+}
--- 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<llvm::Module*>& MV);
 
+/**
+ * Link an executable.
+ * @return 0 on success.
+ */
+int linkExecutable();
+
 #endif // LLVMDC_GEN_LINKER_H
--- 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");
     }
 }