- Rename and rearrange for clarity
[oota-llvm.git] / tools / llvmc / CompilerDriver.cpp
index 038274b72aa6b54978ff866d60a85a42547b90f9..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;
@@ -25,35 +30,27 @@ 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* a) {
-    std::cerr << "command = " << a->program;
-    std::vector<std::string>::iterator I = a->args.begin();
-    while (I != a->args.end()) {
+  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 = " << a->flags << "\n";
-    std::cerr << "inputAt = " << a->inputAt << "\n";
-    std::cerr << "outputAt = " << a->outputAt << "\n";
+    std::cerr << "flags = " << action->flags << "\n";
   }
 
   void DumpConfigData(CompilerDriver::ConfigData* cd, const std::string& type ){
@@ -70,8 +67,70 @@ namespace {
     std::cerr << "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)
@@ -83,46 +142,43 @@ CompilerDriver::CompilerDriver(ConfigDataProvider& confDatProv )
   , timeActions(false)
   , emitRawCode(false)
   , emitNativeCode(false)
+  , keepTemps(false)
   , machine()
   , LibraryPaths()
-  , PreprocessorOptions()
-  , TranslatorOptions()
-  , OptimizerOptions()
-  , AssemblerOptions()
-  , LinkerOptions()
+  , AdditionalArgs()
+  , TempDir()
 {
   // FIXME: These libraries are platform specific
   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;
   LibraryPaths.clear();
-  PreprocessorOptions.clear();
-  TranslatorOptions.clear();
-  OptimizerOptions.clear();
-  AssemblerOptions.clear();
-  LinkerOptions.clear();
+  AdditionalArgs.clear();
 }
 
-void CompilerDriver::error( const std::string& errmsg ) {
-  std::cerr << "Error: " << errmsg << ".\n";
-  exit(1);
+CompilerDriver::ConfigData::ConfigData()
+  : langName()
+  , PreProcessor()
+  , Translator()
+  , Optimizer()
+  , Assembler()
+  , Linker()
+{
+  StringVector emptyVec;
+  for (unsigned i = 0; i < NUM_PHASES; ++i)
+    opts.push_back(emptyVec);
 }
 
-inline std::string makeDashO(CompilerDriver::OptimizationLevels lev) {
-  if (lev == CompilerDriver::OPT_NONE) return "";
-  std::string result("-O");
-  switch (lev) {
-    case CompilerDriver::OPT_FAST_COMPILE :     result.append("1"); break;
-    case CompilerDriver::OPT_SIMPLE:            result.append("2"); break;
-    case CompilerDriver::OPT_AGGRESSIVE:        result.append("3"); break;
-    case CompilerDriver::OPT_LINK_TIME:         result.append("4"); break;
-    case CompilerDriver::OPT_AGGRESSIVE_LINK_TIME: result.append("5"); break;
-    default:                    assert(!"Invalid optimization level!");
-  }
-  return result;
+void CompilerDriver::error( const std::string& errmsg ) {
+  std::cerr << "llvmc: Error: " << errmsg << ".\n";
+  exit(1);
 }
 
 CompilerDriver::Action* CompilerDriver::GetAction(ConfigData* cd, 
@@ -130,7 +186,9 @@ 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;
@@ -144,78 +202,85 @@ CompilerDriver::Action* CompilerDriver::GetAction(ConfigData* cd,
   }
   assert(pat != 0 && "Invalid command pattern");
 
-  // Create the resulting action
-  Action* a = new Action(*pat);
+  // 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++;
+  }
 
-  // Replace the substitution arguments
-  if (pat->inputAt < a->args.size())
-    a->args[pat->inputAt] = input;
-  if (pat->outputAt < a->args.size())
-    a->args[pat->outputAt] = output;
 
-  // Insert specific options for each kind of action type
-  switch (phase) {
-    case PREPROCESSING:
-      a->args.insert(a->args.begin(), PreprocessorOptions.begin(), 
-                    PreprocessorOptions.end());
-      break;
-    case TRANSLATION:   
-      a->args.insert(a->args.begin(), TranslatorOptions.begin(), 
-                    TranslatorOptions.end());
-      if (a->isSet(GROKS_DASH_O_FLAG))
-        a->args.insert(a->args.begin(), makeDashO(optLevel));
-      else if (a->isSet(GROKS_O10N_FLAG))
-        a->args.insert(a->args.begin(), cd->opts[optLevel].begin(),
-            cd->opts[optLevel].end());
-      break;
-    case OPTIMIZATION:  
-      a->args.insert(a->args.begin(), OptimizerOptions.begin(), 
-                    OptimizerOptions.end());
-      if (a->isSet(GROKS_DASH_O_FLAG))
-        a->args.insert(a->args.begin(), makeDashO(optLevel));
-      else if (a->isSet(GROKS_O10N_FLAG))
-        a->args.insert(a->args.begin(), cd->opts[optLevel].begin(),
-            cd->opts[optLevel].end());
-      break;
-    case ASSEMBLY:      
-      a->args.insert(a->args.begin(), AssemblerOptions.begin(), 
-                    AssemblerOptions.end());
-      break;
-    case LINKING:       
-      a->args.insert(a->args.begin(), LinkerOptions.begin(), 
-                    LinkerOptions.end());
-      if (a->isSet(GROKS_DASH_O_FLAG))
-        a->args.insert(a->args.begin(), makeDashO(optLevel));
-      else if (a->isSet(GROKS_O10N_FLAG))
-        a->args.insert(a->args.begin(), cd->opts[optLevel].begin(),
-            cd->opts[optLevel].end());
-      break;
-    default:
-      assert(!"Invalid driver phase!");
-      break;
-  }
-  return a;
+  // 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 << "\",[\"";
-    std::vector<std::string>::iterator I = a->args.begin();
-    while (I != a->args.end()) {
-      std::cerr << *I;
-      ++I;
-      if (I != a->args.end())
-        std::cerr << "\",\"";
-    }
-    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, 
-                            const sys::Path& Output ) {
+                            const std::string& Output ) {
   // Echo the configuration of options if we're running verbose
   if (isDebug)
   {
@@ -248,32 +313,32 @@ int CompilerDriver::execute(const InputList& InpList,
   std::vector<Action*> actions;
 
   // Create a temporary directory for our temporary files
-  sys::Path TempDir(sys::Path::CONSTRUCT_TEMP_DIR);
-  sys::Path TempPreprocessorOut;
-  sys::Path TempTranslatorOut;
-  sys::Path TempOptimizerOut;
-  sys::Path TempAssemblerOut;
-  sys::Path TempLinkerOut;
+  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
@@ -286,95 +351,222 @@ 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
-    Action& a = cd->PreProcessor;
+    Action& action = cd->PreProcessor;
 
     // Get the preprocessing action, if needed, or error if appropriate
-    if (!a.program.empty()) {
-      if (a.isSet(REQUIRED_FLAG) || finalPhase == PREPROCESSING) {
-        TempPreprocessorOut = TempDir;
-        TempPreprocessorOut.append_file("preproc.out");
-        actions.push_back(GetAction(cd,I->first,
-              TempPreprocessorOut,PREPROCESSING));
+    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 (a.isSet(REQUIRED_FLAG)) {
+    } 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
-    a = cd->Translator;
+    action = cd->Translator;
 
     // Get the translation action, if needed, or error if appropriate
-    if (!a.program.empty()) {
-      if (a.isSet(REQUIRED_FLAG) || finalPhase == TRANSLATION) {
-        TempTranslatorOut = TempDir;
-        TempTranslatorOut.append_file("trans.out");
-        actions.push_back(GetAction(cd,I->first,TempTranslatorOut,TRANSLATION));
+    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 (a.isSet(REQUIRED_FLAG)) {
+    } 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
-    a = cd->Optimizer;
+    action = cd->Optimizer;
 
     // Get the optimization action, if needed, or error if appropriate
-    if (!a.program.empty()) {
-      TempOptimizerOut = TempDir;
-      TempOptimizerOut.append_file("trans.out");
-      actions.push_back(GetAction(cd,I->first,TempOptimizerOut,OPTIMIZATION));
-    } else if (finalPhase == OPTIMIZATION) {
-      error(cd->langName + " does not support optimization");
-    } else if (a.isSet(REQUIRED_FLAG)) {
-      error(std::string("Don't know how to optimize ") + 
+    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++;
   }
 
-  // Cleanup files
-  if (TempPreprocessorOut.exists())
-    TempPreprocessorOut.remove_file();
-  if (TempTranslatorOut.exists())
-    TempTranslatorOut.remove_file();
-  if (TempOptimizerOut.exists())
-    TempOptimizerOut.remove_file();
-  if (TempDir.exists())
-    TempDir.remove_directory();
+  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;
 }