From 5c56dc1f6215d49cc66960baab29a9074bf6e165 Mon Sep 17 00:00:00 2001 From: Reid Spencer Date: Fri, 13 Aug 2004 20:22:43 +0000 Subject: [PATCH] Additional functionality. This version handles option parsing and parameter subsitution correctly for at least .ll and .st files. There's still a long way to go (i.e. this isn't worth of review yet). git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@15728 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/llvmc/CompilerDriver.cpp | 227 +++++++++++++++++++++++++++++++++ tools/llvmc/CompilerDriver.h | 140 +++++++++++++++++--- tools/llvmc/llvmc.cpp | 156 +++++++++++++++++----- 3 files changed, 478 insertions(+), 45 deletions(-) create mode 100644 tools/llvmc/CompilerDriver.cpp diff --git a/tools/llvmc/CompilerDriver.cpp b/tools/llvmc/CompilerDriver.cpp new file mode 100644 index 00000000000..7a639b38cdc --- /dev/null +++ b/tools/llvmc/CompilerDriver.cpp @@ -0,0 +1,227 @@ +//===- DriverAction.cpp - Compile Driver Actions ----------------*- C++ -*-===// +// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Reid Spencer and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements support for executable actions in the LLVM Compiler +// Driver (llvmc). +// +//===------------------------------------------------------------------------=== + +#include "CompilerDriver.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); + } + + 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"; + +} + + +CompilerDriver::CompilerDriver(ConfigDataProvider& confDatProv ) + : cdp(&confDatProv) + , finalPhase(LINKING) + , optLevel(OPT_FAST_COMPILE) + , isDryRun(false) + , isVerbose(false) + , isDebug(false) + , timeActions(false) + , emitRawCode(false) + , emitNativeCode(false) + , machine() + , libPaths() +{ + // FIXME: These libraries are platform specific + libPaths.push_back("/lib"); + libPaths.push_back("/usr/lib"); +} + +CompilerDriver::~CompilerDriver() { + cdp = 0; + libPaths.clear(); +} + +void CompilerDriver::error( const std::string& errmsg ) { + std::cerr << "Error: " << errmsg << ".\n"; + exit(1); +} + +CompilerDriver::Action* CompilerDriver::GetAction(ConfigData* cd, + const std::string& input, + const std::string& output, + Phases phase) +{ + Action* pat = 0; + 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"); + Action* a = new Action(*pat); + a->args[pat->inputAt] = input; + a->args[pat->outputAt] = output; + return a; +} + +void CompilerDriver::WriteAction(Action* a) { + std::cerr << a->program; + std::vector::iterator I = a->args.begin(); + while (I != a->args.end()) { + std::cerr << " " + *I; + ++I; + } + std::cerr << "\n"; +} + +void CompilerDriver::DoAction(Action*a) +{ + if (isVerbose) + WriteAction(a); + if (!isDryRun) { + std::cerr << "execve(\"" << a->program << "\",[\n"; + std::vector::iterator I = a->args.begin(); + while (I != a->args.end()) { + std::cerr << " \"" << *I << "\",\n"; + ++I; + } + std::cerr << "],ENV);\n"; + } +} + +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"); + + std::vector actions; + + /// PRE-PROCESSING / TRANSLATION / OPTIMIZATION / ASSEMBLY phases + // for each input item + std::vector LinkageItems; + InputList::const_iterator I = InpList.begin(); + while ( I != InpList.end() ) { + // Get the suffix of the file name + std::string suffix = GetSuffix(I->first); + + // 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") { + // 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 + } + + // 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." ); + + // 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; + } + + /// 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)); + } + } + + // 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)); + // 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)); + // Short-circuit remaining actions if all they want is optimization + if (finalPhase == OPTIMIZATION) { ++I; continue; } + + ++I; + } + + /// LINKING PHASE + + /// RUN THE ACTIONS + std::vector::iterator aIter = actions.begin(); + while (aIter != actions.end()) { + DoAction(*aIter); + aIter++; + } + + return 0; +} + +// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab diff --git a/tools/llvmc/CompilerDriver.h b/tools/llvmc/CompilerDriver.h index d56c9b5b6b4..d6587b0aa92 100644 --- a/tools/llvmc/CompilerDriver.h +++ b/tools/llvmc/CompilerDriver.h @@ -1,4 +1,4 @@ -//===- CompilerDriver.h - Compiler Driver ---------------------------------===// +//===- CompilerDriver.h - Compiler Driver -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -11,6 +11,11 @@ // LLVM Compiler Driver program (llvmc). // //===------------------------------------------------------------------------=== +#ifndef LLVM_TOOLS_LLVMC_COMPILERDRIVER_H +#define LLVM_TOOLS_LLVMC_COMPILERDRIVER_H + +#include +#include namespace llvm { /// This class provides the high level interface to the LLVM Compiler Driver. @@ -24,7 +29,6 @@ namespace llvm { /// @name Types /// @{ public: - typedef unsigned OptimizationLevel; enum Phases { PREPROCESSING, ///< Source language combining, filtering, substitution TRANSLATION, ///< Translate source -> LLVM bytecode/assembly @@ -34,42 +38,146 @@ namespace llvm { }; enum OptimizationLevels { - OPT_NONE, - OPT_FAST_COMPILE, - OPT_SIMPLE, - OPT_AGGRESSIVE, - OPT_LINK_TIME, - OPT_AGGRESSIVE_LINK_TIME + OPT_NONE, ///< Zippo optimizations, nada, nil, none. + OPT_FAST_COMPILE, ///< Optimize to make >compile< go faster + OPT_SIMPLE, ///< Standard/simple optimizations + OPT_AGGRESSIVE, ///< Aggressive optimizations + OPT_LINK_TIME, ///< Aggressive + LinkTime optimizations + OPT_AGGRESSIVE_LINK_TIME ///< Make it go way fast! + }; + + /// This type is the input list to the CompilerDriver. It provides + /// a vector of filename/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; + + /// This type is read from configuration files or otherwise provided to + /// the CompilerDriver through a "ConfigDataProvider". It serves as both + /// the template of what to do and the actual Action to be executed. + /// @brief A structure to hold the action data for a given source + /// language. + struct Action { + std::string program; ///< The program to execve + std::vector args; ///< Arguments to the program + size_t inputAt; ///< Argument index to insert input file + size_t outputAt; ///< Argument index to insert output file + }; + + struct ConfigData { + std::string langName; ///< The name of the source language + bool TranslatorPreprocesses;///< Translator program will pre-process + bool TranslatorOptimizes; ///< Translator program will optimize too + bool TranslatorGroksDashO; ///< Translator understands -O arguments + bool PreprocessorNeeded; ///< Preprocessor is needed for translation + Action PreProcessor; ///< PreProcessor command line + Action Translator; ///< Translator command line + Action Optimizer; ///< Optimizer command line + Action Assembler; ///< Assembler command line + Action Linker; ///< Linker command line + }; + + /// This pure virtual interface class defines the interface between the + /// CompilerDriver and other software that provides ConfigData objects to + /// it. The CompilerDriver must be configured to use an object of this + /// type so it can obtain the configuration data. + /// @see setConfigDataProvider + /// @brief Configuration Data Provider interface + class ConfigDataProvider { + public: + virtual ConfigData* ProvideConfigData(const std::string& filetype) = 0; + virtual void setConfigDir(const std::string& dirName) = 0; }; /// @} /// @name Constructors /// @{ public: - CompilerDriver(); + CompilerDriver(ConfigDataProvider& cdp ); + virtual ~CompilerDriver(); /// @} - /// @name Accessors + /// @name Methods /// @{ public: - void execute(); ///< Execute the actions requested + /// @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); /// @} /// @name Mutators /// @{ public: + /// @brief Set the final phase at which compilation terminates + void setFinalPhase( Phases phase ) { finalPhase = phase; } + /// @brief Set the optimization level for the compilation - void setOptimization( OptimizationLevel level ); - void setFinalPhase( Phases phase ); + void setOptimization( OptimizationLevels level ) { optLevel = level; } + + /// @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 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; } + + /// @brief Set the output machine name. + void setOutputMachine( const std::string& machineName ) { + machine = machineName; + } + + /// @brief Set the list of library paths to be searched for + /// libraries. + void addLibraryPath( const std::string& libPath ) { + libPaths.push_back(libPath); + } + + /// @} + /// @name Functions + /// @{ + private: + Action* GetAction(ConfigData* cd, const std::string& input, + const std::string& output, Phases phase ); + void WriteAction(Action* a); + void DoAction(Action* a); /// @} /// @name Data /// @{ - public: - Phases finalPhase; - OptimizationLevel optLevel; + 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 emitRawCode; ///< Emit Raw (unoptimized) code? + bool emitNativeCode; ///< Emit native code instead of bytecode? + std::string machine; ///< Target machine name + std::vector libPaths; ///< list of dirs to find libraries /// @} }; } + +// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab +#endif diff --git a/tools/llvmc/llvmc.cpp b/tools/llvmc/llvmc.cpp index 75d37fb4f99..878fb65e0c8 100644 --- a/tools/llvmc/llvmc.cpp +++ b/tools/llvmc/llvmc.cpp @@ -1,8 +1,8 @@ -//===--- llvmc.cpp - The LLVM Compiler Driver -----------------------------===// +//===--- llvmc.cpp - The LLVM Compiler Driver -------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // -// This file was developed by Reid Spencerand is distributed under the +// This file was developed by Reid Spencer and is distributed under the // University of Illinois Open Source License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// @@ -15,24 +15,29 @@ //===------------------------------------------------------------------------=== #include "CompilerDriver.h" +#include "ConfigData.h" #include "llvm/System/Signals.h" #include "Support/CommandLine.h" #include using namespace llvm; +namespace { //===------------------------------------------------------------------------=== //=== PHASE OPTIONS //===------------------------------------------------------------------------=== static cl::opt FinalPhase( cl::desc("Choose final phase of compilation:"), + cl::init(CompilerDriver::LINKING), cl::values( clEnumValN(CompilerDriver::PREPROCESSING,"E", - "Stop compilation after pre-processing"), + "Stop compilation after pre-processing phase"), + clEnumValN(CompilerDriver::TRANSLATION, "t", + "Stop compilation after translation phase"), clEnumValN(CompilerDriver::OPTIMIZATION,"c", - "Stop compilation after source code translation and optimization"), + "Stop compilation after optimization phase"), clEnumValN(CompilerDriver::ASSEMBLY,"S", - "Stop compilation after assembly"), + "Stop compilation after assembly phase"), clEnumValEnd ) ); @@ -42,9 +47,10 @@ static cl::opt FinalPhase( //===------------------------------------------------------------------------=== static cl::opt OptLevel( cl::desc("Choose level of optimization to apply:"), + cl::init(CompilerDriver::OPT_FAST_COMPILE), cl::values( clEnumValN(CompilerDriver::OPT_FAST_COMPILE,"O0", - "Optimize for compilation speed, not execution speed."), + "An alias for the -O1 option."), clEnumValN(CompilerDriver::OPT_FAST_COMPILE,"O1", "Optimize for compilation speed, not execution speed."), clEnumValN(CompilerDriver::OPT_SIMPLE,"O2", @@ -64,16 +70,20 @@ static cl::opt OptLevel( //===------------------------------------------------------------------------=== static cl::opt PPToolOpts("Tpp", cl::ZeroOrMore, - cl::desc("Pass specific options to the pre-processor")); + cl::desc("Pass specific options to the pre-processor"), + cl::value_desc("option")); static cl::opt AsmToolOpts("Tasm", cl::ZeroOrMore, - cl::desc("Pass specific options to the assembler")); + cl::desc("Pass specific options to the assembler"), + cl::value_desc("option")); static cl::opt OptToolOpts("Topt", cl::ZeroOrMore, - cl::desc("Pass specific options to the optimizer")); + cl::desc("Pass specific options to the optimizer"), + cl::value_desc("option")); static cl::opt LinkToolOpts("Tlink", cl::ZeroOrMore, - cl::desc("Pass specific options to the linker")); + cl::desc("Pass specific options to the linker"), + cl::value_desc("option")); //===------------------------------------------------------------------------=== //=== INPUT OPTIONS @@ -90,40 +100,47 @@ static cl::list Libraries("l", cl::Prefix, //=== OUTPUT OPTIONS //===------------------------------------------------------------------------=== -static cl::opt OutputFilename("o", cl::init("a.out"), +static cl::opt OutputFilename("o", cl::desc("Override output filename"), cl::value_desc("filename")); -static cl::opt OutputMachne("m", cl::Prefix, +static cl::opt OutputMachine("m", cl::Prefix, cl::desc("Specify a target machine"), cl::value_desc("machine")); -static cl::opt Native("native", +static cl::opt Native("native", cl::init(false), cl::desc("Generative native object and executables instead of bytecode")); //===------------------------------------------------------------------------=== //=== INFORMATION OPTIONS //===------------------------------------------------------------------------=== -static cl::opt NoOperation("no-operation", cl::Optional, - cl::desc("Do not perform actions")); +static cl::opt DryRun("dry-run", cl::Optional, cl::init(false), + cl::desc("Do everything but perform the compilation actions")); -static cl::alias NoOp("n", cl::Optional, - cl::desc("Alias for -no-operation"), cl::aliasopt(NoOperation)); +static cl::alias DryRunAlias("y", cl::Optional, + cl::desc("Alias for -dry-run"), cl::aliasopt(DryRun)); -static cl::opt Verbose("verbose", cl::Optional, +static cl::opt Verbose("verbose", cl::Optional, cl::init(false), cl::desc("Print out each action taken")); -static cl::alias VerboseAlias("v", cl::Optional, +static cl::alias VerboseAlias("v", cl::Optional, cl::desc("Alias for -verbose"), cl::aliasopt(Verbose)); -static cl::opt TimeActions("time-actions", cl::Optional, +static cl::opt Debug("debug", cl::Optional, cl::init(false), + cl::Hidden, cl::desc("Print out debugging information")); + +static cl::alias DebugAlias("d", cl::Optional, + cl::desc("Alias for -debug"), cl::aliasopt(Debug)); + +static cl::opt TimeActions("time-actions", cl::Optional, cl::init(false), cl::desc("Print execution time for each action taken")); //===------------------------------------------------------------------------=== //=== ADVANCED OPTIONS //===------------------------------------------------------------------------=== -static cl::list ConfigFiles("config-dir", cl::Optional, - cl::desc("Specify a configuration directory to override defaults")); +static cl::opt ConfigDir("config-dir", cl::Optional, + cl::desc("Specify a configuration directory to override defaults"), + cl::value_desc("directory")); static cl::opt EmitRawCode("emit-raw-code", cl::Hidden, cl::desc("Emit raw, unoptimized code")); @@ -133,7 +150,37 @@ static cl::opt EmitRawCode("emit-raw-code", cl::Hidden, //===------------------------------------------------------------------------=== static cl::list Files(cl::Positional, cl::OneOrMore, - cl::desc("Source and object files")); + cl::desc("[Sources/objects/libraries]")); + +static cl::list Languages("x", cl::ZeroOrMore, + cl::desc("Specify the source language for subsequent files"), + cl::value_desc("language")); + +//===------------------------------------------------------------------------=== +//=== GetFileType - determine type of a file +//===------------------------------------------------------------------------=== +const std::string GetFileType(const std::string& fname, unsigned pos ) { + static std::vector::iterator langIt = Languages.begin(); + static std::string CurrLang = ""; + + // If a -x LANG option has been specified .. + if ( langIt != Languages.end() ) + // If the -x LANG option came before the current file on command line + if ( Languages.getPosition( langIt - Languages.begin() ) < pos ) { + // use that language + CurrLang = *langIt++; + return CurrLang; + } + + // If there's a current language in effect + if (!CurrLang.empty()) + return CurrLang; // use that language + + // otherwise just determine lang from the filename's suffix + return fname.substr( fname.rfind('.',fname.size()) + 1 ); +} + +} // end anonymous namespace /// @brief The main program for llvmc @@ -148,14 +195,66 @@ int main(int argc, char **argv) { " and source language compiler tools.\n" ); - // Construct the CompilerDriver object - //CompilerDriver CD; + // Deal with unimplemented options. + if (Native) + std::cerr << argv[0] << ": Not implemented yet: -native"; + if (EmitRawCode) + std::cerr << argv[0] << ": Not implemented yet: -emit-raw-code"; - // Set the options for the Compiler Driver + // Default the output file, only if we're going to try to link + if (OutputFilename.empty() && OptLevel == CompilerDriver::LINKING) + OutputFilename = "a.out"; + + // Construct the ConfigDataProvider object + LLVMC_ConfigDataProvider Provider; + + // Construct the CompilerDriver object + CompilerDriver CD(Provider); + + // 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); + std::vector::iterator pathIt = LibPaths.begin(); + while ( pathIt != LibPaths.end() ) { + CD.addLibraryPath( *pathIt++ ); + } + + // Prepare the list of files to be compiled by the CompilerDriver. + CompilerDriver::InputList InpList; + std::vector::iterator fileIt = Files.begin(); + std::vector::iterator libIt = Libraries.begin(); + unsigned libPos = 0, filePos = 0; + while ( 1 ) { + if ( libIt != Libraries.end() ) + libPos = Libraries.getPosition( libIt - Libraries.begin() ); + else + libPos = 0; + if ( fileIt != Files.end() ) + filePos = Files.getPosition( fileIt - Files.begin() ); + else + filePos = 0; + + if ( filePos != 0 && (libPos == 0 || filePos < libPos) ) { + // Add a source file + InpList.push_back( std::make_pair(*fileIt, GetFileType(*fileIt,filePos))); + ++fileIt; + } + else if ( libPos != 0 && (filePos == 0 || libPos < filePos) ) { + // Add a library + InpList.push_back( std::make_pair(*libIt++,"")); + } + else + break; // we're done with the list + } // Tell the driver to do its thing - int result = 0; - // result = CD.execute(); + int result = CD.execute(InpList,OutputFilename); if (result != 0) { std::cerr << argv[0] << ": Error executing actions. Terminated.\n"; return result; @@ -164,4 +263,3 @@ int main(int argc, char **argv) { // All is good, return success return 0; } -