- Rename and rearrange for clarity
[oota-llvm.git] / tools / llvmc / CompilerDriver.cpp
index ff63b2100ede5aae661d3ff04585a11a09a3f152..d84caad24812b54790e6943868bf93069668f33c 100644 (file)
 //===------------------------------------------------------------------------===
 
 #include "CompilerDriver.h"
+#include "ConfigLexer.h"
+#include "llvm/Bytecode/Reader.h"
+#include "llvm/Module.h"
+#include "Support/FileUtilities.h"
+#include "Support/SystemUtils.h"
+#include "Support/StringExtras.h"
 #include <iostream>
 
 using namespace llvm;
@@ -24,45 +30,107 @@ namespace {
     return fullName.substr(0, dotpos);
   }
 
-  inline std::string GetSuffix(const std::string& fullName) {
-    size_t dotpos = fullName.rfind('.',fullName.size());
-    if ( dotpos = std::string::npos ) return "";
-    return fullName.substr(dotpos+1);
-  }
-
   const char OutputSuffix[] = ".o";
 
-  void WriteAction(CompilerDriver::Action* a) {
-    std::cerr << a->program;
-    std::vector<std::string>::iterator I = a->args.begin();
-    while (I != a->args.end()) {
+  void WriteAction(CompilerDriver::Action* action ) {
+    std::cerr << action->program;
+    std::vector<std::string>::iterator I = action->args.begin();
+    while (I != action->args.end()) {
+      std::cerr << " " + *I;
+      ++I;
+    }
+    std::cerr << "\n";
+  }
+
+  void DumpAction(CompilerDriver::Action* action) {
+    std::cerr << "command = " << action->program;
+    std::vector<std::string>::iterator I = action->args.begin();
+    while (I != action->args.end()) {
       std::cerr << " " + *I;
       ++I;
     }
     std::cerr << "\n";
+    std::cerr << "flags = " << action->flags << "\n";
   }
 
   void DumpConfigData(CompilerDriver::ConfigData* cd, const std::string& type ){
     std::cerr << "Configuration Data For '" << cd->langName << "' (" << type 
       << ")\n";
-    std::cerr << "translator.preprocesses=" << cd->TranslatorPreprocesses 
-      << "\n";
-    std::cerr << "translator.groks_dash_O=" << cd->TranslatorGroksDashO << "\n";
-    std::cerr << "translator.optimizes=" << cd->TranslatorOptimizes << "\n";
-    std::cerr << "preprocessor.needed=" << cd->PreprocessorNeeded << "\n";
     std::cerr << "PreProcessor: ";
-    WriteAction(&cd->PreProcessor);
+    DumpAction(&cd->PreProcessor);
     std::cerr << "Translator: ";
-    WriteAction(&cd->Translator);
+    DumpAction(&cd->Translator);
     std::cerr << "Optimizer: ";
-    WriteAction(&cd->Optimizer);
+    DumpAction(&cd->Optimizer);
     std::cerr << "Assembler: ";
-    WriteAction(&cd->Assembler);
+    DumpAction(&cd->Assembler);
     std::cerr << "Linker: ";
-    WriteAction(&cd->Linker);
+    DumpAction(&cd->Linker);
   }
+
+  /// This specifies the passes to run for OPT_FAST_COMPILE (-O1)
+  /// which should reduce the volume of code and make compilation
+  /// faster. This is also safe on any llvm module. 
+  static const char* DefaultFastCompileOptimizations[] = {
+    "-simplifycfg", "-mem2reg", "-instcombine"
+  };
 }
 
+// Stuff in this namespace properly belongs in lib/System and needs
+// to be portable but we're avoiding that for now.
+namespace sys {
+
+  bool FileIsReadable(const std::string& fname) {
+    return 0 == access(fname.c_str(), F_OK | R_OK);
+  }
+
+  void CleanupTempFile(const std::string& fname) {
+    if (FileIsReadable(fname))
+      unlink(fname.c_str());
+  }
+
+  std::string MakeTemporaryDirectory() {
+    char temp_name[64];
+    strcpy(temp_name,"/tmp/llvm_XXXXXX");
+    if (0 == mkdtemp(temp_name))
+      throw std::string("Can't create temporary directory");
+    return temp_name;
+  }
+
+  std::string FindExecutableInPath(const std::string& program) {
+    // First, just see if the program is already executable
+    if (isExecutableFile(program)) return program;
+
+    // Get the path. If its empty, we can't do anything
+    const char *PathStr = getenv("PATH");
+    if (PathStr == 0) return "";
+
+    // Now we have a colon separated list of directories to search; try them.
+    unsigned PathLen = strlen(PathStr);
+    while (PathLen) {
+      // Find the first colon...
+      const char *Colon = std::find(PathStr, PathStr+PathLen, ':');
+    
+      // Check to see if this first directory contains the executable...
+      std::string FilePath = std::string(PathStr, Colon) + '/' + program;
+      if (isExecutableFile(FilePath))
+        return FilePath;                    // Found the executable!
+   
+      // Nope it wasn't in this directory, check the next range!
+      PathLen -= Colon-PathStr;
+      PathStr = Colon;
+
+      // Advance past duplicate coons
+      while (*PathStr == ':') {
+        PathStr++;
+        PathLen--;
+      }
+    }
+
+    // If we fell out, we ran out of directories in PATH to search, return failure
+    return "";
+  }
+}
 
 CompilerDriver::CompilerDriver(ConfigDataProvider& confDatProv )
   : cdp(&confDatProv)
@@ -74,21 +142,42 @@ CompilerDriver::CompilerDriver(ConfigDataProvider& confDatProv )
   , timeActions(false)
   , emitRawCode(false)
   , emitNativeCode(false)
+  , keepTemps(false)
   , machine()
-  , libPaths()
+  , LibraryPaths()
+  , AdditionalArgs()
+  , TempDir()
 {
   // FIXME: These libraries are platform specific
-  libPaths.push_back("/lib");
-  libPaths.push_back("/usr/lib");
+  LibraryPaths.push_back("/lib");
+  LibraryPaths.push_back("/usr/lib");
+  AdditionalArgs.reserve(NUM_PHASES);
+  StringVector emptyVec;
+  for (unsigned i = 0; i < NUM_PHASES; ++i)
+    AdditionalArgs.push_back(emptyVec);
 }
 
 CompilerDriver::~CompilerDriver() {
   cdp = 0;
-  libPaths.clear();
+  LibraryPaths.clear();
+  AdditionalArgs.clear();
+}
+
+CompilerDriver::ConfigData::ConfigData()
+  : langName()
+  , PreProcessor()
+  , Translator()
+  , Optimizer()
+  , Assembler()
+  , Linker()
+{
+  StringVector emptyVec;
+  for (unsigned i = 0; i < NUM_PHASES; ++i)
+    opts.push_back(emptyVec);
 }
 
 void CompilerDriver::error( const std::string& errmsg ) {
-  std::cerr << "Error: " << errmsg << ".\n";
+  std::cerr << "llvmc: Error: " << errmsg << ".\n";
   exit(1);
 }
 
@@ -97,7 +186,10 @@ CompilerDriver::Action* CompilerDriver::GetAction(ConfigData* cd,
                           const std::string& output,
                           Phases phase)
 {
-  Action* pat = 0;
+  Action* pat = 0; ///< The pattern/template for the action
+  Action* action = new Action; ///< The actual action to execute
+
+  // Get the action pattern
   switch (phase) {
     case PREPROCESSING: pat = &cd->PreProcessor; break;
     case TRANSLATION:   pat = &cd->Translator; break;
@@ -109,27 +201,82 @@ CompilerDriver::Action* CompilerDriver::GetAction(ConfigData* cd,
       break;
   }
   assert(pat != 0 && "Invalid command pattern");
-  Action* a = new Action(*pat);
-  if (pat->inputAt < a->args.size())
-    a->args[pat->inputAt] = input;
-  if (pat->outputAt < a->args.size())
-    a->args[pat->outputAt] = output;
-  return a;
+
+  // Copy over some pattern things that don't need to change
+  action->program = pat->program;
+  action->flags = pat->flags;
+
+  // Do the substitutions from the pattern to the actual
+  StringVector::iterator PI = pat->args.begin();
+  StringVector::iterator PE = pat->args.end();
+  while (PI != PE) {
+    if ((*PI)[0] == '%') {
+      if (*PI == "%in%") {
+        action->args.push_back(input);
+      } else if (*PI == "%out%") {
+        action->args.push_back(output);
+      } else if (*PI == "%time%") {
+        if (timePasses)
+          action->args.push_back("-time-passes");
+      } else if (*PI == "%stats%") {
+        if (showStats)
+          action->args.push_back("-stats");
+      } else if (*PI == "%target%") {
+        // FIXME: Ignore for now
+      } else if (*PI == "%opt%") {
+        if (!emitRawCode) {
+          if (cd->opts.size() > static_cast<unsigned>(optLevel) && 
+              !cd->opts[optLevel].empty())
+            action->args.insert(action->args.end(), cd->opts[optLevel].begin(),
+                cd->opts[optLevel].end());
+          else
+            error("Optimization options for level " + utostr(unsigned(optLevel)) + 
+                  " were not specified");
+        }
+      } else if (*PI == "%args%") {
+        if (AdditionalArgs.size() > unsigned(phase))
+          if (!AdditionalArgs[phase].empty()) {
+            // Get specific options for each kind of action type
+            StringVector& addargs = AdditionalArgs[phase];
+            // Add specific options for each kind of action type
+            action->args.insert(action->args.end(), addargs.begin(), addargs.end());
+          }
+      } else {
+        error("Invalid substitution name" + *PI);
+      }
+    } else {
+      // Its not a substitution, just put it in the action
+      action->args.push_back(*PI);
+    }
+    PI++;
+  }
+
+
+  // Finally, we're done
+  return action;
 }
 
-void CompilerDriver::DoAction(Action*a)
-{
+bool CompilerDriver::DoAction(Action*action) {
+  assert(action != 0 && "Invalid Action!");
   if (isVerbose)
-    WriteAction(a);
+    WriteAction(action);
   if (!isDryRun) {
-    std::cerr << "execve(\"" << a->program << "\",[\n";
-    std::vector<std::string>::iterator I = a->args.begin();
-    while (I != a->args.end()) {
-      std::cerr << "  \"" << *I << "\",\n";
-      ++I;
-    }
-    std::cerr << "],ENV);\n";
+    std::string prog(sys::FindExecutableInPath(action->program));
+    if (prog.empty())
+      error("Can't find program '" + action->program + "'");
+
+    // Get the program's arguments
+    const char* argv[action->args.size() + 1];
+    argv[0] = prog.c_str();
+    unsigned i = 1;
+    for (; i <= action->args.size(); ++i)
+      argv[i] = action->args[i-1].c_str();
+    argv[i] = 0;
+
+    // Invoke the program
+    return !ExecWait(argv, environ);
   }
+  return true;
 }
 
 int CompilerDriver::execute(const InputList& InpList, 
@@ -162,27 +309,36 @@ int CompilerDriver::execute(const InputList& InpList,
   if (finalPhase == LINKING && Output.empty())
     error("An output file name must be specified for linker output");
 
+  // This vector holds all the resulting actions of the following loop.
   std::vector<Action*> actions;
 
+  // Create a temporary directory for our temporary files
+  std::string TempDir(sys::MakeTemporaryDirectory());
+  std::string TempPreprocessorOut(TempDir + "/preproc.o");
+  std::string TempTranslatorOut(TempDir + "/trans.o");
+  std::string TempOptimizerOut(TempDir + "/opt.o");
+  std::string TempAssemblerOut(TempDir + "/asm.o");
+
   /// PRE-PROCESSING / TRANSLATION / OPTIMIZATION / ASSEMBLY phases
   // for each input item
   std::vector<std::string> LinkageItems;
+  std::string OutFile(Output);
   InputList::const_iterator I = InpList.begin();
   while ( I != InpList.end() ) {
     // Get the suffix of the file name
-    std::string suffix = GetSuffix(I->first);
+    const std::string& ftype = I->second;
 
     // If its a library, bytecode file, or object file, save 
     // it for linking below and short circuit the 
     // pre-processing/translation/assembly phases
-    if (I->second.empty() || suffix == "o" || suffix == "bc") {
+    if (ftype.empty() ||  ftype == "o" || ftype == "bc") {
       // We shouldn't get any of these types of files unless we're 
       // later going to link. Enforce this limit now.
       if (finalPhase != LINKING) {
         error("Pre-compiled objects found but linking not requested");
       }
       LinkageItems.push_back(I->first);
-      continue; // short circuit remainder of loop
+      ++I; continue; // short circuit remainder of loop
     }
 
     // At this point, we know its something we need to translate
@@ -195,55 +351,223 @@ int CompilerDriver::execute(const InputList& InpList,
     if (isDebug)
       DumpConfigData(cd,I->second);
 
-    // We have valid configuration data, now figure out where the output
-    // of compilation should end up.
-    std::string OutFile;
-    if (finalPhase != LINKING) {
-      if (InpList.size() == 1 && !Output.empty()) 
-        OutFile = Output;
-      else
-        OutFile = RemoveSuffix(I->first) + OutputSuffix;
-    } else {
-      OutFile = Output;
-    }
+    // Initialize the input file
+    std::string InFile(I->first);
 
-    /// PRE-PROCESSING PHASE
-    if (finalPhase == PREPROCESSING) {
-      if (cd->PreProcessor.program.empty())
-        error(cd->langName + " does not support pre-processing");
-      else
-        actions.push_back(GetAction(cd,I->first,OutFile,PREPROCESSING));
-    } else if (cd->PreprocessorNeeded && !cd->TranslatorPreprocesses) {
-      if (!cd->PreProcessor.program.empty()) {
-        actions.push_back(GetAction(cd,I->first,OutFile,PREPROCESSING));
+    // PRE-PROCESSING PHASE
+    Action& action = cd->PreProcessor;
+
+    // Get the preprocessing action, if needed, or error if appropriate
+    if (!action.program.empty()) {
+      if (action.isSet(REQUIRED_FLAG) || finalPhase == PREPROCESSING) {
+        if (finalPhase == PREPROCESSING)
+          actions.push_back(GetAction(cd,InFile,OutFile,PREPROCESSING));
+        else {
+          actions.push_back(GetAction(cd,InFile,TempPreprocessorOut,
+                            PREPROCESSING));
+          InFile = TempPreprocessorOut;
+        }
       }
+    } else if (finalPhase == PREPROCESSING) {
+      error(cd->langName + " does not support pre-processing");
+    } else if (action.isSet(REQUIRED_FLAG)) {
+      error(std::string("Don't know how to pre-process ") + 
+            cd->langName + " files");
     }
 
     // Short-circuit remaining actions if all they want is pre-processing
     if (finalPhase == PREPROCESSING) { ++I; continue; };
 
     /// TRANSLATION PHASE
-    actions.push_back(GetAction(cd,I->first,OutFile,TRANSLATION));
+    action = cd->Translator;
+
+    // Get the translation action, if needed, or error if appropriate
+    if (!action.program.empty()) {
+      if (action.isSet(REQUIRED_FLAG) || finalPhase == TRANSLATION) {
+        if (finalPhase == TRANSLATION) 
+          actions.push_back(GetAction(cd,InFile,OutFile,TRANSLATION));
+        else {
+          actions.push_back(GetAction(cd,InFile,TempTranslatorOut,TRANSLATION));
+          InFile = TempTranslatorOut;
+        }
+
+        // ll -> bc Helper
+        if (action.isSet(OUTPUT_IS_ASM_FLAG)) {
+          /// The output of the translator is an LLVM Assembly program
+          /// We need to translate it to bytecode
+          Action* action = new Action();
+          action->program = "llvm-as";
+          action->args.push_back(InFile);
+          action->args.push_back("-o");
+          InFile += ".bc";
+          action->args.push_back(InFile);
+          actions.push_back(action);
+        }
+      }
+    } else if (finalPhase == TRANSLATION) {
+      error(cd->langName + " does not support translation");
+    } else if (action.isSet(REQUIRED_FLAG)) {
+      error(std::string("Don't know how to translate ") + 
+            cd->langName + " files");
+    }
+
     // Short-circuit remaining actions if all they want is translation
     if (finalPhase == TRANSLATION) { ++I; continue; }
 
     /// OPTIMIZATION PHASE
-    actions.push_back(GetAction(cd,I->first,OutFile,OPTIMIZATION));
+    action = cd->Optimizer;
+
+    // Get the optimization action, if needed, or error if appropriate
+    if (!emitRawCode) {
+      if (!action.program.empty()) {
+        if (action.isSet(REQUIRED_FLAG) || finalPhase == OPTIMIZATION) {
+          if (finalPhase == OPTIMIZATION)
+            actions.push_back(GetAction(cd,InFile,OutFile,OPTIMIZATION));
+          else {
+            actions.push_back(GetAction(cd,InFile,TempOptimizerOut,OPTIMIZATION));
+            InFile = TempOptimizerOut;
+          }
+          // ll -> bc Helper
+          if (action.isSet(OUTPUT_IS_ASM_FLAG)) {
+            /// The output of the translator is an LLVM Assembly program
+            /// We need to translate it to bytecode
+            Action* action = new Action();
+            action->program = "llvm-as";
+            action->args.push_back(InFile);
+            action->args.push_back("-f");
+            action->args.push_back("-o");
+            InFile += ".bc";
+            action->args.push_back(InFile);
+            actions.push_back(action);
+          }
+        }
+      } else if (finalPhase == OPTIMIZATION) {
+        error(cd->langName + " does not support optimization");
+      } else if (action.isSet(REQUIRED_FLAG)) {
+        error(std::string("Don't know how to optimize ") + 
+            cd->langName + " files");
+      }
+    }
+
     // Short-circuit remaining actions if all they want is optimization
     if (finalPhase == OPTIMIZATION) { ++I; continue; }
 
+    /// ASSEMBLY PHASE
+    action = cd->Assembler;
+
+    if (finalPhase == ASSEMBLY || emitNativeCode) {
+      if (emitNativeCode) {
+        if (action.program.empty()) {
+          error(std::string("Native Assembler not specified for ") +
+                cd->langName + " files");
+        } else if (finalPhase == ASSEMBLY) {
+          actions.push_back(GetAction(cd,InFile,OutFile,ASSEMBLY));
+        } else {
+          actions.push_back(GetAction(cd,InFile,TempAssemblerOut,ASSEMBLY));
+          InFile = TempAssemblerOut;
+        }
+      } else {
+        // Just convert back to llvm assembly with llvm-dis
+        Action* action = new Action();
+        action->program = "llvm-dis";
+        action->args.push_back(InFile);
+        action->args.push_back("-f");
+        action->args.push_back("-o");
+        action->args.push_back(OutFile);
+        actions.push_back(action);
+      }
+    }
+
+    // Short-circuit remaining actions if all they want is assembly output
+    if (finalPhase == ASSEMBLY) { ++I; continue; }
+      
+    // Register the OutFile as a link candidate
+    LinkageItems.push_back(InFile);
+
+    // Go to next file to be processed
     ++I;
   }
 
   /// LINKING PHASE
+  if (finalPhase == LINKING) {
+    if (emitNativeCode) {
+      error("llvmc doesn't know how to link native code yet");
+    } else {
+      // First, we need to examine the files to ensure that they all contain
+      // bytecode files. Since the final output is bytecode, we can only
+      // link bytecode.
+      StringVector::const_iterator I = LinkageItems.begin();
+      StringVector::const_iterator E = LinkageItems.end();
+      StringVector lib_list;
+      while (I != E ) {
+        if (sys::FileIsReadable(*I)) {
+          if (CheckMagic(*I, "llvm")) {
+            // Examine the bytecode file for additional bytecode libraries to
+            // link in.
+            ModuleProvider* mp = getBytecodeModuleProvider(*I);
+            assert(mp!=0 && "Couldn't get module provider from bytecode file");
+            Module* M = mp->releaseModule();
+            assert(M!=0 && "Couldn't get module from module provider");
+            Module::lib_iterator LI = M->lib_begin();
+            Module::lib_iterator LE = M->lib_end();
+            while ( LI != LE ) {
+              if (sys::FileIsReadable(*LI)) {
+                if (CheckMagic(*I,"llvm")) {
+                  lib_list.push_back(*LI);
+                } else {
+                  error(std::string("Library '") + *LI + "' required by file '"
+                        + *I + "' is not an LLVM bytecode file");
+                }
+              } else {
+                error(std::string("Library '") + *LI + "' required by file '"
+                      + *I + "' is not readable");
+              }
+              ++I;
+            }
+          } else {
+            error(std::string("File '") + *I + " is not an LLVM byte code file");
+          } 
+        } else {
+          error(std::string("File '") + *I + " is not readable");
+        }
+        ++I;
+      }
+
+      // We're emitting bytecode so let's build an llvm-link Action
+      Action* link = new Action();
+      link->program = "llvm-link";
+      link->args = LinkageItems;
+      link->args.insert(link->args.end(), lib_list.begin(), lib_list.end());
+      link->args.push_back("-f");
+      link->args.push_back("-o");
+      link->args.push_back(OutFile);
+      if (timePasses)
+        link->args.push_back("-time-passes");
+      if (showStats)
+        link->args.push_back("-stats");
+      actions.push_back(link);
+    }
+  }
 
   /// RUN THE ACTIONS
   std::vector<Action*>::iterator aIter = actions.begin();
   while (aIter != actions.end()) {
-    DoAction(*aIter);
+    if (!DoAction(*aIter))
+      error("Action failed");
     aIter++;
   }
 
+  if (!keepTemps) {
+    // Cleanup files
+    ::sys::CleanupTempFile(TempPreprocessorOut);
+    ::sys::CleanupTempFile(TempTranslatorOut);
+    ::sys::CleanupTempFile(TempOptimizerOut);
+
+    // Cleanup temporary directory we created
+    if (::sys::FileIsReadable(TempDir))
+      rmdir(TempDir.c_str());
+  }
+
   return 0;
 }