diff --git a/tools/llvmc/CompilerDriver.cpp b/tools/llvmc/CompilerDriver.cpp index f1d3e58bad9..ff63b2100ed 100644 --- a/tools/llvmc/CompilerDriver.cpp +++ b/tools/llvmc/CompilerDriver.cpp @@ -29,8 +29,38 @@ namespace { 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::iterator I = a->args.begin(); + while (I != a->args.end()) { + std::cerr << " " + *I; + ++I; + } + std::cerr << "\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); + std::cerr << "Translator: "; + WriteAction(&cd->Translator); + std::cerr << "Optimizer: "; + WriteAction(&cd->Optimizer); + std::cerr << "Assembler: "; + WriteAction(&cd->Assembler); + std::cerr << "Linker: "; + WriteAction(&cd->Linker); + } } @@ -80,21 +110,13 @@ CompilerDriver::Action* CompilerDriver::GetAction(ConfigData* cd, } assert(pat != 0 && "Invalid command pattern"); Action* a = new Action(*pat); - a->args[pat->inputAt] = input; - a->args[pat->outputAt] = output; + if (pat->inputAt < a->args.size()) + a->args[pat->inputAt] = input; + if (pat->outputAt < a->args.size()) + 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) @@ -170,6 +192,8 @@ int CompilerDriver::execute(const InputList& InpList, if (cd == 0) error(std::string("Files of type '") + I->second + "' are not recognized." ); + if (isDebug) + DumpConfigData(cd,I->second); // We have valid configuration data, now figure out where the output // of compilation should end up. diff --git a/tools/llvmc/CompilerDriver.h b/tools/llvmc/CompilerDriver.h index d6587b0aa92..f49a780ba5e 100644 --- a/tools/llvmc/CompilerDriver.h +++ b/tools/llvmc/CompilerDriver.h @@ -58,6 +58,7 @@ namespace llvm { /// @brief A structure to hold the action data for a given source /// language. struct Action { + Action() : inputAt(0) , outputAt(0) {} std::string program; ///< The program to execve std::vector args; ///< Arguments to the program size_t inputAt; ///< Argument index to insert input file @@ -65,6 +66,10 @@ namespace llvm { }; struct ConfigData { + ConfigData() : TranslatorPreprocesses(false), + TranslatorOptimizes(false), + TranslatorGroksDashO(false), + PreprocessorNeeded(false) {} std::string langName; ///< The name of the source language bool TranslatorPreprocesses;///< Translator program will pre-process bool TranslatorOptimizes; ///< Translator program will optimize too @@ -155,7 +160,6 @@ namespace llvm { private: Action* GetAction(ConfigData* cd, const std::string& input, const std::string& output, Phases phase ); - void WriteAction(Action* a); void DoAction(Action* a); /// @} diff --git a/tools/llvmc/ConfigData.cpp b/tools/llvmc/ConfigData.cpp index 94949c405de..d2e788bf12b 100644 --- a/tools/llvmc/ConfigData.cpp +++ b/tools/llvmc/ConfigData.cpp @@ -13,391 +13,282 @@ //===------------------------------------------------------------------------=== #include "ConfigData.h" +#include "ConfigLexer.h" #include "CompilerDriver.h" #include "Support/StringExtras.h" #include +#include using namespace llvm; +extern int ::Configlex(); + +namespace llvm { + ConfigLexerInfo ConfigLexerData; + InputProvider* ConfigLexerInput = 0; + unsigned ConfigLexerLine = 1; + + InputProvider::~InputProvider() {} + void InputProvider::error(const std::string& msg) { + std::cerr << name << ":" << ConfigLexerLine << ": Error: " << msg << "\n"; + errCount++; + } + + void InputProvider::checkErrors() { + if (errCount > 0) { + std::cerr << name << " had " << errCount << " errors. Terminating.\n"; + exit(errCount); + } + } + +} + namespace { -// This array of strings provides the input for ".ll" files (LLVM Assembly) -// to the configuration file parser. This data is just "built-in" to -// llvmc so it doesn't have to be read from a configuration file. -static const char* LL_Data[] = { - "lang.name=LLVM Assembly", - "lang.translator.preprocesses=false", - "lang.translator.optimizes=No", - "lang.translator.groks_dash_O=No", - "lang.preprocessor.needed=0", - "preprocessor.prog=", - "preprocessor.args=", - "translator.prog=llvm-as", - "translator.args=@in@ -o @out@", - "optimizer.prog=opt", - "optimizer.args=@in@ -o @out@", - "assembler.prog=llc", - "assembler.args=@in@ -o @out@", - "linker.prog=llvm-link", - "linker.args=@in@ -o @out@" -}; + class FileInputProvider : public InputProvider { + public: + FileInputProvider(const std::string & fname) + : InputProvider(fname) + , F(fname.c_str()) { + ConfigLexerInput = this; + } + virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; } + virtual unsigned read(char *buffer, unsigned max_size) { + if (F.good()) { + F.read(buffer,max_size); + if ( F.gcount() ) return F.gcount() - 1; + } + return 0; + } -// This array of strings provides the input for ".st" files (Stacker). -static const char* ST_Data[] = { - "lang.name=Stacker", - "lang.translator.preprocesses=false", - "lang.translator.optimizes=true", - "lang.translator.groks_dash_O=0", - "lang.preprocessor.needed=0", - "preprocessor.prog=cp", - "preprocessor.args=@in@ @out@", - "translator.prog=stkrc", - "translator.args=@in@ -o @out@ -S 2048", - "optimizer.prog=opt", - "optimizer.args=@in@ -o @out@", - "assembler.prog=llc", - "assembler.args=@in@ -o @out@", - "linker.prog=llvm-link", - "linker.args=@in@ -o @out@" -}; + bool okay() { return F.good(); } + private: + std::ifstream F; + }; -class InputProvider { - public: - virtual bool getLine(std::string& line) = 0; - virtual void error(const std::string& msg) = 0; - virtual bool errorOccurred() = 0; -}; + struct ParseContext + { + int token; + InputProvider* provider; + CompilerDriver::ConfigData* confDat; + CompilerDriver::Action* action; -class StaticInputProvider : public InputProvider { - public: - StaticInputProvider(const char *data[], size_t count, - const std::string& nam) { - TheData = data; - limit = count; - where = 0; - name = nam; - errCount = 0; - } - virtual ~StaticInputProvider() {} - virtual bool getLine(std::string& line) { - if ( where >= limit ) return false; - line = TheData[where++]; - return true; + int next() { return token = Configlex(); } + + bool next_is_real() { + token = Configlex(); + return (token != EOLTOK) && (token != ERRORTOK) && (token != 0); } - virtual void error(const std::string& msg) { - std::cerr << name << ":" << where << ": Error: " << msg << "\n"; - errCount++; + void eatLineRemnant() { + while (next_is_real()) ; } - virtual bool errorOccurred() { return errCount > 0; }; + void error(const std::string& msg, bool skip = true) { + provider->error(msg); + if (skip) + eatLineRemnant(); + } - private: - const char**TheData; - size_t limit; - size_t where; - std::string name; - size_t errCount; -}; + std::string parseName() { + std::string result; + if (next() == EQUALS) { + while (next_is_real()) { + switch (token ) { + case STRING : + case OPTION : + result += ConfigLexerData.StringVal + " "; + break; + default: + error("Invalid name"); + break; + } + } + if (result.empty()) + error("Name exepected"); + else + result.erase(result.size()-1,1); + } else + error("= expected"); + return result; + } -inline bool recognize(const char*& p, const char*token) { - while (*p == *token && *token != '\0') - ++token, p++; - return *token == '\0' && ((*p == '\0') || ( *p == '.' ) || (*p == '=')); -} + bool parseBoolean() { + bool result = true; + if (next() == EQUALS) { + if (next() == FALSETOK) { + result = false; + } else if (token != TRUETOK) { + error("Expecting boolean value"); + return false; + } + if (next() != EOLTOK && token != 0) { + error("Extraneous tokens after boolean"); + } + } + else + error("Expecting '='"); + return result; + } -inline bool getBoolean(const std::string& value) { - switch (value[0]) { - case 't': - case 'T': - case '1': - case 'y': - case 'Y': - return true; - default : - return false; - } - return false; -} + void parseLang() { + if ( next() == NAME ) { + confDat->langName = parseName(); + } else if (token == TRANSLATOR) { + switch (next()) { + case PREPROCESSES: + confDat->TranslatorPreprocesses = parseBoolean(); + break; + case OPTIMIZES: + confDat->TranslatorOptimizes = parseBoolean(); + break; + case GROKS_DASH_O: + confDat->TranslatorGroksDashO = parseBoolean(); + break; + default: + error("Invalid lang.translator identifier"); + break; + } + } + else if (token == PREPROCESSOR) { + if (next() == NEEDED) + confDat->PreprocessorNeeded = parseBoolean(); + } + else { + error("Expecting valid identifier after 'lang.'"); + } + } -inline void skipWhitespace( size_t& pos, const std::string& line ) { - while (pos < line.size() && ( - line[pos] == ' ' || // Space - line[pos] == '\t' || // Horizontal Tab - line[pos] == '\n' || // New Line - line[pos] == '\v' || // Vertical Tab - line[pos] == '\f' || // Form Feed - line[pos] == '\r') // Carriate Return - ) - pos++; -} - -inline void parseArgs(CompilerDriver::Action& pat, - const std::string& value, - InputProvider& provider ) -{ - const char* p = value.c_str(); - const char* argStart = p; - while (*p != '\0') { - switch (*p) { - case ' ': - if (argStart != p) - pat.args.push_back(std::string(argStart, p-argStart)); - argStart = ++p; - break; - case '@' : - { - if (argStart != p) - pat.args.push_back(std::string(argStart,p-argStart)); - const char* token = ++p; - while (*p != '@' && *p != 0) - p++; - if ( *p != '@' ) { - provider.error("Unterminated substitution token"); - return; + void parseCommand(CompilerDriver::Action& action) { + if (next() == EQUALS) { + next(); + if (token == EOLTOK) { + // no value (valid) + action.program.clear(); + action.args.clear(); + action.inputAt = 0; + action.outputAt = 0; + } else { + if (token == STRING || token == OPTION) { + action.program = ConfigLexerData.StringVal; } else { - p++; - bool legal = false; - switch (token[0]) { - case 'i': - if (token[1] == 'n' && token[2] == '@' ) { - pat.inputAt = pat.args.size(); - pat.args.push_back("in"); - legal = true; - argStart = p; - } - break; - case 'o': - if (token[1] == 'u' && token[2] == 't' && token[3] == '@') { - pat.outputAt = pat.args.size(); - pat.args.push_back("out"); - legal = true; - argStart = p; - } - break; - default: - break; - } - if (!legal) { - provider.error("Invalid substitution token"); - return; - } + error("Expecting a program name"); + } + while (next_is_real()) { + if (token == STRING || token == OPTION) + action.args.push_back(ConfigLexerData.StringVal); + else if (token == IN_SUBST) { + action.inputAt = action.args.size(); + action.args.push_back("in"); + } else if (token == OUT_SUBST) { + action.outputAt = action.args.size(); + action.args.push_back("out"); + } else + error("Expecting a program argument", false); } } - break; - default : - p++; - break; + } } + + void parsePreProcessor() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->PreProcessor); + } + + void parseTranslator() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->Translator); + } + + void parseOptimizer() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->Optimizer); + } + + void parseAssembler() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->Assembler); + } + + void parseLinker() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->Linker); + } + + void parseAssignment() { + switch (token) { + case LANG: return parseLang(); + case PREPROCESSOR: return parsePreProcessor(); + case TRANSLATOR: return parseTranslator(); + case OPTIMIZER: return parseOptimizer(); + case ASSEMBLER: return parseAssembler(); + case LINKER: return parseLinker(); + case EOLTOK: break; // just ignore + case ERRORTOK: + default: + error("Invalid top level configuration item identifier"); + } + } + + void parseFile() { + while ( next() != 0 ) { + parseAssignment(); + } + provider->checkErrors(); + } + }; + + void + ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) { + ParseContext ctxt; + ctxt.token = 0; + ctxt.provider = &provider; + ctxt.confDat = &confDat; + ctxt.action = 0; + ctxt.parseFile(); } } CompilerDriver::ConfigData* -ParseConfigData(InputProvider& provider) { - std::string line; - CompilerDriver::ConfigData data; - while ( provider.getLine(line) ) { - // Check line length first - size_t lineLen = line.size(); - if (lineLen > 4096) - provider.error("length of input line (" + utostr(lineLen) + - ") is too long"); - - // First, skip whitespace - size_t stPos = 0; - skipWhitespace(stPos, line); - - // See if there's a hash mark. It and everything after it is - // ignored so lets delete that now. - size_t hashPos = line.find('#'); - if (hashPos != std::string::npos) - line.erase(hashPos); - - // Make sure we have something left to parse - if (line.size() == 0) - continue; // ignore full-line comment or whitespace line - - // Find the equals sign - size_t eqPos = line.find('='); - if (eqPos == std::string::npos) - provider.error("Configuration directive is missing an ="); - - // extract the item name - std::string name(line, stPos, eqPos-stPos); - - // directives without names are illegal - if (name.empty()) - provider.error("Configuration directive name is empty"); - - // Skip whitespace in the value - size_t valPos = eqPos + 1; - skipWhitespace(valPos, line); - - // Skip white space at end of value - size_t endPos = line.length() - 1; - while (line[endPos] == ' ') - endPos--; - - // extract the item value - std::string value(line, valPos, endPos-valPos+1); - - // Get the configuration item as a char pointer - const char*p = name.c_str(); - - // Indicate we haven't found an invalid item yet. - bool invalidItem = false; - - // Parse the contents by examining first character and - // using the recognize function strategically - switch (*p++) { - case 'l' : - // could it be "lang." - if (*p == 'a') { // "lang." ? - if (recognize(p,"ang")) { - p++; - switch (*p++) { - case 'n': - if (recognize(p,"ame")) - data.langName = value; - else - invalidItem = true; - break; - case 't': - if (recognize(p,"ranslator")) { - p++; - if (recognize(p,"preprocesses")) - data.TranslatorPreprocesses = getBoolean(value); - else if (recognize(p, "optimizes")) - data.TranslatorOptimizes = getBoolean(value); - else if (recognize(p, "groks_dash_O")) - data.TranslatorGroksDashO = getBoolean(value); - else - invalidItem = true; - } - else - invalidItem = true; - break; - case 'p': - if (recognize(p,"reprocessor")) { - p++; - if (recognize(p,"needed")) { - data.PreprocessorNeeded = getBoolean(value); - } else - invalidItem = true; - } - else - invalidItem = true; - break; - - default: - invalidItem = true; - break; - } - } - } else if (*p == 'i') { // "linker." ? - if (recognize(p,"inker")) { - p++; - if (recognize(p,"prog")) - data.Linker.program = value; - else if (recognize(p,"args")) - parseArgs(data.Linker,value,provider); - else - invalidItem = true; - } - else - invalidItem = true; - } else { - invalidItem = true; - } - break; - - case 'p' : - if (*p == 'r') { // "preprocessor." ? - if (recognize(p, "reprocessor")) { - p++; - if (recognize(p,"prog")) - data.PreProcessor.program = value; - else if (recognize(p,"args")) - parseArgs(data.PreProcessor,value,provider); - else - invalidItem = true; - } else - invalidItem = true; - } else { - invalidItem = true; - } - break; - - case 't' : - if (*p == 'r') { // "translator." ? - if (recognize(p, "ranslator")) { - p++; - if (recognize(p,"prog")) - data.Translator.program = value; - else if (recognize(p,"args")) - parseArgs(data.Translator,value,provider); - else - invalidItem = true; - } else - invalidItem = true; - } else { - invalidItem = true; - } - break; - - case 'o' : - if (*p == 'p') { // "optimizer." ? - if (recognize(p, "ptimizer")) { - p++; - if (recognize(p,"prog")) - data.Optimizer.program = value; - else if (recognize(p,"args")) - parseArgs(data.Optimizer,value,provider); - else - invalidItem = true; - } else - invalidItem = true; - } else { - invalidItem = true; - } - break; - case 'a' : - if (*p == 's') { // "assembler." ? - if (recognize(p, "ssembler")) { - p++; - if (recognize(p,"prog")) - data.Assembler.program = value; - else if (recognize(p,"args")) - parseArgs(data.Assembler,value,provider); - else - invalidItem = true; - } else - invalidItem = true; - } else { - invalidItem = true; - } - break; +LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) { + CompilerDriver::ConfigData* result = 0; + if (configDir.empty()) { + FileInputProvider fip( std::string("/etc/llvm/") + ftype ); + if (!fip.okay()) { + fip.error("Configuration for '" + ftype + "' is not available."); + fip.checkErrors(); + } + else { + result = new CompilerDriver::ConfigData(); + ParseConfigData(fip,*result); + } + } else { + FileInputProvider fip( configDir + "/" + ftype ); + if (!fip.okay()) { + fip.error("Configuration for '" + ftype + "' is not available."); + fip.checkErrors(); + } + else { + result = new CompilerDriver::ConfigData(); + ParseConfigData(fip,*result); } - if (invalidItem) - provider.error("Invalid configuration item: " + line.substr(stPos, eqPos-stPos)); } - return new CompilerDriver::ConfigData(data); -} - -CompilerDriver::ConfigData* -ReadConfigData(const std::string& ftype) { - if ( ftype == "ll" ) { - StaticInputProvider sip(LL_Data, sizeof(LL_Data)/sizeof(LL_Data[0]), - "LLVM Assembly (internal)"); - return ParseConfigData(sip); - } else if (ftype == "st") { - StaticInputProvider sip(ST_Data, sizeof(ST_Data)/sizeof(ST_Data[0]), - "Stacker (internal)"); - return ParseConfigData(sip); - } - return 0; -} - + return result; } LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider() diff --git a/tools/llvmc/ConfigData.h b/tools/llvmc/ConfigData.h index f11931d9995..e94fb896428 100644 --- a/tools/llvmc/ConfigData.h +++ b/tools/llvmc/ConfigData.h @@ -42,6 +42,9 @@ namespace llvm { /// @brief Allow the configuration directory to be set virtual void setConfigDir(const std::string& dirName) { configDir = dirName; } + private: + CompilerDriver::ConfigData* ReadConfigData(const std::string& ftype); + /// @} /// @name Data /// @{ diff --git a/tools/llvmc/ConfigLexer.h b/tools/llvmc/ConfigLexer.h new file mode 100644 index 00000000000..5a3e9e9f74f --- /dev/null +++ b/tools/llvmc/ConfigLexer.h @@ -0,0 +1,77 @@ +//===- ConfigLexer.h - ConfigLexer Declarations -----------------*- 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 declares the types and data needed by ConfigLexer.l +// +//===------------------------------------------------------------------------=== +#ifndef LLVM_TOOLS_LLVMC_CONFIGLEXER_H +#define LLVM_TOOLS_LLVMC_CONFIGLEXER_H + +#include +#include + +namespace llvm { + +struct ConfigLexerInfo +{ + int64_t IntegerVal; + std::string StringVal; +}; + +extern ConfigLexerInfo ConfigLexerData; +extern unsigned ConfigLexerLine; + +class InputProvider { + public: + InputProvider(const std::string& nm) { + name = nm; + errCount = 0; + } + virtual ~InputProvider(); + virtual unsigned read(char *buf, unsigned max_size) = 0; + virtual void error(const std::string& msg); + virtual void checkErrors(); + + private: + std::string name; + unsigned errCount; +}; + +extern InputProvider* ConfigLexerInput; + +enum ConfigLexerTokens { + EOFTOK = 0, ///< Returned by Configlex when we hit end of file + EOLTOK, ///< End of line + ERRORTOK, ///< Error token + OPTION, ///< A command line option + SEPARATOR, ///< A configuration item separator + EQUALS, ///< The equals sign, = + TRUETOK, ///< A boolean true value (true/yes/on) + FALSETOK, ///< A boolean false value (false/no/off) + INTEGER, ///< An integer + STRING, ///< A quoted string + IN_SUBST, ///< The input substitution item @in@ + OUT_SUBST, ///< The output substitution item @out@ + LANG, ///< The item "lang" (and case variants) + PREPROCESSOR, ///< The item "preprocessor" (and case variants) + TRANSLATOR, ///< The item "translator" (and case variants) + OPTIMIZER, ///< The item "optimizer" (and case variants) + ASSEMBLER, ///< The item "assembler" (and case variants) + LINKER, ///< The item "linker" (and case variants) + NAME, ///< The item "name" (and case variants) + NEEDED, ///< The item "needed" (and case variants) + COMMAND, ///< The item "command" (and case variants) + PREPROCESSES, ///< The item "preprocesses" (and case variants) + GROKS_DASH_O, ///< The item "groks_dash_O" (and case variants) + OPTIMIZES, ///< The item "optimizes" (and case variants) +}; + +} + +#endif diff --git a/tools/llvmc/ConfigLexer.l b/tools/llvmc/ConfigLexer.l new file mode 100644 index 00000000000..58ddd2bdbdf --- /dev/null +++ b/tools/llvmc/ConfigLexer.l @@ -0,0 +1,135 @@ +/*===- ConfigLexer.l - Scanner for CompilerDriver Config Files -*- 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 the flex scanner for configuration files for the +// llvmc CompilerDriver. +// +//===----------------------------------------------------------------------===*/ + + +%option prefix="Config" +%option yylineno +%option nostdinit +%option never-interactive +%option batch +%option noyywrap +%option nodefault +%option 8bit +%option outfile="ConfigLexer.cpp" +%option ecs +%option noreject +%option noyymore +%array + +%{ + +#include "ConfigLexer.h" + +#define YY_INPUT(buf,result,max_size) \ + { \ + assert(ConfigLexerInput != 0 && "Oops"); \ + result = ConfigLexerInput->read(buf,max_size); \ + if (result == 0 ) result = YY_NULL; \ + } + +using namespace llvm; + +/* Conversion of text ints to binary */ +static int64_t IntToVal(const char *Buffer) { + int64_t Result = 0; + for (; *Buffer; Buffer++) { + int64_t OldRes = Result; + Result *= 10; + Result += *Buffer-'0'; + } + return Result; +} + +bool in_value = false; + +%} + +LANG lang|Lang|LANG +PREPROCESSOR preprocessor|PreProcessor|PREPROCESSOR +TRANSLATOR translator|Translator|TRANSLATOR +OPTIMIZER optimizer|Optimizer|OPTIMIZER +ASSEMBLER assembler|Assembler|ASSEMBLER +LINKER linker|Linker|LINKER +NAME name|Name|NAME +NEEDED needed|Needed|NEEDED +COMMAND command|Command|COMMAND +PREPROCESSES preprocesses|PreProcesses|PREPROCESSES +GROKS_DASH_O groks_dash_O|Groks_Dash_O|GROKS_DASH_O +OPTIMIZES optimizes|Optimizes|OPTIMIZES +Comment \#[^\n]* +NewLine \n +White [ \t]* +Option [-A-Za-z0-9_:%+/\\|,]* +Sep \. +Eq \= +String \"[^\"]*\" +Integer [-+]?[0-9]+ +True true|True|TRUE +False false|False|FALSE +On on|On|ON +Off off|Off|OFF +Yes yes|Yes|YES +No no|No|NO + +%% + +{NewLine} { in_value = false; ConfigLexerLine++; return EOLTOK; } +{Comment} { /* Ignore comments */ } +{White} { /* Ignore whitespace */ } + +{LANG} { if (in_value) { ConfigLexerData.StringVal = "lang"; + return OPTION; } else return LANG; } +{PREPROCESSOR} { if (in_value) { ConfigLexerData.StringVal = "preprocessor"; + return OPTION; } else return PREPROCESSOR; } +{TRANSLATOR} { if (in_value) { ConfigLexerData.StringVal = "translator"; + return OPTION; } else return TRANSLATOR; } +{OPTIMIZER} { if (in_value) { ConfigLexerData.StringVal = "optimizer"; + return OPTION; } else return OPTIMIZER; } +{ASSEMBLER} { if (in_value) { ConfigLexerData.StringVal = "assembler"; + return OPTION; } else return ASSEMBLER; } +{LINKER} { if (in_value) { ConfigLexerData.StringVal = "linker"; + return OPTION; } else return LINKER; } +{NAME} { if (in_value) { ConfigLexerData.StringVal = "name"; + return OPTION; } else return NAME; } +{NEEDED} { if (in_value) { ConfigLexerData.StringVal = "needed"; + return OPTION; } else return NEEDED; } +{COMMAND} { if (in_value) { ConfigLexerData.StringVal = "command"; + return OPTION; } else return COMMAND; } +{PREPROCESSES} { if (in_value) { ConfigLexerData.StringVal = "preprocesses"; + return OPTION; } else return PREPROCESSES; } +{GROKS_DASH_O} { if (in_value) { ConfigLexerData.StringVal = "groks_dash_O"; + return OPTION; } else return GROKS_DASH_O; } +{OPTIMIZES} { if (in_value) { ConfigLexerData.StringVal = "optimizes"; + return OPTION; } else return OPTIMIZES; } +{Sep} { if (in_value) { ConfigLexerData.StringVal = yytext; + return OPTION; } } + +@in@ { if (in_value) return IN_SUBST; else return ERRORTOK; } +@out@ { if (in_value) return OUT_SUBST; else return ERRORTOK; } +{True} { if (in_value) return TRUETOK; else return ERRORTOK; } +{On} { if (in_value) return TRUETOK; else return ERRORTOK; } +{Yes} { if (in_value) return TRUETOK; else return ERRORTOK; } +{False} { if (in_value) return FALSETOK; else return ERRORTOK; } +{Off} { if (in_value) return FALSETOK; else return ERRORTOK; } +{No} { if (in_value) return FALSETOK; else return ERRORTOK; } + +{Eq} { in_value = true; return EQUALS; } +{Option} { ConfigLexerData.StringVal = yytext; return OPTION; } +{Integer} { ConfigLexerData.IntegerVal = IntToVal(yytext); return INTEGER; } +{String} { yytext[yyleng-1] = 0; // nuke end quote + ConfigLexerData.StringVal = yytext+1; // Nuke start quote + return STRING; + } + +%% diff --git a/tools/llvmc/Configuration.cpp b/tools/llvmc/Configuration.cpp index 94949c405de..d2e788bf12b 100644 --- a/tools/llvmc/Configuration.cpp +++ b/tools/llvmc/Configuration.cpp @@ -13,391 +13,282 @@ //===------------------------------------------------------------------------=== #include "ConfigData.h" +#include "ConfigLexer.h" #include "CompilerDriver.h" #include "Support/StringExtras.h" #include +#include using namespace llvm; +extern int ::Configlex(); + +namespace llvm { + ConfigLexerInfo ConfigLexerData; + InputProvider* ConfigLexerInput = 0; + unsigned ConfigLexerLine = 1; + + InputProvider::~InputProvider() {} + void InputProvider::error(const std::string& msg) { + std::cerr << name << ":" << ConfigLexerLine << ": Error: " << msg << "\n"; + errCount++; + } + + void InputProvider::checkErrors() { + if (errCount > 0) { + std::cerr << name << " had " << errCount << " errors. Terminating.\n"; + exit(errCount); + } + } + +} + namespace { -// This array of strings provides the input for ".ll" files (LLVM Assembly) -// to the configuration file parser. This data is just "built-in" to -// llvmc so it doesn't have to be read from a configuration file. -static const char* LL_Data[] = { - "lang.name=LLVM Assembly", - "lang.translator.preprocesses=false", - "lang.translator.optimizes=No", - "lang.translator.groks_dash_O=No", - "lang.preprocessor.needed=0", - "preprocessor.prog=", - "preprocessor.args=", - "translator.prog=llvm-as", - "translator.args=@in@ -o @out@", - "optimizer.prog=opt", - "optimizer.args=@in@ -o @out@", - "assembler.prog=llc", - "assembler.args=@in@ -o @out@", - "linker.prog=llvm-link", - "linker.args=@in@ -o @out@" -}; + class FileInputProvider : public InputProvider { + public: + FileInputProvider(const std::string & fname) + : InputProvider(fname) + , F(fname.c_str()) { + ConfigLexerInput = this; + } + virtual ~FileInputProvider() { F.close(); ConfigLexerInput = 0; } + virtual unsigned read(char *buffer, unsigned max_size) { + if (F.good()) { + F.read(buffer,max_size); + if ( F.gcount() ) return F.gcount() - 1; + } + return 0; + } -// This array of strings provides the input for ".st" files (Stacker). -static const char* ST_Data[] = { - "lang.name=Stacker", - "lang.translator.preprocesses=false", - "lang.translator.optimizes=true", - "lang.translator.groks_dash_O=0", - "lang.preprocessor.needed=0", - "preprocessor.prog=cp", - "preprocessor.args=@in@ @out@", - "translator.prog=stkrc", - "translator.args=@in@ -o @out@ -S 2048", - "optimizer.prog=opt", - "optimizer.args=@in@ -o @out@", - "assembler.prog=llc", - "assembler.args=@in@ -o @out@", - "linker.prog=llvm-link", - "linker.args=@in@ -o @out@" -}; + bool okay() { return F.good(); } + private: + std::ifstream F; + }; -class InputProvider { - public: - virtual bool getLine(std::string& line) = 0; - virtual void error(const std::string& msg) = 0; - virtual bool errorOccurred() = 0; -}; + struct ParseContext + { + int token; + InputProvider* provider; + CompilerDriver::ConfigData* confDat; + CompilerDriver::Action* action; -class StaticInputProvider : public InputProvider { - public: - StaticInputProvider(const char *data[], size_t count, - const std::string& nam) { - TheData = data; - limit = count; - where = 0; - name = nam; - errCount = 0; - } - virtual ~StaticInputProvider() {} - virtual bool getLine(std::string& line) { - if ( where >= limit ) return false; - line = TheData[where++]; - return true; + int next() { return token = Configlex(); } + + bool next_is_real() { + token = Configlex(); + return (token != EOLTOK) && (token != ERRORTOK) && (token != 0); } - virtual void error(const std::string& msg) { - std::cerr << name << ":" << where << ": Error: " << msg << "\n"; - errCount++; + void eatLineRemnant() { + while (next_is_real()) ; } - virtual bool errorOccurred() { return errCount > 0; }; + void error(const std::string& msg, bool skip = true) { + provider->error(msg); + if (skip) + eatLineRemnant(); + } - private: - const char**TheData; - size_t limit; - size_t where; - std::string name; - size_t errCount; -}; + std::string parseName() { + std::string result; + if (next() == EQUALS) { + while (next_is_real()) { + switch (token ) { + case STRING : + case OPTION : + result += ConfigLexerData.StringVal + " "; + break; + default: + error("Invalid name"); + break; + } + } + if (result.empty()) + error("Name exepected"); + else + result.erase(result.size()-1,1); + } else + error("= expected"); + return result; + } -inline bool recognize(const char*& p, const char*token) { - while (*p == *token && *token != '\0') - ++token, p++; - return *token == '\0' && ((*p == '\0') || ( *p == '.' ) || (*p == '=')); -} + bool parseBoolean() { + bool result = true; + if (next() == EQUALS) { + if (next() == FALSETOK) { + result = false; + } else if (token != TRUETOK) { + error("Expecting boolean value"); + return false; + } + if (next() != EOLTOK && token != 0) { + error("Extraneous tokens after boolean"); + } + } + else + error("Expecting '='"); + return result; + } -inline bool getBoolean(const std::string& value) { - switch (value[0]) { - case 't': - case 'T': - case '1': - case 'y': - case 'Y': - return true; - default : - return false; - } - return false; -} + void parseLang() { + if ( next() == NAME ) { + confDat->langName = parseName(); + } else if (token == TRANSLATOR) { + switch (next()) { + case PREPROCESSES: + confDat->TranslatorPreprocesses = parseBoolean(); + break; + case OPTIMIZES: + confDat->TranslatorOptimizes = parseBoolean(); + break; + case GROKS_DASH_O: + confDat->TranslatorGroksDashO = parseBoolean(); + break; + default: + error("Invalid lang.translator identifier"); + break; + } + } + else if (token == PREPROCESSOR) { + if (next() == NEEDED) + confDat->PreprocessorNeeded = parseBoolean(); + } + else { + error("Expecting valid identifier after 'lang.'"); + } + } -inline void skipWhitespace( size_t& pos, const std::string& line ) { - while (pos < line.size() && ( - line[pos] == ' ' || // Space - line[pos] == '\t' || // Horizontal Tab - line[pos] == '\n' || // New Line - line[pos] == '\v' || // Vertical Tab - line[pos] == '\f' || // Form Feed - line[pos] == '\r') // Carriate Return - ) - pos++; -} - -inline void parseArgs(CompilerDriver::Action& pat, - const std::string& value, - InputProvider& provider ) -{ - const char* p = value.c_str(); - const char* argStart = p; - while (*p != '\0') { - switch (*p) { - case ' ': - if (argStart != p) - pat.args.push_back(std::string(argStart, p-argStart)); - argStart = ++p; - break; - case '@' : - { - if (argStart != p) - pat.args.push_back(std::string(argStart,p-argStart)); - const char* token = ++p; - while (*p != '@' && *p != 0) - p++; - if ( *p != '@' ) { - provider.error("Unterminated substitution token"); - return; + void parseCommand(CompilerDriver::Action& action) { + if (next() == EQUALS) { + next(); + if (token == EOLTOK) { + // no value (valid) + action.program.clear(); + action.args.clear(); + action.inputAt = 0; + action.outputAt = 0; + } else { + if (token == STRING || token == OPTION) { + action.program = ConfigLexerData.StringVal; } else { - p++; - bool legal = false; - switch (token[0]) { - case 'i': - if (token[1] == 'n' && token[2] == '@' ) { - pat.inputAt = pat.args.size(); - pat.args.push_back("in"); - legal = true; - argStart = p; - } - break; - case 'o': - if (token[1] == 'u' && token[2] == 't' && token[3] == '@') { - pat.outputAt = pat.args.size(); - pat.args.push_back("out"); - legal = true; - argStart = p; - } - break; - default: - break; - } - if (!legal) { - provider.error("Invalid substitution token"); - return; - } + error("Expecting a program name"); + } + while (next_is_real()) { + if (token == STRING || token == OPTION) + action.args.push_back(ConfigLexerData.StringVal); + else if (token == IN_SUBST) { + action.inputAt = action.args.size(); + action.args.push_back("in"); + } else if (token == OUT_SUBST) { + action.outputAt = action.args.size(); + action.args.push_back("out"); + } else + error("Expecting a program argument", false); } } - break; - default : - p++; - break; + } } + + void parsePreProcessor() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->PreProcessor); + } + + void parseTranslator() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->Translator); + } + + void parseOptimizer() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->Optimizer); + } + + void parseAssembler() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->Assembler); + } + + void parseLinker() { + if (next() != COMMAND) { + error("Expecting 'command'"); + return; + } + parseCommand(confDat->Linker); + } + + void parseAssignment() { + switch (token) { + case LANG: return parseLang(); + case PREPROCESSOR: return parsePreProcessor(); + case TRANSLATOR: return parseTranslator(); + case OPTIMIZER: return parseOptimizer(); + case ASSEMBLER: return parseAssembler(); + case LINKER: return parseLinker(); + case EOLTOK: break; // just ignore + case ERRORTOK: + default: + error("Invalid top level configuration item identifier"); + } + } + + void parseFile() { + while ( next() != 0 ) { + parseAssignment(); + } + provider->checkErrors(); + } + }; + + void + ParseConfigData(InputProvider& provider, CompilerDriver::ConfigData& confDat) { + ParseContext ctxt; + ctxt.token = 0; + ctxt.provider = &provider; + ctxt.confDat = &confDat; + ctxt.action = 0; + ctxt.parseFile(); } } CompilerDriver::ConfigData* -ParseConfigData(InputProvider& provider) { - std::string line; - CompilerDriver::ConfigData data; - while ( provider.getLine(line) ) { - // Check line length first - size_t lineLen = line.size(); - if (lineLen > 4096) - provider.error("length of input line (" + utostr(lineLen) + - ") is too long"); - - // First, skip whitespace - size_t stPos = 0; - skipWhitespace(stPos, line); - - // See if there's a hash mark. It and everything after it is - // ignored so lets delete that now. - size_t hashPos = line.find('#'); - if (hashPos != std::string::npos) - line.erase(hashPos); - - // Make sure we have something left to parse - if (line.size() == 0) - continue; // ignore full-line comment or whitespace line - - // Find the equals sign - size_t eqPos = line.find('='); - if (eqPos == std::string::npos) - provider.error("Configuration directive is missing an ="); - - // extract the item name - std::string name(line, stPos, eqPos-stPos); - - // directives without names are illegal - if (name.empty()) - provider.error("Configuration directive name is empty"); - - // Skip whitespace in the value - size_t valPos = eqPos + 1; - skipWhitespace(valPos, line); - - // Skip white space at end of value - size_t endPos = line.length() - 1; - while (line[endPos] == ' ') - endPos--; - - // extract the item value - std::string value(line, valPos, endPos-valPos+1); - - // Get the configuration item as a char pointer - const char*p = name.c_str(); - - // Indicate we haven't found an invalid item yet. - bool invalidItem = false; - - // Parse the contents by examining first character and - // using the recognize function strategically - switch (*p++) { - case 'l' : - // could it be "lang." - if (*p == 'a') { // "lang." ? - if (recognize(p,"ang")) { - p++; - switch (*p++) { - case 'n': - if (recognize(p,"ame")) - data.langName = value; - else - invalidItem = true; - break; - case 't': - if (recognize(p,"ranslator")) { - p++; - if (recognize(p,"preprocesses")) - data.TranslatorPreprocesses = getBoolean(value); - else if (recognize(p, "optimizes")) - data.TranslatorOptimizes = getBoolean(value); - else if (recognize(p, "groks_dash_O")) - data.TranslatorGroksDashO = getBoolean(value); - else - invalidItem = true; - } - else - invalidItem = true; - break; - case 'p': - if (recognize(p,"reprocessor")) { - p++; - if (recognize(p,"needed")) { - data.PreprocessorNeeded = getBoolean(value); - } else - invalidItem = true; - } - else - invalidItem = true; - break; - - default: - invalidItem = true; - break; - } - } - } else if (*p == 'i') { // "linker." ? - if (recognize(p,"inker")) { - p++; - if (recognize(p,"prog")) - data.Linker.program = value; - else if (recognize(p,"args")) - parseArgs(data.Linker,value,provider); - else - invalidItem = true; - } - else - invalidItem = true; - } else { - invalidItem = true; - } - break; - - case 'p' : - if (*p == 'r') { // "preprocessor." ? - if (recognize(p, "reprocessor")) { - p++; - if (recognize(p,"prog")) - data.PreProcessor.program = value; - else if (recognize(p,"args")) - parseArgs(data.PreProcessor,value,provider); - else - invalidItem = true; - } else - invalidItem = true; - } else { - invalidItem = true; - } - break; - - case 't' : - if (*p == 'r') { // "translator." ? - if (recognize(p, "ranslator")) { - p++; - if (recognize(p,"prog")) - data.Translator.program = value; - else if (recognize(p,"args")) - parseArgs(data.Translator,value,provider); - else - invalidItem = true; - } else - invalidItem = true; - } else { - invalidItem = true; - } - break; - - case 'o' : - if (*p == 'p') { // "optimizer." ? - if (recognize(p, "ptimizer")) { - p++; - if (recognize(p,"prog")) - data.Optimizer.program = value; - else if (recognize(p,"args")) - parseArgs(data.Optimizer,value,provider); - else - invalidItem = true; - } else - invalidItem = true; - } else { - invalidItem = true; - } - break; - case 'a' : - if (*p == 's') { // "assembler." ? - if (recognize(p, "ssembler")) { - p++; - if (recognize(p,"prog")) - data.Assembler.program = value; - else if (recognize(p,"args")) - parseArgs(data.Assembler,value,provider); - else - invalidItem = true; - } else - invalidItem = true; - } else { - invalidItem = true; - } - break; +LLVMC_ConfigDataProvider::ReadConfigData(const std::string& ftype) { + CompilerDriver::ConfigData* result = 0; + if (configDir.empty()) { + FileInputProvider fip( std::string("/etc/llvm/") + ftype ); + if (!fip.okay()) { + fip.error("Configuration for '" + ftype + "' is not available."); + fip.checkErrors(); + } + else { + result = new CompilerDriver::ConfigData(); + ParseConfigData(fip,*result); + } + } else { + FileInputProvider fip( configDir + "/" + ftype ); + if (!fip.okay()) { + fip.error("Configuration for '" + ftype + "' is not available."); + fip.checkErrors(); + } + else { + result = new CompilerDriver::ConfigData(); + ParseConfigData(fip,*result); } - if (invalidItem) - provider.error("Invalid configuration item: " + line.substr(stPos, eqPos-stPos)); } - return new CompilerDriver::ConfigData(data); -} - -CompilerDriver::ConfigData* -ReadConfigData(const std::string& ftype) { - if ( ftype == "ll" ) { - StaticInputProvider sip(LL_Data, sizeof(LL_Data)/sizeof(LL_Data[0]), - "LLVM Assembly (internal)"); - return ParseConfigData(sip); - } else if (ftype == "st") { - StaticInputProvider sip(ST_Data, sizeof(ST_Data)/sizeof(ST_Data[0]), - "Stacker (internal)"); - return ParseConfigData(sip); - } - return 0; -} - + return result; } LLVMC_ConfigDataProvider::LLVMC_ConfigDataProvider() diff --git a/tools/llvmc/Configuration.h b/tools/llvmc/Configuration.h index f11931d9995..e94fb896428 100644 --- a/tools/llvmc/Configuration.h +++ b/tools/llvmc/Configuration.h @@ -42,6 +42,9 @@ namespace llvm { /// @brief Allow the configuration directory to be set virtual void setConfigDir(const std::string& dirName) { configDir = dirName; } + private: + CompilerDriver::ConfigData* ReadConfigData(const std::string& ftype); + /// @} /// @name Data /// @{ diff --git a/tools/llvmc/llvmc.cpp b/tools/llvmc/llvmc.cpp index 878fb65e0c8..9cf43cb3fff 100644 --- a/tools/llvmc/llvmc.cpp +++ b/tools/llvmc/llvmc.cpp @@ -207,6 +207,7 @@ int main(int argc, char **argv) { // Construct the ConfigDataProvider object LLVMC_ConfigDataProvider Provider; + Provider.setConfigDir(ConfigDir); // Construct the CompilerDriver object CompilerDriver CD(Provider);