diff --git a/include/llvm/CompilerDriver/Common.td b/include/llvm/CompilerDriver/Common.td index 5b7c543f1c9..bffab59f3ac 100644 --- a/include/llvm/CompilerDriver/Common.td +++ b/include/llvm/CompilerDriver/Common.td @@ -68,6 +68,8 @@ def not_empty; def default; def single_input_file; def multiple_input_files; +def any_switch_on; +def any_not_empty; // Possible actions. @@ -76,7 +78,9 @@ def forward; def forward_as; def stop_compilation; def unpack_values; +def warning; def error; +def unset_option; // Increase/decrease the edge weight. def inc_weight; @@ -90,11 +94,16 @@ class PluginPriority { int priority = p; } -// Option list - used to specify aliases and sometimes help strings. +// Option list - a single place to specify options. class OptionList l> { list options = l; } +// Option preprocessor - actions taken during plugin loading. +class OptionPreprocessor { + dag preprocessor = d; +} + // Map from suffixes to language names class LangToSuffixes lst> { diff --git a/include/llvm/CompilerDriver/Plugin.h b/include/llvm/CompilerDriver/Plugin.h index 9f9eee3c0db..e9a20488a08 100644 --- a/include/llvm/CompilerDriver/Plugin.h +++ b/include/llvm/CompilerDriver/Plugin.h @@ -29,6 +29,11 @@ namespace llvmc { /// first. virtual int Priority() const { return 0; } + /// PreprocessOptions - The auto-generated function that performs various + /// consistency checks on options (like ensuring that -O2 and -O3 are not + /// used together). + virtual void PreprocessOptions() const = 0; + /// PopulateLanguageMap - The auto-generated function that fills in /// the language map (map from file extensions to language names). virtual void PopulateLanguageMap(LanguageMap&) const = 0; @@ -60,13 +65,10 @@ namespace llvmc { PluginLoader(); ~PluginLoader(); - /// PopulateLanguageMap - Fills in the language map by calling - /// PopulateLanguageMap methods of all plugins. - void PopulateLanguageMap(LanguageMap& langMap); - - /// PopulateCompilationGraph - Populates the compilation graph by - /// calling PopulateCompilationGraph methods of all plugins. - void PopulateCompilationGraph(CompilationGraph& tools); + /// RunInitialization - Calls PreprocessOptions, PopulateLanguageMap and + /// PopulateCompilationGraph methods of all plugins. This populates the + /// global language map and the compilation graph. + void RunInitialization(LanguageMap& langMap, CompilationGraph& graph) const; private: // noncopyable diff --git a/lib/CompilerDriver/Main.cpp b/lib/CompilerDriver/Main.cpp index 3e1fc9f124e..c581809d04b 100644 --- a/lib/CompilerDriver/Main.cpp +++ b/lib/CompilerDriver/Main.cpp @@ -95,8 +95,7 @@ int Main(int argc, char** argv) { (argc, argv, "LLVM Compiler Driver (Work In Progress)", true); PluginLoader Plugins; - Plugins.PopulateLanguageMap(langMap); - Plugins.PopulateCompilationGraph(graph); + Plugins.RunInitialization(langMap, graph); if (CheckGraph) { int ret = graph.Check(); diff --git a/lib/CompilerDriver/Plugin.cpp b/lib/CompilerDriver/Plugin.cpp index 7310d120bff..0fdfef4c6a2 100644 --- a/lib/CompilerDriver/Plugin.cpp +++ b/lib/CompilerDriver/Plugin.cpp @@ -62,18 +62,17 @@ namespace llvmc { pluginListInitialized = false; } - void PluginLoader::PopulateLanguageMap(LanguageMap& langMap) { + void PluginLoader::RunInitialization(LanguageMap& langMap, + CompilationGraph& graph) const + { llvm::sys::SmartScopedLock Lock(*PluginMutex); for (PluginList::iterator B = Plugins.begin(), E = Plugins.end(); - B != E; ++B) - (*B)->PopulateLanguageMap(langMap); - } - - void PluginLoader::PopulateCompilationGraph(CompilationGraph& graph) { - llvm::sys::SmartScopedLock Lock(*PluginMutex); - for (PluginList::iterator B = Plugins.begin(), E = Plugins.end(); - B != E; ++B) - (*B)->PopulateCompilationGraph(graph); + B != E; ++B) { + const BasePlugin* BP = *B; + BP->PreprocessOptions(); + BP->PopulateLanguageMap(langMap); + BP->PopulateCompilationGraph(graph); + } } } diff --git a/tools/llvmc/plugins/Base/Base.td.in b/tools/llvmc/plugins/Base/Base.td.in index ddf6a3654ae..c26a567ba54 100644 --- a/tools/llvmc/plugins/Base/Base.td.in +++ b/tools/llvmc/plugins/Base/Base.td.in @@ -65,6 +65,18 @@ def OptList : OptionList<[ (help "Pass options to opt")) ]>; +// Option preprocessor. + +def Preprocess : OptionPreprocessor< +(case (and (switch_on "O3"), (any_switch_on ["O0", "O1", "O2"])), + (unset_option ["O0", "O1", "O2"]), + (and (switch_on "O2"), (any_switch_on ["O0", "O1"])), + (unset_option ["O0", "O1"]), + (and (switch_on "O1"), (switch_on "O0")), + (unset_option "O0")) +>; + + // Tools class llvm_gcc_based : Tool< diff --git a/utils/TableGen/LLVMCConfigurationEmitter.cpp b/utils/TableGen/LLVMCConfigurationEmitter.cpp index 6dfd1b362d5..a8eaa3a16a7 100644 --- a/utils/TableGen/LLVMCConfigurationEmitter.cpp +++ b/utils/TableGen/LLVMCConfigurationEmitter.cpp @@ -351,6 +351,9 @@ class OptionDescriptions { public: /// FindOption - exception-throwing wrapper for find(). const OptionDescription& FindOption(const std::string& OptName) const; + /// FindSwitch - wrapper for FindOption that throws in case the option is not + /// a switch. + const OptionDescription& FindSwitch(const std::string& OptName) const; /// insertDescription - Insert new OptionDescription into /// OptionDescriptions list @@ -372,6 +375,15 @@ OptionDescriptions::FindOption(const std::string& OptName) const throw OptName + ": no such option!"; } +const OptionDescription& +OptionDescriptions::FindSwitch(const std::string& OptName) const +{ + const OptionDescription& OptDesc = this->FindOption(OptName); + if (!OptDesc.isSwitch()) + throw OptName + ": incorrect option type - should be a switch!"; + return OptDesc; +} + void OptionDescriptions::InsertDescription (const OptionDescription& o) { container_type::iterator I = Descriptions.find(o.Name); @@ -996,32 +1008,31 @@ bool EmitCaseTest0Args(const std::string& TestName, raw_ostream& O) { return false; } - -/// EmitCaseTest1Arg - Helper function used by EmitCaseConstructHandler(). -bool EmitCaseTest1Arg(const std::string& TestName, - const DagInit& d, - const OptionDescriptions& OptDescs, - raw_ostream& O) { - checkNumberOfArguments(&d, 1); +/// EmitCaseTest1ArgStr - Helper function used by EmitCaseTest1Arg(); +bool EmitCaseTest1ArgStr(const std::string& TestName, + const DagInit& d, + const OptionDescriptions& OptDescs, + raw_ostream& O) { const std::string& OptName = InitPtrToString(d.getArg(0)); if (TestName == "switch_on") { - const OptionDescription& OptDesc = OptDescs.FindOption(OptName); - if (!OptDesc.isSwitch()) - throw OptName + ": incorrect option type - should be a switch!"; + const OptionDescription& OptDesc = OptDescs.FindSwitch(OptName); O << OptDesc.GenVariableName(); return true; - } else if (TestName == "input_languages_contain") { + } + else if (TestName == "input_languages_contain") { O << "InLangs.count(\"" << OptName << "\") != 0"; return true; - } else if (TestName == "in_language") { + } + else if (TestName == "in_language") { // This works only for single-argument Tool::GenerateAction. Join // tools can process several files in different languages simultaneously. // TODO: make this work with Edge::Weight (if possible). O << "LangMap.GetLanguage(inFile) == \"" << OptName << '\"'; return true; - } else if (TestName == "not_empty" || TestName == "empty") { + } + else if (TestName == "not_empty" || TestName == "empty") { const char* Test = (TestName == "empty") ? "" : "!"; if (OptName == "o") { @@ -1041,6 +1052,47 @@ bool EmitCaseTest1Arg(const std::string& TestName, return false; } +/// EmitCaseTest1ArgList - Helper function used by EmitCaseTest1Arg(); +bool EmitCaseTest1ArgList(const std::string& TestName, + const DagInit& d, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + const ListInit& L = *static_cast(d.getArg(0)); + + if (TestName == "any_switch_on") { + bool isFirst = true; + + for (ListInit::const_iterator B = L.begin(), E = L.end(); B != E; ++B) { + const std::string& OptName = InitPtrToString(*B); + const OptionDescription& OptDesc = OptDescs.FindSwitch(OptName); + + if (isFirst) + isFirst = false; + else + O << " || "; + O << OptDesc.GenVariableName(); + } + + return true; + } + + // TODO: implement any_not_empty, any_empty, switch_on [..], empty [..] + + return false; +} + +/// EmitCaseTest1Arg - Helper function used by EmitCaseConstructHandler(); +bool EmitCaseTest1Arg(const std::string& TestName, + const DagInit& d, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + checkNumberOfArguments(&d, 1); + if (typeid(*d.getArg(0)) == typeid(ListInit)) + return EmitCaseTest1ArgList(TestName, d, OptDescs, O); + else + return EmitCaseTest1ArgStr(TestName, d, OptDescs, O); +} + /// EmitCaseTest2Args - Helper function used by EmitCaseConstructHandler(). bool EmitCaseTest2Args(const std::string& TestName, const DagInit& d, @@ -1130,10 +1182,14 @@ void EmitCaseTest(const DagInit& d, unsigned IndentLevel, throw TestName + ": unknown edge property!"; } -// Emit code that handles the 'case' construct. -// Takes a function object that should emit code for every case clause. -// Callback's type is -// void F(Init* Statement, unsigned IndentLevel, raw_ostream& O). +/// EmitCaseConstructHandler - Emit code that handles the 'case' +/// construct. Takes a function object that should emit code for every case +/// clause. +/// Callback's type is void F(Init* Statement, unsigned IndentLevel, +/// raw_ostream& O). +/// EmitElseIf parameter controls the type of condition that is emitted ('if +/// (..) {...} else if (...) {} ... else {...}' vs. 'if (..) {...} if(...) +/// {...} ...'). template void EmitCaseConstructHandler(const Init* Dag, unsigned IndentLevel, F Callback, bool EmitElseIf, @@ -1876,10 +1932,108 @@ void EmitOptionDefinitions (const OptionDescriptions& descs, O << '\n'; } -/// EmitPopulateLanguageMap - Emit the PopulateLanguageMap() function. +/// PreprocessOptionsCallback - Helper function passed to +/// EmitCaseConstructHandler() by EmitPreprocessOptions(). +class PreprocessOptionsCallback { + const OptionDescriptions& OptDescs_; + + void onUnsetOption(Init* i, unsigned IndentLevel, raw_ostream& O) { + const std::string& OptName = InitPtrToString(i); + const OptionDescription& OptDesc = OptDescs_.FindOption(OptName); + const OptionType::OptionType OptType = OptDesc.Type; + + if (OptType == OptionType::Switch) { + O.indent(IndentLevel) << OptDesc.GenVariableName() << " = false;\n"; + } + else if (OptType == OptionType::Parameter + || OptType == OptionType::Prefix) { + O.indent(IndentLevel) << OptDesc.GenVariableName() << " = \"\";\n"; + } + else { + throw std::string("'unset_option' can only be applied to " + "switches or parameter/prefix options."); + } + } + + void processDag(const Init* I, unsigned IndentLevel, raw_ostream& O) + { + const DagInit& d = InitPtrToDag(I); + const std::string& OpName = d.getOperator()->getAsString(); + + // TOFIX: there is some duplication between this function and + // EmitActionHandler. + if (OpName == "warning") { + checkNumberOfArguments(&d, 1); + O.indent(IndentLevel) << "llvm::errs() << \"" + << InitPtrToString(d.getArg(0)) << "\";\n"; + } + else if (OpName == "error") { + checkNumberOfArguments(&d, 1); + O.indent(IndentLevel) << "throw std::runtime_error(\"" + << InitPtrToString(d.getArg(0)) + << "\");\n"; + } + else if (OpName == "unset_option") { + checkNumberOfArguments(&d, 1); + Init* I = d.getArg(0); + if (typeid(*I) == typeid(ListInit)) { + const ListInit& DagList = *static_cast(I); + for (ListInit::const_iterator B = DagList.begin(), E = DagList.end(); + B != E; ++B) + this->onUnsetOption(*B, IndentLevel, O); + } + else { + this->onUnsetOption(I, IndentLevel, O); + } + } + else { + throw "Unknown operator in the option preprocessor: '" + OpName + "'!" + "\nOnly 'warning', 'error' and 'unset_option' are allowed."; + } + } + +public: + + // TODO: Remove duplication. + void operator()(const Init* I, unsigned IndentLevel, raw_ostream& O) { + if (typeid(*I) == typeid(ListInit)) { + const ListInit& DagList = *static_cast(I); + for (ListInit::const_iterator B = DagList.begin(), E = DagList.end(); + B != E; ++B) + this->processDag(*B, IndentLevel, O); + } + else { + this->processDag(I, IndentLevel, O); + } + } + + PreprocessOptionsCallback(const OptionDescriptions& OptDescs) + : OptDescs_(OptDescs) + {} +}; + +/// EmitPreprocessOptions - Emit the PreprocessOptionsLocal() function. +void EmitPreprocessOptions (const RecordKeeper& Records, + const OptionDescriptions& OptDecs, raw_ostream& O) +{ + O << "void PreprocessOptionsLocal() {\n"; + + const RecordVector& OptionPreprocessors = + Records.getAllDerivedDefinitions("OptionPreprocessor"); + + for (RecordVector::const_iterator B = OptionPreprocessors.begin(), + E = OptionPreprocessors.end(); B!=E; ++B) { + DagInit* Case = (*B)->getValueAsDag("preprocessor"); + EmitCaseConstructHandler(Case, Indent1, PreprocessOptionsCallback(OptDecs), + false, OptDecs, O); + } + + O << "}\n\n"; +} + +/// EmitPopulateLanguageMap - Emit the PopulateLanguageMapLocal() function. void EmitPopulateLanguageMap (const RecordKeeper& Records, raw_ostream& O) { - // Generate code O << "void PopulateLanguageMapLocal(LanguageMap& langMap) {\n"; // Get the relevant field out of RecordKeeper @@ -1922,17 +2076,16 @@ void IncDecWeight (const Init* i, unsigned IndentLevel, O.indent(IndentLevel) << "ret -= "; } else if (OpName == "error") { - O.indent(IndentLevel) - << "throw std::runtime_error(\"" << - (d.getNumArgs() >= 1 ? InitPtrToString(d.getArg(0)) - : "Unknown error!") - << "\");\n"; + checkNumberOfArguments(&d, 1); + O.indent(IndentLevel) << "throw std::runtime_error(\"" + << InitPtrToString(d.getArg(0)) + << "\");\n"; return; } - - else - throw "Unknown operator in edge properties list: " + OpName + '!' + + else { + throw "Unknown operator in edge properties list: '" + OpName + "'!" "\nOnly 'inc_weight', 'dec_weight' and 'error' are allowed."; + } if (d.getNumArgs() > 0) O << InitPtrToInt(d.getArg(0)) << ";\n"; @@ -1981,7 +2134,7 @@ void EmitEdgeClasses (const RecordVector& EdgeVector, } } -/// EmitPopulateCompilationGraph - Emit the PopulateCompilationGraph() +/// EmitPopulateCompilationGraph - Emit the PopulateCompilationGraphLocal() /// function. void EmitPopulateCompilationGraph (const RecordVector& EdgeVector, const ToolDescriptions& ToolDescs, @@ -2110,6 +2263,8 @@ void EmitRegisterPlugin(int Priority, raw_ostream& O) { O << "struct Plugin : public llvmc::BasePlugin {\n\n"; O.indent(Indent1) << "int Priority() const { return " << Priority << "; }\n\n"; + O.indent(Indent1) << "void PreprocessOptions() const\n"; + O.indent(Indent1) << "{ PreprocessOptionsLocal(); }\n\n"; O.indent(Indent1) << "void PopulateLanguageMap(LanguageMap& langMap) const\n"; O.indent(Indent1) << "{ PopulateLanguageMapLocal(langMap); }\n\n"; O.indent(Indent1) @@ -2129,7 +2284,8 @@ void EmitIncludes(raw_ostream& O) { << "#include \"llvm/CompilerDriver/Tool.h\"\n\n" << "#include \"llvm/ADT/StringExtras.h\"\n" - << "#include \"llvm/Support/CommandLine.h\"\n\n" + << "#include \"llvm/Support/CommandLine.h\"\n" + << "#include \"llvm/Support/raw_ostream.h\"\n\n" << "#include \n" << "#include \n\n" @@ -2229,8 +2385,11 @@ void EmitPluginCode(const PluginData& Data, raw_ostream& O) { O << "namespace {\n\n"; - // Emit PopulateLanguageMap() function - // (a language map maps from file extensions to language names). + // Emit PreprocessOptionsLocal() function. + EmitPreprocessOptions(Records, Data.OptDescs, O); + + // Emit PopulateLanguageMapLocal() function + // (language map maps from file extensions to language names). EmitPopulateLanguageMap(Records, O); // Emit Tool classes. @@ -2241,7 +2400,7 @@ void EmitPluginCode(const PluginData& Data, raw_ostream& O) { // Emit Edge# classes. EmitEdgeClasses(Data.Edges, Data.OptDescs, O); - // Emit PopulateCompilationGraph() function. + // Emit PopulateCompilationGraphLocal() function. EmitPopulateCompilationGraph(Data.Edges, Data.ToolDescs, O); // Emit code for plugin registration.