From f915253dfcfbe772ee04872544a4f012c6f851be Mon Sep 17 00:00:00 2001 From: Mikhail Glushenkov Date: Sun, 7 Dec 2008 16:41:11 +0000 Subject: [PATCH] Use (actions) instead of option properties, support external options. Also includes a major refactoring. See documentation for more information. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@60656 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/CompilerDriver/Action.h | 16 +- include/llvm/CompilerDriver/Common.td | 30 +- include/llvm/CompilerDriver/Tool.h | 20 +- include/llvm/CompilerDriver/Tools.td | 85 +- tools/llvmc/driver/CompilationGraph.cpp | 82 +- tools/llvmc/driver/Tool.cpp | 74 + utils/TableGen/LLVMCConfigurationEmitter.cpp | 1896 +++++++++--------- 7 files changed, 1088 insertions(+), 1115 deletions(-) create mode 100644 tools/llvmc/driver/Tool.cpp diff --git a/include/llvm/CompilerDriver/Action.h b/include/llvm/CompilerDriver/Action.h index c3b31d4e558..9cac0aa45b4 100644 --- a/include/llvm/CompilerDriver/Action.h +++ b/include/llvm/CompilerDriver/Action.h @@ -27,14 +27,22 @@ namespace llvmc { std::string Command_; /// Args_ - Command arguments. Stdout redirection ("> file") is allowed. std::vector Args_; + /// StopCompilation_ - Should we stop compilation after executing + /// this action? + bool StopCompilation_; + /// OutFile_ - The output file name. + std::string OutFile_; + public: - Action() {} - Action (const std::string& C, const StrVector& A) - : Command_(C), Args_(A) + Action (const std::string& C, const StrVector& A, + bool S, const std::string& O) + : Command_(C), Args_(A), StopCompilation_(S), OutFile_(O) {} /// Execute - Executes the represented action. - int Execute() const; + int Execute () const; + bool StopCompilation () const { return StopCompilation_; } + const std::string& OutFile() { return OutFile_; } }; } diff --git a/include/llvm/CompilerDriver/Common.td b/include/llvm/CompilerDriver/Common.td index 109a7e5eda1..c5109560048 100644 --- a/include/llvm/CompilerDriver/Common.td +++ b/include/llvm/CompilerDriver/Common.td @@ -15,7 +15,7 @@ class Tool l> { list properties = l; } -// Possible Tool properties +// Possible Tool properties. def in_language; def out_language; @@ -23,8 +23,9 @@ def output_suffix; def cmd_line; def join; def sink; +def actions; -// Possible option types +// Possible option types. def alias_option; def switch_option; @@ -33,17 +34,16 @@ def parameter_list_option; def prefix_option; def prefix_list_option; -// Possible option properties +def extern_switch; +def extern_parameter; +def extern_list; + +// Possible option properties. -def append_cmd; -def forward; -def forward_as; def help; def hidden; def really_hidden; def required; -def stop_compilation; -def unpack_values; // Empty DAG marker. def empty; @@ -51,6 +51,10 @@ def empty; // The 'case' construct. def case; +// Boolean operators. +def and; +def or; + // Primitive tests. def switch_on; def parameter_equals; @@ -59,9 +63,13 @@ def input_languages_contain; def not_empty; def default; -// Boolean operators. -def and; -def or; +// Possible actions. + +def append_cmd; +def forward; +def forward_as; +def stop_compilation; +def unpack_values; // Increase/decrease the edge weight. def inc_weight; diff --git a/include/llvm/CompilerDriver/Tool.h b/include/llvm/CompilerDriver/Tool.h index c422a439923..8e8f6e73c25 100644 --- a/include/llvm/CompilerDriver/Tool.h +++ b/include/llvm/CompilerDriver/Tool.h @@ -36,22 +36,29 @@ namespace llvmc { virtual ~Tool() {} virtual Action GenerateAction (const PathVector& inFiles, - const llvm::sys::Path& outFile, + bool HasChildren, + const llvm::sys::Path& TempDir, const InputLanguagesSet& InLangs, const LanguageMap& LangMap) const = 0; virtual Action GenerateAction (const llvm::sys::Path& inFile, - const llvm::sys::Path& outFile, + bool HasChildren, + const llvm::sys::Path& TempDir, const InputLanguagesSet& InLangs, const LanguageMap& LangMap) const = 0; virtual const char* Name() const = 0; virtual const char** InputLanguages() const = 0; virtual const char* OutputLanguage() const = 0; - virtual const char* OutputSuffix() const = 0; - virtual bool IsLast() const = 0; virtual bool IsJoin() const = 0; + + protected: + /// OutFileName - Generate the output file name. + llvm::sys::Path OutFilename(const llvm::sys::Path& In, + const llvm::sys::Path& TempDir, + bool StopCompilation, + const char* OutputSuffix) const; }; /// JoinTool - A Tool that has an associated input file list. @@ -61,10 +68,11 @@ namespace llvmc { void ClearJoinList() { JoinList_.clear(); } bool JoinListEmpty() const { return JoinList_.empty(); } - Action GenerateAction(const llvm::sys::Path& outFile, + Action GenerateAction(bool HasChildren, + const llvm::sys::Path& TempDir, const InputLanguagesSet& InLangs, const LanguageMap& LangMap) const { - return GenerateAction(JoinList_, outFile, InLangs, LangMap); + return GenerateAction(JoinList_, HasChildren, TempDir, InLangs, LangMap); } // We shouldn't shadow base class's version of GenerateAction. using Tool::GenerateAction; diff --git a/include/llvm/CompilerDriver/Tools.td b/include/llvm/CompilerDriver/Tools.td index e701b476494..d8248acae24 100644 --- a/include/llvm/CompilerDriver/Tools.td +++ b/include/llvm/CompilerDriver/Tools.td @@ -11,6 +11,35 @@ // //===----------------------------------------------------------------------===// +def OptList : OptionList<[ + (switch_option "emit-llvm", + (help "Emit LLVM bitcode files instead of native object files")), + (switch_option "E", + (help "Stop after the preprocessing stage, do not run the compiler")), + (switch_option "fsyntax-only", + (help "Stop after checking the input for syntax errors")), + (switch_option "opt", + (help "Enable opt")), + (switch_option "S", + (help "Stop after compilation, do not assemble")), + (switch_option "c", + (help "Compile and assemble, but do not link")), + (parameter_option "linker", + (help "Choose linker (possible values: gcc, g++)")), + (parameter_list_option "include", + (help "Include the named file prior to preprocessing")), + (prefix_list_option "I", + (help "Add a directory to include path")), + (prefix_list_option "Wa,", + (help "Pass options to assembler")), + (prefix_list_option "L", + (help "Add a directory to link path")), + (prefix_list_option "l", + (help "Search a library when linking")), + (prefix_list_option "Wl,", + (help "Pass options to linker")) +]>; + class llvm_gcc_based : Tool< [(in_language in_lang), (out_language "llvm-bitcode"), @@ -25,16 +54,13 @@ class llvm_gcc_based : Tool< !strconcat(cmd_prefix, " -fsyntax-only $INFILE"), (default), !strconcat(cmd_prefix, " -c $INFILE -o $OUTFILE -emit-llvm"))), - (switch_option "emit-llvm", (stop_compilation), - (help "Emit LLVM intermediate files instead of native object files")), - (switch_option "E", (stop_compilation), - (help "Stop after the preprocessing stage, do not run the compiler")), - (switch_option "fsyntax-only", (stop_compilation), - (help "Stop after checking the input for syntax errors")), - (parameter_list_option "include", (forward), - (help "Include the named file prior to preprocessing")), - (prefix_list_option "I", (forward), - (help "Add a directory to include path")), + (actions + (case + (switch_on "emit-llvm"), (stop_compilation), + (switch_on "E"), (stop_compilation), + (switch_on "fsyntax-only"), (stop_compilation), + (not_empty "include"), (forward "include"), + (not_empty "I"), (forward "include"))), (sink) ]>; @@ -46,7 +72,6 @@ def llvm_gcc_mxx : llvm_gcc_based<"llvm-gcc -x objective-c++", "objective-c++">; def opt : Tool< [(in_language "llvm-bitcode"), (out_language "llvm-bitcode"), - (switch_option "opt", (help "Enable opt")), (output_suffix "bc"), (cmd_line "opt -f $INFILE -o $OUTFILE") ]>; @@ -62,8 +87,8 @@ def llc : Tool< [(in_language "llvm-bitcode"), (out_language "assembler"), (output_suffix "s"), - (switch_option "S", (stop_compilation), - (help "Stop after compilation, do not assemble")), + (actions (case + (switch_on "S"), (stop_compilation))), (cmd_line "llc -f $INFILE -o $OUTFILE") ]>; @@ -72,36 +97,28 @@ def llvm_gcc_assembler : Tool< (out_language "object-code"), (output_suffix "o"), (cmd_line "llvm-gcc -c -x assembler $INFILE -o $OUTFILE"), - (switch_option "c", (stop_compilation), - (help "Compile and assemble, but do not link")), - (prefix_list_option "Wa,", (unpack_values), (help "Pass options to assembler")) + (actions (case + (switch_on "c"), (stop_compilation), + (not_empty "Wa,"), (unpack_values "Wa,"))) ]>; -// Default linker -def llvm_gcc_linker : Tool< +// Base class for linkers +class llvm_gcc_based_linker : Tool< [(in_language "object-code"), (out_language "executable"), (output_suffix "out"), - (cmd_line "llvm-gcc $INFILE -o $OUTFILE"), + (cmd_line !strconcat(cmd_prefix, " $INFILE -o $OUTFILE")), (join), - (prefix_list_option "L", (forward), (help "Add a directory to link path")), - (prefix_list_option "l", (forward), (help "Search a library when linking")), - (prefix_list_option "Wl,", (unpack_values), (help "Pass options to linker")) + (actions (case + (not_empty "L"), (forward "L"), + (not_empty "l"), (forward "l"), + (not_empty "Wl,"), (unpack_values "Wl,"))) ]>; +// Default linker +def llvm_gcc_linker : llvm_gcc_based_linker<"llvm-gcc">; // Alternative linker for C++ -def llvm_gcc_cpp_linker : Tool< -[(in_language "object-code"), - (out_language "executable"), - (output_suffix "out"), - (cmd_line "llvm-g++ $INFILE -o $OUTFILE"), - (join), - (parameter_option "linker", - (help "Choose linker (possible values: gcc, g++)")), - (prefix_list_option "L", (forward)), - (prefix_list_option "l", (forward)), - (prefix_list_option "Wl,", (unpack_values)) -]>; +def llvm_gcc_cpp_linker : llvm_gcc_based_linker<"llvm-g++">; // Language map diff --git a/tools/llvmc/driver/CompilationGraph.cpp b/tools/llvmc/driver/CompilationGraph.cpp index a4fda4834fc..6d7faa3dece 100644 --- a/tools/llvmc/driver/CompilationGraph.cpp +++ b/tools/llvmc/driver/CompilationGraph.cpp @@ -29,7 +29,6 @@ using namespace llvm; using namespace llvmc; extern cl::list InputFilenames; -extern cl::opt OutputFilename; extern cl::list Languages; namespace llvmc { @@ -143,30 +142,6 @@ void CompilationGraph::insertEdge(const std::string& A, Edge* Edg) { B.IncrInEdges(); } -namespace { - sys::Path MakeTempFile(const sys::Path& TempDir, const std::string& BaseName, - const std::string& Suffix) { - sys::Path Out; - - // Make sure we don't end up with path names like '/file.o' if the - // TempDir is empty. - if (TempDir.empty()) { - Out.set(BaseName); - } - else { - Out = TempDir; - Out.appendComponent(BaseName); - } - Out.appendSuffix(Suffix); - // NOTE: makeUnique always *creates* a unique temporary file, - // which is good, since there will be no races. However, some - // tools do not like it when the output file already exists, so - // they have to be placated with -f or something like that. - Out.makeUnique(true, NULL); - return Out; - } -} - // Pass input file through the chain until we bump into a Join node or // a node that says that it is the last. void CompilationGraph::PassThroughGraph (const sys::Path& InFile, @@ -174,12 +149,10 @@ void CompilationGraph::PassThroughGraph (const sys::Path& InFile, const InputLanguagesSet& InLangs, const sys::Path& TempDir, const LanguageMap& LangMap) const { - bool Last = false; sys::Path In = InFile; const Node* CurNode = StartNode; - while(!Last) { - sys::Path Out; + while(true) { Tool* CurTool = CurNode->ToolPtr.getPtr(); if (CurTool->IsJoin()) { @@ -188,32 +161,19 @@ void CompilationGraph::PassThroughGraph (const sys::Path& InFile, break; } - // Since toolchains do not have to end with a Join node, we should - // check if this Node is the last. - if (!CurNode->HasChildren() || CurTool->IsLast()) { - if (!OutputFilename.empty()) { - Out.set(OutputFilename); - } - else { - Out.set(In.getBasename()); - Out.appendSuffix(CurTool->OutputSuffix()); - } - Last = true; - } - else { - Out = MakeTempFile(TempDir, In.getBasename(), CurTool->OutputSuffix()); - } + Action CurAction = CurTool->GenerateAction(In, CurNode->HasChildren(), + TempDir, InLangs, LangMap); - if (int ret = CurTool->GenerateAction(In, Out, InLangs, LangMap).Execute()) + if (int ret = CurAction.Execute()) throw error_code(ret); - if (Last) + if (CurAction.StopCompilation()) return; CurNode = &getNode(ChooseEdge(CurNode->OutEdges, InLangs, CurNode->Name())->ToolName()); - In = Out; Out.clear(); + In = CurAction.OutFile(); } } @@ -351,36 +311,24 @@ int CompilationGraph::Build (const sys::Path& TempDir, sys::Path Out; const Node* CurNode = *B; JoinTool* JT = &dynamic_cast(*CurNode->ToolPtr.getPtr()); - bool IsLast = false; // Are there any files in the join list? if (JT->JoinListEmpty()) continue; - // Is this the last tool in the toolchain? - // NOTE: we can process several toolchains in parallel. - if (!CurNode->HasChildren() || JT->IsLast()) { - if (OutputFilename.empty()) { - Out.set("a"); - Out.appendSuffix(JT->OutputSuffix()); - } - else - Out.set(OutputFilename); - IsLast = true; - } - else { - Out = MakeTempFile(TempDir, "tmp", JT->OutputSuffix()); - } + Action CurAction = JT->GenerateAction(CurNode->HasChildren(), + TempDir, InLangs, LangMap); - if (int ret = JT->GenerateAction(Out, InLangs, LangMap).Execute()) + if (int ret = CurAction.Execute()) throw error_code(ret); - if (!IsLast) { - const Node* NextNode = - &getNode(ChooseEdge(CurNode->OutEdges, InLangs, - CurNode->Name())->ToolName()); + if (CurAction.StopCompilation()) + return 0; + + const Node* NextNode = + &getNode(ChooseEdge(CurNode->OutEdges, InLangs, + CurNode->Name())->ToolName()); PassThroughGraph(Out, NextNode, InLangs, TempDir, LangMap); - } } return 0; diff --git a/tools/llvmc/driver/Tool.cpp b/tools/llvmc/driver/Tool.cpp new file mode 100644 index 00000000000..886b26b5d71 --- /dev/null +++ b/tools/llvmc/driver/Tool.cpp @@ -0,0 +1,74 @@ +//===--- Tool.cpp - The LLVM Compiler Driver --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open +// Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tool base class - implementation details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CompilerDriver/Tool.h" + +#include "llvm/System/Path.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; +using namespace llvmc; + +extern cl::opt OutputFilename; + +namespace { + sys::Path MakeTempFile(const sys::Path& TempDir, const std::string& BaseName, + const std::string& Suffix) { + sys::Path Out; + + // Make sure we don't end up with path names like '/file.o' if the + // TempDir is empty. + if (TempDir.empty()) { + Out.set(BaseName); + } + else { + Out = TempDir; + Out.appendComponent(BaseName); + } + Out.appendSuffix(Suffix); + // NOTE: makeUnique always *creates* a unique temporary file, + // which is good, since there will be no races. However, some + // tools do not like it when the output file already exists, so + // they have to be placated with -f or something like that. + Out.makeUnique(true, NULL); + return Out; + } +} + +sys::Path Tool::OutFilename(const sys::Path& In, + const sys::Path& TempDir, + bool StopCompilation, + const char* OutputSuffix) const { + sys::Path Out; + + if (StopCompilation) { + if (!OutputFilename.empty()) { + Out.set(OutputFilename); + } + else if (IsJoin()) { + Out.set("a"); + Out.appendSuffix(OutputSuffix); + } + else { + Out.set(In.getBasename()); + Out.appendSuffix(OutputSuffix); + } + } + else { + if (IsJoin()) + Out = MakeTempFile(TempDir, "tmp", OutputSuffix); + else + Out = MakeTempFile(TempDir, In.getBasename(), OutputSuffix); + } + return Out; +} diff --git a/utils/TableGen/LLVMCConfigurationEmitter.cpp b/utils/TableGen/LLVMCConfigurationEmitter.cpp index 97ea3d68346..175de459259 100644 --- a/utils/TableGen/LLVMCConfigurationEmitter.cpp +++ b/utils/TableGen/LLVMCConfigurationEmitter.cpp @@ -45,7 +45,6 @@ typedef std::vector StrVector; const char * Indent1 = " "; const char * Indent2 = " "; const char * Indent3 = " "; -const char * Indent4 = " "; // Default help string. const char * DefaultHelpString = "NO HELP MESSAGE PROVIDED"; @@ -56,6 +55,13 @@ const char * SinkOptionName = "AutoGeneratedSinkOption"; //===----------------------------------------------------------------------===// /// Helper functions +/// Id - An 'identity' function object. +struct Id { + template + void operator()(const T&) const { + } +}; + int InitPtrToInt(const Init* ptr) { const IntInit& val = dynamic_cast(*ptr); return val.getValue(); @@ -89,426 +95,330 @@ bool isDagEmpty (const DagInit* d) { return d->getOperator()->getAsString() == "empty"; } +// EscapeVariableName - Escape commas and other symbols not allowed +// in the C++ variable names. Makes it possible to use options named +// like "Wa," (useful for prefix options). +std::string EscapeVariableName(const std::string& Var) { + std::string ret; + for (unsigned i = 0; i != Var.size(); ++i) { + char cur_char = Var[i]; + if (cur_char == ',') { + ret += "_comma_"; + } + else if (cur_char == '+') { + ret += "_plus_"; + } + else if (cur_char == '-') { + ret += "_dash_"; + } + else { + ret.push_back(cur_char); + } + } + return ret; +} + //===----------------------------------------------------------------------===// /// Back-end specific code -// A command-line option can have one of the following types: -// -// Alias - an alias for another option. -// -// Switch - a simple switch without arguments, e.g. -O2 -// -// Parameter - an option that takes one(and only one) argument, e.g. -o file, -// --output=file -// -// ParameterList - same as Parameter, but more than one occurence -// of the option is allowed, e.g. -lm -lpthread -// -// Prefix - argument is everything after the prefix, -// e.g. -Wa,-foo,-bar, -DNAME=VALUE -// -// PrefixList - same as Prefix, but more than one option occurence is -// allowed. +/// OptionType - One of six different option types. See the +/// documentation for detailed description of differences. +/// Extern* options are those that are defined in some other plugin. namespace OptionType { - enum OptionType { Alias, Switch, - Parameter, ParameterList, Prefix, PrefixList}; + enum OptionType { Alias, Switch, Parameter, ParameterList, + Prefix, PrefixList, + ExternSwitch, ExternParameter, ExternList }; + +bool IsList (OptionType t) { + return (t == ParameterList || t == PrefixList || t == ExternList); } -bool IsListOptionType (OptionType::OptionType t) { - return (t == OptionType::ParameterList || t == OptionType::PrefixList); +bool IsSwitch (OptionType t) { + return (t == Switch || t == ExternSwitch); +} + +bool IsParameter (OptionType t) { + return (t == Parameter || t == Prefix || t == ExternParameter); +} + +} + +OptionType::OptionType stringToOptionType(const std::string& T) { + if (T == "alias_option") + return OptionType::Alias; + else if (T == "switch_option") + return OptionType::Switch; + else if (T == "parameter_option") + return OptionType::Parameter; + else if (T == "parameter_list_option") + return OptionType::ParameterList; + else if (T == "prefix_option") + return OptionType::Prefix; + else if (T == "prefix_list_option") + return OptionType::PrefixList; + else if (T == "extern_switch") + return OptionType::ExternSwitch; + else if (T == "extern_parameter") + return OptionType::ExternParameter; + else if (T == "extern_list") + return OptionType::ExternList; + else + throw "Unknown option type: " + T + '!'; } -// Code duplication here is necessary because one option can affect -// several tools and those tools may have different actions associated -// with this option. GlobalOptionDescriptions are used to generate -// the option registration code, while ToolOptionDescriptions are used -// to generate tool-specific code. +namespace OptionDescriptionFlags { + enum OptionDescriptionFlags { Required = 0x1, Hidden = 0x2, + ReallyHidden = 0x4 }; +} -/// OptionDescription - Base class for option descriptions. +/// OptionDescription - Represents data contained in a single +/// OptionList entry. struct OptionDescription { OptionType::OptionType Type; std::string Name; + unsigned Flags; + std::string Help; OptionDescription(OptionType::OptionType t = OptionType::Switch, - const std::string& n = "") - : Type(t), Name(n) + const std::string& n = "", + const std::string& h = DefaultHelpString) + : Type(t), Name(n), Flags(0x0), Help(h) {} - const char* GenTypeDeclaration() const { - switch (Type) { - case OptionType::Alias: - return "cl::alias"; - case OptionType::PrefixList: - case OptionType::ParameterList: - return "cl::list"; - case OptionType::Switch: - return "cl::opt"; - case OptionType::Parameter: - case OptionType::Prefix: - default: - return "cl::opt"; - } - } + /// GenTypeDeclaration - Returns the C++ variable type of this + /// option. + const char* GenTypeDeclaration() const; - // Escape commas and other symbols not allowed in the C++ variable - // names. Makes it possible to use options with names like "Wa," - // (useful for prefix options). - std::string EscapeVariableName(const std::string& Var) const { - std::string ret; - for (unsigned i = 0; i != Var.size(); ++i) { - char cur_char = Var[i]; - if (cur_char == ',') { - ret += "_comma_"; - } - else if (cur_char == '+') { - ret += "_plus_"; - } - else if (cur_char == '-') { - ret += "_dash_"; - } - else { - ret.push_back(cur_char); - } - } - return ret; - } + /// GenVariableName - Returns the variable name used in the + /// generated C++ code. + std::string GenVariableName() const; - std::string GenVariableName() const { - const std::string& EscapedName = EscapeVariableName(Name); - switch (Type) { - case OptionType::Alias: - return "AutoGeneratedAlias" + EscapedName; - case OptionType::Switch: - return "AutoGeneratedSwitch" + EscapedName; - case OptionType::Prefix: - return "AutoGeneratedPrefix" + EscapedName; - case OptionType::PrefixList: - return "AutoGeneratedPrefixList" + EscapedName; - case OptionType::Parameter: - return "AutoGeneratedParameter" + EscapedName; - case OptionType::ParameterList: - default: - return "AutoGeneratedParameterList" + EscapedName; - } - } + /// Merge - Merge two option descriptions. + void Merge (const OptionDescription& other); + + // Misc convenient getters/setters. + + bool isAlias() const; + bool isExtern() const; + + bool isRequired() const; + void setRequired(); + bool isHidden() const; + void setHidden(); + + bool isReallyHidden() const; + void setReallyHidden(); }; -// Global option description. +void OptionDescription::Merge (const OptionDescription& other) +{ + if (other.Type != Type) + throw "Conflicting definitions for the option " + Name + "!"; -namespace GlobalOptionDescriptionFlags { - enum GlobalOptionDescriptionFlags { Required = 0x1, Hidden = 0x2, - ReallyHidden = 0x4 }; + if (Help == other.Help || Help == DefaultHelpString) + Help = other.Help; + else if (other.Help != DefaultHelpString) { + llvm::cerr << "Warning: several different help strings" + " defined for option " + Name + "\n"; + } + + Flags |= other.Flags; } -struct GlobalOptionDescription : public OptionDescription { - std::string Help; - unsigned Flags; +bool OptionDescription::isAlias() const { + return Type == OptionType::Alias; +} - // We need to provide a default constructor because - // StringMap can only store DefaultConstructible objects. - GlobalOptionDescription() : OptionDescription(), Flags(0) - {} +bool OptionDescription::isExtern() const { + return (Type == OptionType::ExternList || Type == OptionType::ExternParameter + || Type == OptionType::ExternSwitch); +} - GlobalOptionDescription (OptionType::OptionType t, const std::string& n, - const std::string& h = DefaultHelpString) - : OptionDescription(t, n), Help(h), Flags(0) - {} +bool OptionDescription::isRequired() const { + return Flags & OptionDescriptionFlags::Required; +} +void OptionDescription::setRequired() { + Flags |= OptionDescriptionFlags::Required; +} - bool isRequired() const { - return Flags & GlobalOptionDescriptionFlags::Required; - } - void setRequired() { - Flags |= GlobalOptionDescriptionFlags::Required; - } +bool OptionDescription::isHidden() const { + return Flags & OptionDescriptionFlags::Hidden; +} +void OptionDescription::setHidden() { + Flags |= OptionDescriptionFlags::Hidden; +} - bool isHidden() const { - return Flags & GlobalOptionDescriptionFlags::Hidden; - } - void setHidden() { - Flags |= GlobalOptionDescriptionFlags::Hidden; - } +bool OptionDescription::isReallyHidden() const { + return Flags & OptionDescriptionFlags::ReallyHidden; +} +void OptionDescription::setReallyHidden() { + Flags |= OptionDescriptionFlags::ReallyHidden; +} - bool isReallyHidden() const { - return Flags & GlobalOptionDescriptionFlags::ReallyHidden; - } - void setReallyHidden() { - Flags |= GlobalOptionDescriptionFlags::ReallyHidden; +const char* OptionDescription::GenTypeDeclaration() const { + switch (Type) { + case OptionType::Alias: + return "cl::alias"; + case OptionType::ExternList: + case OptionType::PrefixList: + case OptionType::ParameterList: + return "cl::list"; + case OptionType::Switch: + case OptionType::ExternSwitch: + return "cl::opt"; + case OptionType::ExternParameter: + case OptionType::Parameter: + case OptionType::Prefix: + default: + return "cl::opt"; } +} - /// Merge - Merge two option descriptions. - void Merge (const GlobalOptionDescription& other) - { - if (other.Type != Type) - throw "Conflicting definitions for the option " + Name + "!"; - - if (Help == other.Help || Help == DefaultHelpString) - Help = other.Help; - else if (other.Help != DefaultHelpString) { - llvm::cerr << "Warning: several different help strings" - " defined for option " + Name + "\n"; - } - - Flags |= other.Flags; +std::string OptionDescription::GenVariableName() const { + const std::string& EscapedName = EscapeVariableName(Name); + switch (Type) { + case OptionType::Alias: + return "AutoGeneratedAlias_" + EscapedName; + case OptionType::PrefixList: + case OptionType::ParameterList: + case OptionType::ExternList: + return "AutoGeneratedList_" + EscapedName; + case OptionType::ExternSwitch: + case OptionType::Switch: + return "AutoGeneratedSwitch_" + EscapedName; + case OptionType::ExternParameter: + case OptionType::Prefix: + case OptionType::Parameter: + default: + return "AutoGeneratedParameter_" + EscapedName; } -}; +} -/// GlobalOptionDescriptions - A GlobalOptionDescription array -/// together with some flags affecting generation of option -/// declarations. -struct GlobalOptionDescriptions { - typedef StringMap container_type; - typedef container_type::const_iterator const_iterator; +/// OptionDescriptions - An OptionDescription array plus some helper +/// functions. +class OptionDescriptions { + typedef StringMap container_type; - /// Descriptions - A list of GlobalOptionDescriptions. + /// Descriptions - A list of OptionDescriptions. container_type Descriptions; - /// HasSink - Should the emitter generate a "cl::sink" option? - bool HasSink; +public: /// FindOption - exception-throwing wrapper for find(). - const GlobalOptionDescription& FindOption(const std::string& OptName) const { - const_iterator I = Descriptions.find(OptName); - if (I != Descriptions.end()) - return I->second; - else - throw OptName + ": no such option!"; - } + const OptionDescription& FindOption(const std::string& OptName) const; - /// insertDescription - Insert new GlobalOptionDescription into - /// GlobalOptionDescriptions list - void insertDescription (const GlobalOptionDescription& o) - { - container_type::iterator I = Descriptions.find(o.Name); - if (I != Descriptions.end()) { - GlobalOptionDescription& D = I->second; - D.Merge(o); - } - else { - Descriptions[o.Name] = o; - } - } + /// insertDescription - Insert new OptionDescription into + /// OptionDescriptions list + void InsertDescription (const OptionDescription& o); // Support for STL-style iteration + typedef container_type::const_iterator const_iterator; const_iterator begin() const { return Descriptions.begin(); } const_iterator end() const { return Descriptions.end(); } }; - -// Tool-local option description. - -// Properties without arguments are implemented as flags. -namespace ToolOptionDescriptionFlags { - enum ToolOptionDescriptionFlags { StopCompilation = 0x1, - Forward = 0x2, UnpackValues = 0x4}; -} -namespace OptionPropertyType { - enum OptionPropertyType { AppendCmd, ForwardAs, OutputSuffix }; +const OptionDescription& +OptionDescriptions::FindOption(const std::string& OptName) const +{ + const_iterator I = Descriptions.find(OptName); + if (I != Descriptions.end()) + return I->second; + else + throw OptName + ": no such option!"; } -typedef std::pair -OptionProperty; -typedef SmallVector OptionPropertyList; - -struct ToolOptionDescription : public OptionDescription { - unsigned Flags; - OptionPropertyList Props; - - // StringMap can only store DefaultConstructible objects - ToolOptionDescription() : OptionDescription(), Flags(0) {} - - ToolOptionDescription (OptionType::OptionType t, const std::string& n) - : OptionDescription(t, n) - {} - - // Various boolean properties - bool isStopCompilation() const { - return Flags & ToolOptionDescriptionFlags::StopCompilation; - } - void setStopCompilation() { - Flags |= ToolOptionDescriptionFlags::StopCompilation; - } - - bool isForward() const { - return Flags & ToolOptionDescriptionFlags::Forward; +void OptionDescriptions::InsertDescription (const OptionDescription& o) +{ + container_type::iterator I = Descriptions.find(o.Name); + if (I != Descriptions.end()) { + OptionDescription& D = I->second; + D.Merge(o); } - void setForward() { - Flags |= ToolOptionDescriptionFlags::Forward; + else { + Descriptions[o.Name] = o; } +} - bool isUnpackValues() const { - return Flags & ToolOptionDescriptionFlags::UnpackValues; - } - void setUnpackValues() { - Flags |= ToolOptionDescriptionFlags::UnpackValues; - } +/// HandlerTable - A base class for function objects implemented as +/// 'tables of handlers'. +template +class HandlerTable { +protected: + // Implementation details. - void AddProperty (OptionPropertyType::OptionPropertyType t, - const std::string& val) - { - Props.push_back(std::make_pair(t, val)); - } -}; + /// Handler - + typedef void (T::* Handler) (const DagInit*); + /// HandlerMap - A map from property names to property handlers + typedef StringMap HandlerMap; -typedef StringMap ToolOptionDescriptions; + static HandlerMap Handlers_; + static bool staticMembersInitialized_; -// Tool information record + T* childPtr; +public: -namespace ToolFlags { - enum ToolFlags { Join = 0x1, Sink = 0x2 }; -} + HandlerTable(T* cp) : childPtr(cp) + {} -struct ToolProperties : public RefCountedBase { - std::string Name; - Init* CmdLine; - StrVector InLanguage; - std::string OutLanguage; - std::string OutputSuffix; - unsigned Flags; - ToolOptionDescriptions OptDescs; + /// operator() - Just forwards to the corresponding property + /// handler. + void operator() (Init* i) { + const DagInit& property = InitPtrToDag(i); + const std::string& property_name = property.getOperator()->getAsString(); + typename HandlerMap::iterator method = Handlers_.find(property_name); - // Various boolean properties - void setSink() { Flags |= ToolFlags::Sink; } - bool isSink() const { return Flags & ToolFlags::Sink; } - void setJoin() { Flags |= ToolFlags::Join; } - bool isJoin() const { return Flags & ToolFlags::Join; } + if (method != Handlers_.end()) { + Handler h = method->second; + (childPtr->*h)(&property); + } + else { + throw "No handler found for property " + property_name + "!"; + } + } - // Default ctor here is needed because StringMap can only store - // DefaultConstructible objects - ToolProperties() : CmdLine(0), Flags(0) {} - ToolProperties (const std::string& n) : Name(n), CmdLine(0), Flags(0) {} + void AddHandler(const char* Property, Handler Handl) { + Handlers_[Property] = Handl; + } }; - -/// ToolPropertiesList - A list of Tool information records -/// IntrusiveRefCntPtrs are used here because StringMap has no copy -/// constructor (and we want to avoid copying ToolProperties anyway). -typedef std::vector > ToolPropertiesList; +template typename HandlerTable::HandlerMap +HandlerTable::Handlers_; +template bool HandlerTable::staticMembersInitialized_ = false; -/// CollectOptionProperties - Function object for iterating over a -/// list (usually, a DAG) of option property records. -class CollectOptionProperties { +/// CollectOptionProperties - Function object for iterating over an +/// option property list. +class CollectOptionProperties : public HandlerTable { private: - // Implementation details. - - /// OptionPropertyHandler - a function that extracts information - /// about a given option property from its DAG representation. - typedef void (CollectOptionProperties::* OptionPropertyHandler) - (const DagInit*); - /// OptionPropertyHandlerMap - A map from option property names to - /// option property handlers - typedef StringMap OptionPropertyHandlerMap; - - static OptionPropertyHandlerMap optionPropertyHandlers_; - static bool staticMembersInitialized_; - - /// This is where the information is stored - - /// toolProps_ - Properties of the current Tool. - ToolProperties* toolProps_; - /// optDescs_ - OptionDescriptions table (used to register options - /// globally). - GlobalOptionDescription& optDesc_; + /// optDescs_ - OptionDescriptions table. This is where the + /// information is stored. + OptionDescription& optDesc_; public: - explicit CollectOptionProperties(ToolProperties* TP, - GlobalOptionDescription& OD) - : toolProps_(TP), optDesc_(OD) + explicit CollectOptionProperties(OptionDescription& OD) + : HandlerTable(this), optDesc_(OD) { if (!staticMembersInitialized_) { - optionPropertyHandlers_["append_cmd"] = - &CollectOptionProperties::onAppendCmd; - optionPropertyHandlers_["forward"] = - &CollectOptionProperties::onForward; - optionPropertyHandlers_["forward_as"] = - &CollectOptionProperties::onForwardAs; - optionPropertyHandlers_["help"] = - &CollectOptionProperties::onHelp; - optionPropertyHandlers_["hidden"] = - &CollectOptionProperties::onHidden; - optionPropertyHandlers_["output_suffix"] = - &CollectOptionProperties::onOutputSuffix; - optionPropertyHandlers_["really_hidden"] = - &CollectOptionProperties::onReallyHidden; - optionPropertyHandlers_["required"] = - &CollectOptionProperties::onRequired; - optionPropertyHandlers_["stop_compilation"] = - &CollectOptionProperties::onStopCompilation; - optionPropertyHandlers_["unpack_values"] = - &CollectOptionProperties::onUnpackValues; + AddHandler("help", &CollectOptionProperties::onHelp); + AddHandler("hidden", &CollectOptionProperties::onHidden); + AddHandler("really_hidden", &CollectOptionProperties::onReallyHidden); + AddHandler("required", &CollectOptionProperties::onRequired); staticMembersInitialized_ = true; } } - /// operator() - Gets called for every option property; Just forwards - /// to the corresponding property handler. - void operator() (Init* i) { - const DagInit& option_property = InitPtrToDag(i); - const std::string& option_property_name - = option_property.getOperator()->getAsString(); - OptionPropertyHandlerMap::iterator method - = optionPropertyHandlers_.find(option_property_name); - - if (method != optionPropertyHandlers_.end()) { - OptionPropertyHandler h = method->second; - (this->*h)(&option_property); - } - else { - throw "Unknown option property: " + option_property_name + "!"; - } - } - private: /// Option property handlers -- - /// Methods that handle properties that are common for all types of - /// options (like append_cmd, stop_compilation) - - void onAppendCmd (const DagInit* d) { - checkNumberOfArguments(d, 1); - checkToolProps(d); - const std::string& cmd = InitPtrToString(d->getArg(0)); - - toolProps_->OptDescs[optDesc_.Name]. - AddProperty(OptionPropertyType::AppendCmd, cmd); - } - - void onOutputSuffix (const DagInit* d) { - checkNumberOfArguments(d, 1); - checkToolProps(d); - const std::string& suf = InitPtrToString(d->getArg(0)); - - if (toolProps_->OptDescs[optDesc_.Name].Type != OptionType::Switch) - throw "Option " + optDesc_.Name - + " can't have 'output_suffix' property since it isn't a switch!"; - - toolProps_->OptDescs[optDesc_.Name].AddProperty - (OptionPropertyType::OutputSuffix, suf); - } - - void onForward (const DagInit* d) { - checkNumberOfArguments(d, 0); - checkToolProps(d); - toolProps_->OptDescs[optDesc_.Name].setForward(); - } - - void onForwardAs (const DagInit* d) { - checkNumberOfArguments(d, 1); - checkToolProps(d); - const std::string& cmd = InitPtrToString(d->getArg(0)); - - toolProps_->OptDescs[optDesc_.Name]. - AddProperty(OptionPropertyType::ForwardAs, cmd); - } + /// Methods that handle option properties such as (help) or (hidden). void onHelp (const DagInit* d) { checkNumberOfArguments(d, 1); const std::string& help_message = InitPtrToString(d->getArg(0)); - optDesc_.Help = help_message; } @@ -530,20 +440,6 @@ private: optDesc_.setRequired(); } - void onStopCompilation (const DagInit* d) { - checkNumberOfArguments(d, 0); - checkToolProps(d); - if (optDesc_.Type != OptionType::Switch) - throw std::string("Only options of type Switch can stop compilation!"); - toolProps_->OptDescs[optDesc_.Name].setStopCompilation(); - } - - void onUnpackValues (const DagInit* d) { - checkNumberOfArguments(d, 0); - checkToolProps(d); - toolProps_->OptDescs[optDesc_.Name].setUnpackValues(); - } - // Helper functions /// checkToolProps - Throw an error if toolProps_ == 0. @@ -555,146 +451,128 @@ private: }; -CollectOptionProperties::OptionPropertyHandlerMap -CollectOptionProperties::optionPropertyHandlers_; - -bool CollectOptionProperties::staticMembersInitialized_ = false; - - -/// processOptionProperties - Go through the list of option -/// properties and call a corresponding handler for each. -void processOptionProperties (const DagInit* d, ToolProperties* t, - GlobalOptionDescription& o) { - checkNumberOfArguments(d, 2); - DagInit::const_arg_iterator B = d->arg_begin(); - // Skip the first argument: it's always the option name. - ++B; - std::for_each(B, d->arg_end(), CollectOptionProperties(t, o)); -} - -/// AddOption - A function object wrapper for -/// processOptionProperties. Used by CollectProperties and -/// CollectPropertiesFromOptionList. +/// AddOption - A function object that is applied to every option +/// description. Used by CollectOptionDescriptions. class AddOption { private: - GlobalOptionDescriptions& OptDescs_; - ToolProperties* ToolProps_; + OptionDescriptions& OptDescs_; public: - explicit AddOption(GlobalOptionDescriptions& OD, ToolProperties* TP = 0) - : OptDescs_(OD), ToolProps_(TP) + explicit AddOption(OptionDescriptions& OD) : OptDescs_(OD) {} void operator()(const Init* i) { const DagInit& d = InitPtrToDag(i); - checkNumberOfArguments(&d, 2); + checkNumberOfArguments(&d, 1); const OptionType::OptionType Type = - getOptionType(d.getOperator()->getAsString()); + stringToOptionType(d.getOperator()->getAsString()); const std::string& Name = InitPtrToString(d.getArg(0)); - GlobalOptionDescription OD(Type, Name); - if (Type != OptionType::Alias) { - processOptionProperties(&d, ToolProps_, OD); - if (ToolProps_) { - ToolProps_->OptDescs[Name].Type = Type; - ToolProps_->OptDescs[Name].Name = Name; - } - } - else { + OptionDescription OD(Type, Name); + + if (!OD.isExtern()) + checkNumberOfArguments(&d, 2); + + if (OD.isAlias()) { + // Aliases store the aliased option name in the 'Help' field. OD.Help = InitPtrToString(d.getArg(1)); } - OptDescs_.insertDescription(OD); + else if (!OD.isExtern()) { + processOptionProperties(&d, OD); + } + OptDescs_.InsertDescription(OD); } private: - OptionType::OptionType getOptionType(const std::string& T) const { - if (T == "alias_option") - return OptionType::Alias; - else if (T == "switch_option") - return OptionType::Switch; - else if (T == "parameter_option") - return OptionType::Parameter; - else if (T == "parameter_list_option") - return OptionType::ParameterList; - else if (T == "prefix_option") - return OptionType::Prefix; - else if (T == "prefix_list_option") - return OptionType::PrefixList; - else - throw "Unknown option type: " + T + '!'; + /// processOptionProperties - Go through the list of option + /// properties and call a corresponding handler for each. + static void processOptionProperties (const DagInit* d, OptionDescription& o) { + checkNumberOfArguments(d, 2); + DagInit::const_arg_iterator B = d->arg_begin(); + // Skip the first argument: it's always the option name. + ++B; + std::for_each(B, d->arg_end(), CollectOptionProperties(o)); } + }; +/// CollectOptionDescriptions - Collects option properties from all +/// OptionLists. +void CollectOptionDescriptions (RecordVector::const_iterator B, + RecordVector::const_iterator E, + OptionDescriptions& OptDescs) +{ + // For every OptionList: + for (; B!=E; ++B) { + RecordVector::value_type T = *B; + // Throws an exception if the value does not exist. + ListInit* PropList = T->getValueAsListInit("options"); -/// CollectProperties - Function object for iterating over a list of -/// tool property records. -class CollectProperties { -private: + // For every option description in this list: + // collect the information and + std::for_each(PropList->begin(), PropList->end(), AddOption(OptDescs)); + } +} - // Implementation details +// Tool information record - /// PropertyHandler - a function that extracts information - /// about a given tool property from its DAG representation - typedef void (CollectProperties::*PropertyHandler)(const DagInit*); +namespace ToolFlags { + enum ToolFlags { Join = 0x1, Sink = 0x2 }; +} - /// PropertyHandlerMap - A map from property names to property - /// handlers. - typedef StringMap PropertyHandlerMap; +struct ToolDescription : public RefCountedBase { + std::string Name; + Init* CmdLine; + Init* Actions; + StrVector InLanguage; + std::string OutLanguage; + std::string OutputSuffix; + unsigned Flags; - // Static maps from strings to CollectProperties methods("handlers") - static PropertyHandlerMap propertyHandlers_; - static bool staticMembersInitialized_; + // Various boolean properties + void setSink() { Flags |= ToolFlags::Sink; } + bool isSink() const { return Flags & ToolFlags::Sink; } + void setJoin() { Flags |= ToolFlags::Join; } + bool isJoin() const { return Flags & ToolFlags::Join; } + // Default ctor here is needed because StringMap can only store + // DefaultConstructible objects + ToolDescription() : CmdLine(0), Actions(0), Flags(0) {} + ToolDescription (const std::string& n) + : Name(n), CmdLine(0), Actions(0), Flags(0) + {} +}; - /// This is where the information is stored +/// ToolDescriptions - A list of Tool information records. +typedef std::vector > ToolDescriptions; - /// toolProps_ - Properties of the current Tool. - ToolProperties& toolProps_; - /// optDescs_ - OptionDescriptions table (used to register options - /// globally). - GlobalOptionDescriptions& optDescs_; + +/// CollectToolProperties - Function object for iterating over a list of +/// tool property records. +class CollectToolProperties : public HandlerTable { +private: + + /// toolDesc_ - Properties of the current Tool. This is where the + /// information is stored. + ToolDescription& toolDesc_; public: - explicit CollectProperties (ToolProperties& p, GlobalOptionDescriptions& d) - : toolProps_(p), optDescs_(d) + explicit CollectToolProperties (ToolDescription& d) + : HandlerTable(this) , toolDesc_(d) { if (!staticMembersInitialized_) { - propertyHandlers_["cmd_line"] = &CollectProperties::onCmdLine; - propertyHandlers_["in_language"] = &CollectProperties::onInLanguage; - propertyHandlers_["join"] = &CollectProperties::onJoin; - propertyHandlers_["out_language"] = &CollectProperties::onOutLanguage; - propertyHandlers_["output_suffix"] = &CollectProperties::onOutputSuffix; - propertyHandlers_["parameter_option"] - = &CollectProperties::addOption; - propertyHandlers_["parameter_list_option"] = - &CollectProperties::addOption; - propertyHandlers_["prefix_option"] = &CollectProperties::addOption; - propertyHandlers_["prefix_list_option"] = - &CollectProperties::addOption; - propertyHandlers_["sink"] = &CollectProperties::onSink; - propertyHandlers_["switch_option"] = &CollectProperties::addOption; - propertyHandlers_["alias_option"] = &CollectProperties::addOption; - - staticMembersInitialized_ = true; - } - } - /// operator() - Gets called for every tool property; Just forwards - /// to the corresponding property handler. - void operator() (Init* i) { - const DagInit& d = InitPtrToDag(i); - const std::string& property_name = d.getOperator()->getAsString(); - PropertyHandlerMap::iterator method - = propertyHandlers_.find(property_name); + AddHandler("actions", &CollectToolProperties::onActions); + AddHandler("cmd_line", &CollectToolProperties::onCmdLine); + AddHandler("in_language", &CollectToolProperties::onInLanguage); + AddHandler("join", &CollectToolProperties::onJoin); + AddHandler("out_language", &CollectToolProperties::onOutLanguage); + AddHandler("output_suffix", &CollectToolProperties::onOutputSuffix); + AddHandler("sink", &CollectToolProperties::onSink); - if (method != propertyHandlers_.end()) { - PropertyHandler h = method->second; - (this->*h)(&d); - } - else { - throw "Unknown tool property: " + property_name + "!"; + staticMembersInitialized_ = true; } } @@ -704,9 +582,14 @@ private: /// Functions that extract information about tool properties from /// DAG representation. + void onActions (const DagInit* d) { + checkNumberOfArguments(d, 1); + toolDesc_.Actions = d->getArg(0); + } + void onCmdLine (const DagInit* d) { checkNumberOfArguments(d, 1); - toolProps_.CmdLine = d->getArg(0); + toolDesc_.CmdLine = d->getArg(0); } void onInLanguage (const DagInit* d) { @@ -716,12 +599,12 @@ private: // Find out the argument's type. if (typeid(*arg) == typeid(StringInit)) { // It's a string. - toolProps_.InLanguage.push_back(InitPtrToString(arg)); + toolDesc_.InLanguage.push_back(InitPtrToString(arg)); } else { // It's a list. const ListInit& lst = InitPtrToList(arg); - StrVector& out = toolProps_.InLanguage; + StrVector& out = toolDesc_.InLanguage; // Copy strings to the output vector. for (ListInit::const_iterator B = lst.begin(), E = lst.end(); @@ -738,46 +621,33 @@ private: void onJoin (const DagInit* d) { checkNumberOfArguments(d, 0); - toolProps_.setJoin(); + toolDesc_.setJoin(); } void onOutLanguage (const DagInit* d) { checkNumberOfArguments(d, 1); - toolProps_.OutLanguage = InitPtrToString(d->getArg(0)); + toolDesc_.OutLanguage = InitPtrToString(d->getArg(0)); } void onOutputSuffix (const DagInit* d) { checkNumberOfArguments(d, 1); - toolProps_.OutputSuffix = InitPtrToString(d->getArg(0)); + toolDesc_.OutputSuffix = InitPtrToString(d->getArg(0)); } void onSink (const DagInit* d) { checkNumberOfArguments(d, 0); - optDescs_.HasSink = true; - toolProps_.setSink(); - } - - // Just forwards to the AddOption function object. Somewhat - // non-optimal, but avoids code duplication. - void addOption (const DagInit* d) { - checkNumberOfArguments(d, 2); - AddOption(optDescs_, &toolProps_)(d); + toolDesc_.setSink(); } }; -// Defintions of static members of CollectProperties. -CollectProperties::PropertyHandlerMap CollectProperties::propertyHandlers_; -bool CollectProperties::staticMembersInitialized_ = false; - -/// CollectToolProperties - Gather information about tool properties +/// CollectToolDescriptions - Gather information about tool properties /// from the parsed TableGen data (basically a wrapper for the -/// CollectProperties function object). -void CollectToolProperties (RecordVector::const_iterator B, - RecordVector::const_iterator E, - ToolPropertiesList& TPList, - GlobalOptionDescriptions& OptDescs) +/// CollectToolProperties function object). +void CollectToolDescriptions (RecordVector::const_iterator B, + RecordVector::const_iterator E, + ToolDescriptions& ToolDescs) { // Iterate over a properties list of every Tool definition for (;B!=E;++B) { @@ -785,55 +655,211 @@ void CollectToolProperties (RecordVector::const_iterator B, // Throws an exception if the value does not exist. ListInit* PropList = T->getValueAsListInit("properties"); - IntrusiveRefCntPtr - ToolProps(new ToolProperties(T->getName())); + IntrusiveRefCntPtr + ToolDesc(new ToolDescription(T->getName())); std::for_each(PropList->begin(), PropList->end(), - CollectProperties(*ToolProps, OptDescs)); - TPList.push_back(ToolProps); + CollectToolProperties(*ToolDesc)); + ToolDescs.push_back(ToolDesc); } } +/// FillInEdgeVector - Merge all compilation graph definitions into +/// one single edge list. +void FillInEdgeVector(RecordVector::const_iterator B, + RecordVector::const_iterator E, RecordVector& Out) { + for (; B != E; ++B) { + const ListInit* edges = (*B)->getValueAsListInit("edges"); -/// CollectPropertiesFromOptionLists - Gather information about -/// *global* option properties from all OptionLists. -void CollectPropertiesFromOptionLists (RecordVector::const_iterator B, - RecordVector::const_iterator E, - GlobalOptionDescriptions& OptDescs) -{ - // Iterate over a properties list of every Tool definition + for (unsigned i = 0; i < edges->size(); ++i) + Out.push_back(edges->getElementAsRecord(i)); + } +} + +/// CalculatePriority - Calculate the priority of this plugin. +int CalculatePriority(RecordVector::const_iterator B, + RecordVector::const_iterator E) { + int total = 0; for (; B!=E; ++B) { - RecordVector::value_type T = *B; - // Throws an exception if the value does not exist. - ListInit* PropList = T->getValueAsListInit("options"); + total += static_cast((*B)->getValueAsInt("priority")); + } + return total; +} - std::for_each(PropList->begin(), PropList->end(), AddOption(OptDescs)); +/// NotInGraph - Helper function object for FilterNotInGraph. +struct NotInGraph { +private: + const llvm::StringSet<>& ToolsInGraph_; + +public: + NotInGraph(const llvm::StringSet<>& ToolsInGraph) + : ToolsInGraph_(ToolsInGraph) + {} + + bool operator()(const IntrusiveRefCntPtr& x) { + return (ToolsInGraph_.count(x->Name) == 0); + } +}; + +/// FilterNotInGraph - Filter out from ToolDescs all Tools not +/// mentioned in the compilation graph definition. +void FilterNotInGraph (const RecordVector& EdgeVector, + ToolDescriptions& ToolDescs) { + + // List all tools mentioned in the graph. + llvm::StringSet<> ToolsInGraph; + + for (RecordVector::const_iterator B = EdgeVector.begin(), + E = EdgeVector.end(); B != E; ++B) { + + const Record* Edge = *B; + const std::string& A = Edge->getValueAsString("a"); + const std::string& B = Edge->getValueAsString("b"); + + if (A != "root") + ToolsInGraph.insert(A); + ToolsInGraph.insert(B); } + + // Filter ToolPropertiesList. + ToolDescriptions::iterator new_end = + std::remove_if(ToolDescs.begin(), ToolDescs.end(), + NotInGraph(ToolsInGraph)); + ToolDescs.erase(new_end, ToolDescs.end()); } +/// FillInToolToLang - Fills in two tables that map tool names to +/// (input, output) languages. Helper function used by TypecheckGraph(). +void FillInToolToLang (const ToolDescriptions& ToolDescs, + StringMap >& ToolToInLang, + StringMap& ToolToOutLang) { + for (ToolDescriptions::const_iterator B = ToolDescs.begin(), + E = ToolDescs.end(); B != E; ++B) { + const ToolDescription& D = *(*B); + for (StrVector::const_iterator B = D.InLanguage.begin(), + E = D.InLanguage.end(); B != E; ++B) + ToolToInLang[D.Name].insert(*B); + ToolToOutLang[D.Name] = D.OutLanguage; + } +} + +/// TypecheckGraph - Check that names for output and input languages +/// on all edges do match. This doesn't do much when the information +/// about the whole graph is not available (i.e. when compiling most +/// plugins). +void TypecheckGraph (const RecordVector& EdgeVector, + const ToolDescriptions& ToolDescs) { + StringMap > ToolToInLang; + StringMap ToolToOutLang; + + FillInToolToLang(ToolDescs, ToolToInLang, ToolToOutLang); + StringMap::iterator IAE = ToolToOutLang.end(); + StringMap >::iterator IBE = ToolToInLang.end(); + + for (RecordVector::const_iterator B = EdgeVector.begin(), + E = EdgeVector.end(); B != E; ++B) { + const Record* Edge = *B; + const std::string& A = Edge->getValueAsString("a"); + const std::string& B = Edge->getValueAsString("b"); + StringMap::iterator IA = ToolToOutLang.find(A); + StringMap >::iterator IB = ToolToInLang.find(B); + + if (A != "root") { + if (IA != IAE && IB != IBE && IB->second.count(IA->second) == 0) + throw "Edge " + A + "->" + B + + ": output->input language mismatch"; + } + + if (B == "root") + throw std::string("Edges back to the root are not allowed!"); + } +} + +/// WalkCase - Walks the 'case' expression DAG and invokes +/// TestCallback on every test, and StatementCallback on every +/// statement. Handles 'case' nesting, but not the 'and' and 'or' +/// combinators. +// TODO: Re-implement EmitCaseConstructHandler on top of this function? +template +void WalkCase(Init* Case, F1 TestCallback, F2 StatementCallback) { + const DagInit& d = InitPtrToDag(Case); + bool even = false; + for (DagInit::const_arg_iterator B = d.arg_begin(), E = d.arg_end(); + B != E; ++B) { + Init* arg = *B; + if (even && dynamic_cast(arg) + && static_cast(arg)->getOperator()->getAsString() == "case") + WalkCase(arg, TestCallback, StatementCallback); + else if (!even) + TestCallback(arg); + else + StatementCallback(arg); + even = !even; + } +} + +/// ExtractOptionNames - A helper function object used by +/// CheckForSuperfluousOptions() to walk the 'case' DAG. +class ExtractOptionNames { + llvm::StringSet<>& OptionNames_; +public: + ExtractOptionNames(llvm::StringSet<>& OptionNames) : OptionNames_(OptionNames) + {} + + void operator()(const Init* Statement) { + const DagInit& Stmt = InitPtrToDag(Statement); + const std::string& ActionName = Stmt.getOperator()->getAsString(); + if (ActionName == "forward" || ActionName == "forward_as" || + ActionName == "unpack_values" || ActionName == "switch_on" || + ActionName == "parameter_equals" || ActionName == "element_in_list" || + ActionName == "not_empty") { + checkNumberOfArguments(&Stmt, 1); + const std::string& Name = InitPtrToString(Stmt.getArg(0)); + OptionNames_.insert(Name); + } + else if (ActionName == "and" || ActionName == "or") { + for (unsigned i = 0, NumArgs = Stmt.getNumArgs(); i < NumArgs; ++i) { + this->operator()(Stmt.getArg(i)); + } + } + } +}; + /// CheckForSuperfluousOptions - Check that there are no side /// effect-free options (specified only in the OptionList). Otherwise, /// output a warning. -void CheckForSuperfluousOptions (const ToolPropertiesList& TPList, - const GlobalOptionDescriptions& OptDescs) { +void CheckForSuperfluousOptions (const RecordVector& Edges, + const ToolDescriptions& ToolDescs, + const OptionDescriptions& OptDescs) { llvm::StringSet<> nonSuperfluousOptions; - // Add all options mentioned in the TPList to the set of + // Add all options mentioned in the ToolDesc.Actions to the set of // non-superfluous options. - for (ToolPropertiesList::const_iterator B = TPList.begin(), - E = TPList.end(); B != E; ++B) { - const ToolProperties& TP = *(*B); - for (ToolOptionDescriptions::const_iterator B = TP.OptDescs.begin(), - E = TP.OptDescs.end(); B != E; ++B) { - nonSuperfluousOptions.insert(B->first()); - } + for (ToolDescriptions::const_iterator B = ToolDescs.begin(), + E = ToolDescs.end(); B != E; ++B) { + const ToolDescription& TD = *(*B); + ExtractOptionNames Callback(nonSuperfluousOptions); + if (TD.Actions) + WalkCase(TD.Actions, Callback, Callback); + } + + // Add all options mentioned in the 'case' clauses of the + // OptionalEdges of the compilation graph to the set of + // non-superfluous options. + for (RecordVector::const_iterator B = Edges.begin(), E = Edges.end(); + B != E; ++B) { + const Record* Edge = *B; + DagInit* Weight = Edge->getValueAsDag("weight"); + + if (!isDagEmpty(Weight)) + WalkCase(Weight, ExtractOptionNames(nonSuperfluousOptions), Id()); } // Check that all options in OptDescs belong to the set of // non-superfluous options. - for (GlobalOptionDescriptions::const_iterator B = OptDescs.begin(), + for (OptionDescriptions::const_iterator B = OptDescs.begin(), E = OptDescs.end(); B != E; ++B) { - const GlobalOptionDescription& Val = B->second; + const OptionDescription& Val = B->second; if (!nonSuperfluousOptions.count(Val.Name) && Val.Type != OptionType::Alias) llvm::cerr << "Warning: option '-" << Val.Name << "' has no effect! " @@ -845,14 +871,14 @@ void CheckForSuperfluousOptions (const ToolPropertiesList& TPList, /// EmitCaseConstructHandler. bool EmitCaseTest1Arg(const std::string& TestName, const DagInit& d, - const GlobalOptionDescriptions& OptDescs, + const OptionDescriptions& OptDescs, std::ostream& O) { checkNumberOfArguments(&d, 1); const std::string& OptName = InitPtrToString(d.getArg(0)); if (TestName == "switch_on") { - const GlobalOptionDescription& OptDesc = OptDescs.FindOption(OptName); - if (OptDesc.Type != OptionType::Switch) - throw OptName + ": incorrect option type!"; + const OptionDescription& OptDesc = OptDescs.FindOption(OptName); + if (!OptionType::IsSwitch(OptDesc.Type)) + throw OptName + ": incorrect option type - should be a switch!"; O << OptDesc.GenVariableName(); return true; } else if (TestName == "input_languages_contain") { @@ -871,9 +897,10 @@ bool EmitCaseTest1Arg(const std::string& TestName, return true; } else { - const GlobalOptionDescription& OptDesc = OptDescs.FindOption(OptName); - if (OptDesc.Type == OptionType::Switch) - throw OptName + ": incorrect option type!"; + const OptionDescription& OptDesc = OptDescs.FindOption(OptName); + if (OptionType::IsSwitch(OptDesc.Type)) + throw OptName + + ": incorrect option type - should be a list or parameter!"; O << '!' << OptDesc.GenVariableName() << ".empty()"; return true; } @@ -887,24 +914,22 @@ bool EmitCaseTest1Arg(const std::string& TestName, bool EmitCaseTest2Args(const std::string& TestName, const DagInit& d, const char* IndentLevel, - const GlobalOptionDescriptions& OptDescs, + const OptionDescriptions& OptDescs, std::ostream& O) { checkNumberOfArguments(&d, 2); const std::string& OptName = InitPtrToString(d.getArg(0)); const std::string& OptArg = InitPtrToString(d.getArg(1)); - const GlobalOptionDescription& OptDesc = OptDescs.FindOption(OptName); + const OptionDescription& OptDesc = OptDescs.FindOption(OptName); if (TestName == "parameter_equals") { - if (OptDesc.Type != OptionType::Parameter - && OptDesc.Type != OptionType::Prefix) - throw OptName + ": incorrect option type!"; + if (!OptionType::IsParameter(OptDesc.Type)) + throw OptName + ": incorrect option type - should be a parameter!"; O << OptDesc.GenVariableName() << " == \"" << OptArg << "\""; return true; } else if (TestName == "element_in_list") { - if (OptDesc.Type != OptionType::ParameterList - && OptDesc.Type != OptionType::PrefixList) - throw OptName + ": incorrect option type!"; + if (!OptionType::IsList(OptDesc.Type)) + throw OptName + ": incorrect option type - should be a list!"; const std::string& VarName = OptDesc.GenVariableName(); O << "std::find(" << VarName << ".begin(),\n" << IndentLevel << Indent1 << VarName << ".end(), \"" @@ -918,14 +943,14 @@ bool EmitCaseTest2Args(const std::string& TestName, // Forward declaration. // EmitLogicalOperationTest and EmitCaseTest are mutually recursive. void EmitCaseTest(const DagInit& d, const char* IndentLevel, - const GlobalOptionDescriptions& OptDescs, + const OptionDescriptions& OptDescs, std::ostream& O); /// EmitLogicalOperationTest - Helper function used by /// EmitCaseConstructHandler. void EmitLogicalOperationTest(const DagInit& d, const char* LogicOp, const char* IndentLevel, - const GlobalOptionDescriptions& OptDescs, + const OptionDescriptions& OptDescs, std::ostream& O) { O << '('; for (unsigned j = 0, NumArgs = d.getNumArgs(); j < NumArgs; ++j) { @@ -940,7 +965,7 @@ void EmitLogicalOperationTest(const DagInit& d, const char* LogicOp, /// EmitCaseTest - Helper function used by EmitCaseConstructHandler. void EmitCaseTest(const DagInit& d, const char* IndentLevel, - const GlobalOptionDescriptions& OptDescs, + const OptionDescriptions& OptDescs, std::ostream& O) { const std::string& TestName = d.getOperator()->getAsString(); @@ -961,11 +986,14 @@ void EmitCaseTest(const DagInit& d, const char* IndentLevel, // Callback's type is // void F(Init* Statement, const char* IndentLevel, std::ostream& O). template -void EmitCaseConstructHandler(const DagInit* d, const char* IndentLevel, +void EmitCaseConstructHandler(const Init* Dag, const char* IndentLevel, F Callback, bool EmitElseIf, - const GlobalOptionDescriptions& OptDescs, + const OptionDescriptions& OptDescs, std::ostream& O) { - assert(d->getOperator()->getAsString() == "case"); + const DagInit* d = &InitPtrToDag(Dag); + if (d->getOperator()->getAsString() != "case") + throw std::string("EmitCaseConstructHandler should be invoked" + " only on 'case' expressions!"); unsigned numArgs = d->getNumArgs(); if (d->getNumArgs() < 2) @@ -975,163 +1003,37 @@ void EmitCaseConstructHandler(const DagInit* d, const char* IndentLevel, for (unsigned i = 0; i != numArgs; ++i) { const DagInit& Test = InitPtrToDag(d->getArg(i)); - // Emit the test. - if (Test.getOperator()->getAsString() == "default") { - if (i+2 != numArgs) - throw std::string("The 'default' clause should be the last in the" - "'case' construct!"); - O << IndentLevel << "else {\n"; - } - else { - O << IndentLevel << ((i != 0 && EmitElseIf) ? "else if (" : "if ("); - EmitCaseTest(Test, IndentLevel, OptDescs, O); - O << ") {\n"; - } - - // Emit the corresponding statement. - ++i; - if (i == numArgs) - throw "Case construct handler: no corresponding action " - "found for the test " + Test.getAsString() + '!'; - - Init* arg = d->getArg(i); - if (dynamic_cast(arg) - && static_cast(arg)->getOperator()->getAsString() == "case") { - EmitCaseConstructHandler(static_cast(arg), - (std::string(IndentLevel) + Indent1).c_str(), - Callback, EmitElseIf, OptDescs, O); - } - else { - Callback(arg, IndentLevel, O); - } - O << IndentLevel << "}\n"; - } -} - -/// EmitForwardOptionPropertyHandlingCode - Helper function used to -/// implement EmitOptionPropertyHandlingCode(). Emits code for -/// handling the (forward) and (forward_as) option properties. -void EmitForwardOptionPropertyHandlingCode (const ToolOptionDescription& D, - const std::string& NewName, - std::ostream& O) { - const std::string& Name = NewName.empty() - ? ("-" + D.Name) - : NewName; - - switch (D.Type) { - case OptionType::Switch: - O << Indent3 << "vec.push_back(\"" << Name << "\");\n"; - break; - case OptionType::Parameter: - O << Indent3 << "vec.push_back(\"" << Name << "\");\n"; - O << Indent3 << "vec.push_back(" << D.GenVariableName() << ");\n"; - break; - case OptionType::Prefix: - O << Indent3 << "vec.push_back(\"" << Name << "\" + " - << D.GenVariableName() << ");\n"; - break; - case OptionType::PrefixList: - O << Indent3 << "for (" << D.GenTypeDeclaration() - << "::iterator B = " << D.GenVariableName() << ".begin(),\n" - << Indent3 << "E = " << D.GenVariableName() << ".end(); B != E; ++B)\n" - << Indent4 << "vec.push_back(\"" << Name << "\" + " - << "*B);\n"; - break; - case OptionType::ParameterList: - O << Indent3 << "for (" << D.GenTypeDeclaration() - << "::iterator B = " << D.GenVariableName() << ".begin(),\n" - << Indent3 << "E = " << D.GenVariableName() - << ".end() ; B != E; ++B) {\n" - << Indent4 << "vec.push_back(\"" << Name << "\");\n" - << Indent4 << "vec.push_back(*B);\n" - << Indent3 << "}\n"; - break; - case OptionType::Alias: - default: - throw std::string("Aliases are not allowed in tool option descriptions!"); - } -} - -// ToolOptionHasInterestingProperties - A helper function used by -// EmitOptionPropertyHandlingCode() that tells us whether we should -// emit any property handling code at all. -bool ToolOptionHasInterestingProperties(const ToolOptionDescription& D) { - bool ret = false; - for (OptionPropertyList::const_iterator B = D.Props.begin(), - E = D.Props.end(); B != E; ++B) { - const OptionProperty& OptProp = *B; - if (OptProp.first == OptionPropertyType::AppendCmd - || OptProp.first == OptionPropertyType::ForwardAs) - ret = true; + // Emit the test. + if (Test.getOperator()->getAsString() == "default") { + if (i+2 != numArgs) + throw std::string("The 'default' clause should be the last in the" + "'case' construct!"); + O << IndentLevel << "else {\n"; } - if (D.isForward() || D.isUnpackValues()) - ret = true; - return ret; -} - -/// EmitOptionPropertyHandlingCode - Helper function used by -/// EmitGenerateActionMethod(). Emits code that handles option -/// properties. -void EmitOptionPropertyHandlingCode (const ToolOptionDescription& D, - std::ostream& O) -{ - if (!ToolOptionHasInterestingProperties(D)) - return; - // Start of the if-clause. - O << Indent2 << "if ("; - if (D.Type == OptionType::Switch) - O << D.GenVariableName(); - else - O << '!' << D.GenVariableName() << ".empty()"; - - O <<") {\n"; - - // Handle option properties that take an argument. - for (OptionPropertyList::const_iterator B = D.Props.begin(), - E = D.Props.end(); B!=E; ++B) { - const OptionProperty& val = *B; - - switch (val.first) { - // (append_cmd cmd) property - case OptionPropertyType::AppendCmd: - O << Indent3 << "vec.push_back(\"" << val.second << "\");\n"; - break; - // (forward_as) property - case OptionPropertyType::ForwardAs: - EmitForwardOptionPropertyHandlingCode(D, val.second, O); - break; - // Other properties with argument - default: - break; + else { + O << IndentLevel << ((i != 0 && EmitElseIf) ? "else if (" : "if ("); + EmitCaseTest(Test, IndentLevel, OptDescs, O); + O << ") {\n"; } - } - - // Handle flags - // (forward) property - if (D.isForward()) - EmitForwardOptionPropertyHandlingCode(D, "", O); + // Emit the corresponding statement. + ++i; + if (i == numArgs) + throw "Case construct handler: no corresponding action " + "found for the test " + Test.getAsString() + '!'; - // (unpack_values) property - if (D.isUnpackValues()) { - if (IsListOptionType(D.Type)) { - O << Indent3 << "for (" << D.GenTypeDeclaration() - << "::iterator B = " << D.GenVariableName() << ".begin(),\n" - << Indent3 << "E = " << D.GenVariableName() - << ".end(); B != E; ++B)\n" - << Indent4 << "llvm::SplitString(*B, vec, \",\");\n"; - } - else if (D.Type == OptionType::Prefix || D.Type == OptionType::Parameter){ - O << Indent3 << "llvm::SplitString(" - << D.GenVariableName() << ", vec, \",\");\n"; + Init* arg = d->getArg(i); + const DagInit* nd = dynamic_cast(arg); + if (nd && (nd->getOperator()->getAsString() == "case")) { + // Handle the nested 'case'. + EmitCaseConstructHandler(nd, (std::string(IndentLevel) + Indent1).c_str(), + Callback, EmitElseIf, OptDescs, O); } else { - throw std::string("Switches can't have unpack_values property!"); + Callback(arg, (std::string(IndentLevel) + Indent1).c_str(), O); } + O << IndentLevel << "}\n"; } - - // End of the if-clause. - O << Indent2 << "}\n"; } /// SubstituteSpecialCommands - Perform string substitution for $CALL @@ -1172,7 +1074,7 @@ std::string SubstituteSpecialCommands(const std::string& cmd) { /// EmitCmdLineVecFill - Emit code that fills in the command line /// vector. Helper function used by EmitGenerateActionMethod(). void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName, - bool Version, const char* IndentLevel, + bool IsJoin, const char* IndentLevel, std::ostream& O) { StrVector StrVec; SplitString(InitPtrToString(CmdLine), StrVec); @@ -1186,7 +1088,7 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName, O << IndentLevel; if (cmd.at(0) == '$') { if (cmd == "$INFILE") { - if (Version) + if (IsJoin) O << "for (PathVector::const_iterator B = inFiles.begin()" << ", E = inFiles.end();\n" << IndentLevel << "B != E; ++B)\n" @@ -1195,7 +1097,7 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName, O << "vec.push_back(inFile.toString());\n"; } else if (cmd == "$OUTFILE") { - O << "vec.push_back(outFile.toString());\n"; + O << "vec.push_back(out_file);\n"; } else { O << "vec.push_back(" << SubstituteSpecialCommands(cmd); @@ -1216,151 +1118,236 @@ void EmitCmdLineVecFill(const Init* CmdLine, const std::string& ToolName, /// EmitCmdLineVecFill(). Used by EmitGenerateActionMethod() as an /// argument to EmitCaseConstructHandler(). class EmitCmdLineVecFillCallback { - bool Version; + bool IsJoin; const std::string& ToolName; public: - EmitCmdLineVecFillCallback(bool Ver, const std::string& TN) - : Version(Ver), ToolName(TN) {} + EmitCmdLineVecFillCallback(bool J, const std::string& TN) + : IsJoin(J), ToolName(TN) {} + + void operator()(const Init* Statement, const char* IndentLevel, + std::ostream& O) const + { + EmitCmdLineVecFill(Statement, ToolName, IsJoin, + IndentLevel, O); + } +}; + +/// EmitForwardOptionPropertyHandlingCode - Helper function used to +/// implement EmitActionHandler. Emits code for +/// handling the (forward) and (forward_as) option properties. +void EmitForwardOptionPropertyHandlingCode (const OptionDescription& D, + const char* Indent, + const std::string& NewName, + std::ostream& O) { + const std::string& Name = NewName.empty() + ? ("-" + D.Name) + : NewName; + + switch (D.Type) { + case OptionType::Switch: + case OptionType::ExternSwitch: + O << Indent << "vec.push_back(\"" << Name << "\");\n"; + break; + case OptionType::Parameter: + case OptionType::ExternParameter: + O << Indent << "vec.push_back(\"" << Name << "\");\n"; + O << Indent << "vec.push_back(" << D.GenVariableName() << ");\n"; + break; + case OptionType::Prefix: + O << Indent << "vec.push_back(\"" << Name << "\" + " + << D.GenVariableName() << ");\n"; + break; + case OptionType::PrefixList: + O << Indent << "for (" << D.GenTypeDeclaration() + << "::iterator B = " << D.GenVariableName() << ".begin(),\n" + << Indent << "E = " << D.GenVariableName() << ".end(); B != E; ++B)\n" + << Indent << Indent1 << "vec.push_back(\"" << Name << "\" + " + << "*B);\n"; + break; + case OptionType::ParameterList: + case OptionType::ExternList: + O << Indent << "for (" << D.GenTypeDeclaration() + << "::iterator B = " << D.GenVariableName() << ".begin(),\n" + << Indent << "E = " << D.GenVariableName() + << ".end() ; B != E; ++B) {\n" + << Indent << Indent1 << "vec.push_back(\"" << Name << "\");\n" + << Indent << Indent1 << "vec.push_back(*B);\n" + << Indent << "}\n"; + break; + case OptionType::Alias: + default: + throw std::string("Aliases are not allowed in tool option descriptions!"); + } +} + +/// EmitActionHandler - Emit code that handles actions. Used by +/// EmitGenerateActionMethod() as an argument to +/// EmitCaseConstructHandler(). +class EmitActionHandler { + const OptionDescriptions& OptDescs; + public: + EmitActionHandler(const OptionDescriptions& OD) + : OptDescs(OD) {} void operator()(const Init* Statement, const char* IndentLevel, std::ostream& O) const { - EmitCmdLineVecFill(Statement, ToolName, Version, - (std::string(IndentLevel) + Indent1).c_str(), O); + const DagInit& Dag = InitPtrToDag(Statement); + const std::string& ActionName = Dag.getOperator()->getAsString(); + + if (ActionName == "append_cmd") { + checkNumberOfArguments(&Dag, 1); + const std::string& Cmd = InitPtrToString(Dag.getArg(0)); + O << IndentLevel << "vec.push_back(\"" << Cmd << "\");\n"; + } + else if (ActionName == "forward") { + checkNumberOfArguments(&Dag, 1); + const std::string& Name = InitPtrToString(Dag.getArg(0)); + EmitForwardOptionPropertyHandlingCode(OptDescs.FindOption(Name), + IndentLevel, "", O); + } + else if (ActionName == "forward_as") { + checkNumberOfArguments(&Dag, 2); + const std::string& Name = InitPtrToString(Dag.getArg(0)); + const std::string& NewName = InitPtrToString(Dag.getArg(0)); + EmitForwardOptionPropertyHandlingCode(OptDescs.FindOption(Name), + IndentLevel, NewName, O); + } + else if (ActionName == "output_suffix") { + checkNumberOfArguments(&Dag, 1); + const std::string& OutSuf = InitPtrToString(Dag.getArg(0)); + O << IndentLevel << "output_suffix = \"" << OutSuf << "\";\n"; + } + else if (ActionName == "stop_compilation") { + O << IndentLevel << "stop_compilation = true;\n"; + } + else if (ActionName == "unpack_values") { + checkNumberOfArguments(&Dag, 1); + const std::string& Name = InitPtrToString(Dag.getArg(0)); + const OptionDescription& D = OptDescs.FindOption(Name); + + if (OptionType::IsList(D.Type)) { + O << IndentLevel << "for (" << D.GenTypeDeclaration() + << "::iterator B = " << D.GenVariableName() << ".begin(),\n" + << IndentLevel << "E = " << D.GenVariableName() + << ".end(); B != E; ++B)\n" + << IndentLevel << Indent1 << "llvm::SplitString(*B, vec, \",\");\n"; + } + else if (OptionType::IsParameter(D.Type)){ + O << Indent3 << "llvm::SplitString(" + << D.GenVariableName() << ", vec, \",\");\n"; + } + else { + throw "Option '" + D.Name + + "': switches can't have the 'unpack_values' property!"; + } + } + else { + throw "Unknown action name: " + ActionName + "!"; + } } }; // EmitGenerateActionMethod - Emit one of two versions of the // Tool::GenerateAction() method. -void EmitGenerateActionMethod (const ToolProperties& P, - const GlobalOptionDescriptions& OptDescs, - bool Version, std::ostream& O) { - if (Version) +void EmitGenerateActionMethod (const ToolDescription& D, + const OptionDescriptions& OptDescs, + bool IsJoin, std::ostream& O) { + if (IsJoin) O << Indent1 << "Action GenerateAction(const PathVector& inFiles,\n"; else O << Indent1 << "Action GenerateAction(const sys::Path& inFile,\n"; - O << Indent2 << "const sys::Path& outFile,\n" + O << Indent2 << "bool HasChildren,\n" + << Indent2 << "const llvm::sys::Path& TempDir,\n" << Indent2 << "const InputLanguagesSet& InLangs,\n" << Indent2 << "const LanguageMap& LangMap) const\n" << Indent1 << "{\n" << Indent2 << "std::string cmd;\n" - << Indent2 << "std::vector vec;\n"; + << Indent2 << "std::vector vec;\n" + << Indent2 << "bool stop_compilation = !HasChildren;\n" + << Indent2 << "const char* output_suffix = \"" << D.OutputSuffix << "\";\n" + << Indent2 << "std::string out_file;\n\n"; + + // For every understood option, emit handling code. + if (D.Actions) + EmitCaseConstructHandler(D.Actions, Indent2, EmitActionHandler(OptDescs), + false, OptDescs, O); + + O << '\n' << Indent2 + << "out_file = OutFilename(" << (IsJoin ? "sys::Path(),\n" : "inFile,\n") + << Indent3 << "TempDir, stop_compilation, output_suffix).toString();\n\n"; // cmd_line is either a string or a 'case' construct. - if (typeid(*P.CmdLine) == typeid(StringInit)) - EmitCmdLineVecFill(P.CmdLine, P.Name, Version, Indent2, O); + if (!D.CmdLine) + throw "Tool " + D.Name + " has no cmd_line property!"; + else if (typeid(*D.CmdLine) == typeid(StringInit)) + EmitCmdLineVecFill(D.CmdLine, D.Name, IsJoin, Indent2, O); else - EmitCaseConstructHandler(&InitPtrToDag(P.CmdLine), Indent2, - EmitCmdLineVecFillCallback(Version, P.Name), + EmitCaseConstructHandler(D.CmdLine, Indent2, + EmitCmdLineVecFillCallback(IsJoin, D.Name), true, OptDescs, O); - // For every understood option, emit handling code. - for (ToolOptionDescriptions::const_iterator B = P.OptDescs.begin(), - E = P.OptDescs.end(); B != E; ++B) { - const ToolOptionDescription& val = B->second; - EmitOptionPropertyHandlingCode(val, O); - } - // Handle the Sink property. - if (P.isSink()) { + if (D.isSink()) { O << Indent2 << "if (!" << SinkOptionName << ".empty()) {\n" << Indent3 << "vec.insert(vec.end(), " << SinkOptionName << ".begin(), " << SinkOptionName << ".end());\n" << Indent2 << "}\n"; } - O << Indent2 << "return Action(cmd, vec);\n" + O << Indent2 << "return Action(cmd, vec, stop_compilation, out_file);\n" << Indent1 << "}\n\n"; } /// EmitGenerateActionMethods - Emit two GenerateAction() methods for /// a given Tool class. -void EmitGenerateActionMethods (const ToolProperties& P, - const GlobalOptionDescriptions& OptDescs, +void EmitGenerateActionMethods (const ToolDescription& ToolDesc, + const OptionDescriptions& OptDescs, std::ostream& O) { - if (!P.isJoin()) + if (!ToolDesc.isJoin()) O << Indent1 << "Action GenerateAction(const PathVector& inFiles,\n" - << Indent2 << "const llvm::sys::Path& outFile,\n" + << Indent2 << "bool HasChildren,\n" + << Indent2 << "const llvm::sys::Path& TempDir,\n" << Indent2 << "const InputLanguagesSet& InLangs,\n" << Indent2 << "const LanguageMap& LangMap) const\n" << Indent1 << "{\n" - << Indent2 << "throw std::runtime_error(\"" << P.Name + << Indent2 << "throw std::runtime_error(\"" << ToolDesc.Name << " is not a Join tool!\");\n" << Indent1 << "}\n\n"; else - EmitGenerateActionMethod(P, OptDescs, true, O); - - EmitGenerateActionMethod(P, OptDescs, false, O); -} - -/// EmitIsLastMethod - Emit the IsLast() method for a given Tool -/// class. -void EmitIsLastMethod (const ToolProperties& P, std::ostream& O) { - O << Indent1 << "bool IsLast() const {\n" - << Indent2 << "bool last = false;\n"; - - for (ToolOptionDescriptions::const_iterator B = P.OptDescs.begin(), - E = P.OptDescs.end(); B != E; ++B) { - const ToolOptionDescription& val = B->second; - - if (val.isStopCompilation()) - O << Indent2 - << "if (" << val.GenVariableName() - << ")\n" << Indent3 << "last = true;\n"; - } + EmitGenerateActionMethod(ToolDesc, OptDescs, true, O); - O << Indent2 << "return last;\n" - << Indent1 << "}\n\n"; + EmitGenerateActionMethod(ToolDesc, OptDescs, false, O); } /// EmitInOutLanguageMethods - Emit the [Input,Output]Language() /// methods for a given Tool class. -void EmitInOutLanguageMethods (const ToolProperties& P, std::ostream& O) { +void EmitInOutLanguageMethods (const ToolDescription& D, std::ostream& O) { O << Indent1 << "const char** InputLanguages() const {\n" << Indent2 << "return InputLanguages_;\n" << Indent1 << "}\n\n"; - O << Indent1 << "const char* OutputLanguage() const {\n" - << Indent2 << "return \"" << P.OutLanguage << "\";\n" - << Indent1 << "}\n\n"; -} - -/// EmitOutputSuffixMethod - Emit the OutputSuffix() method for a -/// given Tool class. -void EmitOutputSuffixMethod (const ToolProperties& P, std::ostream& O) { - O << Indent1 << "const char* OutputSuffix() const {\n" - << Indent2 << "const char* ret = \"" << P.OutputSuffix << "\";\n"; - - for (ToolOptionDescriptions::const_iterator B = P.OptDescs.begin(), - E = P.OptDescs.end(); B != E; ++B) { - const ToolOptionDescription& OptDesc = B->second; - for (OptionPropertyList::const_iterator B = OptDesc.Props.begin(), - E = OptDesc.Props.end(); B != E; ++B) { - const OptionProperty& OptProp = *B; - if (OptProp.first == OptionPropertyType::OutputSuffix) { - O << Indent2 << "if (" << OptDesc.GenVariableName() << ")\n" - << Indent3 << "ret = \"" << OptProp.second << "\";\n"; - } - } - } + if (D.OutLanguage.empty()) + throw "Tool " + D.Name + " has no 'out_language' property!"; - O << Indent2 << "return ret;\n" + O << Indent1 << "const char* OutputLanguage() const {\n" + << Indent2 << "return \"" << D.OutLanguage << "\";\n" << Indent1 << "}\n\n"; } /// EmitNameMethod - Emit the Name() method for a given Tool class. -void EmitNameMethod (const ToolProperties& P, std::ostream& O) { +void EmitNameMethod (const ToolDescription& D, std::ostream& O) { O << Indent1 << "const char* Name() const {\n" - << Indent2 << "return \"" << P.Name << "\";\n" + << Indent2 << "return \"" << D.Name << "\";\n" << Indent1 << "}\n\n"; } /// EmitIsJoinMethod - Emit the IsJoin() method for a given Tool /// class. -void EmitIsJoinMethod (const ToolProperties& P, std::ostream& O) { +void EmitIsJoinMethod (const ToolDescription& D, std::ostream& O) { O << Indent1 << "bool IsJoin() const {\n"; - if (P.isJoin()) + if (D.isJoin()) O << Indent2 << "return true;\n"; else O << Indent2 << "return false;\n"; @@ -1369,24 +1356,27 @@ void EmitIsJoinMethod (const ToolProperties& P, std::ostream& O) { /// EmitStaticMemberDefinitions - Emit static member definitions for a /// given Tool class. -void EmitStaticMemberDefinitions(const ToolProperties& P, std::ostream& O) { - O << "const char* " << P.Name << "::InputLanguages_[] = {"; - for (StrVector::const_iterator B = P.InLanguage.begin(), - E = P.InLanguage.end(); B != E; ++B) +void EmitStaticMemberDefinitions(const ToolDescription& D, std::ostream& O) { + if (D.InLanguage.empty()) + throw "Tool " + D.Name + " has no 'in_language' property!"; + + O << "const char* " << D.Name << "::InputLanguages_[] = {"; + for (StrVector::const_iterator B = D.InLanguage.begin(), + E = D.InLanguage.end(); B != E; ++B) O << '\"' << *B << "\", "; O << "0};\n\n"; } /// EmitToolClassDefinition - Emit a Tool class definition. -void EmitToolClassDefinition (const ToolProperties& P, - const GlobalOptionDescriptions& OptDescs, +void EmitToolClassDefinition (const ToolDescription& D, + const OptionDescriptions& OptDescs, std::ostream& O) { - if (P.Name == "root") + if (D.Name == "root") return; // Header - O << "class " << P.Name << " : public "; - if (P.isJoin()) + O << "class " << D.Name << " : public "; + if (D.isJoin()) O << "JoinTool"; else O << "Tool"; @@ -1395,40 +1385,48 @@ void EmitToolClassDefinition (const ToolProperties& P, << Indent1 << "static const char* InputLanguages_[];\n\n"; O << "public:\n"; - EmitNameMethod(P, O); - EmitInOutLanguageMethods(P, O); - EmitOutputSuffixMethod(P, O); - EmitIsJoinMethod(P, O); - EmitGenerateActionMethods(P, OptDescs, O); - EmitIsLastMethod(P, O); + EmitNameMethod(D, O); + EmitInOutLanguageMethods(D, O); + EmitIsJoinMethod(D, O); + EmitGenerateActionMethods(D, OptDescs, O); // Close class definition O << "};\n"; - EmitStaticMemberDefinitions(P, O); + EmitStaticMemberDefinitions(D, O); } -/// EmitOptionDescriptions - Iterate over a list of option -/// descriptions and emit registration code. -void EmitOptionDescriptions (const GlobalOptionDescriptions& descs, - std::ostream& O) +/// EmitOptionDefintions - Iterate over a list of option descriptions +/// and emit registration code. +void EmitOptionDefintions (const OptionDescriptions& descs, + bool HasSink, + std::ostream& O) { - std::vector Aliases; + std::vector Aliases; // Emit static cl::Option variables. - for (GlobalOptionDescriptions::const_iterator B = descs.begin(), + for (OptionDescriptions::const_iterator B = descs.begin(), E = descs.end(); B!=E; ++B) { - const GlobalOptionDescription& val = B->second; + const OptionDescription& val = B->second; if (val.Type == OptionType::Alias) { Aliases.push_back(val); continue; } + if (val.isExtern()) + O << "extern "; + O << val.GenTypeDeclaration() << ' ' - << val.GenVariableName() - << "(\"" << val.Name << '\"'; + << val.GenVariableName(); + + if (val.isExtern()) { + O << ";\n"; + continue; + } + + O << "(\"" << val.Name << '\"'; if (val.Type == OptionType::Prefix || val.Type == OptionType::PrefixList) O << ", cl::Prefix"; @@ -1462,26 +1460,22 @@ void EmitOptionDescriptions (const GlobalOptionDescriptions& descs, } // Emit the aliases (they should go after all the 'proper' options). - for (std::vector::const_iterator + for (std::vector::const_iterator B = Aliases.begin(), E = Aliases.end(); B != E; ++B) { - const GlobalOptionDescription& val = *B; + const OptionDescription& val = *B; O << val.GenTypeDeclaration() << ' ' << val.GenVariableName() << "(\"" << val.Name << '\"'; - GlobalOptionDescriptions::container_type - ::const_iterator F = descs.Descriptions.find(val.Help); - if (F != descs.Descriptions.end()) - O << ", cl::aliasopt(" << F->second.GenVariableName() << ")"; - else - throw val.Name + ": alias to an unknown option!"; + const OptionDescription& D = descs.FindOption(val.Help); + O << ", cl::aliasopt(" << D.GenVariableName() << ")"; O << ", cl::desc(\"" << "An alias for -" + val.Help << "\"));\n"; } // Emit the sink option. - if (descs.HasSink) + if (HasSink) O << "cl::list " << SinkOptionName << "(cl::Sink);\n"; O << '\n'; @@ -1520,53 +1514,6 @@ void EmitPopulateLanguageMap (const RecordKeeper& Records, std::ostream& O) O << "}\n\n}\n\n"; } -/// FillInToolToLang - Fills in two tables that map tool names to -/// (input, output) languages. Used by the typechecker. -void FillInToolToLang (const ToolPropertiesList& TPList, - StringMap >& ToolToInLang, - StringMap& ToolToOutLang) { - for (ToolPropertiesList::const_iterator B = TPList.begin(), E = TPList.end(); - B != E; ++B) { - const ToolProperties& P = *(*B); - for (StrVector::const_iterator B = P.InLanguage.begin(), - E = P.InLanguage.end(); B != E; ++B) - ToolToInLang[P.Name].insert(*B); - ToolToOutLang[P.Name] = P.OutLanguage; - } -} - -/// TypecheckGraph - Check that names for output and input languages -/// on all edges do match. This doesn't do much when the information -/// about the whole graph is not available (i.e. when compiling most -/// plugins). -void TypecheckGraph (const RecordVector& EdgeVector, - const ToolPropertiesList& TPList) { - StringMap > ToolToInLang; - StringMap ToolToOutLang; - - FillInToolToLang(TPList, ToolToInLang, ToolToOutLang); - StringMap::iterator IAE = ToolToOutLang.end(); - StringMap >::iterator IBE = ToolToInLang.end(); - - for (RecordVector::const_iterator Beg = EdgeVector.begin(), - E = EdgeVector.end(); Beg != E; ++Beg) { - const Record* Edge = *Beg; - const std::string& A = Edge->getValueAsString("a"); - const std::string& B = Edge->getValueAsString("b"); - StringMap::iterator IA = ToolToOutLang.find(A); - StringMap >::iterator IB = ToolToInLang.find(B); - - if (A != "root") { - if (IA != IAE && IB != IBE && IB->second.count(IA->second) == 0) - throw "Edge " + A + "->" + B - + ": output->input language mismatch"; - } - - if (B == "root") - throw std::string("Edges back to the root are not allowed!"); - } -} - /// IncDecWeight - Helper function passed to EmitCaseConstructHandler() /// by EmitEdgeClass(). void IncDecWeight (const Init* i, const char* IndentLevel, @@ -1575,9 +1522,9 @@ void IncDecWeight (const Init* i, const char* IndentLevel, const std::string& OpName = d.getOperator()->getAsString(); if (OpName == "inc_weight") - O << IndentLevel << Indent1 << "ret += "; + O << IndentLevel << "ret += "; else if (OpName == "dec_weight") - O << IndentLevel << Indent1 << "ret -= "; + O << IndentLevel << "ret -= "; else throw "Unknown operator in edge properties list: " + OpName + '!'; @@ -1590,7 +1537,7 @@ void IncDecWeight (const Init* i, const char* IndentLevel, /// EmitEdgeClass - Emit a single Edge# class. void EmitEdgeClass (unsigned N, const std::string& Target, - DagInit* Case, const GlobalOptionDescriptions& OptDescs, + DagInit* Case, const OptionDescriptions& OptDescs, std::ostream& O) { // Class constructor. @@ -1612,7 +1559,7 @@ void EmitEdgeClass (unsigned N, const std::string& Target, /// EmitEdgeClasses - Emit Edge* classes that represent graph edges. void EmitEdgeClasses (const RecordVector& EdgeVector, - const GlobalOptionDescriptions& OptDescs, + const OptionDescriptions& OptDescs, std::ostream& O) { int i = 0; for (RecordVector::const_iterator Beg = EdgeVector.begin(), @@ -1630,14 +1577,14 @@ void EmitEdgeClasses (const RecordVector& EdgeVector, /// EmitPopulateCompilationGraph - Emit the PopulateCompilationGraph() /// function. void EmitPopulateCompilationGraph (const RecordVector& EdgeVector, - const ToolPropertiesList& ToolProps, + const ToolDescriptions& ToolDescs, std::ostream& O) { O << "namespace {\n\n"; O << "void PopulateCompilationGraphLocal(CompilationGraph& G) {\n"; - for (ToolPropertiesList::const_iterator B = ToolProps.begin(), - E = ToolProps.end(); B != E; ++B) + for (ToolDescriptions::const_iterator B = ToolDescs.begin(), + E = ToolDescs.end(); B != E; ++B) O << Indent1 << "G.insertNode(new " << (*B)->Name << "());\n"; O << '\n'; @@ -1669,73 +1616,61 @@ void EmitPopulateCompilationGraph (const RecordVector& EdgeVector, /// ExtractHookNames - Extract the hook names from all instances of /// $CALL(HookName) in the provided command line string. Helper /// function used by FillInHookNames(). -void ExtractHookNames(const Init* CmdLine, StrVector& HookNames) { - StrVector cmds; - llvm::SplitString(InitPtrToString(CmdLine), cmds); - for (StrVector::const_iterator B = cmds.begin(), E = cmds.end(); - B != E; ++B) { - const std::string& cmd = *B; - if (cmd.find("$CALL(") == 0) { - if (cmd.size() == 6) - throw std::string("$CALL invocation: empty argument list!"); - HookNames.push_back(std::string(cmd.begin() + 6, - cmd.begin() + cmd.find(")"))); +class ExtractHookNames { + llvm::StringSet<>& HookNames_; +public: + ExtractHookNames(llvm::StringSet<>& HookNames) + : HookNames_(HookNames_) {} + + void operator()(const Init* CmdLine) { + StrVector cmds; + llvm::SplitString(InitPtrToString(CmdLine), cmds); + for (StrVector::const_iterator B = cmds.begin(), E = cmds.end(); + B != E; ++B) { + const std::string& cmd = *B; + if (cmd.find("$CALL(") == 0) { + if (cmd.size() == 6) + throw std::string("$CALL invocation: empty argument list!"); + HookNames_.insert(std::string(cmd.begin() + 6, + cmd.begin() + cmd.find(")"))); + } } } -} - -/// ExtractHookNamesFromCaseConstruct - Extract hook names from the -/// 'case' expression, handle nesting. Helper function used by -/// FillInHookNames(). -void ExtractHookNamesFromCaseConstruct(Init* Case, StrVector& HookNames) { - const DagInit& d = InitPtrToDag(Case); - bool even = false; - for (DagInit::const_arg_iterator B = d.arg_begin(), E = d.arg_end(); - B != E; ++B) { - Init* arg = *B; - if (even && dynamic_cast(arg) - && static_cast(arg)->getOperator()->getAsString() == "case") - ExtractHookNamesFromCaseConstruct(arg, HookNames); - else if (even) - ExtractHookNames(arg, HookNames); - even = !even; - } -} +}; /// FillInHookNames - Actually extract the hook names from all command /// line strings. Helper function used by EmitHookDeclarations(). -void FillInHookNames(const ToolPropertiesList& TPList, - StrVector& HookNames) { +void FillInHookNames(const ToolDescriptions& ToolDescs, + llvm::StringSet<>& HookNames) +{ // For all command lines: - for (ToolPropertiesList::const_iterator B = TPList.begin(), - E = TPList.end(); B != E; ++B) { - const ToolProperties& P = *(*B); - if (!P.CmdLine) + for (ToolDescriptions::const_iterator B = ToolDescs.begin(), + E = ToolDescs.end(); B != E; ++B) { + const ToolDescription& D = *(*B); + if (!D.CmdLine) continue; - if (dynamic_cast(P.CmdLine)) + if (dynamic_cast(D.CmdLine)) // This is a string. - ExtractHookNames(P.CmdLine, HookNames); + ExtractHookNames(HookNames).operator()(D.CmdLine); else // This is a 'case' construct. - ExtractHookNamesFromCaseConstruct(P.CmdLine, HookNames); + WalkCase(D.CmdLine, Id(), ExtractHookNames(HookNames)); } } /// EmitHookDeclarations - Parse CmdLine fields of all the tool /// property records and emit hook function declaration for each /// instance of $CALL(HookName). -void EmitHookDeclarations(const ToolPropertiesList& ToolProps, - std::ostream& O) { - StrVector HookNames; - FillInHookNames(ToolProps, HookNames); +void EmitHookDeclarations(const ToolDescriptions& ToolDescs, std::ostream& O) { + llvm::StringSet<> HookNames; + FillInHookNames(ToolDescs, HookNames); if (HookNames.empty()) return; - std::sort(HookNames.begin(), HookNames.end()); - StrVector::const_iterator E = std::unique(HookNames.begin(), HookNames.end()); O << "namespace hooks {\n"; - for (StrVector::const_iterator B = HookNames.begin(); B != E; ++B) - O << Indent1 << "std::string " << *B << "();\n"; + for (StringSet<>::const_iterator B = HookNames.begin(), E = HookNames.end(); + B != E; ++B) + O << Indent1 << "std::string " << B->first() << "();\n"; O << "}\n\n"; } @@ -1777,138 +1712,113 @@ void EmitIncludes(std::ostream& O) { << "{ return s == NULL ? \"\" : s; }\n\n"; } -/// NotInGraph - Helper function object for FilterNotInGraph. -struct NotInGraph { -private: - const llvm::StringSet<>& ToolsInGraph_; - -public: - NotInGraph(const llvm::StringSet<>& ToolsInGraph) - : ToolsInGraph_(ToolsInGraph) - {} - bool operator()(const IntrusiveRefCntPtr& x) { - return (ToolsInGraph_.count(x->Name) == 0); - } +/// PluginData - Holds all information about a plugin. +struct PluginData { + OptionDescriptions OptDescs; + bool HasSink; + ToolDescriptions ToolDescs; + RecordVector Edges; + int Priority; }; -/// FilterNotInGraph - Filter out from ToolProps all Tools not -/// mentioned in the compilation graph definition. -void FilterNotInGraph (const RecordVector& EdgeVector, - ToolPropertiesList& ToolProps) { - - // List all tools mentioned in the graph. - llvm::StringSet<> ToolsInGraph; - - for (RecordVector::const_iterator Beg = EdgeVector.begin(), - E = EdgeVector.end(); Beg != E; ++Beg) { - - const Record* Edge = *Beg; - const std::string& A = Edge->getValueAsString("a"); - const std::string& B = Edge->getValueAsString("b"); - - if (A != "root") - ToolsInGraph.insert(A); - ToolsInGraph.insert(B); - } - - // Filter ToolPropertiesList. - ToolPropertiesList::iterator new_end = - std::remove_if(ToolProps.begin(), ToolProps.end(), - NotInGraph(ToolsInGraph)); - ToolProps.erase(new_end, ToolProps.end()); -} - -/// CalculatePriority - Calculate the priority of this plugin. -int CalculatePriority(RecordVector::const_iterator B, - RecordVector::const_iterator E) { - int total = 0; - for (; B!=E; ++B) { - total += static_cast((*B)->getValueAsInt("priority")); - } - return total; -} - -/// FillInEdgeVector - Merge all compilation graph definitions into -/// one single edge list. -void FillInEdgeVector(RecordVector::const_iterator B, - RecordVector::const_iterator E, RecordVector& Out) { - for (; B != E; ++B) { - const ListInit* edges = (*B)->getValueAsListInit("edges"); - - for (unsigned i = 0; i < edges->size(); ++i) - Out.push_back(edges->getElementAsRecord(i)); - } -} +/// HasSink - Go through the list of tool descriptions and check if +/// there is one with the 'sink' property set. +bool HasSink(const ToolDescriptions& ToolDescs) { + for (ToolDescriptions::const_iterator B = ToolDescs.begin(), + E = ToolDescs.end(); B != E; ++B) + if ((*B)->isSink()) + return true; -// End of anonymous namespace + return false; } -/// run - The back-end entry point. -void LLVMCConfigurationEmitter::run (std::ostream &O) { - try { - - // Emit file header. - EmitSourceFileHeader("LLVMC Configuration Library", O); - EmitIncludes(O); - - // Collect tool properties. - RecordVector Tools = Records.getAllDerivedDefinitions("Tool"); - ToolPropertiesList ToolProps; - GlobalOptionDescriptions OptDescs; - CollectToolProperties(Tools.begin(), Tools.end(), ToolProps, OptDescs); - +/// CollectPluginData - Collect tool and option properties, +/// compilation graph edges and plugin priority from the parse tree. +void CollectPluginData (const RecordKeeper& Records, PluginData& Data) { // Collect option properties. const RecordVector& OptionLists = Records.getAllDerivedDefinitions("OptionList"); - CollectPropertiesFromOptionLists(OptionLists.begin(), OptionLists.end(), - OptDescs); + CollectOptionDescriptions(OptionLists.begin(), OptionLists.end(), + Data.OptDescs); + + // Collect tool properties. + const RecordVector& Tools = Records.getAllDerivedDefinitions("Tool"); + CollectToolDescriptions(Tools.begin(), Tools.end(), Data.ToolDescs); + Data.HasSink = HasSink(Data.ToolDescs); // Collect compilation graph edges. const RecordVector& CompilationGraphs = Records.getAllDerivedDefinitions("CompilationGraph"); - RecordVector EdgeVector; FillInEdgeVector(CompilationGraphs.begin(), CompilationGraphs.end(), - EdgeVector); + Data.Edges); + // Calculate the priority of this plugin. + const RecordVector& Priorities = + Records.getAllDerivedDefinitions("PluginPriority"); + Data.Priority = CalculatePriority(Priorities.begin(), Priorities.end()); +} + +/// CheckPluginData - Perform some sanity checks on the collected data. +void CheckPluginData(PluginData& Data) { // Filter out all tools not mentioned in the compilation graph. - FilterNotInGraph(EdgeVector, ToolProps); + FilterNotInGraph(Data.Edges, Data.ToolDescs); // Typecheck the compilation graph. - TypecheckGraph(EdgeVector, ToolProps); + TypecheckGraph(Data.Edges, Data.ToolDescs); // Check that there are no options without side effects (specified // only in the OptionList). - CheckForSuperfluousOptions(ToolProps, OptDescs); + CheckForSuperfluousOptions(Data.Edges, Data.ToolDescs, Data.OptDescs); + +} + +void EmitPluginCode(const PluginData& Data, std::ostream& O) { + // Emit file header. + EmitIncludes(O); // Emit global option registration code. - EmitOptionDescriptions(OptDescs, O); + EmitOptionDefintions(Data.OptDescs, Data.HasSink, O); // Emit hook declarations. - EmitHookDeclarations(ToolProps, O); + EmitHookDeclarations(Data.ToolDescs, O); // Emit PopulateLanguageMap() function // (a language map maps from file extensions to language names). EmitPopulateLanguageMap(Records, O); // Emit Tool classes. - for (ToolPropertiesList::const_iterator B = ToolProps.begin(), - E = ToolProps.end(); B!=E; ++B) - EmitToolClassDefinition(*(*B), OptDescs, O); + for (ToolDescriptions::const_iterator B = Data.ToolDescs.begin(), + E = Data.ToolDescs.end(); B!=E; ++B) + EmitToolClassDefinition(*(*B), Data.OptDescs, O); // Emit Edge# classes. - EmitEdgeClasses(EdgeVector, OptDescs, O); + EmitEdgeClasses(Data.Edges, Data.OptDescs, O); // Emit PopulateCompilationGraph() function. - EmitPopulateCompilationGraph(EdgeVector, ToolProps, O); + EmitPopulateCompilationGraph(Data.Edges, Data.ToolDescs, O); // Emit code for plugin registration. - const RecordVector& Priorities = - Records.getAllDerivedDefinitions("PluginPriority"); - EmitRegisterPlugin(CalculatePriority(Priorities.begin(), - Priorities.end()), O); + EmitRegisterPlugin(Data.Priority, O); // EOF +} + + +// End of anonymous namespace +} + +/// run - The back-end entry point. +void LLVMCConfigurationEmitter::run (std::ostream &O) { + try { + PluginData Data; + + CollectPluginData(Records, Data); + CheckPluginData(Data); + + EmitSourceFileHeader("LLVMC Configuration Library", O); + EmitPluginCode(Data, O); + } catch (std::exception& Error) { throw Error.what() + std::string(" - usually this means a syntax error."); } -- 2.34.1