diff --git a/tools/llvmc/CompilerDriver.cpp b/tools/llvmc/CompilerDriver.cpp index 08d60929a01..7dadf433261 100644 --- a/tools/llvmc/CompilerDriver.cpp +++ b/tools/llvmc/CompilerDriver.cpp @@ -14,26 +14,19 @@ #include "CompilerDriver.h" #include "ConfigLexer.h" -#include "llvm/Bytecode/Reader.h" #include "llvm/Module.h" +#include "llvm/Bytecode/Reader.h" #include "Support/FileUtilities.h" -#include "Support/SystemUtils.h" +#include "Support/SetVector.h" #include "Support/StringExtras.h" #include using namespace llvm; namespace { - inline std::string RemoveSuffix(const std::string& fullName) { - size_t dotpos = fullName.rfind('.',fullName.size()); - if ( dotpos == std::string::npos ) return fullName; - return fullName.substr(0, dotpos); - } - - const char OutputSuffix[] = ".o"; void WriteAction(CompilerDriver::Action* action ) { - std::cerr << action->program; + std::cerr << action->program.c_str(); std::vector::iterator I = action->args.begin(); while (I != action->args.end()) { std::cerr << " " + *I; @@ -43,7 +36,7 @@ namespace { } void DumpAction(CompilerDriver::Action* action) { - std::cerr << "command = " << action->program; + std::cerr << "command = " << action->program.c_str(); std::vector::iterator I = action->args.begin(); while (I != action->args.end()) { std::cerr << " " + *I; @@ -74,93 +67,597 @@ namespace { 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--; + class CompilerDriverImpl : public CompilerDriver { + /// @name Constructors + /// @{ + public: + CompilerDriverImpl(ConfigDataProvider& confDatProv ) + : cdp(&confDatProv) + , finalPhase(LINKING) + , optLevel(OPT_FAST_COMPILE) + , Flags(0) + , machine() + , LibraryPaths() + , TempDir() + , AdditionalArgs() + { + TempDir = sys::Path::GetTemporaryDirectory(); + sys::RemoveDirectoryOnSignal(TempDir); + AdditionalArgs.reserve(NUM_PHASES); + StringVector emptyVec; + for (unsigned i = 0; i < NUM_PHASES; ++i) + AdditionalArgs.push_back(emptyVec); } - } - // If we fell out, we ran out of directories in PATH to search, return failure - return ""; - } -} + virtual ~CompilerDriverImpl() { + cleanup(); + cdp = 0; + LibraryPaths.clear(); + AdditionalArgs.clear(); + } -CompilerDriver::CompilerDriver(ConfigDataProvider& confDatProv ) - : cdp(&confDatProv) - , finalPhase(LINKING) - , optLevel(OPT_FAST_COMPILE) - , isDryRun(false) - , isVerbose(false) - , isDebug(false) - , timeActions(false) - , emitRawCode(false) - , emitNativeCode(false) - , keepTemps(false) - , machine() - , LibraryPaths() - , 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); + /// @} + /// @name Methods + /// @{ + public: + virtual void setFinalPhase( Phases phase ) { + finalPhase = phase; + } + + virtual void setOptimization( OptimizationLevels level ) { + optLevel = level; + } + + virtual void setDriverFlags( unsigned flags ) { + Flags = flags & DRIVER_FLAGS_MASK; + } + + virtual void setOutputMachine( const std::string& machineName ) { + machine = machineName; + } + + virtual void setPhaseArgs(Phases phase, const StringVector& opts) { + assert(phase <= LINKING && phase >= PREPROCESSING); + AdditionalArgs[phase] = opts; + } + + virtual void setLibraryPaths(const StringVector& paths) { + StringVector::const_iterator I = paths.begin(); + StringVector::const_iterator E = paths.end(); + while (I != E) { + sys::Path tmp; + tmp.set_directory(*I); + LibraryPaths.push_back(tmp); + ++I; + } + } + + virtual void addLibraryPath( const sys::Path& libPath ) { + LibraryPaths.push_back(libPath); + } + + /// @} + /// @name Functions + /// @{ + private: + bool isSet(DriverFlags flag) { + return 0 != ((flag & DRIVER_FLAGS_MASK) & Flags); + } + + void cleanup() { + if (!isSet(KEEP_TEMPS_FLAG)) { + if (TempDir.is_directory() && TempDir.writable()) + TempDir.destroy_directory(/*remove_contents=*/true); + } else { + std::cout << "Temporary files are in " << TempDir.get() << "\n"; + } + } + + sys::Path MakeTempFile(const std::string& basename, const std::string& suffix ) { + sys::Path result(TempDir); + if (!result.append_file(basename)) + throw basename + ": can't use this file name"; + if (!result.append_suffix(suffix)) + throw suffix + ": can't use this file suffix"; + return result; + } + + Action* GetAction(ConfigData* cd, + const sys::Path& input, + const sys::Path& output, + Phases phase) + { + 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; + case OPTIMIZATION: pat = &cd->Optimizer; break; + case ASSEMBLY: pat = &cd->Assembler; break; + case LINKING: pat = &cd->Linker; break; + default: + assert(!"Invalid driver phase!"); + break; + } + assert(pat != 0 && "Invalid command pattern"); + + // 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.get()); + } else if (*PI == "%out%") { + action->args.push_back(output.get()); + } else if (*PI == "%time%") { + if (isSet(TIME_PASSES_FLAG)) + action->args.push_back("-time-passes"); + } else if (*PI == "%stats%") { + if (isSet(SHOW_STATS_FLAG)) + action->args.push_back("-stats"); + } else if (*PI == "%force%") { + if (isSet(FORCE_FLAG)) + action->args.push_back("-f"); + } else if (*PI == "%verbose%") { + if (isSet(VERBOSE_FLAG)) + action->args.push_back("-v"); + } else if (*PI == "%target%") { + // FIXME: Ignore for now + } else if (*PI == "%opt%") { + if (!isSet(EMIT_RAW_FLAG)) { + if (cd->opts.size() > static_cast(optLevel) && + !cd->opts[optLevel].empty()) + action->args.insert(action->args.end(), cd->opts[optLevel].begin(), + cd->opts[optLevel].end()); + else + throw std::string("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 { + throw "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; + } + + bool DoAction(Action*action) { + assert(action != 0 && "Invalid Action!"); + if (isSet(VERBOSE_FLAG)) + WriteAction(action); + if (!isSet(DRY_RUN_FLAG)) { + action->program = sys::Program::FindProgramByName(action->program.get()); + if (action->program.is_empty()) + throw "Can't find program '" + action->program.get() + "'"; + + // Invoke the program + return 0 == action->program.ExecuteAndWait(action->args); + } + return true; + } + + /// This method tries various variants of a linkage item's file + /// name to see if it can find an appropriate file to link with + /// in the directory specified. + llvm::sys::Path GetPathForLinkageItem(const std::string& link_item, + const sys::Path& dir) { + sys::Path fullpath(dir); + fullpath.append_file(link_item); + fullpath.append_suffix("bc"); + if (fullpath.readable()) + return fullpath; + fullpath.elide_suffix(); + fullpath.append_suffix("o"); + if (fullpath.readable()) + return fullpath; + fullpath = dir; + fullpath.append_file(std::string("lib") + link_item); + fullpath.append_suffix("a"); + if (fullpath.readable()) + return fullpath; + fullpath.elide_suffix(); + fullpath.append_suffix("so"); + if (fullpath.readable()) + return fullpath; + + // Didn't find one. + fullpath.clear(); + return fullpath; + } + + /// This method processes a linkage item. The item could be a + /// Bytecode file needing translation to native code and that is + /// dependent on other bytecode libraries, or a native code + /// library that should just be linked into the program. + bool ProcessLinkageItem(const llvm::sys::Path& link_item, + SetVector& set, + std::string& err) { + // First, see if the unadorned file name is not readable. If so, + // we must track down the file in the lib search path. + sys::Path fullpath; + if (!link_item.readable()) { + // First, look for the library using the -L arguments specified + // on the command line. + PathVector::iterator PI = LibraryPaths.begin(); + PathVector::iterator PE = LibraryPaths.end(); + while (PI != PE && fullpath.is_empty()) { + fullpath = GetPathForLinkageItem(link_item.get(),*PI); + ++PI; + } + + // If we didn't find the file in any of the library search paths + // so we have to bail. No where else to look. + if (fullpath.is_empty()) { + err = std::string("Can't find linkage item '") + link_item.get() + "'"; + return false; + } + } else { + fullpath = link_item; + } + + // If we got here fullpath is the path to the file, and its readable. + set.insert(fullpath); + + // If its an LLVM bytecode file ... + if (CheckMagic(fullpath.get(), "llvm")) { + // Process the dependent libraries recursively + Module::LibraryListType modlibs; + if (GetBytecodeDependentLibraries(fullpath.get(),modlibs)) { + // Traverse the dependent libraries list + Module::lib_iterator LI = modlibs.begin(); + Module::lib_iterator LE = modlibs.end(); + while ( LI != LE ) { + if (!ProcessLinkageItem(sys::Path(*LI),set,err)) { + if (err.empty()) { + err = std::string("Library '") + *LI + + "' is not valid for linking but is required by file '" + + fullpath.get() + "'"; + } else { + err += " which is required by file '" + fullpath.get() + "'"; + } + return false; + } + ++LI; + } + } else if (err.empty()) { + err = std::string("The dependent libraries could not be extracted from '") + + fullpath.get(); + return false; + } + } + return true; + } + + /// @} + /// @name Methods + /// @{ + public: + virtual int execute(const InputList& InpList, const sys::Path& Output ) { + try { + // Echo the configuration of options if we're running verbose + if (isSet(DEBUG_FLAG)) { + std::cerr << "Compiler Driver Options:\n"; + std::cerr << "DryRun = " << isSet(DRY_RUN_FLAG) << "\n"; + std::cerr << "Verbose = " << isSet(VERBOSE_FLAG) << " \n"; + std::cerr << "TimeActions = " << isSet(TIME_ACTIONS_FLAG) << "\n"; + std::cerr << "TimePasses = " << isSet(TIME_PASSES_FLAG) << "\n"; + std::cerr << "ShowStats = " << isSet(SHOW_STATS_FLAG) << "\n"; + std::cerr << "EmitRawCode = " << isSet(EMIT_RAW_FLAG) << "\n"; + std::cerr << "EmitNativeCode = " << isSet(EMIT_NATIVE_FLAG) << "\n"; + std::cerr << "ForceOutput = " << isSet(FORCE_FLAG) << "\n"; + std::cerr << "KeepTemps = " << isSet(KEEP_TEMPS_FLAG) << "\n"; + std::cerr << "OutputMachine = " << machine << "\n"; + InputList::const_iterator I = InpList.begin(); + while ( I != InpList.end() ) { + std::cerr << "Input: " << I->first.get() << "(" << I->second << ")\n"; + ++I; + } + std::cerr << "Output: " << Output.get() << "\n"; + } + + // If there's no input, we're done. + if (InpList.empty()) + throw std::string("Nothing to compile."); + + // If they are asking for linking and didn't provide an output + // file then its an error (no way for us to "make up" a meaningful + // file name based on the various linker input files). + if (finalPhase == LINKING && Output.is_empty()) + throw std::string( + "An output file name must be specified for linker output"); + + // This vector holds all the resulting actions of the following loop. + std::vector actions; + + /// PRE-PROCESSING / TRANSLATION / OPTIMIZATION / ASSEMBLY phases + // for each input item + SetVector LinkageItems; + sys::Path OutFile(Output); + InputList::const_iterator I = InpList.begin(); + while ( I != InpList.end() ) { + // Get the suffix of the file name + 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 (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) { + throw std::string( + "Pre-compiled objects found but linking not requested"); + } + LinkageItems.insert(I->first); + ++I; continue; // short circuit remainder of loop + } + + // At this point, we know its something we need to translate + // and/or optimize. See if we can get the configuration data + // for this kind of file. + ConfigData* cd = cdp->ProvideConfigData(I->second); + if (cd == 0) + throw std::string("Files of type '") + I->second + + "' are not recognized."; + if (isSet(DEBUG_FLAG)) + DumpConfigData(cd,I->second); + + // Initialize the input file + sys::Path InFile(I->first); + + // PRE-PROCESSING PHASE + Action& action = cd->PreProcessor; + + // Get the preprocessing action, if needed, or error if appropriate + if (!action.program.is_empty()) { + if (action.isSet(REQUIRED_FLAG) || finalPhase == PREPROCESSING) { + if (finalPhase == PREPROCESSING) + actions.push_back(GetAction(cd,InFile,OutFile,PREPROCESSING)); + else { + sys::Path TempFile(MakeTempFile(I->first.get(),"E")); + actions.push_back(GetAction(cd,InFile,TempFile,PREPROCESSING)); + InFile = TempFile; + } + } + } else if (finalPhase == PREPROCESSING) { + throw cd->langName + " does not support pre-processing"; + } else if (action.isSet(REQUIRED_FLAG)) { + throw 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 + action = cd->Translator; + + // Get the translation action, if needed, or error if appropriate + if (!action.program.is_empty()) { + if (action.isSet(REQUIRED_FLAG) || finalPhase == TRANSLATION) { + if (finalPhase == TRANSLATION) + actions.push_back(GetAction(cd,InFile,OutFile,TRANSLATION)); + else { + sys::Path TempFile(MakeTempFile(I->first.get(),"trans")); + actions.push_back(GetAction(cd,InFile,TempFile,TRANSLATION)); + InFile = TempFile; + } + + // 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.set_file("llvm-as"); + action->args.push_back(InFile.get()); + action->args.push_back("-o"); + InFile.append_suffix("bc"); + action->args.push_back(InFile.get()); + actions.push_back(action); + } + } + } else if (finalPhase == TRANSLATION) { + throw cd->langName + " does not support translation"; + } else if (action.isSet(REQUIRED_FLAG)) { + throw 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 + action = cd->Optimizer; + + // Get the optimization action, if needed, or error if appropriate + if (!isSet(EMIT_RAW_FLAG)) { + if (!action.program.is_empty()) { + if (action.isSet(REQUIRED_FLAG) || finalPhase == OPTIMIZATION) { + if (finalPhase == OPTIMIZATION) + actions.push_back(GetAction(cd,InFile,OutFile,OPTIMIZATION)); + else { + sys::Path TempFile(MakeTempFile(I->first.get(),"opt")); + actions.push_back(GetAction(cd,InFile,TempFile,OPTIMIZATION)); + InFile = TempFile; + } + // 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.set_file("llvm-as"); + action->args.push_back(InFile.get()); + action->args.push_back("-f"); + action->args.push_back("-o"); + InFile.append_suffix("bc"); + action->args.push_back(InFile.get()); + actions.push_back(action); + } + } + } else if (finalPhase == OPTIMIZATION) { + throw cd->langName + " does not support optimization"; + } else if (action.isSet(REQUIRED_FLAG)) { + throw 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 || isSet(EMIT_NATIVE_FLAG)) { + if (isSet(EMIT_NATIVE_FLAG)) { + if (action.program.is_empty()) { + throw std::string("Native Assembler not specified for ") + + cd->langName + " files"; + } else if (finalPhase == ASSEMBLY) { + actions.push_back(GetAction(cd,InFile,OutFile,ASSEMBLY)); + } else { + sys::Path TempFile(MakeTempFile(I->first.get(),"S")); + actions.push_back(GetAction(cd,InFile,TempFile,ASSEMBLY)); + InFile = TempFile; + } + } else { + // Just convert back to llvm assembly with llvm-dis + Action* action = new Action(); + action->program.set_file("llvm-dis"); + action->args.push_back(InFile.get()); + action->args.push_back("-f"); + action->args.push_back("-o"); + action->args.push_back(OutFile.get()); + 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.insert(InFile); + + // Go to next file to be processed + ++I; + } + + /// RUN THE COMPILATION ACTIONS + std::vector::iterator AI = actions.begin(); + std::vector::iterator AE = actions.end(); + while (AI != AE) { + if (!DoAction(*AI)) + throw "Action failed"; + AI++; + } + + /// LINKING PHASE + actions.clear(); + if (finalPhase == LINKING) { + if (isSet(EMIT_NATIVE_FLAG)) { + throw "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. + SetVector::const_iterator I = LinkageItems.begin(); + SetVector::const_iterator E = LinkageItems.end(); + std::string errmsg; + + while (I != E && ProcessLinkageItem(*I,LinkageItems,errmsg)) + ++I; + + if (!errmsg.empty()) + throw errmsg; + + // Insert the system libraries. + LibraryPaths.push_back(sys::Path::GetSystemLibraryPath1()); + LibraryPaths.push_back(sys::Path::GetSystemLibraryPath2()); + + // We're emitting bytecode so let's build an llvm-link Action + Action* link = new Action(); + link->program.set_file("llvm-link"); + for (PathVector::const_iterator I=LinkageItems.begin(), + E=LinkageItems.end(); I != E; ++I ) + link->args.push_back(I->get()); + if (isSet(VERBOSE_FLAG)) + link->args.push_back("-v"); + link->args.push_back("-f"); + link->args.push_back("-o"); + link->args.push_back(OutFile.get()); + if (isSet(TIME_PASSES_FLAG)) + link->args.push_back("-time-passes"); + if (isSet(SHOW_STATS_FLAG)) + link->args.push_back("-stats"); + actions.push_back(link); + } + } + + /// RUN THE LINKING ACTIONS + AI = actions.begin(); + AE = actions.end(); + while (AI != AE) { + if (!DoAction(*AI)) + throw std::string("Action failed"); + AI++; + } + } catch (std::string& msg) { + cleanup(); + throw; + } catch (...) { + cleanup(); + throw std::string("Unspecified error"); + } + cleanup(); + return 0; + } + + /// @} + /// @name Data + /// @{ + private: + ConfigDataProvider* cdp; ///< Where we get configuration data from + Phases finalPhase; ///< The final phase of compilation + OptimizationLevels optLevel; ///< The optimization level to apply + unsigned Flags; ///< The driver flags + std::string machine; ///< Target machine name + PathVector LibraryPaths; ///< -L options + sys::Path TempDir; ///< Name of the temporary directory. + StringTable AdditionalArgs; ///< The -Txyz options + + /// @} + }; } CompilerDriver::~CompilerDriver() { - cdp = 0; - LibraryPaths.clear(); - AdditionalArgs.clear(); +} + +CompilerDriver* +CompilerDriver::Get(ConfigDataProvider& CDP) { + return new CompilerDriverImpl(CDP); } CompilerDriver::ConfigData::ConfigData() @@ -176,456 +673,4 @@ CompilerDriver::ConfigData::ConfigData() opts.push_back(emptyVec); } -void CompilerDriver::error( const std::string& errmsg ) { - std::cerr << "llvmc: Error: " << errmsg << ".\n"; - exit(1); -} - -CompilerDriver::Action* CompilerDriver::GetAction(ConfigData* cd, - const std::string& input, - const std::string& output, - Phases phase) -{ - 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; - case OPTIMIZATION: pat = &cd->Optimizer; break; - case ASSEMBLY: pat = &cd->Assembler; break; - case LINKING: pat = &cd->Linker; break; - default: - assert(!"Invalid driver phase!"); - break; - } - assert(pat != 0 && "Invalid command pattern"); - - // 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(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; -} - -bool CompilerDriver::DoAction(Action*action) { - assert(action != 0 && "Invalid Action!"); - if (isVerbose) - WriteAction(action); - if (!isDryRun) { - 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; -} - -/// This method tries various variants of a linkage item's file -/// name to see if it can find an appropriate file to link with -/// in the directory specified. -std::string CompilerDriver::GetPathForLinkageItem(const std::string& link_item, - const std::string& dir) { - std::string fullpath(dir + "/" + link_item + ".o"); - if (::sys::FileIsReadable(fullpath)) - return fullpath; - fullpath = dir + "/" + link_item + ".bc"; - if (::sys::FileIsReadable(fullpath)) - return fullpath; - fullpath = dir + "/lib" + link_item + ".a"; - if (::sys::FileIsReadable(fullpath)) - return fullpath; - fullpath = dir + "/lib" + link_item + ".so"; - if (::sys::FileIsReadable(fullpath)) - return fullpath; - return ""; -} - -/// This method processes a linkage item. The item could be a -/// Bytecode file needing translation to native code and that is -/// dependent on other bytecode libraries, or a native code -/// library that should just be linked into the program. -bool CompilerDriver::ProcessLinkageItem(const std::string& link_item, - SetVector& set, - std::string& err) { - // First, see if the unadorned file name is not readable. If so, - // we must track down the file in the lib search path. - std::string fullpath; - if (!sys::FileIsReadable(link_item)) { - // First, look for the library using the -L arguments specified - // on the command line. - StringVector::iterator PI = LibraryPaths.begin(); - StringVector::iterator PE = LibraryPaths.end(); - while (PI != PE && fullpath.empty()) { - fullpath = GetPathForLinkageItem(link_item,*PI); - ++PI; - } - - // If we didn't find the file in any of the library search paths - // so we have to bail. No where else to look. - if (fullpath.empty()) { - err = std::string("Can't find linkage item '") + link_item + "'"; - return false; - } - } else { - fullpath = link_item; - } - - // If we got here fullpath is the path to the file, and its readable. - set.insert(fullpath); - - // If its an LLVM bytecode file ... - if (CheckMagic(fullpath, "llvm")) { - // Process the dependent libraries recursively - Module::LibraryListType modlibs; - if (GetBytecodeDependentLibraries(fullpath,modlibs)) { - // Traverse the dependent libraries list - Module::lib_iterator LI = modlibs.begin(); - Module::lib_iterator LE = modlibs.end(); - while ( LI != LE ) { - if (!ProcessLinkageItem(*LI,set,err)) { - if (err.empty()) { - err = std::string("Library '") + *LI + - "' is not valid for linking but is required by file '" + - fullpath + "'"; - } else { - err += " which is required by file '" + fullpath + "'"; - } - return false; - } - ++LI; - } - } else if (err.empty()) { - err = std::string("The dependent libraries could not be extracted from '") - + fullpath; - return false; - } - } - return true; -} - - -int CompilerDriver::execute(const InputList& InpList, - const std::string& Output ) { - // Echo the configuration of options if we're running verbose - if (isDebug) - { - std::cerr << "Compiler Driver Options:\n"; - std::cerr << "DryRun = " << isDryRun << "\n"; - std::cerr << "Verbose = " << isVerbose << " \n"; - std::cerr << "TimeActions = " << timeActions << "\n"; - std::cerr << "EmitRawCode = " << emitRawCode << "\n"; - std::cerr << "OutputMachine = " << machine << "\n"; - std::cerr << "EmitNativeCode = " << emitNativeCode << "\n"; - InputList::const_iterator I = InpList.begin(); - while ( I != InpList.end() ) { - std::cerr << "Input: " << I->first << "(" << I->second << ")\n"; - ++I; - } - std::cerr << "Output: " << Output << "\n"; - } - - // If there's no input, we're done. - if (InpList.empty()) - error("Nothing to compile."); - - // If they are asking for linking and didn't provide an output - // file then its an error (no way for us to "make up" a meaningful - // file name based on the various linker input files). - 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 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 LinkageItems; - std::string OutFile(Output); - InputList::const_iterator I = InpList.begin(); - while ( I != InpList.end() ) { - // Get the suffix of the file name - 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 (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); - ++I; continue; // short circuit remainder of loop - } - - // At this point, we know its something we need to translate - // and/or optimize. See if we can get the configuration data - // for this kind of file. - ConfigData* cd = cdp->ProvideConfigData(I->second); - if (cd == 0) - error(std::string("Files of type '") + I->second + - "' are not recognized." ); - if (isDebug) - DumpConfigData(cd,I->second); - - // Initialize the input file - std::string InFile(I->first); - - // 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 - 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 - 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; - } - - /// RUN THE COMPILATION ACTIONS - std::vector::iterator aIter = actions.begin(); - while (aIter != actions.end()) { - if (!DoAction(*aIter)) - error("Action failed"); - aIter++; - } - - /// 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(); - SetVector link_items; - std::string errmsg; - while (I != E && ProcessLinkageItem(*I,link_items,errmsg)) - ++I; - - if (!errmsg.empty()) - error(errmsg); - - // 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(), link_items.begin(), link_items.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); - } - } - - 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; -} - // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab diff --git a/tools/llvmc/CompilerDriver.h b/tools/llvmc/CompilerDriver.h index a6d0be084b6..ea999b44d99 100644 --- a/tools/llvmc/CompilerDriver.h +++ b/tools/llvmc/CompilerDriver.h @@ -16,7 +16,7 @@ #include #include -#include "Support/SetVector.h" +#include "llvm/System/Program.h" namespace llvm { /// This class provides the high level interface to the LLVM Compiler Driver. @@ -30,9 +30,12 @@ namespace llvm { /// @name Types /// @{ public: - /// @brief A vector of strings, commonly used + /// @brief A vector of strings, used for argument lists typedef std::vector StringVector; + /// @brief A vector of sys::Path, used for path lists + typedef std::vector PathVector; + /// @brief A table of strings, indexed typically by Phases typedef std::vector StringTable; @@ -65,11 +68,26 @@ namespace llvm { FLAGS_MASK = 0x000F, ///< Union of all flags }; + /// @brief Driver specific flags + enum DriverFlags { + DRY_RUN_FLAG = 0x0001, ///< Do everything but execute actions + FORCE_FLAG = 0x0002, ///< Force overwrite of output files + VERBOSE_FLAG = 0x0004, ///< Print each action + DEBUG_FLAG = 0x0008, ///< Print debug information + TIME_PASSES_FLAG = 0x0010, ///< Time the passes as they execute + TIME_ACTIONS_FLAG = 0x0020, ///< Time the actions as they execute + SHOW_STATS_FLAG = 0x0040, ///< Show pass statistics + EMIT_NATIVE_FLAG = 0x0080, ///< Emit native code instead of bc + EMIT_RAW_FLAG = 0x0100, ///< Emit raw, unoptimized bytecode + KEEP_TEMPS_FLAG = 0x0200, ///< Don't delete temporary files + DRIVER_FLAGS_MASK = 0x02FF, ///< Union of the above flags + }; + /// This type is the input list to the CompilerDriver. It provides - /// a vector of filename/filetype pairs. The filetype is used to look up + /// a vector of pathname/filetype pairs. The filetype is used to look up /// the configuration of the actions to be taken by the driver. /// @brief The Input Data to the execute method - typedef std::vector > InputList; + typedef std::vector > InputList; /// This type is read from configuration files or otherwise provided to /// the CompilerDriver through a "ConfigDataProvider". It serves as both @@ -78,7 +96,7 @@ namespace llvm { /// language. struct Action { Action() : flags(0) {} - std::string program; ///< The program to execve + sys::Program program; ///< The program to execve StringVector args; ///< Arguments to the program unsigned flags; ///< Action specific flags void set(unsigned fl ) { flags |= fl; } @@ -107,127 +125,49 @@ namespace llvm { class ConfigDataProvider { public: virtual ConfigData* ProvideConfigData(const std::string& filetype) = 0; - virtual void setConfigDir(const std::string& dirName) = 0; + virtual void setConfigDir(const sys::Path& dirName) = 0; }; /// @} /// @name Constructors /// @{ public: - CompilerDriver(ConfigDataProvider& cdp ); + /// @brief Static Constructor + static CompilerDriver* Get(ConfigDataProvider& CDP); + + /// @brief Virtual destructor virtual ~CompilerDriver(); /// @} /// @name Methods /// @{ public: - /// @brief Handle an error - virtual void error(const std::string& errmsg); - /// @brief Execute the actions requested for the given input list. - virtual int execute(const InputList& list, const std::string& output); + virtual int execute(const InputList& list, const sys::Path& output) = 0; - /// @} - /// @name Mutators - /// @{ - public: /// @brief Set the final phase at which compilation terminates - void setFinalPhase( Phases phase ) { finalPhase = phase; } + virtual void setFinalPhase(Phases phase) = 0; /// @brief Set the optimization level for the compilation - void setOptimization( OptimizationLevels level ) { optLevel = level; } + virtual void setOptimization(OptimizationLevels level) = 0; - /// @brief Prevent the CompilerDriver from taking any actions - void setDryRun( bool TF ) { isDryRun = TF; } - - /// @brief Cause the CompilerDriver to print to stderr all the - /// actions it is taking. - void setVerbose( bool TF ) { isVerbose = TF; } - - /// @brief Cause the CompilerDriver to print to stderr very verbose - /// information that might be useful in debugging the driver's actions - void setDebug( bool TF ) { isDebug = TF; } - - /// @brief Cause the CompilerDriver to print to stderr the - /// execution time of each action taken. - void setTimeActions( bool TF ) { timeActions = TF; } - - /// @brief Cause the CompilerDriver to print timings for each pass. - void setTimePasses( bool TF ) { timePasses = TF; } - - /// @brief Cause the CompilerDriver to show statistics gathered - void setShowStats( bool TF ) { showStats = TF; } - - /// @brief Indicate that native code is to be generated instead - /// of LLVM bytecode. - void setEmitNativeCode( bool TF ) { emitNativeCode = TF; } - - /// @brief Indicate that raw, unoptimized code is to be generated. - void setEmitRawCode(bool TF ) { emitRawCode = TF; } - - void setKeepTemporaries(bool TF) { keepTemps = TF; } + /// @brief Set the driver flags. + virtual void setDriverFlags(unsigned flags) = 0; /// @brief Set the output machine name. - void setOutputMachine( const std::string& machineName ) { - machine = machineName; - } + virtual void setOutputMachine(const std::string& machineName) = 0; /// @brief Set Preprocessor specific options - void setPhaseArgs(Phases phase, const std::vector& opts) { - assert(phase <= LINKING && phase >= PREPROCESSING); - AdditionalArgs[phase] = opts; - } + virtual void setPhaseArgs(Phases phase, const StringVector& opts) = 0; /// @brief Set Library Paths - void setLibraryPaths(const std::vector& paths) { - LibraryPaths = paths; - } + virtual void setLibraryPaths(const StringVector& paths) = 0; /// @brief Set the list of library paths to be searched for /// libraries. - void addLibraryPath( const std::string& libPath ) { - LibraryPaths.push_back(libPath); - } + virtual void addLibraryPath( const sys::Path& libPath ) = 0; /// @} - /// @name Functions - /// @{ - private: - Action* GetAction(ConfigData* cd, const std::string& input, - const std::string& output, Phases phase ); - - bool DoAction(Action* a); - - std::string GetPathForLinkageItem(const std::string& link_item, - const std::string& dir); - - bool ProcessLinkageItem(const std::string& link_item, - SetVector& set, - std::string& err); - - /// @} - /// @name Data - /// @{ - private: - ConfigDataProvider* cdp; ///< Where we get configuration data from - Phases finalPhase; ///< The final phase of compilation - OptimizationLevels optLevel; ///< The optimization level to apply - bool isDryRun; ///< Prevent actions ? - bool isVerbose; ///< Print actions? - bool isDebug; ///< Print lotsa debug info? - bool timeActions; ///< Time the actions executed ? - bool timePasses; ///< Time each pass and print timing ? - bool showStats; ///< Show gathered statistics ? - bool emitRawCode; ///< Emit Raw (unoptimized) code? - bool emitNativeCode; ///< Emit native code instead of bytecode? - bool keepTemps; ///< Keep temporary files? - std::string machine; ///< Target machine name - StringVector LibraryPaths; ///< -L options - StringTable AdditionalArgs; ///< The -Txyz options - std::string TempDir; ///< Name of the temporary directory. - - /// @} - }; } diff --git a/tools/llvmc/ConfigLexer.h b/tools/llvmc/ConfigLexer.h index 1808ac95f4e..bb731d3c6f0 100644 --- a/tools/llvmc/ConfigLexer.h +++ b/tools/llvmc/ConfigLexer.h @@ -57,6 +57,7 @@ enum ConfigLexerTokens { COMMAND, ///< The name "command" (and variants) EQUALS, ///< The equals sign, = FALSETOK, ///< A boolean false value (false/no/off) + FORCE_SUBST, ///< The substitution item %force% IN_SUBST, ///< The substitution item %in% INTEGER, ///< An integer LANG, ///< The name "lang" (and variants) @@ -85,6 +86,7 @@ enum ConfigLexerTokens { TRANSLATES, ///< The name "translates" (and variants) TRANSLATOR, ///< The name "translator" (and variants) TRUETOK, ///< A boolean true value (true/yes/on) + VERBOSE_SUBST,///< The substitution item %verbose% VERSION, ///< The name "version" (and variants) }; diff --git a/tools/llvmc/ConfigLexer.l b/tools/llvmc/ConfigLexer.l index d9a44c1d228..21f5fe1a0bc 100644 --- a/tools/llvmc/ConfigLexer.l +++ b/tools/llvmc/ConfigLexer.l @@ -160,12 +160,14 @@ White [ \t]* {LINKER} { return handleNameContext(LINKER); } %args% { return handleSubstitution(ARGS_SUBST); } +%force% { return handleSubstitution(FORCE_SUBST); } %in% { return handleSubstitution(IN_SUBST); } -%out% { return handleSubstitution(OUT_SUBST); } -%time% { return handleSubstitution(TIME_SUBST); } -%stats% { return handleSubstitution(STATS_SUBST); } %opt% { return handleSubstitution(OPT_SUBST); } +%out% { return handleSubstitution(OUT_SUBST); } +%stats% { return handleSubstitution(STATS_SUBST); } %target% { return handleSubstitution(TARGET_SUBST); } +%time% { return handleSubstitution(TIME_SUBST); } +%verbose% { return handleSubstitution(VERBOSE_SUBST); } {BadSubst} { YY_FATAL_ERROR("Invalid substitution token"); } {Assembly} { return handleValueContext(ASSEMBLY); } diff --git a/tools/llvmc/Configuration.cpp b/tools/llvmc/Configuration.cpp index 4770875833c..a8c6791545e 100644 --- a/tools/llvmc/Configuration.cpp +++ b/tools/llvmc/Configuration.cpp @@ -90,19 +90,19 @@ namespace { InputProvider* provider; CompilerDriver::ConfigData* confDat; - int next() { + inline int next() { token = Configlex(); if (DumpTokens) std::cerr << token << "\n"; return token; } - bool next_is_real() { + inline bool next_is_real() { next(); return (token != EOLTOK) && (token != ERRORTOK) && (token != 0); } - void eatLineRemnant() { + inline void eatLineRemnant() { while (next_is_real()) ; } @@ -162,6 +162,8 @@ namespace { case STATS_SUBST: optList.push_back("%stats%"); break; case OPT_SUBST: optList.push_back("%opt%"); break; case TARGET_SUBST: optList.push_back("%target%"); break; + case FORCE_SUBST: optList.push_back("%force%"); break; + case VERBOSE_SUBST: optList.push_back("%verbose%"); break; default: return false; } @@ -229,7 +231,7 @@ namespace { action.args.clear(); } else { if (token == STRING || token == OPTION) { - action.program = ConfigLexerState.StringVal; + action.program.set_file(ConfigLexerState.StringVal); } else { error("Expecting a program name"); } @@ -415,42 +417,46 @@ namespace { CompilerDriver::ConfigData* LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) { CompilerDriver::ConfigData* result = 0; - std::string dir_name; - if (configDir.empty()) { + sys::Path confFile; + if (configDir.is_empty()) { // Try the environment variable const char* conf = getenv("LLVM_CONFIG_DIR"); if (conf) { - dir_name = conf; - dir_name += "/"; - if (!::sys::FileIsReadable(dir_name + ftype)) + confFile.set_directory(conf); + confFile.append_file(ftype); + if (!confFile.readable()) throw "Configuration file for '" + ftype + "' is not available."; } else { // Try the user's home directory - const char* home = getenv("HOME"); - if (home) { - dir_name = home; - dir_name += "/.llvm/etc/"; - if (!::sys::FileIsReadable(dir_name + ftype)) { - // Okay, try the LLVM installation directory - dir_name = LLVM_ETCDIR; - dir_name += "/"; - if (!::sys::FileIsReadable(dir_name + ftype)) { - // Okay, try the "standard" place - dir_name = "/etc/llvm/"; - if (!::sys::FileIsReadable(dir_name + ftype)) { - throw "Configuration file for '" + ftype + "' is not available."; - } + confFile = sys::Path::GetUserHomeDirectory(); + if (!confFile.is_empty()) { + confFile.append_directory(".llvm"); + confFile.append_directory("etc"); + confFile.append_file(ftype); + if (!confFile.readable()) + confFile.clear(); + } + if (!confFile.is_empty()) { + // Okay, try the LLVM installation directory + confFile = sys::Path::GetLLVMConfigDir(); + confFile.append_file(ftype); + if (!confFile.readable()) { + // Okay, try the "standard" place + confFile = sys::Path::GetLLVMDefaultConfigDir(); + confFile.append_file(ftype); + if (!confFile.readable()) { + throw "Configuration file for '" + ftype + "' is not available."; } } } } } else { - dir_name = configDir + "/"; - if (!::sys::FileIsReadable(dir_name + ftype)) { + confFile = configDir; + confFile.append_file(ftype); + if (!confFile.readable()) throw "Configuration file for '" + ftype + "' is not available."; - } } - FileInputProvider fip( dir_name + ftype ); + FileInputProvider fip( confFile.get() ); if (!fip.okay()) { throw "Configuration file for '" + ftype + "' is not available."; } @@ -459,13 +465,6 @@ LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) { return result; } -LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider() - : Configurations() - , configDir() -{ - Configurations.clear(); -} - LLVMC_ConfigDataProvider::~LLVMC_ConfigDataProvider() { ConfigDataMap::iterator cIt = Configurations.begin(); @@ -491,7 +490,7 @@ LLVMC_ConfigDataProvider::ProvideConfigData(const std::string& filetype) { // The configuration data doesn't exist, we have to go read it. result = ReadConfigData(filetype); // If we got one, cache it - if ( result != 0 ) + if (result != 0) Configurations.insert(std::make_pair(filetype,result)); } return result; // Might return 0 diff --git a/tools/llvmc/Configuration.h b/tools/llvmc/Configuration.h index 59335aa92f6..2ed565a80ed 100644 --- a/tools/llvmc/Configuration.h +++ b/tools/llvmc/Configuration.h @@ -29,7 +29,6 @@ namespace llvm { /// @name Constructor /// @{ public: - LLVMC_ConfigDataProvider(); virtual ~LLVMC_ConfigDataProvider(); /// @name Methods @@ -40,7 +39,9 @@ namespace llvm { ProvideConfigData(const std::string& filetype); /// @brief Allow the configuration directory to be set - virtual void setConfigDir(const std::string& dirName) { configDir = dirName; } + virtual void setConfigDir(const sys::Path& dirName) { + configDir = dirName; + } private: CompilerDriver::ConfigData* ReadConfigData(const std::string& ftype); @@ -53,7 +54,7 @@ namespace llvm { typedef hash_map,std::equal_to > ConfigDataMap; ConfigDataMap Configurations; ///< The cache of configurations - std::string configDir; + sys::Path configDir; /// @} }; } diff --git a/tools/llvmc/Makefile b/tools/llvmc/Makefile index 6cbe4fa2efb..71f04292736 100644 --- a/tools/llvmc/Makefile +++ b/tools/llvmc/Makefile @@ -8,7 +8,7 @@ ##===----------------------------------------------------------------------===## LEVEL = ../.. TOOLNAME = llvmc -USEDLIBS = bcreader vmcore support.a +USEDLIBS = bcreader vmcore support.a LLVMsystem.a CONFIG_FILES = st ll include $(LEVEL)/Makefile.common diff --git a/tools/llvmc/llvmc.cpp b/tools/llvmc/llvmc.cpp index 8c5102a0c4a..ad0d2187aae 100644 --- a/tools/llvmc/llvmc.cpp +++ b/tools/llvmc/llvmc.cpp @@ -108,6 +108,9 @@ cl::list Libraries("l", cl::Prefix, cl::opt OutputFilename("o", cl::desc("Override output filename"), cl::value_desc("filename")); +cl::opt ForceOutput("f", cl::Optional, cl::init(false), + cl::desc("Force output files to be overridden")); + cl::opt OutputMachine("m", cl::Prefix, cl::desc("Specify a target machine"), cl::value_desc("machine")); @@ -156,7 +159,7 @@ static cl::opt EmitRawCode("emit-raw-code", cl::Hidden, cl::Optional, static cl::opt PipeCommands("pipe", cl::Optional, cl::desc("Invoke sub-commands by linking input/output with pipes")); -static cl::opt KeepTemporaries("keep-temps", cl::Optional, +static cl::opt KeepTemps("keep-temps", cl::Optional, cl::desc("Don't delete the temporary files created during compilation")); //===------------------------------------------------------------------------=== @@ -199,15 +202,16 @@ const std::string GetFileType(const std::string& fname, unsigned pos ) { /// @brief The main program for llvmc int main(int argc, char **argv) { + // Make sure we print stack trace if we get bad signals + sys::PrintStackTraceOnErrorSignal(); + try { - // Make sure we print stack trace if we get bad signals - PrintStackTraceOnErrorSignal(); // Parse the command line options cl::ParseCommandLineOptions(argc, argv, - " LLVM Compilation Driver (llvmc)\n\n" + " LLVM Compiler Driver (llvmc)\n\n" " This program provides easy invocation of the LLVM tool set\n" - " and source language compiler tools.\n" + " and other compiler tools.\n" ); // Deal with unimplemented options. @@ -220,13 +224,12 @@ int main(int argc, char **argv) { else throw "An output file must be specified. Please use the -o option"; - // Construct the ConfigDataProvider object LLVMC_ConfigDataProvider Provider; - Provider.setConfigDir(ConfigDir); + Provider.setConfigDir(sys::Path(ConfigDir)); // Construct the CompilerDriver object - CompilerDriver CD(Provider); + CompilerDriver* CD = CompilerDriver::Get(Provider); // If the LLVM_LIB_SEARCH_PATH environment variable is // set, append it to the list of places to search for libraries @@ -234,30 +237,37 @@ int main(int argc, char **argv) { if (!srchPath.empty()) LibPaths.push_back(srchPath); - // Configure the driver based on options - CD.setVerbose(Verbose); - CD.setDebug(Debug); - CD.setDryRun(DryRun); - CD.setFinalPhase(FinalPhase); - CD.setOptimization(OptLevel); - CD.setOutputMachine(OutputMachine); - CD.setEmitNativeCode(Native); - CD.setEmitRawCode(EmitRawCode); - CD.setTimeActions(TimeActions); - CD.setTimePasses(TimePassesIsEnabled); - CD.setShowStats(ShowStats); - CD.setKeepTemporaries(KeepTemporaries); - CD.setLibraryPaths(LibPaths); + // Set the driver flags based on command line options + unsigned flags = 0; + if (Verbose) flags |= CompilerDriver::VERBOSE_FLAG; + if (Debug) flags |= CompilerDriver::DEBUG_FLAG; + if (DryRun) flags |= CompilerDriver::DRY_RUN_FLAG; + if (ForceOutput) flags |= CompilerDriver::FORCE_FLAG; + if (Native) flags |= CompilerDriver::EMIT_NATIVE_FLAG; + if (EmitRawCode) flags |= CompilerDriver::EMIT_RAW_FLAG; + if (KeepTemps) flags |= CompilerDriver::KEEP_TEMPS_FLAG; + if (ShowStats) flags |= CompilerDriver::SHOW_STATS_FLAG; + if (TimeActions) flags |= CompilerDriver::TIME_ACTIONS_FLAG; + if (TimePassesIsEnabled) flags |= CompilerDriver::TIME_PASSES_FLAG; + CD->setDriverFlags(flags); + + // Specify requred parameters + CD->setFinalPhase(FinalPhase); + CD->setOptimization(OptLevel); + CD->setOutputMachine(OutputMachine); + CD->setLibraryPaths(LibPaths); + + // Provide additional tool arguments if (!PreprocessorToolOpts.empty()) - CD.setPhaseArgs(CompilerDriver::PREPROCESSING, PreprocessorToolOpts); + CD->setPhaseArgs(CompilerDriver::PREPROCESSING, PreprocessorToolOpts); if (!TranslatorToolOpts.empty()) - CD.setPhaseArgs(CompilerDriver::TRANSLATION, TranslatorToolOpts); + CD->setPhaseArgs(CompilerDriver::TRANSLATION, TranslatorToolOpts); if (!OptimizerToolOpts.empty()) - CD.setPhaseArgs(CompilerDriver::OPTIMIZATION, OptimizerToolOpts); + CD->setPhaseArgs(CompilerDriver::OPTIMIZATION, OptimizerToolOpts); if (!AssemblerToolOpts.empty()) - CD.setPhaseArgs(CompilerDriver::ASSEMBLY,AssemblerToolOpts); + CD->setPhaseArgs(CompilerDriver::ASSEMBLY,AssemblerToolOpts); if (!LinkerToolOpts.empty()) - CD.setPhaseArgs(CompilerDriver::LINKING, LinkerToolOpts); + CD->setPhaseArgs(CompilerDriver::LINKING, LinkerToolOpts); // Prepare the list of files to be compiled by the CompilerDriver. CompilerDriver::InputList InpList; @@ -288,7 +298,7 @@ int main(int argc, char **argv) { } // Tell the driver to do its thing - int result = CD.execute(InpList,OutputFilename); + int result = CD->execute(InpList,sys::Path(OutputFilename)); if (result != 0) { throw "Error executing actions. Terminated.\n"; return result; diff --git a/tools/llvmc/st b/tools/llvmc/st index c08c8d1c7c2..9cedc3d068a 100644 --- a/tools/llvmc/st +++ b/tools/llvmc/st @@ -34,7 +34,7 @@ # To compile stacker source, we just run the stacker # compiler with a default stack size of 2048 entries. translator.command=stkrc -s 2048 %in% -o %out% %time% \ - %stats% %args% + %stats% %force% %args% # stkrc doesn't preprocess but we set this to true so # that we don't run the cp command by default. @@ -52,7 +52,7 @@ # For optimization, we use the LLVM "opt" program optimizer.command=opt %in% -o %out% %opt% %time% %stats% \ - %args% + %force% %args% optimizer.required = true