//===------------------------------------------------------------------------===
#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;
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)
, 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);
}
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;
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,
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
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;
}