From: Daniel Dunbar Date: Wed, 18 Nov 2009 20:19:36 +0000 (+0000) Subject: Driver: Rework OptTable to have no dependency on the options it manages. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a79a2b5bf23d1422eed9be3793186ebbba7532ec;p=clang Driver: Rework OptTable to have no dependency on the options it manages. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89234 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Driver/Options.h b/include/clang/Driver/Options.h index 7fcaf3f497..20c3d2ff93 100644 --- a/include/clang/Driver/Options.h +++ b/include/clang/Driver/Options.h @@ -10,18 +10,19 @@ #ifndef CLANG_DRIVER_OPTIONS_H_ #define CLANG_DRIVER_OPTIONS_H_ +#include + namespace clang { namespace driver { namespace options { - enum ID { - OPT_INVALID = 0, // This is not an option ID. - OPT_INPUT, // Reserved ID for input option. - OPT_UNKNOWN, // Reserved ID for unknown option. -#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) OPT_##ID, -#include "clang/Driver/Options.def" - LastOption -#undef OPTION + enum DriverFlag { + DriverOption = (1 << 0), + LinkerInput = (1 << 1), + NoArgumentUnused = (1 << 2), + RenderAsInput = (1 << 3), + RenderJoined = (1 << 4), + RenderSeparate = (1 << 5), + Unsupported = (1 << 6) }; } @@ -31,59 +32,125 @@ namespace options { /// OptTable - Provide access to the Option info table. /// - /// The OptTable class provides a layer of indirection which allows - /// Option instance to be created lazily. In the common case, only a - /// few options will be needed at runtime; the OptTable class - /// maintains enough information to parse command lines without - /// instantiating Options, while letting other parts of the driver - /// still use Option instances where convient. + /// The OptTable class provides a layer of indirection which allows Option + /// instance to be created lazily. In the common case, only a few options will + /// be needed at runtime; the OptTable class maintains enough information to + /// parse command lines without instantiating Options, while letting other + /// parts of the driver still use Option instances where convenient. + // + // FIXME: Introduce an OptionSpecifier class to wrap the option ID + // variant? class OptTable { - /// The table of options which have been constructed, indexed by - /// option::ID - 1. + public: + /// Info - Entry for a single option instance in the option data table. + struct Info { + const char *Name; + const char *HelpText; + const char *MetaVar; + unsigned char Kind; + unsigned char Flags; + unsigned char Param; + unsigned short GroupID; + unsigned short AliasID; + }; + + private: + /// The static option information table. + const Info *OptionInfos; + unsigned NumOptionInfos; + + /// The lazily constructed options table, indexed by option::ID - 1. mutable Option **Options; - /// The index of the first option which can be parsed (i.e., is - /// not a special option like 'input' or 'unknown', and is not an - /// option group). - unsigned FirstSearchableOption; + /// Prebound input option instance. + const Option *TheInputOption; + + /// Prebound unknown option instance. + const Option *TheUnknownOption; + + /// The index of the first option which can be parsed (i.e., is not a + /// special option like 'input' or 'unknown', and is not an option group). + unsigned FirstSearchableIndex; + + private: + const Info &getInfo(unsigned id) const { + assert(id > 0 && id - 1 < getNumOptions() && "Invalid Option ID."); + return OptionInfos[id - 1]; + } - Option *constructOption(options::ID id) const; + Option *CreateOption(unsigned id) const; + protected: + OptTable(const Info *_OptionInfos, unsigned _NumOptionInfos); public: - OptTable(); ~OptTable(); - unsigned getNumOptions() const; + /// getNumOptions - Return the total number of option classes. + unsigned getNumOptions() const { return NumOptionInfos; } - const char *getOptionName(options::ID id) const; + /// getOption - Get the given \arg id's Option instance, lazily creating it + /// if necessary. + /// + /// \return The option, or null for the INVALID option id. + const Option *getOption(unsigned id) const { + if (id == 0) + return 0; + + assert((unsigned) (id - 1) < getNumOptions() && "Invalid ID."); + Option *&Entry = Options[id - 1]; + if (!Entry) + Entry = CreateOption(id); + return Entry; + } - /// getOption - Get the given \arg id's Option instance, lazily - /// creating it if necessary. - const Option *getOption(options::ID id) const; + /// getOptionName - Lookup the name of the given option. + const char *getOptionName(unsigned id) const { + return getInfo(id).Name; + } /// getOptionKind - Get the kind of the given option. - unsigned getOptionKind(options::ID id) const; + unsigned getOptionKind(unsigned id) const { + return getInfo(id).Kind; + } - /// getOptionHelpText - Get the help text to use to describe this - /// option. - const char *getOptionHelpText(options::ID id) const; + /// getOptionHelpText - Get the help text to use to describe this option. + const char *getOptionHelpText(unsigned id) const { + return getInfo(id).HelpText; + } - /// getOptionMetaVar - Get the meta-variable name to use when - /// describing this options values in the help text. - const char *getOptionMetaVar(options::ID id) const; + /// getOptionMetaVar - Get the meta-variable name to use when describing + /// this options values in the help text. + const char *getOptionMetaVar(unsigned id) const { + return getInfo(id).MetaVar; + } - /// parseOneArg - Parse a single argument; returning the new - /// argument and updating Index. + /// parseOneArg - Parse a single argument; returning the new argument and + /// updating Index. /// - /// \param [in] [out] Index - The current parsing position in the - /// argument string list; on return this will be the index of the - /// next argument string to parse. + /// \param [in] [out] Index - The current parsing position in the argument + /// string list; on return this will be the index of the next argument + /// string to parse. /// - /// \return - The parsed argument, or 0 if the argument is missing - /// values (in which case Index still points at the conceptual - /// next argument string to parse). + /// \return - The parsed argument, or 0 if the argument is missing values + /// (in which case Index still points at the conceptual next argument string + /// to parse). Arg *ParseOneArg(const InputArgList &Args, unsigned &Index) const; }; + +namespace options { + enum ID { + OPT_INVALID = 0, // This is not an option ID. + OPT_INPUT, // Reserved ID for input option. + OPT_UNKNOWN, // Reserved ID for unknown option. +#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ + HELPTEXT, METAVAR) OPT_##ID, +#include "clang/Driver/Options.def" + LastOption +#undef OPTION + }; +} + + OptTable *createDriverOptTable(); } } diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index b4693f2ac9..a93812632c 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -44,7 +44,7 @@ Driver::Driver(const char *_Name, const char *_Dir, const char *_DefaultHostTriple, const char *_DefaultImageName, bool IsProduction, Diagnostic &_Diags) - : Opts(new OptTable()), Diags(_Diags), + : Opts(createDriverOptTable()), Diags(_Diags), Name(_Name), Dir(_Dir), DefaultHostTriple(_DefaultHostTriple), DefaultImageName(_DefaultImageName), Host(0), diff --git a/lib/Driver/OptTable.cpp b/lib/Driver/OptTable.cpp index 3391fe60c1..67e3019e0f 100644 --- a/lib/Driver/OptTable.cpp +++ b/lib/Driver/OptTable.cpp @@ -18,28 +18,6 @@ using namespace clang::driver; using namespace clang::driver::options; -enum DriverFlag { - DriverOption = (1 << 0), - LinkerInput = (1 << 1), - NoArgumentUnused = (1 << 2), - RenderAsInput = (1 << 3), - RenderJoined = (1 << 4), - RenderSeparate = (1 << 5), - Unsupported = (1 << 6) -}; - -struct Info { - const char *Name; - const char *HelpText; - const char *MetaVar; - - unsigned char Kind; - unsigned char Flags; - unsigned char Param; - unsigned short GroupID; - unsigned short AliasID; -}; - // Ordering on Info. The ordering is *almost* lexicographic, with two // exceptions. First, '\0' comes at the end of the alphabet instead of // the beginning (thus options preceed any other options which prefix @@ -66,7 +44,7 @@ static int StrCmpOptionName(const char *A, const char *B) { return (a < b) ? -1 : 1; } -static inline bool operator<(const Info &A, const Info &B) { +static inline bool operator<(const OptTable::Info &A, const OptTable::Info &B) { if (&A == &B) return false; @@ -82,55 +60,46 @@ static inline bool operator<(const Info &A, const Info &B) { // -static Info OptionInfos[] = { - // The InputOption info - { "", 0, 0, Option::InputClass, DriverOption, 0, OPT_INVALID, OPT_INVALID }, - // The UnknownOption info - { "", 0, 0, Option::UnknownClass, 0, 0, OPT_INVALID, OPT_INVALID }, - -#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ - HELPTEXT, METAVAR) \ - { NAME, HELPTEXT, METAVAR, Option::KIND##Class, FLAGS, PARAM, \ - OPT_##GROUP, OPT_##ALIAS }, -#include "clang/Driver/Options.def" -}; -static const unsigned numOptions = sizeof(OptionInfos) / sizeof(OptionInfos[0]); - -static Info &getInfo(unsigned id) { - assert(id > 0 && id - 1 < numOptions && "Invalid Option ID."); - return OptionInfos[id - 1]; -} - -OptTable::OptTable() : Options(new Option*[numOptions]) { +OptTable::OptTable(const Info *_OptionInfos, unsigned _NumOptionInfos) + : OptionInfos(_OptionInfos), NumOptionInfos(_NumOptionInfos), + Options(new Option*[NumOptionInfos]), + TheInputOption(0), TheUnknownOption(0), FirstSearchableIndex(0) +{ // Explicitly zero initialize the error to work around a bug in array // value-initialization on MinGW with gcc 4.3.5. - memset(Options, 0, sizeof(*Options) * numOptions); + memset(Options, 0, sizeof(*Options) * NumOptionInfos); // Find start of normal options. - FirstSearchableOption = 0; - for (unsigned i = OPT_UNKNOWN + 1; i < LastOption; ++i) { - if (getInfo(i).Kind != Option::GroupClass) { - FirstSearchableOption = i; + for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { + unsigned Kind = getInfo(i + 1).Kind; + if (Kind == Option::InputClass) { + assert(!TheInputOption && "Cannot have multiple input options!"); + TheInputOption = getOption(i + 1); + } else if (Kind == Option::UnknownClass) { + assert(!TheUnknownOption && "Cannot have multiple input options!"); + TheUnknownOption = getOption(i + 1); + } else if (Kind != Option::GroupClass) { + FirstSearchableIndex = i; break; } } - assert(FirstSearchableOption != 0 && "No searchable options?"); + assert(FirstSearchableIndex != 0 && "No searchable options?"); #ifndef NDEBUG // Check that everything after the first searchable option is a // regular option class. - for (unsigned i = FirstSearchableOption; i < LastOption; ++i) { - Option::OptionClass Kind = (Option::OptionClass) getInfo(i).Kind; + for (unsigned i = FirstSearchableIndex, e = getNumOptions(); i != e; ++i) { + Option::OptionClass Kind = (Option::OptionClass) getInfo(i + 1).Kind; assert((Kind != Option::InputClass && Kind != Option::UnknownClass && Kind != Option::GroupClass) && "Special options should be defined first!"); } // Check that options are in order. - for (unsigned i = FirstSearchableOption + 1; i < LastOption; ++i) { - if (!(getInfo(i - 1) < getInfo(i))) { - getOption((options::ID) (i - 1))->dump(); - getOption((options::ID) i)->dump(); + for (unsigned i = FirstSearchableIndex+1, e = getNumOptions(); i != e; ++i) { + if (!(getInfo(i) < getInfo(i + 1))) { + getOption(i)->dump(); + getOption(i + 1)->dump(); assert(0 && "Options are not in order!"); } } @@ -138,49 +107,16 @@ OptTable::OptTable() : Options(new Option*[numOptions]) { } OptTable::~OptTable() { - for (unsigned i = 0; i < numOptions; ++i) + for (unsigned i = 0, e = getNumOptions(); i != e; ++i) delete Options[i]; delete[] Options; } -unsigned OptTable::getNumOptions() const { - return numOptions; -} - -const char *OptTable::getOptionName(options::ID id) const { - return getInfo(id).Name; -} - -unsigned OptTable::getOptionKind(options::ID id) const { - return getInfo(id).Kind; -} - -const char *OptTable::getOptionHelpText(options::ID id) const { - return getInfo(id).HelpText; -} - -const char *OptTable::getOptionMetaVar(options::ID id) const { - return getInfo(id).MetaVar; -} - -const Option *OptTable::getOption(options::ID id) const { - if (id == OPT_INVALID) - return 0; - - assert((unsigned) (id - 1) < numOptions && "Invalid ID."); - - Option *&Entry = Options[id - 1]; - if (!Entry) - Entry = constructOption(id); - - return Entry; -} - -Option *OptTable::constructOption(options::ID id) const { - Info &info = getInfo(id); +Option *OptTable::CreateOption(unsigned id) const { + const Info &info = getInfo(id); const OptionGroup *Group = - cast_or_null(getOption((options::ID) info.GroupID)); - const Option *Alias = getOption((options::ID) info.AliasID); + cast_or_null(getOption(info.GroupID)); + const Option *Alias = getOption(info.AliasID); Option *Opt = 0; switch (info.Kind) { @@ -229,12 +165,16 @@ Option *OptTable::constructOption(options::ID id) const { } // Support lower_bound between info and an option name. -static inline bool operator<(struct Info &I, const char *Name) { +namespace clang { +namespace driver { +static inline bool operator<(const OptTable::Info &I, const char *Name) { return StrCmpOptionName(I.Name, Name) == -1; } -static inline bool operator<(const char *Name, struct Info &I) { +static inline bool operator<(const char *Name, const OptTable::Info &I) { return StrCmpOptionName(Name, I.Name) == -1; } +} +} Arg *OptTable::ParseOneArg(const InputArgList &Args, unsigned &Index) const { unsigned Prev = Index; @@ -242,10 +182,10 @@ Arg *OptTable::ParseOneArg(const InputArgList &Args, unsigned &Index) const { // Anything that doesn't start with '-' is an input, as is '-' itself. if (Str[0] != '-' || Str[1] == '\0') - return new PositionalArg(getOption(OPT_INPUT), Index++); + return new PositionalArg(TheInputOption, Index++); - struct Info *Start = OptionInfos + FirstSearchableOption - 1; - struct Info *End = OptionInfos + LastOption - 1; + const Info *Start = OptionInfos + FirstSearchableIndex; + const Info *End = OptionInfos + LastOption - 1; // Search for the first next option which could be a prefix. Start = std::lower_bound(Start, End, Str); @@ -276,6 +216,34 @@ Arg *OptTable::ParseOneArg(const InputArgList &Args, unsigned &Index) const { return 0; } - return new PositionalArg(getOption(OPT_UNKNOWN), Index++); + return new PositionalArg(TheUnknownOption, Index++); +} + +// + +static OptTable::Info InfoTable[] = { + // The InputOption info + { "", 0, 0, Option::InputClass, DriverOption, 0, OPT_INVALID, OPT_INVALID }, + // The UnknownOption info + { "", 0, 0, Option::UnknownClass, 0, 0, OPT_INVALID, OPT_INVALID }, + +#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ + HELPTEXT, METAVAR) \ + { NAME, HELPTEXT, METAVAR, Option::KIND##Class, FLAGS, PARAM, \ + OPT_##GROUP, OPT_##ALIAS }, +#include "clang/Driver/Options.def" +}; + +namespace { + +class DriverOptTable : public OptTable { +public: + DriverOptTable() + : OptTable(InfoTable, sizeof(InfoTable) / sizeof(InfoTable[0])) {} +}; + } +OptTable *clang::driver::createDriverOptTable() { + return new DriverOptTable(); +}