X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=utils%2FTableGen%2FLLVMCConfigurationEmitter.cpp;h=c40a39dff729824674db75a5eb1d102c59aee2c6;hb=75f6e89ea9f8fe9cf8c8f9fe6a3322bd6566fdf1;hp=da4ed78a24cbf8ffc61feddb7769b247d02732ba;hpb=3acc921b26f0858ebddbc897763738ff81ca34c6;p=oota-llvm.git diff --git a/utils/TableGen/LLVMCConfigurationEmitter.cpp b/utils/TableGen/LLVMCConfigurationEmitter.cpp index da4ed78a24c..c40a39dff72 100644 --- a/utils/TableGen/LLVMCConfigurationEmitter.cpp +++ b/utils/TableGen/LLVMCConfigurationEmitter.cpp @@ -15,11 +15,8 @@ #include "Record.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" -#include "llvm/Support/Streams.h" #include #include @@ -28,6 +25,7 @@ #include #include + using namespace llvm; namespace { @@ -36,26 +34,41 @@ namespace { /// Typedefs typedef std::vector RecordVector; +typedef std::vector DagVector; typedef std::vector StrVector; //===----------------------------------------------------------------------===// /// Constants -// Indentation strings. -const char * Indent1 = " "; -const char * Indent2 = " "; -const char * Indent3 = " "; -const char * Indent4 = " "; +// Indentation. +const unsigned TabWidth = 4; +const unsigned Indent1 = TabWidth*1; +const unsigned Indent2 = TabWidth*2; +const unsigned Indent3 = TabWidth*3; +const unsigned Indent4 = TabWidth*4; // Default help string. -const char * DefaultHelpString = "NO HELP MESSAGE PROVIDED"; +const char * const DefaultHelpString = "NO HELP MESSAGE PROVIDED"; // Name for the "sink" option. -const char * SinkOptionName = "AutoGeneratedSinkOption"; +const char * const SinkOptionName = "SinkOption"; //===----------------------------------------------------------------------===// /// Helper functions +/// Id - An 'identity' function object. +struct Id { + template + void operator()(const T0&) const { + } + template + void operator()(const T0&, const T1&) const { + } + template + void operator()(const T0&, const T1&, const T2&) const { + } +}; + int InitPtrToInt(const Init* ptr) { const IntInit& val = dynamic_cast(*ptr); return val.getValue(); @@ -76,595 +89,809 @@ const DagInit& InitPtrToDag(const Init* ptr) { return val; } -// checkNumberOfArguments - Ensure that the number of args in d is -// less than or equal to min_arguments, otherwise throw an exception. -void checkNumberOfArguments (const DagInit* d, unsigned min_arguments) { - if (d->getNumArgs() < min_arguments) - throw "Property " + d->getOperator()->getAsString() - + " has too few arguments!"; +const std::string GetOperatorName(const DagInit& D) { + return D.getOperator()->getAsString(); +} + +/// CheckBooleanConstant - Check that the provided value is a boolean constant. +void CheckBooleanConstant(const Init* I) { + const DefInit& val = dynamic_cast(*I); + const std::string& str = val.getAsString(); + + if (str != "true" && str != "false") { + throw "Incorrect boolean value: '" + str + + "': must be either 'true' or 'false'"; + } +} + +// CheckNumberOfArguments - Ensure that the number of args in d is +// greater than or equal to min_arguments, otherwise throw an exception. +void CheckNumberOfArguments (const DagInit& d, unsigned minArgs) { + if (d.getNumArgs() < minArgs) + throw GetOperatorName(d) + ": too few arguments!"; +} + +// 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; +} + +/// EscapeQuotes - Replace '"' with '\"'. +std::string EscapeQuotes (const std::string& Var) { + std::string ret; + for (unsigned i = 0; i != Var.size(); ++i) { + char cur_char = Var[i]; + if (cur_char == '"') { + ret += "\\\""; + } + else { + ret.push_back(cur_char); + } + } + return ret; +} + +/// OneOf - Does the input string contain this character? +bool OneOf(const char* lst, char c) { + while (*lst) { + if (*lst++ == c) + return true; + } + return false; } -// isDagEmpty - is this DAG marked with an empty marker? -bool isDagEmpty (const DagInit* d) { - return d->getOperator()->getAsString() == "empty"; +template +void CheckedIncrement(I& P, I E, S ErrorString) { + ++P; + if (P == E) + throw ErrorString; } //===----------------------------------------------------------------------===// /// 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. namespace OptionType { - enum OptionType { Alias, Switch, - Parameter, ParameterList, Prefix, PrefixList}; + + enum OptionType { Alias, Switch, SwitchList, + Parameter, ParameterList, Prefix, PrefixList }; + + bool IsAlias(OptionType t) { + return (t == Alias); + } + + bool IsList (OptionType t) { + return (t == SwitchList || t == ParameterList || t == PrefixList); + } + + bool IsSwitch (OptionType t) { + return (t == Switch); + } + + bool IsSwitchList (OptionType t) { + return (t == SwitchList); + } + + bool IsParameter (OptionType t) { + return (t == Parameter || t == Prefix); + } + } -bool IsListOptionType (OptionType::OptionType t) { - return (t == OptionType::ParameterList || t == OptionType::PrefixList); +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 == "switch_list_option") + return OptionType::SwitchList; + 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 + '!'; } -// 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, OneOrMore = 0x8, + Optional = 0x10, CommaSeparated = 0x20, + ForwardNotSplit = 0x40, ZeroOrMore = 0x80 }; +} -/// 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; + unsigned MultiVal; + Init* InitVal; 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), MultiVal(1), InitVal(0) {} - 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"; - } - } - - // 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; + /// GenTypeDeclaration - Returns the C++ variable type of this + /// option. + const char* GenTypeDeclaration() const; + + /// GenVariableName - Returns the variable name used in the + /// generated C++ code. + std::string GenVariableName() const + { return "autogenerated::" + GenOptionType() + EscapeVariableName(Name); } + + /// GenPlainVariableName - Returns the variable name without the namespace + /// prefix. + std::string GenPlainVariableName() const + { return GenOptionType() + EscapeVariableName(Name); } + + /// Merge - Merge two option descriptions. + void Merge (const OptionDescription& other); + + /// CheckConsistency - Check that the flags are consistent. + void CheckConsistency() const; + + // Misc convenient getters/setters. + + bool isAlias() const; + + bool isMultiVal() const; + + bool isCommaSeparated() const; + void setCommaSeparated(); + + bool isForwardNotSplit() const; + void setForwardNotSplit(); + + bool isRequired() const; + void setRequired(); + + bool isOneOrMore() const; + void setOneOrMore(); + + bool isZeroOrMore() const; + void setZeroOrMore(); + + bool isOptional() const; + void setOptional(); + + bool isHidden() const; + void setHidden(); + + bool isReallyHidden() const; + void setReallyHidden(); + + bool isSwitch() const + { return OptionType::IsSwitch(this->Type); } + + bool isSwitchList() const + { return OptionType::IsSwitchList(this->Type); } + + bool isParameter() const + { return OptionType::IsParameter(this->Type); } + + bool isList() const + { return OptionType::IsList(this->Type); } + + bool isParameterList() const + { return (OptionType::IsList(this->Type) + && !OptionType::IsSwitchList(this->Type)); } + +private: + + // GenOptionType - Helper function used by GenVariableName(). + std::string GenOptionType() const; +}; + +void OptionDescription::CheckConsistency() const { + unsigned i = 0; + + i += this->isRequired(); + i += this->isOptional(); + i += this->isOneOrMore(); + i += this->isZeroOrMore(); + + if (i > 1) { + throw "Only one of (required), (optional), (one_or_more) or " + "(zero_or_more) properties is allowed!"; } +} - 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; - } +void OptionDescription::Merge (const OptionDescription& 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::errs() << "Warning: several different help strings" + " defined for option " + Name + "\n"; } -}; + Flags |= other.Flags; +} -// Global option description. +bool OptionDescription::isAlias() const { + return OptionType::IsAlias(this->Type); +} -namespace GlobalOptionDescriptionFlags { - enum GlobalOptionDescriptionFlags { Required = 0x1 }; +bool OptionDescription::isMultiVal() const { + return MultiVal > 1; } -struct GlobalOptionDescription : public OptionDescription { - std::string Help; - unsigned Flags; +bool OptionDescription::isCommaSeparated() const { + return Flags & OptionDescriptionFlags::CommaSeparated; +} +void OptionDescription::setCommaSeparated() { + Flags |= OptionDescriptionFlags::CommaSeparated; +} - // We need to provide a default constructor because - // StringMap can only store DefaultConstructible objects. - GlobalOptionDescription() : OptionDescription(), Flags(0) - {} +bool OptionDescription::isForwardNotSplit() const { + return Flags & OptionDescriptionFlags::ForwardNotSplit; +} +void OptionDescription::setForwardNotSplit() { + Flags |= OptionDescriptionFlags::ForwardNotSplit; +} - 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::isOneOrMore() const { + return Flags & OptionDescriptionFlags::OneOrMore; +} +void OptionDescription::setOneOrMore() { + Flags |= OptionDescriptionFlags::OneOrMore; +} - /// Merge - Merge two option descriptions. - void Merge (const GlobalOptionDescription& other) - { - if (other.Type != Type) - throw "Conflicting definitions for the option " + Name + "!"; +bool OptionDescription::isZeroOrMore() const { + return Flags & OptionDescriptionFlags::ZeroOrMore; +} +void OptionDescription::setZeroOrMore() { + Flags |= OptionDescriptionFlags::ZeroOrMore; +} - 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"; - } +bool OptionDescription::isOptional() const { + return Flags & OptionDescriptionFlags::Optional; +} +void OptionDescription::setOptional() { + Flags |= OptionDescriptionFlags::Optional; +} + +bool OptionDescription::isHidden() const { + return Flags & OptionDescriptionFlags::Hidden; +} +void OptionDescription::setHidden() { + Flags |= OptionDescriptionFlags::Hidden; +} + +bool OptionDescription::isReallyHidden() const { + return Flags & OptionDescriptionFlags::ReallyHidden; +} +void OptionDescription::setReallyHidden() { + Flags |= OptionDescriptionFlags::ReallyHidden; +} + +const char* OptionDescription::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::SwitchList: + return "cl::list"; + case OptionType::Parameter: + case OptionType::Prefix: + default: + return "cl::opt"; + } +} - Flags |= other.Flags; +std::string OptionDescription::GenOptionType() const { + switch (Type) { + case OptionType::Alias: + return "Alias_"; + case OptionType::PrefixList: + case OptionType::ParameterList: + return "List_"; + case OptionType::Switch: + return "Switch_"; + case OptionType::SwitchList: + return "SwitchList_"; + case OptionType::Prefix: + case OptionType::Parameter: + default: + return "Parameter_"; } -}; +} -/// 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!"; - } - - /// 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; - } - } + const OptionDescription& FindOption(const std::string& OptName) const; + + // Wrappers for FindOption that throw an exception in case the option has a + // wrong type. + const OptionDescription& FindSwitch(const std::string& OptName) const; + const OptionDescription& FindParameter(const std::string& OptName) const; + const OptionDescription& FindParameterList(const std::string& OptName) const; + const OptionDescription& + FindListOrParameter(const std::string& OptName) const; + const OptionDescription& + FindParameterListOrParameter(const std::string& OptName) const; + + /// 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}; +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!"; } -namespace OptionPropertyType { - enum OptionPropertyType { AppendCmd, ForwardAs, OutputSuffix }; + +const OptionDescription& +OptionDescriptions::FindSwitch(const std::string& OptName) const { + const OptionDescription& OptDesc = this->FindOption(OptName); + if (!OptDesc.isSwitch()) + throw OptName + ": incorrect option type - should be a switch!"; + return OptDesc; } -typedef std::pair -OptionProperty; -typedef SmallVector OptionPropertyList; +const OptionDescription& +OptionDescriptions::FindParameterList(const std::string& OptName) const { + const OptionDescription& OptDesc = this->FindOption(OptName); + if (!OptDesc.isList() || OptDesc.isSwitchList()) + throw OptName + ": incorrect option type - should be a parameter list!"; + return OptDesc; +} -struct ToolOptionDescription : public OptionDescription { - unsigned Flags; - OptionPropertyList Props; +const OptionDescription& +OptionDescriptions::FindParameter(const std::string& OptName) const { + const OptionDescription& OptDesc = this->FindOption(OptName); + if (!OptDesc.isParameter()) + throw OptName + ": incorrect option type - should be a parameter!"; + return OptDesc; +} - // StringMap can only store DefaultConstructible objects - ToolOptionDescription() : OptionDescription(), Flags(0) {} +const OptionDescription& +OptionDescriptions::FindListOrParameter(const std::string& OptName) const { + const OptionDescription& OptDesc = this->FindOption(OptName); + if (!OptDesc.isList() && !OptDesc.isParameter()) + throw OptName + + ": incorrect option type - should be a list or parameter!"; + return OptDesc; +} - ToolOptionDescription (OptionType::OptionType t, const std::string& n) - : OptionDescription(t, n) - {} +const OptionDescription& +OptionDescriptions::FindParameterListOrParameter +(const std::string& OptName) const { + const OptionDescription& OptDesc = this->FindOption(OptName); + if ((!OptDesc.isList() && !OptDesc.isParameter()) || OptDesc.isSwitchList()) + throw OptName + + ": incorrect option type - should be a parameter list or parameter!"; + return OptDesc; +} - // Various boolean properties - bool isStopCompilation() const { - return Flags & ToolOptionDescriptionFlags::StopCompilation; +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 setStopCompilation() { - Flags |= ToolOptionDescriptionFlags::StopCompilation; + else { + Descriptions[o.Name] = o; } +} - bool isForward() const { - return Flags & ToolOptionDescriptionFlags::Forward; - } - void setForward() { - Flags |= ToolOptionDescriptionFlags::Forward; - } +/// HandlerTable - A base class for function objects implemented as +/// 'tables of handlers'. +template +class HandlerTable { +protected: + // Implementation details. - bool isUnpackValues() const { - return Flags & ToolOptionDescriptionFlags::UnpackValues; - } - void setUnpackValues() { - Flags |= ToolOptionDescriptionFlags::UnpackValues; - } + /// HandlerMap - A map from property names to property handlers + typedef StringMap HandlerMap; - void AddProperty (OptionPropertyType::OptionPropertyType t, - const std::string& val) - { - Props.push_back(std::make_pair(t, val)); + static HandlerMap Handlers_; + static bool staticMembersInitialized_; + +public: + + Handler GetHandler (const std::string& HandlerName) const { + typename HandlerMap::iterator method = Handlers_.find(HandlerName); + + if (method != Handlers_.end()) { + Handler h = method->second; + return h; + } + else { + throw "No handler found for property " + HandlerName + "!"; + } } -}; -typedef StringMap ToolOptionDescriptions; + void AddHandler(const char* Property, Handler H) { + Handlers_[Property] = H; + } -// Tool information record +}; -namespace ToolFlags { - enum ToolFlags { Join = 0x1, Sink = 0x2 }; +template +Handler GetHandler(FunctionObject* Obj, const DagInit& Dag) { + const std::string& HandlerName = GetOperatorName(Dag); + return Obj->GetHandler(HandlerName); } -struct ToolProperties : public RefCountedBase { - std::string Name; - Init* CmdLine; - StrVector InLanguage; - std::string OutLanguage; - std::string OutputSuffix; - unsigned Flags; - ToolOptionDescriptions OptDescs; +template +void InvokeDagInitHandler(FunctionObject* Obj, Init* I) { + typedef void (FunctionObject::*Handler) (const DagInit&); - // 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; } + const DagInit& Dag = InitPtrToDag(I); + Handler h = GetHandler(Obj, Dag); - // 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) {} -}; + ((Obj)->*(h))(Dag); +} +template +void InvokeDagInitHandler(const FunctionObject* const Obj, + const Init* I, unsigned IndentLevel, raw_ostream& O) +{ + typedef void (FunctionObject::*Handler) + (const DagInit&, unsigned IndentLevel, raw_ostream& O) const; -/// 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; + const DagInit& Dag = InitPtrToDag(I); + Handler h = GetHandler(Obj, Dag); + ((Obj)->*(h))(Dag, IndentLevel, O); +} -/// CollectOptionProperties - Function object for iterating over a -/// list (usually, a DAG) of option property records. -class CollectOptionProperties { -private: - // Implementation details. +template +typename HandlerTable::HandlerMap HandlerTable::Handlers_; - /// OptionPropertyHandler - a function that extracts information - /// about a given option property from its DAG representation. - typedef void (CollectOptionProperties::* OptionPropertyHandler) - (const DagInit*); +template +bool HandlerTable::staticMembersInitialized_ = false; - /// OptionPropertyHandlerMap - A map from option property names to - /// option property handlers - typedef StringMap OptionPropertyHandlerMap; - static OptionPropertyHandlerMap optionPropertyHandlers_; - static bool staticMembersInitialized_; +/// CollectOptionProperties - Function object for iterating over an +/// option property list. +class CollectOptionProperties; +typedef void (CollectOptionProperties::* CollectOptionPropertiesHandler) +(const DagInit&); - /// This is where the information is stored +class CollectOptionProperties +: public HandlerTable +{ +private: - /// 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) + : optDesc_(OD) { if (!staticMembersInitialized_) { - optionPropertyHandlers_["append_cmd"] = - &CollectOptionProperties::onAppendCmd; - optionPropertyHandlers_["forward"] = - &CollectOptionProperties::onForward; - optionPropertyHandlers_["forward_as"] = - &CollectOptionProperties::onForwardAs; - optionPropertyHandlers_["help"] = - &CollectOptionProperties::onHelp; - optionPropertyHandlers_["output_suffix"] = - &CollectOptionProperties::onOutputSuffix; - optionPropertyHandlers_["required"] = - &CollectOptionProperties::onRequired; - optionPropertyHandlers_["stop_compilation"] = - &CollectOptionProperties::onStopCompilation; - optionPropertyHandlers_["unpack_values"] = - &CollectOptionProperties::onUnpackValues; + AddHandler("help", &CollectOptionProperties::onHelp); + AddHandler("hidden", &CollectOptionProperties::onHidden); + AddHandler("init", &CollectOptionProperties::onInit); + AddHandler("multi_val", &CollectOptionProperties::onMultiVal); + AddHandler("one_or_more", &CollectOptionProperties::onOneOrMore); + AddHandler("zero_or_more", &CollectOptionProperties::onZeroOrMore); + AddHandler("really_hidden", &CollectOptionProperties::onReallyHidden); + AddHandler("required", &CollectOptionProperties::onRequired); + AddHandler("optional", &CollectOptionProperties::onOptional); + AddHandler("comma_separated", &CollectOptionProperties::onCommaSeparated); + AddHandler("forward_not_split", + &CollectOptionProperties::onForwardNotSplit); 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 + "!"; - } + /// operator() - Just forwards to the corresponding property + /// handler. + void operator() (Init* I) { + InvokeDagInitHandler(this, I); } private: /// Option property handlers -- - /// Methods that handle properties that are common for all types of - /// options (like append_cmd, stop_compilation) + /// Methods that handle option properties such as (help) or (hidden). - 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 onHelp (const DagInit& d) { + CheckNumberOfArguments(d, 1); + optDesc_.Help = EscapeQuotes(InitPtrToString(d.getArg(0))); } - void onOutputSuffix (const DagInit* d) { - checkNumberOfArguments(d, 1); - checkToolProps(d); - const std::string& suf = InitPtrToString(d->getArg(0)); + void onHidden (const DagInit& d) { + CheckNumberOfArguments(d, 0); + optDesc_.setHidden(); + } - if (toolProps_->OptDescs[optDesc_.Name].Type != OptionType::Switch) - throw "Option " + optDesc_.Name - + " can't have 'output_suffix' property since it isn't a switch!"; + void onReallyHidden (const DagInit& d) { + CheckNumberOfArguments(d, 0); + optDesc_.setReallyHidden(); + } - toolProps_->OptDescs[optDesc_.Name].AddProperty - (OptionPropertyType::OutputSuffix, suf); + void onCommaSeparated (const DagInit& d) { + CheckNumberOfArguments(d, 0); + if (!optDesc_.isParameterList()) + throw "'comma_separated' is valid only on parameter list options!"; + optDesc_.setCommaSeparated(); } - void onForward (const DagInit* d) { - checkNumberOfArguments(d, 0); - checkToolProps(d); - toolProps_->OptDescs[optDesc_.Name].setForward(); + void onForwardNotSplit (const DagInit& d) { + CheckNumberOfArguments(d, 0); + if (!optDesc_.isParameter()) + throw "'forward_not_split' is valid only for parameter options!"; + optDesc_.setForwardNotSplit(); } - void onForwardAs (const DagInit* d) { - checkNumberOfArguments(d, 1); - checkToolProps(d); - const std::string& cmd = InitPtrToString(d->getArg(0)); + void onRequired (const DagInit& d) { + CheckNumberOfArguments(d, 0); - toolProps_->OptDescs[optDesc_.Name]. - AddProperty(OptionPropertyType::ForwardAs, cmd); + optDesc_.setRequired(); + optDesc_.CheckConsistency(); } - void onHelp (const DagInit* d) { - checkNumberOfArguments(d, 1); - const std::string& help_message = InitPtrToString(d->getArg(0)); + void onInit (const DagInit& d) { + CheckNumberOfArguments(d, 1); + Init* i = d.getArg(0); + const std::string& str = i->getAsString(); - optDesc_.Help = help_message; - } + bool correct = optDesc_.isParameter() && dynamic_cast(i); + correct |= (optDesc_.isSwitch() && (str == "true" || str == "false")); - void onRequired (const DagInit* d) { - checkNumberOfArguments(d, 0); - checkToolProps(d); - optDesc_.setRequired(); - } + if (!correct) + throw "Incorrect usage of the 'init' option property!"; - 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(); + optDesc_.InitVal = i; } - void onUnpackValues (const DagInit* d) { - checkNumberOfArguments(d, 0); - checkToolProps(d); - toolProps_->OptDescs[optDesc_.Name].setUnpackValues(); + void onOneOrMore (const DagInit& d) { + CheckNumberOfArguments(d, 0); + + optDesc_.setOneOrMore(); + optDesc_.CheckConsistency(); } - // Helper functions + void onZeroOrMore (const DagInit& d) { + CheckNumberOfArguments(d, 0); - /// checkToolProps - Throw an error if toolProps_ == 0. - void checkToolProps(const DagInit* d) { - if (!d) - throw "Option property " + d->getOperator()->getAsString() - + " can't be used in this context"; + if (optDesc_.isList()) + llvm::errs() << "Warning: specifying the 'zero_or_more' property " + "on a list option has no effect.\n"; + + optDesc_.setZeroOrMore(); + optDesc_.CheckConsistency(); } -}; + void onOptional (const DagInit& d) { + CheckNumberOfArguments(d, 0); -CollectOptionProperties::OptionPropertyHandlerMap -CollectOptionProperties::optionPropertyHandlers_; + if (!optDesc_.isList()) + llvm::errs() << "Warning: specifying the 'optional' property" + "on a non-list option has no effect.\n"; -bool CollectOptionProperties::staticMembersInitialized_ = false; + optDesc_.setOptional(); + optDesc_.CheckConsistency(); + } + void onMultiVal (const DagInit& d) { + CheckNumberOfArguments(d, 1); + int val = InitPtrToInt(d.getArg(0)); + if (val < 2) + throw "Error in the 'multi_val' property: " + "the value must be greater than 1!"; + if (!optDesc_.isParameterList()) + throw "The multi_val property is valid only on list options!"; + optDesc_.MultiVal = val; + } -/// 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(GetOperatorName(d)); 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; - } + OptionDescription OD(Type, Name); + + CheckNumberOfArguments(d, 2); + + // Alias option store the aliased option name in the 'Help' field and do not + // have any properties. + if (OD.isAlias()) { + OD.Help = InitPtrToString(d.getArg(1)); } else { - OD.Help = InitPtrToString(d.getArg(1)); + processOptionProperties(d, OD); + } + + // Switch options are ZeroOrMore by default. + if (OD.isSwitch()) { + if (!(OD.isOptional() || OD.isOneOrMore() || OD.isRequired())) + OD.setZeroOrMore(); } - OptDescs_.insertDescription(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 (const RecordVector& V, + OptionDescriptions& OptDescs) +{ + // For every OptionList: + for (RecordVector::const_iterator B = V.begin(), E = V.end(); B!=E; ++B) + { + // Throws an exception if the value does not exist. + ListInit* PropList = (*B)->getValueAsListInit("options"); -/// CollectProperties - Function object for iterating over a list of -/// tool property records. -class CollectProperties { -private: + // For every option description in this list: invoke AddOption. + 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 InFileOption; + std::string OutFileOption; + StrVector OutLanguage; + std::string OutputSuffix; + unsigned Flags; + const Init* OnEmpty; - // 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 (const std::string &n = "") + : Name(n), CmdLine(0), Actions(0), OutFileOption("-o"), + Flags(0), OnEmpty(0) + {} +}; +/// ToolDescriptions - A list of Tool information records. +typedef std::vector > ToolDescriptions; - /// This is where the information is stored - /// 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; +typedef void (CollectToolProperties::* CollectToolPropertiesHandler) +(const DagInit&); + +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) + : 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; + + AddHandler("actions", &CollectToolProperties::onActions); + AddHandler("command", &CollectToolProperties::onCommand); + AddHandler("in_language", &CollectToolProperties::onInLanguage); + AddHandler("join", &CollectToolProperties::onJoin); + AddHandler("out_language", &CollectToolProperties::onOutLanguage); + + AddHandler("out_file_option", &CollectToolProperties::onOutFileOption); + AddHandler("in_file_option", &CollectToolProperties::onInFileOption); + + AddHandler("output_suffix", &CollectToolProperties::onOutputSuffix); + AddHandler("sink", &CollectToolProperties::onSink); + AddHandler("works_on_empty", &CollectToolProperties::onWorksOnEmpty); 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); - - if (method != propertyHandlers_.end()) { - PropertyHandler h = method->second; - (this->*h)(&d); - } - else { - throw "Unknown tool property: " + property_name + "!"; - } + void operator() (Init* I) { + InvokeDagInitHandler(this, I); } private: @@ -673,210 +900,534 @@ private: /// Functions that extract information about tool properties from /// DAG representation. - void onCmdLine (const DagInit* d) { - checkNumberOfArguments(d, 1); - toolProps_.CmdLine = d->getArg(0); + void onActions (const DagInit& d) { + CheckNumberOfArguments(d, 1); + Init* Case = d.getArg(0); + if (typeid(*Case) != typeid(DagInit) || + GetOperatorName(static_cast(*Case)) != "case") + throw "The argument to (actions) should be a 'case' construct!"; + toolDesc_.Actions = Case; } - void onInLanguage (const DagInit* d) { - checkNumberOfArguments(d, 1); - Init* arg = d->getArg(0); + void onCommand (const DagInit& d) { + CheckNumberOfArguments(d, 1); + toolDesc_.CmdLine = d.getArg(0); + } + + /// onInOutLanguage - Common implementation of on{In,Out}Language(). + void onInOutLanguage (const DagInit& d, StrVector& OutVec) { + CheckNumberOfArguments(d, 1); - // Find out the argument's type. - if (typeid(*arg) == typeid(StringInit)) { - // It's a string. - toolProps_.InLanguage.push_back(InitPtrToString(arg)); + // Copy strings to the output vector. + for (unsigned i = 0, NumArgs = d.getNumArgs(); i < NumArgs; ++i) { + OutVec.push_back(InitPtrToString(d.getArg(i))); } - else { - // It's a list. - const ListInit& lst = InitPtrToList(arg); - StrVector& out = toolProps_.InLanguage; - // Copy strings to the output vector. - for (ListInit::const_iterator B = lst.begin(), E = lst.end(); - B != E; ++B) { - out.push_back(InitPtrToString(*B)); - } + // Remove duplicates. + std::sort(OutVec.begin(), OutVec.end()); + StrVector::iterator newE = std::unique(OutVec.begin(), OutVec.end()); + OutVec.erase(newE, OutVec.end()); + } - // Remove duplicates. - std::sort(out.begin(), out.end()); - StrVector::iterator newE = std::unique(out.begin(), out.end()); - out.erase(newE, out.end()); - } + + void onInLanguage (const DagInit& d) { + this->onInOutLanguage(d, toolDesc_.InLanguage); } - void onJoin (const DagInit* d) { - checkNumberOfArguments(d, 0); - toolProps_.setJoin(); + void onJoin (const DagInit& d) { + CheckNumberOfArguments(d, 0); + toolDesc_.setJoin(); } - void onOutLanguage (const DagInit* d) { - checkNumberOfArguments(d, 1); - toolProps_.OutLanguage = InitPtrToString(d->getArg(0)); + void onOutLanguage (const DagInit& d) { + this->onInOutLanguage(d, toolDesc_.OutLanguage); } - void onOutputSuffix (const DagInit* d) { - checkNumberOfArguments(d, 1); - toolProps_.OutputSuffix = InitPtrToString(d->getArg(0)); + void onOutFileOption (const DagInit& d) { + CheckNumberOfArguments(d, 1); + toolDesc_.OutFileOption = InitPtrToString(d.getArg(0)); } - void onSink (const DagInit* d) { - checkNumberOfArguments(d, 0); - optDescs_.HasSink = true; - toolProps_.setSink(); + void onInFileOption (const DagInit& d) { + CheckNumberOfArguments(d, 1); + toolDesc_.InFileOption = InitPtrToString(d.getArg(0)); } - // 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); + void onOutputSuffix (const DagInit& d) { + CheckNumberOfArguments(d, 1); + toolDesc_.OutputSuffix = InitPtrToString(d.getArg(0)); } -}; + void onSink (const DagInit& d) { + CheckNumberOfArguments(d, 0); + toolDesc_.setSink(); + } -// Defintions of static members of CollectProperties. -CollectProperties::PropertyHandlerMap CollectProperties::propertyHandlers_; -bool CollectProperties::staticMembersInitialized_ = false; + void onWorksOnEmpty (const DagInit& d) { + toolDesc_.OnEmpty = d.getArg(0); + } +}; -/// 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 (const RecordVector& Tools, + ToolDescriptions& ToolDescs) { // Iterate over a properties list of every Tool definition - for (;B!=E;++B) { + for (RecordVector::const_iterator B = Tools.begin(), + E = Tools.end(); B!=E; ++B) { const Record* T = *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(const RecordVector& CompilationGraphs, + DagVector& Out) { + for (RecordVector::const_iterator B = CompilationGraphs.begin(), + E = CompilationGraphs.end(); B != E; ++B) { + const ListInit* Edges = (*B)->getValueAsListInit("edges"); + + for (ListInit::const_iterator B = Edges->begin(), + E = Edges->end(); B != E; ++B) { + Out.push_back(&InitPtrToDag(*B)); + } + } +} + +/// 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 DagVector& EdgeVector, + ToolDescriptions& ToolDescs) { + + // List all tools mentioned in the graph. + llvm::StringSet<> ToolsInGraph; + + for (DagVector::const_iterator B = EdgeVector.begin(), + E = EdgeVector.end(); B != E; ++B) { + + const DagInit* Edge = *B; + const std::string& NodeA = InitPtrToString(Edge->getArg(0)); + const std::string& NodeB = InitPtrToString(Edge->getArg(1)); + + if (NodeA != "root") + ToolsInGraph.insert(NodeA); + ToolsInGraph.insert(NodeB); + } + + // 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 language names. 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); + for (StrVector::const_iterator B = D.OutLanguage.begin(), + E = D.OutLanguage.end(); B != E; ++B) + ToolToOutLang[D.Name].insert(*B); + } +} + +/// Intersect - Is set intersection non-empty? +bool Intersect (const StringSet<>& S1, const StringSet<>& S2) { + for (StringSet<>::const_iterator B = S1.begin(), E = S1.end(); B != E; ++B) { + if (S2.count(B->first()) != 0) + return true; + } + return false; +} + +/// TypecheckGraph - Check that names for output and input languages +/// on all edges do match. +void TypecheckGraph (const DagVector& EdgeVector, + const ToolDescriptions& ToolDescs) { + StringMap > ToolToInLang; + StringMap > ToolToOutLang; + + FillInToolToLang(ToolDescs, ToolToInLang, ToolToOutLang); + + for (DagVector::const_iterator B = EdgeVector.begin(), + E = EdgeVector.end(); B != E; ++B) { + const DagInit* Edge = *B; + const std::string& NodeA = InitPtrToString(Edge->getArg(0)); + const std::string& NodeB = InitPtrToString(Edge->getArg(1)); + StringMap >::iterator IA = ToolToOutLang.find(NodeA); + StringMap >::iterator IB = ToolToInLang.find(NodeB); -/// CollectPropertiesFromOptionLists - Gather information about -/// *global* option properties from all OptionLists. -void CollectPropertiesFromOptionLists (RecordVector::const_iterator B, - RecordVector::const_iterator E, - GlobalOptionDescriptions& OptDescs) + if (NodeB == "root") + throw "Edges back to the root are not allowed!"; + + if (NodeA != "root") { + if (IA == ToolToOutLang.end()) + throw NodeA + ": no output language defined!"; + if (IB == ToolToInLang.end()) + throw NodeB + ": no input language defined!"; + + if (!Intersect(IA->second, IB->second)) { + throw "Edge " + NodeA + "->" + NodeB + + ": output->input language mismatch"; + } + } + } +} + +/// 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 (that is, they are passed directly to TestCallback). +/// TestCallback must have type 'void TestCallback(const DagInit*, unsigned +/// IndentLevel, bool FirstTest)'. +/// StatementCallback must have type 'void StatementCallback(const Init*, +/// unsigned IndentLevel)'. +template +void WalkCase(const Init* Case, F1 TestCallback, F2 StatementCallback, + unsigned IndentLevel = 0) { - // Iterate over a properties list of every Tool definition - for (; B!=E; ++B) { - RecordVector::value_type T = *B; - // Throws an exception if the value does not exist. - ListInit* PropList = T->getValueAsListInit("options"); + const DagInit& d = InitPtrToDag(Case); - std::for_each(PropList->begin(), PropList->end(), AddOption(OptDescs)); + // Error checks. + if (GetOperatorName(d) != "case") + throw "WalkCase should be invoked only on 'case' expressions!"; + + if (d.getNumArgs() < 2) + throw "There should be at least one clause in the 'case' expression:\n" + + d.getAsString(); + + // Main loop. + bool even = false; + const unsigned numArgs = d.getNumArgs(); + unsigned i = 1; + for (DagInit::const_arg_iterator B = d.arg_begin(), E = d.arg_end(); + B != E; ++B) { + Init* arg = *B; + + if (!even) + { + // Handle test. + const DagInit& Test = InitPtrToDag(arg); + + if (GetOperatorName(Test) == "default" && (i+1 != numArgs)) + throw "The 'default' clause should be the last in the " + "'case' construct!"; + if (i == numArgs) + throw "Case construct handler: no corresponding action " + "found for the test " + Test.getAsString() + '!'; + + TestCallback(Test, IndentLevel, (i == 1)); + } + else + { + if (dynamic_cast(arg) + && GetOperatorName(static_cast(*arg)) == "case") { + // Nested 'case'. + WalkCase(arg, TestCallback, StatementCallback, IndentLevel + Indent1); + } + + // Handle statement. + StatementCallback(arg, IndentLevel); + } + + ++i; + even = !even; } } +/// ExtractOptionNames - A helper function object used by +/// CheckForSuperfluousOptions() to walk the 'case' DAG. +class ExtractOptionNames { + llvm::StringSet<>& OptionNames_; + + void processDag(const Init* Statement) { + const DagInit& Stmt = InitPtrToDag(Statement); + const std::string& ActionName = GetOperatorName(Stmt); + if (ActionName == "forward" || ActionName == "forward_as" || + ActionName == "forward_value" || + ActionName == "forward_transformed_value" || + ActionName == "parameter_equals" || ActionName == "element_in_list") { + CheckNumberOfArguments(Stmt, 1); + + Init* Arg = Stmt.getArg(0); + if (typeid(*Arg) == typeid(StringInit)) + OptionNames_.insert(InitPtrToString(Arg)); + } + else if (ActionName == "any_switch_on" || ActionName == "switch_on" || + ActionName == "any_not_empty" || ActionName == "any_empty" || + ActionName == "not_empty" || ActionName == "empty") { + for (unsigned i = 0, NumArgs = Stmt.getNumArgs(); i < NumArgs; ++i) { + Init* Arg = Stmt.getArg(i); + if (typeid(*Arg) == typeid(StringInit)) + OptionNames_.insert(InitPtrToString(Arg)); + } + } + else if (ActionName == "and" || ActionName == "or" || ActionName == "not") { + for (unsigned i = 0, NumArgs = Stmt.getNumArgs(); i < NumArgs; ++i) { + this->processDag(Stmt.getArg(i)); + } + } + } + +public: + ExtractOptionNames(llvm::StringSet<>& OptionNames) : OptionNames_(OptionNames) + {} + + void operator()(const Init* Statement) { + // Statement is either a dag, or a list of dags. + if (typeid(*Statement) == typeid(ListInit)) { + const ListInit& DagList = *static_cast(Statement); + for (ListInit::const_iterator B = DagList.begin(), E = DagList.end(); + B != E; ++B) + this->processDag(*B); + } + else { + this->processDag(Statement); + } + } + + void operator()(const DagInit& Test, unsigned, bool) { + this->operator()(&Test); + } + void operator()(const Init* Statement, unsigned) { + this->operator()(Statement); + } +}; + +/// IsOptionalEdge - Validate that the 'optional_edge' has proper structure. +bool IsOptionalEdge (const DagInit& Edg) { + return (GetOperatorName(Edg) == "optional_edge") && (Edg.getNumArgs() > 2); +} + /// 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 DagVector& EdgeVector, + 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 (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 (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 (DagVector::const_iterator B = EdgeVector.begin(), + E = EdgeVector.end(); B != E; ++B) { + const DagInit& Edge = **B; + if (IsOptionalEdge(Edge)) { + const DagInit& Weight = InitPtrToDag(Edge.getArg(2)); + 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! " + llvm::errs() << "Warning: option '-" << Val.Name << "' has no effect! " "Probable cause: this option is specified only in the OptionList.\n"; } } -/// EmitCaseTest1Arg - Helper function used by -/// EmitCaseConstructHandler. -bool EmitCaseTest1Arg(const std::string& TestName, - const DagInit& d, - const GlobalOptionDescriptions& 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!"; +/// EmitCaseTest0Args - Helper function used by EmitCaseConstructHandler(). +bool EmitCaseTest0Args(const std::string& TestName, raw_ostream& O) { + if (TestName == "single_input_file") { + O << "InputFilenames.size() == 1"; + return true; + } + else if (TestName == "multiple_input_files") { + O << "InputFilenames.size() > 1"; + return true; + } + + return false; +} + +/// EmitMultipleArgumentTest - Helper function used by +/// EmitCaseTestMultipleArgs() +template +void EmitMultipleArgumentTest(const DagInit& D, const char* LogicOp, + F Callback, raw_ostream& O) +{ + for (unsigned i = 0, NumArgs = D.getNumArgs(); i < NumArgs; ++i) { + if (i != 0) + O << ' ' << LogicOp << ' '; + Callback(InitPtrToString(D.getArg(i)), O); + } +} + +// Callbacks for use with EmitMultipleArgumentTest + +class EmitSwitchOn { + const OptionDescriptions& OptDescs_; +public: + EmitSwitchOn(const OptionDescriptions& OptDescs) : OptDescs_(OptDescs) + {} + + void operator()(const std::string& OptName, raw_ostream& O) const { + const OptionDescription& OptDesc = OptDescs_.FindSwitch(OptName); O << OptDesc.GenVariableName(); + } +}; + +class EmitEmptyTest { + bool EmitNegate_; + const OptionDescriptions& OptDescs_; +public: + EmitEmptyTest(bool EmitNegate, const OptionDescriptions& OptDescs) + : EmitNegate_(EmitNegate), OptDescs_(OptDescs) + {} + + void operator()(const std::string& OptName, raw_ostream& O) const { + const char* Neg = (EmitNegate_ ? "!" : ""); + if (OptName == "o") { + O << Neg << "OutputFilename.empty()"; + } + else if (OptName == "save-temps") { + O << Neg << "(SaveTemps == SaveTempsEnum::Unset)"; + } + else { + const OptionDescription& OptDesc = OptDescs_.FindListOrParameter(OptName); + O << Neg << OptDesc.GenVariableName() << ".empty()"; + } + } +}; + + +/// EmitCaseTestMultipleArgs - Helper function used by EmitCaseTest1Arg() +bool EmitCaseTestMultipleArgs (const std::string& TestName, + const DagInit& d, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + if (TestName == "any_switch_on") { + EmitMultipleArgumentTest(d, "||", EmitSwitchOn(OptDescs), O); return true; - } else if (TestName == "input_languages_contain") { - O << "InLangs.count(\"" << OptName << "\") != 0"; + } + else if (TestName == "switch_on") { + EmitMultipleArgumentTest(d, "&&", EmitSwitchOn(OptDescs), O); + return true; + } + else if (TestName == "any_not_empty") { + EmitMultipleArgumentTest(d, "||", EmitEmptyTest(true, OptDescs), O); + return true; + } + else if (TestName == "any_empty") { + EmitMultipleArgumentTest(d, "||", EmitEmptyTest(false, OptDescs), O); + return true; + } + else if (TestName == "not_empty") { + EmitMultipleArgumentTest(d, "&&", EmitEmptyTest(true, OptDescs), O); return true; - } else if (TestName == "in_language") { + } + else if (TestName == "empty") { + EmitMultipleArgumentTest(d, "&&", EmitEmptyTest(false, OptDescs), O); + return true; + } + + return false; +} + +/// EmitCaseTest1Arg - Helper function used by EmitCaseTest1OrMoreArgs() +bool EmitCaseTest1Arg (const std::string& TestName, + const DagInit& d, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + const std::string& Arg = InitPtrToString(d.getArg(0)); + + if (TestName == "input_languages_contain") { + O << "InLangs.count(\"" << Arg << "\") != 0"; + return true; + } + else if (TestName == "in_language") { // This works only for single-argument Tool::GenerateAction. Join // tools can process several files in different languages simultaneously. // TODO: make this work with Edge::Weight (if possible). - O << "LangMap.GetLanguage(inFile) == \"" << OptName << '\"'; + O << "LangMap.GetLanguage(inFile) == \"" << Arg << '\"'; return true; - } else if (TestName == "not_empty") { - if (OptName == "o") { - O << "!OutputFilename.empty()"; - return true; - } - else { - const GlobalOptionDescription& OptDesc = OptDescs.FindOption(OptName); - if (OptDesc.Type == OptionType::Switch) - throw OptName + ": incorrect option type!"; - O << '!' << OptDesc.GenVariableName() << ".empty()"; - return true; - } } return false; } -/// EmitCaseTest2Args - Helper function used by -/// EmitCaseConstructHandler. +/// EmitCaseTest1OrMoreArgs - Helper function used by +/// EmitCaseConstructHandler() +bool EmitCaseTest1OrMoreArgs(const std::string& TestName, + const DagInit& d, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + CheckNumberOfArguments(d, 1); + return EmitCaseTest1Arg(TestName, d, OptDescs, O) || + EmitCaseTestMultipleArgs(TestName, d, OptDescs, O); +} + +/// EmitCaseTest2Args - Helper function used by EmitCaseConstructHandler(). bool EmitCaseTest2Args(const std::string& TestName, const DagInit& d, - const char* IndentLevel, - const GlobalOptionDescriptions& OptDescs, - std::ostream& O) { - checkNumberOfArguments(&d, 2); + unsigned IndentLevel, + const OptionDescriptions& OptDescs, + raw_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); if (TestName == "parameter_equals") { - if (OptDesc.Type != OptionType::Parameter - && OptDesc.Type != OptionType::Prefix) - throw OptName + ": incorrect option type!"; + const OptionDescription& OptDesc = OptDescs.FindParameter(OptName); 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!"; + const OptionDescription& OptDesc = OptDescs.FindParameterList(OptName); const std::string& VarName = OptDesc.GenVariableName(); - O << "std::find(" << VarName << ".begin(),\n" - << IndentLevel << Indent1 << VarName << ".end(), \"" + O << "std::find(" << VarName << ".begin(),\n"; + O.indent(IndentLevel + Indent1) + << VarName << ".end(), \"" << OptArg << "\") != " << VarName << ".end()"; return true; } @@ -886,509 +1437,916 @@ 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, - std::ostream& O); +void EmitCaseTest(const DagInit& d, unsigned IndentLevel, + const OptionDescriptions& OptDescs, + raw_ostream& O); /// EmitLogicalOperationTest - Helper function used by /// EmitCaseConstructHandler. void EmitLogicalOperationTest(const DagInit& d, const char* LogicOp, - const char* IndentLevel, - const GlobalOptionDescriptions& OptDescs, - std::ostream& O) { + unsigned IndentLevel, + const OptionDescriptions& OptDescs, + raw_ostream& O) { O << '('; - for (unsigned j = 0, NumArgs = d.getNumArgs(); j < NumArgs; ++j) { - const DagInit& InnerTest = InitPtrToDag(d.getArg(j)); + for (unsigned i = 0, NumArgs = d.getNumArgs(); i < NumArgs; ++i) { + const DagInit& InnerTest = InitPtrToDag(d.getArg(i)); EmitCaseTest(InnerTest, IndentLevel, OptDescs, O); - if (j != NumArgs - 1) - O << ")\n" << IndentLevel << Indent1 << ' ' << LogicOp << " ("; - else + if (i != NumArgs - 1) { + O << ")\n"; + O.indent(IndentLevel + Indent1) << ' ' << LogicOp << " ("; + } + else { O << ')'; + } } } +void EmitLogicalNot(const DagInit& d, unsigned IndentLevel, + const OptionDescriptions& OptDescs, raw_ostream& O) +{ + CheckNumberOfArguments(d, 1); + const DagInit& InnerTest = InitPtrToDag(d.getArg(0)); + O << "! ("; + EmitCaseTest(InnerTest, IndentLevel, OptDescs, O); + O << ")"; +} + /// EmitCaseTest - Helper function used by EmitCaseConstructHandler. -void EmitCaseTest(const DagInit& d, const char* IndentLevel, - const GlobalOptionDescriptions& OptDescs, - std::ostream& O) { - const std::string& TestName = d.getOperator()->getAsString(); +void EmitCaseTest(const DagInit& d, unsigned IndentLevel, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + const std::string& TestName = GetOperatorName(d); if (TestName == "and") EmitLogicalOperationTest(d, "&&", IndentLevel, OptDescs, O); else if (TestName == "or") EmitLogicalOperationTest(d, "||", IndentLevel, OptDescs, O); - else if (EmitCaseTest1Arg(TestName, d, OptDescs, O)) + else if (TestName == "not") + EmitLogicalNot(d, IndentLevel, OptDescs, O); + else if (EmitCaseTest0Args(TestName, O)) + return; + else if (EmitCaseTest1OrMoreArgs(TestName, d, OptDescs, O)) return; else if (EmitCaseTest2Args(TestName, d, IndentLevel, OptDescs, O)) return; else - throw TestName + ": unknown edge property!"; + throw "Unknown test '" + TestName + "' used in the 'case' construct!"; } -// Emit code that handles the 'case' construct. -// Takes a function object that should emit code for every case clause. -// Callback's type is -// void F(Init* Statement, const char* IndentLevel, std::ostream& O). + +/// EmitCaseTestCallback - Callback used by EmitCaseConstructHandler. +class EmitCaseTestCallback { + bool EmitElseIf_; + const OptionDescriptions& OptDescs_; + raw_ostream& O_; +public: + + EmitCaseTestCallback(bool EmitElseIf, + const OptionDescriptions& OptDescs, raw_ostream& O) + : EmitElseIf_(EmitElseIf), OptDescs_(OptDescs), O_(O) + {} + + void operator()(const DagInit& Test, unsigned IndentLevel, bool FirstTest) + { + if (GetOperatorName(Test) == "default") { + O_.indent(IndentLevel) << "else {\n"; + } + else { + O_.indent(IndentLevel) + << ((!FirstTest && EmitElseIf_) ? "else if (" : "if ("); + EmitCaseTest(Test, IndentLevel, OptDescs_, O_); + O_ << ") {\n"; + } + } +}; + +/// EmitCaseStatementCallback - Callback used by EmitCaseConstructHandler. template -void EmitCaseConstructHandler(const DagInit* d, const char* IndentLevel, +class EmitCaseStatementCallback { + F Callback_; + raw_ostream& O_; +public: + + EmitCaseStatementCallback(F Callback, raw_ostream& O) + : Callback_(Callback), O_(O) + {} + + void operator() (const Init* Statement, unsigned IndentLevel) { + // Is this a nested 'case'? + bool IsCase = dynamic_cast(Statement) && + GetOperatorName(static_cast(*Statement)) == "case"; + + // If so, ignore it, it is handled by our caller, WalkCase. + if (!IsCase) { + if (typeid(*Statement) == typeid(ListInit)) { + const ListInit& DagList = *static_cast(Statement); + for (ListInit::const_iterator B = DagList.begin(), E = DagList.end(); + B != E; ++B) + Callback_(*B, (IndentLevel + Indent1), O_); + } + else { + Callback_(Statement, (IndentLevel + Indent1), O_); + } + } + O_.indent(IndentLevel) << "}\n"; + } + +}; + +/// EmitCaseConstructHandler - Emit code that handles the 'case' +/// construct. Takes a function object that should emit code for every case +/// clause. Implemented on top of WalkCase. +/// Callback's type is void F(const Init* Statement, unsigned IndentLevel, +/// raw_ostream& O). +/// EmitElseIf parameter controls the type of condition that is emitted ('if +/// (..) {..} else if (..) {} .. else {..}' vs. 'if (..) {..} if(..) {..} +/// .. else {..}'). +template +void EmitCaseConstructHandler(const Init* Case, unsigned IndentLevel, F Callback, bool EmitElseIf, - const GlobalOptionDescriptions& OptDescs, - std::ostream& O) { - assert(d->getOperator()->getAsString() == "case"); + const OptionDescriptions& OptDescs, + raw_ostream& O) { + WalkCase(Case, EmitCaseTestCallback(EmitElseIf, OptDescs, O), + EmitCaseStatementCallback(Callback, O), IndentLevel); +} - unsigned numArgs = d->getNumArgs(); - if (d->getNumArgs() < 2) - throw "There should be at least one clause in the 'case' expression:\n" - + d->getAsString(); +/// TokenizeCmdLine - converts from +/// "$CALL(HookName, 'Arg1', 'Arg2')/path -arg1 -arg2" to +/// ["$CALL(", "HookName", "Arg1", "Arg2", ")/path", "-arg1", "-arg2"]. +void TokenizeCmdLine(const std::string& CmdLine, StrVector& Out) { + const char* Delimiters = " \t\n\v\f\r"; + enum TokenizerState + { Normal, SpecialCommand, InsideSpecialCommand, InsideQuotationMarks } + cur_st = Normal; + + if (CmdLine.empty()) + return; + Out.push_back(""); + + std::string::size_type B = CmdLine.find_first_not_of(Delimiters), + E = CmdLine.size(); + + for (; B != E; ++B) { + char cur_ch = CmdLine[B]; + + switch (cur_st) { + case Normal: + if (cur_ch == '$') { + cur_st = SpecialCommand; + break; + } + if (OneOf(Delimiters, cur_ch)) { + // Skip whitespace + B = CmdLine.find_first_not_of(Delimiters, B); + if (B == std::string::npos) { + B = E-1; + continue; + } + --B; + Out.push_back(""); + continue; + } + break; + + + case SpecialCommand: + if (OneOf(Delimiters, cur_ch)) { + cur_st = Normal; + Out.push_back(""); + continue; + } + if (cur_ch == '(') { + Out.push_back(""); + cur_st = InsideSpecialCommand; + continue; + } + break; + + case InsideSpecialCommand: + if (OneOf(Delimiters, cur_ch)) { + continue; + } + if (cur_ch == '\'') { + cur_st = InsideQuotationMarks; + Out.push_back(""); + continue; + } + if (cur_ch == ')') { + cur_st = Normal; + Out.push_back(""); + } + if (cur_ch == ',') { + continue; + } - for (unsigned i = 0; i != numArgs; ++i) { - const DagInit& Test = InitPtrToDag(d->getArg(i)); + break; - // 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"; + case InsideQuotationMarks: + if (cur_ch == '\'') { + cur_st = InsideSpecialCommand; + continue; + } + break; + } + + Out.back().push_back(cur_ch); + } +} + +/// SubstituteCall - Given "$CALL(HookName, [Arg1 [, Arg2 [...]]])", output +/// "hooks::HookName([Arg1 [, Arg2 [, ...]]])". Helper function used by +/// SubstituteSpecialCommands(). +StrVector::const_iterator +SubstituteCall (StrVector::const_iterator Pos, + StrVector::const_iterator End, + bool IsJoin, raw_ostream& O) +{ + const char* errorMessage = "Syntax error in $CALL invocation!"; + CheckedIncrement(Pos, End, errorMessage); + const std::string& CmdName = *Pos; + + if (CmdName == ")") + throw "$CALL invocation: empty argument list!"; + + O << "hooks::"; + O << CmdName << "("; + + + bool firstIteration = true; + while (true) { + CheckedIncrement(Pos, End, errorMessage); + const std::string& Arg = *Pos; + assert(Arg.size() != 0); + + if (Arg[0] == ')') + break; + + if (firstIteration) + firstIteration = false; + else + O << ", "; + + if (Arg == "$INFILE") { + if (IsJoin) + throw "$CALL(Hook, $INFILE) can't be used with a Join tool!"; + else + O << "inFile.c_str()"; } else { - O << IndentLevel << ((i != 0 && EmitElseIf) ? "else if (" : "if ("); - EmitCaseTest(Test, IndentLevel, OptDescs, O); - O << ") {\n"; + O << '"' << Arg << '"'; } + } - // Emit the corresponding statement. - ++i; - if (i == numArgs) - throw "Case construct handler: no corresponding action " - "found for the test " + Test.getAsString() + '!'; + O << ')'; - 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); + return Pos; +} + +/// SubstituteEnv - Given '$ENV(VAR_NAME)', output 'getenv("VAR_NAME")'. Helper +/// function used by SubstituteSpecialCommands(). +StrVector::const_iterator +SubstituteEnv (StrVector::const_iterator Pos, + StrVector::const_iterator End, raw_ostream& O) +{ + const char* errorMessage = "Syntax error in $ENV invocation!"; + CheckedIncrement(Pos, End, errorMessage); + const std::string& EnvName = *Pos; + + if (EnvName == ")") + throw "$ENV invocation: empty argument list!"; + + O << "checkCString(std::getenv(\""; + O << EnvName; + O << "\"))"; + + CheckedIncrement(Pos, End, errorMessage); + + return Pos; +} + +/// SubstituteSpecialCommands - Given an invocation of $CALL or $ENV, output +/// handler code. Helper function used by EmitCmdLineVecFill(). +StrVector::const_iterator +SubstituteSpecialCommands (StrVector::const_iterator Pos, + StrVector::const_iterator End, + bool IsJoin, raw_ostream& O) +{ + + const std::string& cmd = *Pos; + + // Perform substitution. + if (cmd == "$CALL") { + Pos = SubstituteCall(Pos, End, IsJoin, O); + } + else if (cmd == "$ENV") { + Pos = SubstituteEnv(Pos, End, O); + } + else { + throw "Unknown special command: " + cmd; + } + + // Handle '$CMD(ARG)/additional/text'. + const std::string& Leftover = *Pos; + assert(Leftover.at(0) == ')'); + if (Leftover.size() != 1) + O << " + std::string(\"" << (Leftover.c_str() + 1) << "\")"; + + return Pos; +} + +/// 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 IsJoin, unsigned IndentLevel, + raw_ostream& O) { + StrVector StrVec; + TokenizeCmdLine(InitPtrToString(CmdLine), StrVec); + + if (StrVec.empty()) + throw "Tool '" + ToolName + "' has empty command line!"; + + StrVector::const_iterator B = StrVec.begin(), E = StrVec.end(); + + // Emit the command itself. + assert(!StrVec[0].empty()); + O.indent(IndentLevel) << "cmd = "; + if (StrVec[0][0] == '$') { + B = SubstituteSpecialCommands(B, E, IsJoin, O); + ++B; + } + else { + O << '"' << StrVec[0] << '"'; + ++B; + } + O << ";\n"; + + // Go through the command arguments. + assert(B <= E); + for (; B != E; ++B) { + const std::string& cmd = *B; + + assert(!cmd.empty()); + O.indent(IndentLevel); + + if (cmd.at(0) == '$') { + O << "vec.push_back(std::make_pair(0, "; + B = SubstituteSpecialCommands(B, E, IsJoin, O); + O << "));\n"; } else { - Callback(arg, IndentLevel, O); + O << "vec.push_back(std::make_pair(0, \"" << cmd << "\"));\n"; } - O << IndentLevel << "}\n"; } + +} + +/// EmitForEachListElementCycleHeader - Emit common code for iterating through +/// all elements of a list. Helper function used by +/// EmitForwardOptionPropertyHandlingCode. +void EmitForEachListElementCycleHeader (const OptionDescription& D, + unsigned IndentLevel, + raw_ostream& O) { + unsigned IndentLevel1 = IndentLevel + Indent1; + + O.indent(IndentLevel) + << "for (" << D.GenTypeDeclaration() + << "::iterator B = " << D.GenVariableName() << ".begin(),\n"; + O.indent(IndentLevel) + << "E = " << D.GenVariableName() << ".end(); B != E;) {\n"; + O.indent(IndentLevel1) << "unsigned pos = " << D.GenVariableName() + << ".getPosition(B - " << D.GenVariableName() + << ".begin());\n"; } /// EmitForwardOptionPropertyHandlingCode - Helper function used to -/// implement EmitOptionPropertyHandlingCode(). Emits code for +/// implement EmitActionHandler. Emits code for /// handling the (forward) and (forward_as) option properties. -void EmitForwardOptionPropertyHandlingCode (const ToolOptionDescription& D, +void EmitForwardOptionPropertyHandlingCode (const OptionDescription& D, + unsigned IndentLevel, const std::string& NewName, - std::ostream& O) { + raw_ostream& O) { const std::string& Name = NewName.empty() ? ("-" + D.Name) : NewName; + unsigned IndentLevel1 = IndentLevel + Indent1; switch (D.Type) { case OptionType::Switch: - O << Indent3 << "vec.push_back(\"" << Name << "\");\n"; + O.indent(IndentLevel) + << "vec.push_back(std::make_pair(" << D.GenVariableName() + << ".getPosition(), \"" << Name << "\"));\n"; break; case OptionType::Parameter: - O << Indent3 << "vec.push_back(\"" << Name << "\");\n"; - O << Indent3 << "vec.push_back(" << D.GenVariableName() << ");\n"; + O.indent(IndentLevel) << "vec.push_back(std::make_pair(" + << D.GenVariableName() + <<".getPosition(), \"" << Name; + + if (!D.isForwardNotSplit()) { + O << "\"));\n"; + O.indent(IndentLevel) << "vec.push_back(std::make_pair(" + << D.GenVariableName() << ".getPosition(), " + << D.GenVariableName() << "));\n"; + } + else { + O << "=\" + " << D.GenVariableName() << "));\n"; + } break; case OptionType::Prefix: - O << Indent3 << "vec.push_back(\"" << Name << "\" + " - << D.GenVariableName() << ");\n"; + O.indent(IndentLevel) << "vec.push_back(std::make_pair(" + << D.GenVariableName() << ".getPosition(), \"" + << 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"; + EmitForEachListElementCycleHeader(D, IndentLevel, O); + O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, \"" + << Name << "\" + " << "*B));\n"; + O.indent(IndentLevel1) << "++B;\n"; + + for (int i = 1, j = D.MultiVal; i < j; ++i) { + O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, *B));\n"; + O.indent(IndentLevel1) << "++B;\n"; + } + + O.indent(IndentLevel) << "}\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"; + EmitForEachListElementCycleHeader(D, IndentLevel, O); + O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, \"" + << Name << "\"));\n"; + + for (int i = 0, j = D.MultiVal; i < j; ++i) { + O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, *B));\n"; + O.indent(IndentLevel1) << "++B;\n"; + } + + O.indent(IndentLevel) << "}\n"; + break; + case OptionType::SwitchList: + EmitForEachListElementCycleHeader(D, IndentLevel, O); + O.indent(IndentLevel1) << "vec.push_back(std::make_pair(pos, \"" + << Name << "\"));\n"; + O.indent(IndentLevel1) << "++B;\n"; + O.indent(IndentLevel) << "}\n"; break; case OptionType::Alias: default: - throw std::string("Aliases are not allowed in tool option descriptions!"); + throw "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; - } - if (D.isForward() || D.isUnpackValues()) - ret = true; - return ret; -} +/// ActionHandlingCallbackBase - Base class of EmitActionHandlersCallback and +/// EmitPreprocessOptionsCallback. +struct ActionHandlingCallbackBase +{ + + void onErrorDag(const DagInit& d, + unsigned IndentLevel, raw_ostream& O) const + { + O.indent(IndentLevel) + << "PrintError(\"" + << (d.getNumArgs() >= 1 ? InitPtrToString(d.getArg(0)) : "Unknown error!") + << "\");\n"; + O.indent(IndentLevel) << "return 1;\n"; + } + + void onWarningDag(const DagInit& d, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(d, 1); + O.indent(IndentLevel) << "llvm::errs() << \"" + << InitPtrToString(d.getArg(0)) << "\";\n"; + } + +}; -/// EmitOptionPropertyHandlingCode - Helper function used by -/// EmitGenerateActionMethod(). Emits code that handles option -/// properties. -void EmitOptionPropertyHandlingCode (const ToolOptionDescription& D, - std::ostream& O) +/// EmitActionHandlersCallback - Emit code that handles actions. Used by +/// EmitGenerateActionMethod() as an argument to EmitCaseConstructHandler(). +class EmitActionHandlersCallback; + +typedef void (EmitActionHandlersCallback::* EmitActionHandlersCallbackHandler) +(const DagInit&, unsigned, raw_ostream&) const; + +class EmitActionHandlersCallback : + public ActionHandlingCallbackBase, + public HandlerTable { - 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()"; + typedef EmitActionHandlersCallbackHandler Handler; - O <<") {\n"; + const OptionDescriptions& OptDescs; - // 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; + /// EmitHookInvocation - Common code for hook invocation from actions. Used by + /// onAppendCmd and onOutputSuffix. + void EmitHookInvocation(const std::string& Str, + const char* BlockOpen, const char* BlockClose, + unsigned IndentLevel, raw_ostream& O) const + { + StrVector Out; + TokenizeCmdLine(Str, Out); - 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; + for (StrVector::const_iterator B = Out.begin(), E = Out.end(); + B != E; ++B) { + const std::string& cmd = *B; + + O.indent(IndentLevel) << BlockOpen; + + if (cmd.at(0) == '$') + B = SubstituteSpecialCommands(B, E, /* IsJoin = */ true, O); + else + O << '"' << cmd << '"'; + + O << BlockClose; } } - // Handle flags + void onAppendCmd (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(Dag, 1); + this->EmitHookInvocation(InitPtrToString(Dag.getArg(0)), + "vec.push_back(std::make_pair(65536, ", "));\n", + IndentLevel, O); + } - // (forward) property - if (D.isForward()) - EmitForwardOptionPropertyHandlingCode(D, "", O); + void onForward (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(Dag, 1); + const std::string& Name = InitPtrToString(Dag.getArg(0)); + EmitForwardOptionPropertyHandlingCode(OptDescs.FindOption(Name), + IndentLevel, "", O); + } + + void onForwardAs (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(Dag, 2); + const std::string& Name = InitPtrToString(Dag.getArg(0)); + const std::string& NewName = InitPtrToString(Dag.getArg(1)); + EmitForwardOptionPropertyHandlingCode(OptDescs.FindOption(Name), + IndentLevel, NewName, O); + } + + void onForwardValue (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(Dag, 1); + const std::string& Name = InitPtrToString(Dag.getArg(0)); + const OptionDescription& D = OptDescs.FindParameterListOrParameter(Name); - // (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"; + if (D.isSwitchList()) { + throw std::runtime_error + ("forward_value is not allowed with switch_list"); } - else if (D.Type == OptionType::Prefix || D.Type == OptionType::Parameter){ - O << Indent3 << "llvm::SplitString(" - << D.GenVariableName() << ", vec, \",\");\n"; + + if (D.isParameter()) { + O.indent(IndentLevel) << "vec.push_back(std::make_pair(" + << D.GenVariableName() << ".getPosition(), " + << D.GenVariableName() << "));\n"; } else { - throw std::string("Switches can't have unpack_values property!"); + O.indent(IndentLevel) << "for (" << D.GenTypeDeclaration() + << "::iterator B = " << D.GenVariableName() + << ".begin(), \n"; + O.indent(IndentLevel + Indent1) << " E = " << D.GenVariableName() + << ".end(); B != E; ++B)\n"; + O.indent(IndentLevel) << "{\n"; + O.indent(IndentLevel + Indent1) + << "unsigned pos = " << D.GenVariableName() + << ".getPosition(B - " << D.GenVariableName() + << ".begin());\n"; + O.indent(IndentLevel + Indent1) + << "vec.push_back(std::make_pair(pos, *B));\n"; + O.indent(IndentLevel) << "}\n"; } } - // End of the if-clause. - O << Indent2 << "}\n"; -} - -/// SubstituteSpecialCommands - Perform string substitution for $CALL -/// and $ENV. Helper function used by EmitCmdLineVecFill(). -std::string SubstituteSpecialCommands(const std::string& cmd) { - size_t cparen = cmd.find(")"); - std::string ret; + void onForwardTransformedValue (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(Dag, 2); + const std::string& Name = InitPtrToString(Dag.getArg(0)); + const std::string& Hook = InitPtrToString(Dag.getArg(1)); + const OptionDescription& D = OptDescs.FindParameterListOrParameter(Name); - if (cmd.find("$CALL(") == 0) { - if (cmd.size() == 6) - throw std::string("$CALL invocation: empty argument list!"); + O.indent(IndentLevel) << "vec.push_back(std::make_pair(" + << D.GenVariableName() << ".getPosition(" + << (D.isList() ? "0" : "") << "), " + << "hooks::" << Hook << "(" << D.GenVariableName() + << (D.isParameter() ? ".c_str()" : "") << ")));\n"; + } - ret += "hooks::"; - ret += std::string(cmd.begin() + 6, cmd.begin() + cparen); - ret += "()"; + void onNoOutFile (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(Dag, 0); + O.indent(IndentLevel) << "no_out_file = true;\n"; } - else if (cmd.find("$ENV(") == 0) { - if (cmd.size() == 5) - throw std::string("$ENV invocation: empty argument list!"); - ret += "checkCString(std::getenv(\""; - ret += std::string(cmd.begin() + 5, cmd.begin() + cparen); - ret += "\"))"; + void onOutputSuffix (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(Dag, 1); + this->EmitHookInvocation(InitPtrToString(Dag.getArg(0)), + "output_suffix = ", ";\n", IndentLevel, O); } - else { - throw "Unknown special command: " + cmd; + + void onStopCompilation (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + O.indent(IndentLevel) << "stop_compilation = true;\n"; } - if (cmd.begin() + cparen + 1 != cmd.end()) { - ret += " + std::string(\""; - ret += (cmd.c_str() + cparen + 1); - ret += "\")"; + + void onUnpackValues (const DagInit& Dag, + unsigned IndentLevel, raw_ostream& O) const + { + throw "'unpack_values' is deprecated. " + "Use 'comma_separated' + 'forward_value' instead!"; } - return ret; -} + public: -/// 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, - std::ostream& O) { - StrVector StrVec; - SplitString(InitPtrToString(CmdLine), StrVec); - if (StrVec.empty()) - throw "Tool " + ToolName + " has empty command line!"; + explicit EmitActionHandlersCallback(const OptionDescriptions& OD) + : OptDescs(OD) + { + if (!staticMembersInitialized_) { + AddHandler("error", &EmitActionHandlersCallback::onErrorDag); + AddHandler("warning", &EmitActionHandlersCallback::onWarningDag); + AddHandler("append_cmd", &EmitActionHandlersCallback::onAppendCmd); + AddHandler("forward", &EmitActionHandlersCallback::onForward); + AddHandler("forward_as", &EmitActionHandlersCallback::onForwardAs); + AddHandler("forward_value", &EmitActionHandlersCallback::onForwardValue); + AddHandler("forward_transformed_value", + &EmitActionHandlersCallback::onForwardTransformedValue); + AddHandler("no_out_file", + &EmitActionHandlersCallback::onNoOutFile); + AddHandler("output_suffix", &EmitActionHandlersCallback::onOutputSuffix); + AddHandler("stop_compilation", + &EmitActionHandlersCallback::onStopCompilation); + AddHandler("unpack_values", + &EmitActionHandlersCallback::onUnpackValues); - StrVector::const_iterator I = StrVec.begin(); - ++I; - for (StrVector::const_iterator E = StrVec.end(); I != E; ++I) { - const std::string& cmd = *I; - O << IndentLevel; - if (cmd.at(0) == '$') { - if (cmd == "$INFILE") { - if (Version) - O << "for (PathVector::const_iterator B = inFiles.begin()" - << ", E = inFiles.end();\n" - << IndentLevel << "B != E; ++B)\n" - << IndentLevel << Indent1 << "vec.push_back(B->toString());\n"; - else - O << "vec.push_back(inFile.toString());\n"; - } - else if (cmd == "$OUTFILE") { - O << "vec.push_back(outFile.toString());\n"; - } - else { - O << "vec.push_back(" << SubstituteSpecialCommands(cmd); - O << ");\n"; - } - } - else { - O << "vec.push_back(\"" << cmd << "\");\n"; + + staticMembersInitialized_ = true; } } - O << IndentLevel << "cmd = " - << ((StrVec[0][0] == '$') ? SubstituteSpecialCommands(StrVec[0]) - : "\"" + StrVec[0] + "\"") - << ";\n"; -} - -/// EmitCmdLineVecFillCallback - A function object wrapper around -/// EmitCmdLineVecFill(). Used by EmitGenerateActionMethod() as an -/// argument to EmitCaseConstructHandler(). -class EmitCmdLineVecFillCallback { - bool Version; - const std::string& ToolName; - public: - EmitCmdLineVecFillCallback(bool Ver, const std::string& TN) - : Version(Ver), ToolName(TN) {} - void operator()(const Init* Statement, const char* IndentLevel, - std::ostream& O) const + void operator()(const Init* I, + unsigned IndentLevel, raw_ostream& O) const { - EmitCmdLineVecFill(Statement, ToolName, Version, - (std::string(IndentLevel) + Indent1).c_str(), O); + InvokeDagInitHandler(this, I, IndentLevel, O); } }; -// 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) - 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" - << Indent2 << "const InputLanguagesSet& InLangs,\n" - << Indent2 << "const LanguageMap& LangMap) const\n" - << Indent1 << "{\n" - << Indent2 << "std::string cmd;\n" - << Indent2 << "std::vector vec;\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); - else - EmitCaseConstructHandler(&InitPtrToDag(P.CmdLine), Indent2, - EmitCmdLineVecFillCallback(Version, P.Name), - true, OptDescs, O); +void EmitGenerateActionMethodHeader(const ToolDescription& D, + bool IsJoin, bool Naked, + raw_ostream& O) +{ + O.indent(Indent1) << "int GenerateAction(Action& Out,\n"; - // 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); + if (IsJoin) + O.indent(Indent2) << "const PathVector& inFiles,\n"; + else + O.indent(Indent2) << "const sys::Path& inFile,\n"; + + O.indent(Indent2) << "const bool HasChildren,\n"; + O.indent(Indent2) << "const llvm::sys::Path& TempDir,\n"; + O.indent(Indent2) << "const InputLanguagesSet& InLangs,\n"; + O.indent(Indent2) << "const LanguageMap& LangMap) const\n"; + O.indent(Indent1) << "{\n"; + + if (!Naked) { + O.indent(Indent2) << "std::string cmd;\n"; + O.indent(Indent2) << "std::string out_file;\n"; + O.indent(Indent2) + << "std::vector > vec;\n"; + O.indent(Indent2) << "bool stop_compilation = !HasChildren;\n"; + O.indent(Indent2) << "bool no_out_file = false;\n"; + O.indent(Indent2) << "std::string output_suffix(\"" + << D.OutputSuffix << "\");\n"; } +} - // Handle the Sink property. - if (P.isSink()) { - O << Indent2 << "if (!" << SinkOptionName << ".empty()) {\n" - << Indent3 << "vec.insert(vec.end(), " - << SinkOptionName << ".begin(), " << SinkOptionName << ".end());\n" - << Indent2 << "}\n"; - } +// EmitGenerateActionMethod - Emit either a normal or a "join" version of the +// Tool::GenerateAction() method. +void EmitGenerateActionMethod (const ToolDescription& D, + const OptionDescriptions& OptDescs, + bool IsJoin, raw_ostream& O) { - O << Indent2 << "return Action(cmd, vec);\n" - << Indent1 << "}\n\n"; -} + EmitGenerateActionMethodHeader(D, IsJoin, /* Naked = */ false, O); -/// EmitGenerateActionMethods - Emit two GenerateAction() methods for -/// a given Tool class. -void EmitGenerateActionMethods (const ToolProperties& P, - const GlobalOptionDescriptions& OptDescs, - std::ostream& O) { - if (!P.isJoin()) - O << Indent1 << "Action GenerateAction(const PathVector& inFiles,\n" - << Indent2 << "const llvm::sys::Path& outFile,\n" - << Indent2 << "const InputLanguagesSet& InLangs,\n" - << Indent2 << "const LanguageMap& LangMap) const\n" - << Indent1 << "{\n" - << Indent2 << "throw std::runtime_error(\"" << P.Name - << " is not a Join tool!\");\n" - << Indent1 << "}\n\n"; - else - EmitGenerateActionMethod(P, OptDescs, true, O); + if (!D.CmdLine) + throw "Tool " + D.Name + " has no cmd_line property!"; - EmitGenerateActionMethod(P, OptDescs, false, O); -} + // Process the 'command' property. + O << '\n'; + EmitCmdLineVecFill(D.CmdLine, D.Name, IsJoin, Indent2, O); + O << '\n'; -/// 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"; + // Process the 'actions' list of this tool. + if (D.Actions) + EmitCaseConstructHandler(D.Actions, Indent2, + EmitActionHandlersCallback(OptDescs), + false, OptDescs, O); + O << '\n'; - for (ToolOptionDescriptions::const_iterator B = P.OptDescs.begin(), - E = P.OptDescs.end(); B != E; ++B) { - const ToolOptionDescription& val = B->second; + // Input file (s) + if (!D.InFileOption.empty()) { + O.indent(Indent2) + << "vec.push_back(std::make_pair(InputFilenames.getPosition(0), \"" + << D.InFileOption << "\");\n"; + } - if (val.isStopCompilation()) - O << Indent2 - << "if (" << val.GenVariableName() - << ")\n" << Indent3 << "last = true;\n"; + if (IsJoin) { + O.indent(Indent2) + << "for (PathVector::const_iterator B = inFiles.begin(),\n"; + O.indent(Indent3) << "E = inFiles.end(); B != E; ++B)\n"; + O.indent(Indent2) << "{\n"; + O.indent(Indent3) << "vec.push_back(std::make_pair(" + << "InputFilenames.getPosition(B - inFiles.begin()), " + << "B->str()));\n"; + O.indent(Indent2) << "}\n"; + } + else { + O.indent(Indent2) << "vec.push_back(std::make_pair(" + << "InputFilenames.getPosition(0), inFile.str()));\n"; } - O << Indent2 << "return last;\n" - << Indent1 << "}\n\n"; -} + // Output file + O.indent(Indent2) << "if (!no_out_file) {\n"; + if (!D.OutFileOption.empty()) + O.indent(Indent3) << "vec.push_back(std::make_pair(65536, \"" + << D.OutFileOption << "\"));\n"; -/// EmitInOutLanguageMethods - Emit the [Input,Output]Language() -/// methods for a given Tool class. -void EmitInOutLanguageMethods (const ToolProperties& P, std::ostream& O) { - O << Indent1 << "const char** InputLanguages() const {\n" - << Indent2 << "return InputLanguages_;\n" - << Indent1 << "}\n\n"; + O.indent(Indent3) << "out_file = this->OutFilename(" + << (IsJoin ? "sys::Path(),\n" : "inFile,\n"); + O.indent(Indent4) << + "TempDir, stop_compilation, output_suffix.c_str()).str();\n\n"; + O.indent(Indent3) << "vec.push_back(std::make_pair(65536, out_file));\n"; - O << Indent1 << "const char* OutputLanguage() const {\n" - << Indent2 << "return \"" << P.OutLanguage << "\";\n" - << Indent1 << "}\n\n"; + O.indent(Indent2) << "}\n\n"; + + // Handle the Sink property. + std::string SinkOption("autogenerated::"); + SinkOption += SinkOptionName; + if (D.isSink()) { + O.indent(Indent2) << "if (!" << SinkOption << ".empty()) {\n"; + O.indent(Indent3) << "for (cl::list::iterator B = " + << SinkOption << ".begin(), E = " << SinkOption + << ".end(); B != E; ++B)\n"; + O.indent(Indent4) << "vec.push_back(std::make_pair(" << SinkOption + << ".getPosition(B - " << SinkOption + << ".begin()), *B));\n"; + O.indent(Indent2) << "}\n"; + } + + O.indent(Indent2) << "Out.Construct(cmd, this->SortArgs(vec), " + << "stop_compilation, out_file);\n"; + O.indent(Indent2) << "return 0;\n"; + O.indent(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"; - } - } +/// EmitGenerateActionMethods - Emit two GenerateAction() methods for +/// a given Tool class. +void EmitGenerateActionMethods (const ToolDescription& ToolDesc, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + if (!ToolDesc.isJoin()) { + EmitGenerateActionMethodHeader(ToolDesc, /* IsJoin = */ true, + /* Naked = */ true, O); + O.indent(Indent2) << "PrintError(\"" << ToolDesc.Name + << " is not a Join tool!\");\n"; + O.indent(Indent2) << "return -1;\n"; + O.indent(Indent1) << "}\n\n"; + } + else { + EmitGenerateActionMethod(ToolDesc, OptDescs, true, O); } - O << Indent2 << "return ret;\n" - << Indent1 << "}\n\n"; + EmitGenerateActionMethod(ToolDesc, OptDescs, false, O); +} + +/// EmitInOutLanguageMethods - Emit the [Input,Output]Language() +/// methods for a given Tool class. +void EmitInOutLanguageMethods (const ToolDescription& D, raw_ostream& O) { + O.indent(Indent1) << "const char** InputLanguages() const {\n"; + O.indent(Indent2) << "return InputLanguages_;\n"; + O.indent(Indent1) << "}\n\n"; + + O.indent(Indent1) << "const char** OutputLanguages() const {\n"; + O.indent(Indent2) << "return OutputLanguages_;\n"; + O.indent(Indent1) << "}\n\n"; } /// EmitNameMethod - Emit the Name() method for a given Tool class. -void EmitNameMethod (const ToolProperties& P, std::ostream& O) { - O << Indent1 << "const char* Name() const {\n" - << Indent2 << "return \"" << P.Name << "\";\n" - << Indent1 << "}\n\n"; +void EmitNameMethod (const ToolDescription& D, raw_ostream& O) { + O.indent(Indent1) << "const char* Name() const {\n"; + O.indent(Indent2) << "return \"" << D.Name << "\";\n"; + O.indent(Indent1) << "}\n\n"; } /// EmitIsJoinMethod - Emit the IsJoin() method for a given Tool /// class. -void EmitIsJoinMethod (const ToolProperties& P, std::ostream& O) { - O << Indent1 << "bool IsJoin() const {\n"; - if (P.isJoin()) - O << Indent2 << "return true;\n"; +void EmitIsJoinMethod (const ToolDescription& D, raw_ostream& O) { + O.indent(Indent1) << "bool IsJoin() const {\n"; + if (D.isJoin()) + O.indent(Indent2) << "return true;\n"; + else + O.indent(Indent2) << "return false;\n"; + O.indent(Indent1) << "}\n\n"; +} + +/// EmitWorksOnEmptyCallback - Callback used by EmitWorksOnEmptyMethod in +/// conjunction with EmitCaseConstructHandler. +void EmitWorksOnEmptyCallback (const Init* Value, + unsigned IndentLevel, raw_ostream& O) { + CheckBooleanConstant(Value); + O.indent(IndentLevel) << "return " << Value->getAsString() << ";\n"; +} + +/// EmitWorksOnEmptyMethod - Emit the WorksOnEmpty() method for a given Tool +/// class. +void EmitWorksOnEmptyMethod (const ToolDescription& D, + const OptionDescriptions& OptDescs, + raw_ostream& O) +{ + O.indent(Indent1) << "bool WorksOnEmpty() const {\n"; + if (D.OnEmpty == 0) + O.indent(Indent2) << "return false;\n"; else - O << Indent2 << "return false;\n"; - O << Indent1 << "}\n\n"; + EmitCaseConstructHandler(D.OnEmpty, Indent2, EmitWorksOnEmptyCallback, + /*EmitElseIf = */ true, OptDescs, O); + O.indent(Indent1) << "}\n\n"; +} + +/// EmitStrArray - Emit definition of a 'const char**' static member +/// variable. Helper used by EmitStaticMemberDefinitions(); +void EmitStrArray(const std::string& Name, const std::string& VarName, + const StrVector& StrVec, raw_ostream& O) { + O << "const char* " << Name << "::" << VarName << "[] = {"; + for (StrVector::const_iterator B = StrVec.begin(), E = StrVec.end(); + B != E; ++B) + O << '\"' << *B << "\", "; + O << "0};\n"; } /// 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) - O << '\"' << *B << "\", "; - O << "0};\n\n"; +void EmitStaticMemberDefinitions(const ToolDescription& D, raw_ostream& O) { + if (D.InLanguage.empty()) + throw "Tool " + D.Name + " has no 'in_language' property!"; + if (D.OutLanguage.empty()) + throw "Tool " + D.Name + " has no 'out_language' property!"; + + EmitStrArray(D.Name, "InputLanguages_", D.InLanguage, O); + EmitStrArray(D.Name, "OutputLanguages_", D.OutLanguage, O); + O << '\n'; } /// EmitToolClassDefinition - Emit a Tool class definition. -void EmitToolClassDefinition (const ToolProperties& P, - const GlobalOptionDescriptions& OptDescs, - std::ostream& O) { - if (P.Name == "root") +void EmitToolClassDefinition (const ToolDescription& D, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + 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"; - O << "{\nprivate:\n" - << Indent1 << "static const char* InputLanguages_[];\n\n"; + O << " {\nprivate:\n"; + O.indent(Indent1) << "static const char* InputLanguages_[];\n"; + O.indent(Indent1) << "static const char* OutputLanguages_[];\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); + EmitWorksOnEmptyMethod(D, OptDescs, 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) +/// EmitOptionDefinitions - Iterate over a list of option descriptions +/// and emit registration code. +void EmitOptionDefinitions (const OptionDescriptions& descs, + bool HasSink, raw_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); @@ -1396,150 +2354,346 @@ void EmitOptionDescriptions (const GlobalOptionDescriptions& descs, } O << val.GenTypeDeclaration() << ' ' - << val.GenVariableName() - << "(\"" << val.Name << '\"'; + << val.GenPlainVariableName(); + + O << "(\"" << val.Name << "\"\n"; if (val.Type == OptionType::Prefix || val.Type == OptionType::PrefixList) O << ", cl::Prefix"; if (val.isRequired()) { - switch (val.Type) { - case OptionType::PrefixList: - case OptionType::ParameterList: + if (val.isList() && !val.isMultiVal()) O << ", cl::OneOrMore"; - break; - default: + else O << ", cl::Required"; - } + } + + if (val.isOptional()) + O << ", cl::Optional"; + + if (val.isOneOrMore()) + O << ", cl::OneOrMore"; + + if (val.isZeroOrMore()) + O << ", cl::ZeroOrMore"; + + if (val.isReallyHidden()) + O << ", cl::ReallyHidden"; + else if (val.isHidden()) + O << ", cl::Hidden"; + + if (val.isCommaSeparated()) + O << ", cl::CommaSeparated"; + + if (val.MultiVal > 1) + O << ", cl::multi_val(" << val.MultiVal << ')'; + + if (val.InitVal) { + const std::string& str = val.InitVal->getAsString(); + O << ", cl::init(" << str << ')'; } if (!val.Help.empty()) O << ", cl::desc(\"" << val.Help << "\")"; - O << ");\n"; + O << ");\n\n"; } // 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.GenPlainVariableName() << "(\"" << 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'; } -/// EmitPopulateLanguageMap - Emit the PopulateLanguageMap() function. -void EmitPopulateLanguageMap (const RecordKeeper& Records, std::ostream& O) +/// EmitPreprocessOptionsCallback - Helper function passed to +/// EmitCaseConstructHandler() by EmitPreprocessOptions(). + +class EmitPreprocessOptionsCallback; + +typedef void +(EmitPreprocessOptionsCallback::* EmitPreprocessOptionsCallbackHandler) +(const DagInit&, unsigned, raw_ostream&) const; + +class EmitPreprocessOptionsCallback : + public ActionHandlingCallbackBase, + public HandlerTable { - // Generate code - O << "namespace {\n\n"; - O << "void PopulateLanguageMapLocal(LanguageMap& langMap) {\n"; + typedef EmitPreprocessOptionsCallbackHandler Handler; + typedef void + (EmitPreprocessOptionsCallback::* HandlerImpl) + (const Init*, unsigned, raw_ostream&) const; + + const OptionDescriptions& OptDescs_; + + void onEachArgument(const DagInit& d, HandlerImpl h, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(d, 1); + + for (unsigned i = 0, NumArgs = d.getNumArgs(); i < NumArgs; ++i) { + ((this)->*(h))(d.getArg(i), IndentLevel, O); + } + } + + void onUnsetOptionImpl(const Init* I, + unsigned IndentLevel, raw_ostream& O) const + { + const std::string& OptName = InitPtrToString(I); + const OptionDescription& OptDesc = OptDescs_.FindOption(OptName); - // Get the relevant field out of RecordKeeper - const Record* LangMapRecord = Records.getDef("LanguageMap"); + if (OptDesc.isSwitch()) { + O.indent(IndentLevel) << OptDesc.GenVariableName() << " = false;\n"; + } + else if (OptDesc.isParameter()) { + O.indent(IndentLevel) << OptDesc.GenVariableName() << " = \"\";\n"; + } + else if (OptDesc.isList()) { + O.indent(IndentLevel) << OptDesc.GenVariableName() << ".clear();\n"; + } + else { + throw "Can't apply 'unset_option' to alias option '" + OptName + "'!"; + } + } - // It is allowed for a plugin to have no language map. - if (LangMapRecord) { + void onUnsetOption(const DagInit& d, + unsigned IndentLevel, raw_ostream& O) const + { + this->onEachArgument(d, &EmitPreprocessOptionsCallback::onUnsetOptionImpl, + IndentLevel, O); + } - ListInit* LangsToSuffixesList = LangMapRecord->getValueAsListInit("map"); - if (!LangsToSuffixesList) - throw std::string("Error in the language map definition!"); + void onSetOptionImpl(const DagInit& D, + unsigned IndentLevel, raw_ostream& O) const { + CheckNumberOfArguments(D, 2); - for (unsigned i = 0; i < LangsToSuffixesList->size(); ++i) { - const Record* LangToSuffixes = LangsToSuffixesList->getElementAsRecord(i); + const std::string& OptName = InitPtrToString(D.getArg(0)); + const OptionDescription& OptDesc = OptDescs_.FindOption(OptName); + const Init* Value = D.getArg(1); - const std::string& Lang = LangToSuffixes->getValueAsString("lang"); - const ListInit* Suffixes = LangToSuffixes->getValueAsListInit("suffixes"); + if (OptDesc.isList()) { + const ListInit& List = InitPtrToList(Value); - for (unsigned i = 0; i < Suffixes->size(); ++i) - O << Indent1 << "langMap[\"" - << InitPtrToString(Suffixes->getElement(i)) - << "\"] = \"" << Lang << "\";\n"; + O.indent(IndentLevel) << OptDesc.GenVariableName() << ".clear();\n"; + for (ListInit::const_iterator B = List.begin(), E = List.end(); + B != E; ++B) { + const Init* CurElem = *B; + if (OptDesc.isSwitchList()) + CheckBooleanConstant(CurElem); + + O.indent(IndentLevel) + << OptDesc.GenVariableName() << ".push_back(\"" + << (OptDesc.isSwitchList() ? CurElem->getAsString() + : InitPtrToString(CurElem)) + << "\");\n"; + } + } + else if (OptDesc.isSwitch()) { + CheckBooleanConstant(Value); + O.indent(IndentLevel) << OptDesc.GenVariableName() + << " = " << Value->getAsString() << ";\n"; + } + else if (OptDesc.isParameter()) { + const std::string& Str = InitPtrToString(Value); + O.indent(IndentLevel) << OptDesc.GenVariableName() + << " = \"" << Str << "\";\n"; + } + else { + throw "Can't apply 'set_option' to alias option '" + OptName + "'!"; } } - O << "}\n\n}\n\n"; -} + void onSetSwitch(const Init* I, + unsigned IndentLevel, raw_ostream& O) const { + const std::string& OptName = InitPtrToString(I); + const OptionDescription& OptDesc = OptDescs_.FindOption(OptName); -/// 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; + if (OptDesc.isSwitch()) + O.indent(IndentLevel) << OptDesc.GenVariableName() << " = true;\n"; + else + throw "set_option: -" + OptName + " is not a switch option!"; + } + + void onSetOption(const DagInit& d, + unsigned IndentLevel, raw_ostream& O) const + { + CheckNumberOfArguments(d, 1); + + // 2-argument form: (set_option "A", true), (set_option "B", "C"), + // (set_option "D", ["E", "F"]) + if (d.getNumArgs() == 2) { + const OptionDescription& OptDesc = + OptDescs_.FindOption(InitPtrToString(d.getArg(0))); + const Init* Opt2 = d.getArg(1); + + if (!OptDesc.isSwitch() || typeid(*Opt2) != typeid(StringInit)) { + this->onSetOptionImpl(d, IndentLevel, O); + return; + } + } + + // Multiple argument form: (set_option "A"), (set_option "B", "C", "D") + this->onEachArgument(d, &EmitPreprocessOptionsCallback::onSetSwitch, + IndentLevel, O); + } + +public: + + EmitPreprocessOptionsCallback(const OptionDescriptions& OptDescs) + : OptDescs_(OptDescs) + { + if (!staticMembersInitialized_) { + AddHandler("error", &EmitPreprocessOptionsCallback::onErrorDag); + AddHandler("warning", &EmitPreprocessOptionsCallback::onWarningDag); + AddHandler("unset_option", &EmitPreprocessOptionsCallback::onUnsetOption); + AddHandler("set_option", &EmitPreprocessOptionsCallback::onSetOption); + + staticMembersInitialized_ = true; + } + } + + void operator()(const Init* I, + unsigned IndentLevel, raw_ostream& O) const + { + InvokeDagInitHandler(this, I, IndentLevel, O); + } + +}; + +/// EmitPreprocessOptions - Emit the PreprocessOptions() function. +void EmitPreprocessOptions (const RecordKeeper& Records, + const OptionDescriptions& OptDecs, raw_ostream& O) +{ + O << "int PreprocessOptions () {\n"; + + const RecordVector& OptionPreprocessors = + Records.getAllDerivedDefinitions("OptionPreprocessor"); + + for (RecordVector::const_iterator B = OptionPreprocessors.begin(), + E = OptionPreprocessors.end(); B!=E; ++B) { + DagInit* Case = (*B)->getValueAsDag("preprocessor"); + EmitCaseConstructHandler(Case, Indent1, + EmitPreprocessOptionsCallback(OptDecs), + false, OptDecs, O); } + + O << '\n'; + O.indent(Indent1) << "return 0;\n"; + O << "}\n\n"; } -/// 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). -// TODO: add a --check-graph switch to llvmc2. It would also make it -// possible to detect cycles and multiple default edges. -void TypecheckGraph (const Record* CompilationGraph, - const ToolPropertiesList& TPList) { - StringMap > ToolToInLang; - StringMap ToolToOutLang; - - FillInToolToLang(TPList, ToolToInLang, ToolToOutLang); - ListInit* edges = CompilationGraph->getValueAsListInit("edges"); - StringMap::iterator IAE = ToolToOutLang.end(); - StringMap >::iterator IBE = ToolToInLang.end(); - - for (unsigned i = 0; i < edges->size(); ++i) { - const Record* Edge = edges->getElementAsRecord(i); - 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"; +class DoEmitPopulateLanguageMap; +typedef void (DoEmitPopulateLanguageMap::* DoEmitPopulateLanguageMapHandler) +(const DagInit& D); + +class DoEmitPopulateLanguageMap +: public HandlerTable +{ +private: + raw_ostream& O_; + +public: + + explicit DoEmitPopulateLanguageMap (raw_ostream& O) : O_(O) { + if (!staticMembersInitialized_) { + AddHandler("lang_to_suffixes", + &DoEmitPopulateLanguageMap::onLangToSuffixes); + + staticMembersInitialized_ = true; + } + } + + void operator() (Init* I) { + InvokeDagInitHandler(this, I); + } + +private: + + void onLangToSuffixes (const DagInit& d) { + CheckNumberOfArguments(d, 2); + + const std::string& Lang = InitPtrToString(d.getArg(0)); + Init* Suffixes = d.getArg(1); + + // Second argument to lang_to_suffixes is either a single string... + if (typeid(*Suffixes) == typeid(StringInit)) { + O_.indent(Indent1) << "langMap[\"" << InitPtrToString(Suffixes) + << "\"] = \"" << Lang << "\";\n"; + } + // ...or a list of strings. + else { + const ListInit& Lst = InitPtrToList(Suffixes); + assert(Lst.size() != 0); + for (ListInit::const_iterator B = Lst.begin(), E = Lst.end(); + B != E; ++B) { + O_.indent(Indent1) << "langMap[\"" << InitPtrToString(*B) + << "\"] = \"" << Lang << "\";\n"; + } } + } + +}; + +/// EmitPopulateLanguageMap - Emit the PopulateLanguageMap() function. +void EmitPopulateLanguageMap (const RecordKeeper& Records, raw_ostream& O) +{ + O << "int PopulateLanguageMap (LanguageMap& langMap) {\n"; - if (B == "root") - throw std::string("Edges back to the root are not allowed!"); + // For each LanguageMap: + const RecordVector& LangMaps = + Records.getAllDerivedDefinitions("LanguageMap"); + + // Call DoEmitPopulateLanguageMap. + for (RecordVector::const_iterator B = LangMaps.begin(), + E = LangMaps.end(); B!=E; ++B) { + ListInit* LangMap = (*B)->getValueAsListInit("map"); + std::for_each(LangMap->begin(), LangMap->end(), + DoEmitPopulateLanguageMap(O)); } + + O << '\n'; + O.indent(Indent1) << "return 0;\n"; + O << "}\n\n"; } -/// IncDecWeight - Helper function passed to EmitCaseConstructHandler() -/// by EmitEdgeClass(). -void IncDecWeight (const Init* i, const char* IndentLevel, - std::ostream& O) { +/// EmitEdgePropertyHandlerCallback - Emits code that handles edge +/// properties. Helper function passed to EmitCaseConstructHandler() by +/// EmitEdgeClass(). +void EmitEdgePropertyHandlerCallback (const Init* i, unsigned IndentLevel, + raw_ostream& O) { const DagInit& d = InitPtrToDag(i); - const std::string& OpName = d.getOperator()->getAsString(); + const std::string& OpName = GetOperatorName(d); - if (OpName == "inc_weight") - O << IndentLevel << Indent1 << "ret += "; - else if (OpName == "dec_weight") - O << IndentLevel << Indent1 << "ret -= "; - else - throw "Unknown operator in edge properties list: " + OpName + '!'; + if (OpName == "inc_weight") { + O.indent(IndentLevel) << "ret += "; + } + else if (OpName == "error") { + CheckNumberOfArguments(d, 1); + O.indent(IndentLevel) << "PrintError(\"" + << InitPtrToString(d.getArg(0)) + << "\");\n"; + O.indent(IndentLevel) << "return -1;\n"; + return; + } + else { + throw "Unknown operator in edge properties list: '" + OpName + "'!" + "\nOnly 'inc_weight', 'dec_weight' and 'error' are allowed."; + } if (d.getNumArgs() > 0) O << InitPtrToInt(d.getArg(0)) << ";\n"; @@ -1550,314 +2704,401 @@ 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, - std::ostream& O) { + const DagInit& Case, const OptionDescriptions& OptDescs, + raw_ostream& O) { // Class constructor. O << "class Edge" << N << ": public Edge {\n" - << "public:\n" - << Indent1 << "Edge" << N << "() : Edge(\"" << Target - << "\") {}\n\n" + << "public:\n"; + O.indent(Indent1) << "Edge" << N << "() : Edge(\"" << Target + << "\") {}\n\n"; // Function Weight(). - << Indent1 << "unsigned Weight(const InputLanguagesSet& InLangs) const {\n" - << Indent2 << "unsigned ret = 0;\n"; + O.indent(Indent1) + << "int Weight(const InputLanguagesSet& InLangs) const {\n"; + O.indent(Indent2) << "unsigned ret = 0;\n"; // Handle the 'case' construct. - EmitCaseConstructHandler(Case, Indent2, IncDecWeight, false, OptDescs, O); + EmitCaseConstructHandler(&Case, Indent2, EmitEdgePropertyHandlerCallback, + false, OptDescs, O); - O << Indent2 << "return ret;\n" - << Indent1 << "};\n\n};\n\n"; + O.indent(Indent2) << "return ret;\n"; + O.indent(Indent1) << "}\n\n};\n\n"; } /// EmitEdgeClasses - Emit Edge* classes that represent graph edges. -void EmitEdgeClasses (const Record* CompilationGraph, - const GlobalOptionDescriptions& OptDescs, - std::ostream& O) { - ListInit* edges = CompilationGraph->getValueAsListInit("edges"); - - for (unsigned i = 0; i < edges->size(); ++i) { - const Record* Edge = edges->getElementAsRecord(i); - const std::string& B = Edge->getValueAsString("b"); - DagInit* Weight = Edge->getValueAsDag("weight"); - - if (isDagEmpty(Weight)) - continue; +void EmitEdgeClasses (const DagVector& EdgeVector, + const OptionDescriptions& OptDescs, + raw_ostream& O) { + int i = 0; + for (DagVector::const_iterator B = EdgeVector.begin(), + E = EdgeVector.end(); B != E; ++B) { + const DagInit& Edge = **B; + const std::string& Name = GetOperatorName(Edge); + + if (Name == "optional_edge") { + assert(IsOptionalEdge(Edge)); + const std::string& NodeB = InitPtrToString(Edge.getArg(1)); + + const DagInit& Weight = InitPtrToDag(Edge.getArg(2)); + EmitEdgeClass(i, NodeB, Weight, OptDescs, O); + } + else if (Name != "edge") { + throw "Unknown edge class: '" + Name + "'!"; + } - EmitEdgeClass(i, B, Weight, OptDescs, O); + ++i; } } -/// EmitPopulateCompilationGraph - Emit the PopulateCompilationGraph() -/// function. -void EmitPopulateCompilationGraph (const Record* CompilationGraph, - const ToolPropertiesList& ToolProps, - std::ostream& O) +/// EmitPopulateCompilationGraph - Emit the PopulateCompilationGraph() function. +void EmitPopulateCompilationGraph (const DagVector& EdgeVector, + const ToolDescriptions& ToolDescs, + raw_ostream& O) { - ListInit* edges = CompilationGraph->getValueAsListInit("edges"); - - O << "namespace {\n\n"; - O << "void PopulateCompilationGraphLocal(CompilationGraph& G) {\n"; + O << "int PopulateCompilationGraph (CompilationGraph& G) {\n"; - for (ToolPropertiesList::const_iterator B = ToolProps.begin(), - E = ToolProps.end(); B != E; ++B) - O << Indent1 << "G.insertNode(new " << (*B)->Name << "());\n"; + for (ToolDescriptions::const_iterator B = ToolDescs.begin(), + E = ToolDescs.end(); B != E; ++B) + O.indent(Indent1) << "G.insertNode(new " << (*B)->Name << "());\n"; O << '\n'; // Insert edges. - for (unsigned i = 0; i < edges->size(); ++i) { - const Record* Edge = edges->getElementAsRecord(i); - const std::string& A = Edge->getValueAsString("a"); - const std::string& B = Edge->getValueAsString("b"); - DagInit* Weight = Edge->getValueAsDag("weight"); + int i = 0; + for (DagVector::const_iterator B = EdgeVector.begin(), + E = EdgeVector.end(); B != E; ++B) { + const DagInit& Edge = **B; + const std::string& NodeA = InitPtrToString(Edge.getArg(0)); + const std::string& NodeB = InitPtrToString(Edge.getArg(1)); - O << Indent1 << "G.insertEdge(\"" << A << "\", "; + O.indent(Indent1) << "if (int ret = G.insertEdge(\"" << NodeA << "\", "; - if (isDagEmpty(Weight)) - O << "new SimpleEdge(\"" << B << "\")"; - else + if (IsOptionalEdge(Edge)) O << "new Edge" << i << "()"; + else + O << "new SimpleEdge(\"" << NodeB << "\")"; - O << ");\n"; + O << "))\n"; + O.indent(Indent2) << "return ret;\n"; + + ++i; } - O << "}\n\n}\n\n"; + O << '\n'; + O.indent(Indent1) << "return 0;\n"; + O << "}\n\n"; } +/// HookInfo - Information about the hook type and number of arguments. +struct HookInfo { + + // A hook can either have a single parameter of type std::vector, + // or NumArgs parameters of type const char*. + enum HookType { ListHook, ArgHook }; + + HookType Type; + unsigned NumArgs; + + HookInfo() : Type(ArgHook), NumArgs(1) + {} + + HookInfo(HookType T) : Type(T), NumArgs(1) + {} + + HookInfo(unsigned N) : Type(ArgHook), NumArgs(N) + {} +}; + +typedef llvm::StringMap HookInfoMap; + /// ExtractHookNames - Extract the hook names from all instances of -/// $CALL(HookName) in the provided command line string. Helper +/// $CALL(HookName) in the provided command line string/action. 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 { + HookInfoMap& HookNames_; + const OptionDescriptions& OptDescs_; +public: + ExtractHookNames(HookInfoMap& HookNames, const OptionDescriptions& OptDescs) + : HookNames_(HookNames), OptDescs_(OptDescs) + {} + + void onAction (const DagInit& Dag) { + const std::string& Name = GetOperatorName(Dag); + + if (Name == "forward_transformed_value") { + CheckNumberOfArguments(Dag, 2); + const std::string& OptName = InitPtrToString(Dag.getArg(0)); + const std::string& HookName = InitPtrToString(Dag.getArg(1)); + const OptionDescription& D = + OptDescs_.FindParameterListOrParameter(OptName); + + HookNames_[HookName] = HookInfo(D.isList() ? HookInfo::ListHook + : HookInfo::ArgHook); + } + else if (Name == "append_cmd" || Name == "output_suffix") { + CheckNumberOfArguments(Dag, 1); + this->onCmdLine(InitPtrToString(Dag.getArg(0))); } } -} -/// 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; + void onCmdLine(const std::string& Cmd) { + StrVector cmds; + TokenizeCmdLine(Cmd, cmds); + + for (StrVector::const_iterator B = cmds.begin(), E = cmds.end(); + B != E; ++B) { + const std::string& cmd = *B; + + if (cmd == "$CALL") { + unsigned NumArgs = 0; + CheckedIncrement(B, E, "Syntax error in $CALL invocation!"); + const std::string& HookName = *B; + + if (HookName.at(0) == ')') + throw "$CALL invoked with no arguments!"; + + while (++B != E && B->at(0) != ')') { + ++NumArgs; + } + + HookInfoMap::const_iterator H = HookNames_.find(HookName); + + if (H != HookNames_.end() && H->second.NumArgs != NumArgs && + H->second.Type != HookInfo::ArgHook) + throw "Overloading of hooks is not allowed. Overloaded hook: " + + HookName; + else + HookNames_[HookName] = HookInfo(NumArgs); + } + } } -} + + void operator()(const Init* Arg) { + + // We're invoked on an action (either a dag or a dag list). + if (typeid(*Arg) == typeid(DagInit)) { + const DagInit& Dag = InitPtrToDag(Arg); + this->onAction(Dag); + return; + } + else if (typeid(*Arg) == typeid(ListInit)) { + const ListInit& List = InitPtrToList(Arg); + for (ListInit::const_iterator B = List.begin(), E = List.end(); B != E; + ++B) { + const DagInit& Dag = InitPtrToDag(*B); + this->onAction(Dag); + } + return; + } + + // We're invoked on a command line string. + this->onCmdLine(InitPtrToString(Arg)); + } + + void operator()(const Init* Statement, unsigned) { + this->operator()(Statement); + } +}; /// FillInHookNames - Actually extract the hook names from all command /// line strings. Helper function used by EmitHookDeclarations(). -void FillInHookNames(const ToolPropertiesList& TPList, - StrVector& 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) +void FillInHookNames(const ToolDescriptions& ToolDescs, + const OptionDescriptions& OptDescs, + HookInfoMap& HookNames) +{ + // For all tool descriptions: + for (ToolDescriptions::const_iterator B = ToolDescs.begin(), + E = ToolDescs.end(); B != E; ++B) { + const ToolDescription& D = *(*B); + + // Look for 'forward_transformed_value' in 'actions'. + if (D.Actions) + WalkCase(D.Actions, Id(), ExtractHookNames(HookNames, OptDescs)); + + // Look for hook invocations in 'cmd_line'. + if (!D.CmdLine) continue; - if (dynamic_cast(P.CmdLine)) + if (dynamic_cast(D.CmdLine)) // This is a string. - ExtractHookNames(P.CmdLine, HookNames); + ExtractHookNames(HookNames, OptDescs).operator()(D.CmdLine); else // This is a 'case' construct. - ExtractHookNamesFromCaseConstruct(P.CmdLine, HookNames); + WalkCase(D.CmdLine, Id(), ExtractHookNames(HookNames, OptDescs)); } } /// 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, + const OptionDescriptions& OptDescs, raw_ostream& O) { + HookInfoMap HookNames; + + FillInHookNames(ToolDescs, OptDescs, 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 (HookInfoMap::const_iterator B = HookNames.begin(), + E = HookNames.end(); B != E; ++B) { + const char* HookName = B->first(); + const HookInfo& Info = B->second; - O << "}\n\n"; -} + O.indent(Indent1) << "std::string " << HookName << "("; -/// EmitRegisterPlugin - Emit code to register this plugin. -void EmitRegisterPlugin(int Priority, std::ostream& O) { - O << "namespace {\n\n" - << "struct Plugin : public llvmc::BasePlugin {\n\n" - << Indent1 << "int Priority() const { return " << Priority << "; }\n\n" - << Indent1 << "void PopulateLanguageMap(LanguageMap& langMap) const\n" - << Indent1 << "{ PopulateLanguageMapLocal(langMap); }\n\n" - << Indent1 - << "void PopulateCompilationGraph(CompilationGraph& graph) const\n" - << Indent1 << "{ PopulateCompilationGraphLocal(graph); }\n" - << "};\n\n" + if (Info.Type == HookInfo::ArgHook) { + for (unsigned i = 0, j = Info.NumArgs; i < j; ++i) { + O << "const char* Arg" << i << (i+1 == j ? "" : ", "); + } + } + else { + O << "const std::vector& Arg"; + } - << "static llvmc::RegisterPlugin RP;\n\n}\n\n"; + O <<");\n"; + } } /// EmitIncludes - Emit necessary #include directives and some /// additional declarations. -void EmitIncludes(std::ostream& O) { - O << "#include \"llvm/CompilerDriver/CompilationGraph.h\"\n" - << "#include \"llvm/CompilerDriver/Plugin.h\"\n" +void EmitIncludes(raw_ostream& O) { + O << "#include \"llvm/CompilerDriver/BuiltinOptions.h\"\n" + << "#include \"llvm/CompilerDriver/CompilationGraph.h\"\n" + << "#include \"llvm/CompilerDriver/Error.h\"\n" << "#include \"llvm/CompilerDriver/Tool.h\"\n\n" - << "#include \"llvm/ADT/StringExtras.h\"\n" - << "#include \"llvm/Support/CommandLine.h\"\n\n" + << "#include \"llvm/Support/CommandLine.h\"\n" + << "#include \"llvm/Support/raw_ostream.h\"\n\n" + << "#include \n" << "#include \n" + << "#include \n" << "#include \n\n" << "using namespace llvm;\n" << "using namespace llvmc;\n\n" - << "extern cl::opt OutputFilename;\n\n" - << "inline const char* checkCString(const char* s)\n" << "{ 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); - } +/// DriverData - Holds all information about the driver. +struct DriverData { + OptionDescriptions OptDescs; + ToolDescriptions ToolDescs; + DagVector Edges; + bool HasSink; }; -/// FilterNotInGraph - Filter out from ToolProps all Tools not -/// mentioned in the compilation graph definition. -void FilterNotInGraph (const Record* CompilationGraph, - ToolPropertiesList& ToolProps) { +/// HasSink - Go through the list of tool descriptions and check if +/// there are any 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; - // List all tools mentioned in the graph. - llvm::StringSet<> ToolsInGraph; - ListInit* edges = CompilationGraph->getValueAsListInit("edges"); + return false; +} - for (unsigned i = 0; i < edges->size(); ++i) { - const Record* Edge = edges->getElementAsRecord(i); - const std::string& A = Edge->getValueAsString("a"); - const std::string& B = Edge->getValueAsString("b"); +/// CollectDriverData - Collect compilation graph edges, tool properties and +/// option properties from the parse tree. +void CollectDriverData (const RecordKeeper& Records, DriverData& Data) { + // Collect option properties. + const RecordVector& OptionLists = + Records.getAllDerivedDefinitions("OptionList"); + CollectOptionDescriptions(OptionLists, Data.OptDescs); - if (A != "root") - ToolsInGraph.insert(A); - ToolsInGraph.insert(B); - } + // Collect tool properties. + const RecordVector& Tools = Records.getAllDerivedDefinitions("Tool"); + CollectToolDescriptions(Tools, Data.ToolDescs); + Data.HasSink = HasSink(Data.ToolDescs); - // Filter ToolPropertiesList. - ToolPropertiesList::iterator new_end = - std::remove_if(ToolProps.begin(), ToolProps.end(), - NotInGraph(ToolsInGraph)); - ToolProps.erase(new_end, ToolProps.end()); + // Collect compilation graph edges. + const RecordVector& CompilationGraphs = + Records.getAllDerivedDefinitions("CompilationGraph"); + FillInEdgeVector(CompilationGraphs, Data.Edges); } -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; -} +/// CheckDriverData - Perform some sanity checks on the collected data. +void CheckDriverData(DriverData& Data) { + // Filter out all tools not mentioned in the compilation graph. + FilterNotInGraph(Data.Edges, Data.ToolDescs); -// End of anonymous namespace -} + // Typecheck the compilation graph. + TypecheckGraph(Data.Edges, Data.ToolDescs); -/// run - The back-end entry point. -void LLVMCConfigurationEmitter::run (std::ostream &O) { - try { + // Check that there are no options without side effects (specified + // only in the OptionList). + CheckForSuperfluousOptions(Data.Edges, Data.ToolDescs, Data.OptDescs); +} +void EmitDriverCode(const DriverData& Data, + raw_ostream& O, RecordKeeper &Records) { // Emit file header. - EmitSourceFileHeader("LLVMC Configuration Library", O); EmitIncludes(O); - // Get a list of all defined Tools. - - RecordVector Tools = Records.getAllDerivedDefinitions("Tool"); - if (Tools.empty()) - throw std::string("No tool definitions found!"); - - // Gather information from the Tool description dags. - ToolPropertiesList ToolProps; - GlobalOptionDescriptions OptDescs; - CollectToolProperties(Tools.begin(), Tools.end(), ToolProps, OptDescs); + // Emit global option registration code. + O << "namespace llvmc {\n" + << "namespace autogenerated {\n\n"; + EmitOptionDefinitions(Data.OptDescs, Data.HasSink, O); + O << "} // End namespace autogenerated.\n" + << "} // End namespace llvmc.\n\n"; - const RecordVector& OptionLists = - Records.getAllDerivedDefinitions("OptionList"); - CollectPropertiesFromOptionLists(OptionLists.begin(), OptionLists.end(), - OptDescs); + // Emit hook declarations. + O << "namespace hooks {\n"; + EmitHookDeclarations(Data.ToolDescs, Data.OptDescs, O); + O << "} // End namespace hooks.\n\n"; - // TOTHINK: Nothing actually prevents us from having multiple - // compilation graphs in a single plugin; OTOH, I do not see how - // that could be useful. - const Record* CompilationGraphRecord = Records.getDef("CompilationGraph"); - if (!CompilationGraphRecord) - throw std::string("Compilation graph description not found!"); + O << "namespace {\n\n"; + O << "using namespace llvmc::autogenerated;\n\n"; - FilterNotInGraph(CompilationGraphRecord, ToolProps); + // Emit Tool classes. + for (ToolDescriptions::const_iterator B = Data.ToolDescs.begin(), + E = Data.ToolDescs.end(); B!=E; ++B) + EmitToolClassDefinition(*(*B), Data.OptDescs, O); - // Typecheck the compilation graph. - TypecheckGraph(CompilationGraphRecord, ToolProps); + // Emit Edge# classes. + EmitEdgeClasses(Data.Edges, Data.OptDescs, O); - // Check that there are no options without side effects (specified - // only in the OptionList). - CheckForSuperfluousOptions(ToolProps, OptDescs); + O << "} // End anonymous namespace.\n\n"; - // Emit global option registration code. - EmitOptionDescriptions(OptDescs, O); + O << "namespace llvmc {\n"; + O << "namespace autogenerated {\n\n"; - // Emit hook declarations. - EmitHookDeclarations(ToolProps, O); + // Emit PreprocessOptions() function. + EmitPreprocessOptions(Records, Data.OptDescs, O); // Emit PopulateLanguageMap() function - // (a language map maps from file extensions to language names). + // (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); - - // Emit Edge# classes. - EmitEdgeClasses(CompilationGraphRecord, OptDescs, O); - // Emit PopulateCompilationGraph() function. - EmitPopulateCompilationGraph(CompilationGraphRecord, 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); + O << "} // End namespace autogenerated.\n"; + O << "} // End namespace llvmc.\n\n"; // EOF +} + + +// End of anonymous namespace +} + +/// run - The back-end entry point. +void LLVMCConfigurationEmitter::run (raw_ostream &O) { + try { + DriverData Data; + + CollectDriverData(Records, Data); + CheckDriverData(Data); + + this->EmitSourceFileHeader("llvmc-based driver: auto-generated code", O); + EmitDriverCode(Data, O, Records); + } catch (std::exception& Error) { throw Error.what() + std::string(" - usually this means a syntax error."); }