changeset 676:1f0a78174598

Make ldc call gcc to assemble.
author Christian Kamm <kamm incasoftware de>
date Sat, 11 Oct 2008 20:00:36 +0200
parents bfe5229f9d8e
children 075c1272a01d
files dmd/mars.c dmd/module.h gen/toobj.cpp
diffstat 3 files changed, 165 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/dmd/mars.c	Sat Oct 11 13:07:59 2008 +0200
+++ b/dmd/mars.c	Sat Oct 11 20:00:36 2008 +0200
@@ -244,7 +244,7 @@
 );
 }
 
-int main(int argc, char *argv[])
+int main(int argc, char *argv[], char** envp)
 {
     int i;
     Array files;
@@ -1140,7 +1140,7 @@
 	    printf("code      %s\n", m->toChars());
 	if (global.params.obj)
 	{
-	    m->genobjfile(0);
+	    m->genobjfile(0, envp);
 	    global.params.objfiles->push(m->objfile->name->str);
 	}
 	if (global.errors)
--- a/dmd/module.h	Sat Oct 11 13:07:59 2008 +0200
+++ b/dmd/module.h	Sat Oct 11 20:00:36 2008 +0200
@@ -133,7 +133,7 @@
 #ifdef _DH
     void genhdrfile();  // generate D import file
 #endif
-    void genobjfile(int multiobj);
+    void genobjfile(int multiobj, char** envp);
 //    void gensymfile();
     void gendocfile();
     int needModuleInfo();
--- a/gen/toobj.cpp	Sat Oct 11 13:07:59 2008 +0200
+++ b/gen/toobj.cpp	Sat Oct 11 20:00:36 2008 +0200
@@ -20,6 +20,7 @@
 #include "llvm/Module.h"
 #include "llvm/ModuleProvider.h"
 #include "llvm/PassManager.h"
+#include "llvm/System/Program.h"
 #include "llvm/System/Path.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -58,10 +59,11 @@
 
 // fwd decl
 void write_asm_to_file(llvm::Module& m, llvm::raw_fd_ostream& Out);
+void assemble(const llvm::sys::Path& asmpath, const llvm::sys::Path& objpath, char** envp);
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-void Module::genobjfile(int multiobj)
+void Module::genobjfile(int multiobj, char** envp)
 {
     bool logenabled = Logger::enabled();
     if (llvmForceLogging && !logenabled)
@@ -209,10 +211,14 @@
     }
     Logger::println("Writing native asm to: %s\n", spath.c_str());
     std::string err;
-    llvm::raw_fd_ostream out(spath.c_str(), err);
-    write_asm_to_file(*ir.module, out);
+    {
+        llvm::raw_fd_ostream out(spath.c_str(), err);
+        write_asm_to_file(*ir.module, out);
+    }
 
-    //TODO: call gcc to convert assembly to object file
+    // call gcc to convert assembly to object file
+    LLPath objpath = LLPath(objfile->name->toChars());
+    assemble(spath, objpath, envp);
 
     if (!global.params.output_s) {
         spath.eraseFromDisk();
@@ -230,6 +236,7 @@
 
 /* ================================================================== */
 
+// based on llc code, University of Illinois Open Source License
 void write_asm_to_file(llvm::Module& m, llvm::raw_fd_ostream& out)
 {
     using namespace llvm;
@@ -290,9 +297,159 @@
 
 /* ================================================================== */
 
+// helper functions for gcc call to assemble
+// based on llvm-ld code, University of Illinois Open Source License
+
+/// CopyEnv - This function takes an array of environment variables and makes a
+/// copy of it.  This copy can then be manipulated any way the caller likes
+/// without affecting the process's real environment.
+///
+/// Inputs:
+///  envp - An array of C strings containing an environment.
+///
+/// Return value:
+///  NULL - An error occurred.
+///
+///  Otherwise, a pointer to a new array of C strings is returned.  Every string
+///  in the array is a duplicate of the one in the original array (i.e. we do
+///  not copy the char *'s from one array to another).
+///
+static char ** CopyEnv(char ** const envp) {
+  // Count the number of entries in the old list;
+  unsigned entries;   // The number of entries in the old environment list
+  for (entries = 0; envp[entries] != NULL; entries++)
+    /*empty*/;
+
+  // Add one more entry for the NULL pointer that ends the list.
+  ++entries;
+
+  // If there are no entries at all, just return NULL.
+  if (entries == 0)
+    return NULL;
+
+  // Allocate a new environment list.
+  char **newenv = new char* [entries];
+  if ((newenv = new char* [entries]) == NULL)
+    return NULL;
+
+  // Make a copy of the list.  Don't forget the NULL that ends the list.
+  entries = 0;
+  while (envp[entries] != NULL) {
+    newenv[entries] = new char[strlen (envp[entries]) + 1];
+    strcpy (newenv[entries], envp[entries]);
+    ++entries;
+  }
+  newenv[entries] = NULL;
+
+  return newenv;
+}
+
+// based on llvm-ld code, University of Illinois Open Source License
+
+/// RemoveEnv - Remove the specified environment variable from the environment
+/// array.
+///
+/// Inputs:
+///  name - The name of the variable to remove.  It cannot be NULL.
+///  envp - The array of environment variables.  It cannot be NULL.
+///
+/// Notes:
+///  This is mainly done because functions to remove items from the environment
+///  are not available across all platforms.  In particular, Solaris does not
+///  seem to have an unsetenv() function or a setenv() function (or they are
+///  undocumented if they do exist).
+///
+static void RemoveEnv(const char * name, char ** const envp) {
+  for (unsigned index=0; envp[index] != NULL; index++) {
+    // Find the first equals sign in the array and make it an EOS character.
+    char *p = strchr (envp[index], '=');
+    if (p == NULL)
+      continue;
+    else
+      *p = '\0';
+
+    // Compare the two strings.  If they are equal, zap this string.
+    // Otherwise, restore it.
+    if (!strcmp(name, envp[index]))
+      *envp[index] = '\0';
+    else
+      *p = '=';
+  }
+
+  return;
+}
+
+// uses gcc to make an obj out of an assembly file
+// based on llvm-ld code, University of Illinois Open Source License
+void assemble(const llvm::sys::Path& asmpath, const llvm::sys::Path& objpath, char** envp)
+{
+    using namespace llvm;
+
+    sys::Path gcc = llvm::sys::Program::FindProgramByName("gcc");
+
+    // Remove these environment variables from the environment of the
+    // programs that we will execute.  It appears that GCC sets these
+    // environment variables so that the programs it uses can configure
+    // themselves identically.
+    //
+    // However, when we invoke GCC below, we want it to use its normal
+    // configuration.  Hence, we must sanitize its environment.
+    char ** clean_env = CopyEnv(envp);
+    if (clean_env == NULL)
+        return;
+    RemoveEnv("LIBRARY_PATH", clean_env);
+    RemoveEnv("COLLECT_GCC_OPTIONS", clean_env);
+    RemoveEnv("GCC_EXEC_PREFIX", clean_env);
+    RemoveEnv("COMPILER_PATH", clean_env);
+    RemoveEnv("COLLECT_GCC", clean_env);
+
+
+    // Run GCC to assemble and link the program into native code.
+    //
+    // Note:
+    //  We can't just assemble and link the file with the system assembler
+    //  and linker because we don't know where to put the _start symbol.
+    //  GCC mysteriously knows how to do it.
+    std::vector<std::string> args;
+    args.push_back(gcc.toString());
+    args.push_back("-fno-strict-aliasing");
+    args.push_back("-O3");
+    args.push_back("-c");
+    args.push_back("-xassembler");
+    args.push_back(asmpath.toString());
+    args.push_back("-o");
+    args.push_back(objpath.toString());
+
+//TODO: Add other options, like -fpic
+
+    // Now that "args" owns all the std::strings for the arguments, call the c_str
+    // method to get the underlying string array.  We do this game so that the
+    // std::string array is guaranteed to outlive the const char* array.
+    std::vector<const char *> Args;
+    for (unsigned i = 0, e = args.size(); i != e; ++i)
+        Args.push_back(args[i].c_str());
+    Args.push_back(0);
+
+    Logger::println("Assembling with: ");
+    std::vector<const char*>::const_iterator I = Args.begin(), E = Args.end(); 
+    std::ostream& logstr = Logger::cout();
+    for (; I != E; ++I)
+        if (*I)
+            logstr << "'" << *I << "'" << " ";
+    logstr << "\n" << std::flush;
+
+    // Run the compiler to assembly the program.
+    std::string ErrMsg;
+    int R = sys::Program::ExecuteAndWait(
+        gcc, &Args[0], (const char**)clean_env, 0, 0, 0, &ErrMsg);
+    delete [] clean_env;
+}
+
+
+/* ================================================================== */
 // build module ctor
 
-static llvm::Function* build_module_ctor()
+llvm::Function* build_module_ctor()
 {
     if (gIR->ctors.empty())
         return NULL;