diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -23,7 +23,9 @@ def target_feature : Separate<["-"], "target-feature">, HelpText<"Target specific attributes">; def triple : Separate<["-"], "triple">, - HelpText<"Specify target triple (e.g. i686-apple-darwin9)">; + HelpText<"Specify target triple (e.g. i686-apple-darwin9)">, + MarshallingInfoString<"TargetOpts->Triple", "llvm::sys::getDefaultTargetTriple()", "std::string">, + AlwaysEmit, Normalizer<"normalizeTriple">, DenormalizeString; def target_abi : Separate<["-"], "target-abi">, HelpText<"Target a particular ABI type">; def target_sdk_version_EQ : Joined<["-"], "target-sdk-version=">, @@ -212,7 +214,11 @@ "Note this may change .s semantics and shouldn't generally be used " "on compiler-generated code.">; def mrelocation_model : Separate<["-"], "mrelocation-model">, - HelpText<"The relocation model to use">, Values<"static,pic,ropi,rwpi,ropi-rwpi,dynamic-no-pic">; + HelpText<"The relocation model to use">, Values<"static,pic,ropi,rwpi,ropi-rwpi,dynamic-no-pic">, + NormalizedValuesScope<"llvm::Reloc">, + NormalizedValues<["Static", "PIC_", "ROPI", "RWPI", "ROPI_RWPI", "DynamicNoPIC"]>, + MarshallingInfoString<"CodeGenOpts.RelocationModel", "PIC_", "Model">, + AutoNormalizeEnum; def fno_math_builtin : Flag<["-"], "fno-math-builtin">, HelpText<"Disable implicit builtin knowledge of math functions">; } @@ -837,7 +843,8 @@ HelpText<"Enable hashing the content of a module file">; def fmodules_strict_context_hash : Flag<["-"], "fmodules-strict-context-hash">, HelpText<"Enable hashing of all compiler options that could impact the " - "semantics of a module in an implicit build">; + "semantics of a module in an implicit build">, + MarshallingInfoFlag<"HeaderSearchOpts->ModulesStrictContextHash", "false">; def c_isystem : JoinedOrSeparate<["-"], "c-isystem">, MetaVarName<"">, HelpText<"Add directory to the C SYSTEM include search path">; def objc_isystem : JoinedOrSeparate<["-"], "objc-isystem">, diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -153,6 +153,8 @@ /// one of the vaild-to-access (albeit arbitrary) states. /// /// \param [out] Res - The resulting invocation. + /// \param [in] CommandLineArgs - Array of argument strings, this must not + /// contain "-cc1". static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef CommandLineArgs, DiagnosticsEngine &Diags, @@ -184,6 +186,18 @@ /// identifying the conditions under which the module was built. std::string getModuleHash() const; + using StringAllocator = llvm::function_ref; + /// Generate a cc1-compatible command line arguments from this instance. + /// + /// \param [out] Args - The generated arguments. Note that the caller is + /// responsible for inserting the path to the clang executable and "-cc1" if + /// desired. + /// \param SA - A function that given a Twine can allocate storage for a given + /// command line argument and return a pointer to the newly allocated string. + /// The returned pointer is what gets appended to Args. + void generateCC1CommandLine(llvm::SmallVectorImpl &Args, + StringAllocator SA) const; + /// @} /// @name Option Subgroups /// @{ @@ -222,6 +236,16 @@ } /// @} + +private: + /// Parse options for flags that expose marshalling information in their + /// table-gen definition + /// + /// \param Args - The argument list containing the arguments to parse + /// \param Diags - The DiagnosticsEngine associated with CreateFromArgs + /// \returns - True if parsing was successful, false otherwise + bool parseSimpleArgs(const llvm::opt::ArgList &Args, + DiagnosticsEngine &Diags); }; IntrusiveRefCntPtr diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -118,6 +118,64 @@ CompilerInvocationBase::~CompilerInvocationBase() = default; //===----------------------------------------------------------------------===// +// Normalizers +//===----------------------------------------------------------------------===// + +#define SIMPLE_ENUM_VALUE_TABLE +#include "clang/Driver/Options.inc" +#undef SIMPLE_ENUM_VALUE_TABLE + +static llvm::Optional normalizeSimpleEnum(OptSpecifier Opt, + unsigned TableIndex, + const ArgList &Args, + DiagnosticsEngine &Diags) { + assert(TableIndex >= 0); + assert(TableIndex < SimpleEnumValueTablesSize); + const SimpleEnumValueTable &Table = SimpleEnumValueTables[TableIndex]; + + auto *Arg = Args.getLastArg(Opt); + if (!Arg) + return None; + + StringRef ArgValue = Arg->getValue(); + for (int I = 0, E = Table.Size; I != E; ++I) + if (ArgValue == Table.Table[I].Name) + return Table.Table[I].Value; + + Diags.Report(diag::err_drv_invalid_value) + << Arg->getAsString(Args) << ArgValue; + return None; +} + +static const char *denormalizeSimpleEnum(CompilerInvocation::StringAllocator SA, + unsigned TableIndex, unsigned Value) { + assert(TableIndex >= 0); + assert(TableIndex < SimpleEnumValueTablesSize); + const SimpleEnumValueTable &Table = SimpleEnumValueTables[TableIndex]; + for (int I = 0, E = Table.Size; I != E; ++I) + if (Value == Table.Table[I].Value) + return Table.Table[I].Name; + + llvm_unreachable("The simple enum value was not correctly defined in " + "the tablegen option description"); +} + +static const char *denormalizeString(CompilerInvocation::StringAllocator SA, + unsigned TableIndex, + const std::string &Value) { + return SA(Value); +} + +static Optional normalizeTriple(OptSpecifier Opt, int TableIndex, + const ArgList &Args, + DiagnosticsEngine &Diags) { + auto *Arg = Args.getLastArg(Opt); + if (!Arg) + return None; + return llvm::Triple::normalize(Arg->getValue()); +} + +//===----------------------------------------------------------------------===// // Deserialization (from args) //===----------------------------------------------------------------------===// @@ -528,25 +586,6 @@ Opts.ParseAllComments = Args.hasArg(OPT_fparse_all_comments); } -static llvm::Reloc::Model getRelocModel(ArgList &Args, - DiagnosticsEngine &Diags) { - if (Arg *A = Args.getLastArg(OPT_mrelocation_model)) { - StringRef Value = A->getValue(); - auto RM = llvm::StringSwitch>(Value) - .Case("static", llvm::Reloc::Static) - .Case("pic", llvm::Reloc::PIC_) - .Case("ropi", llvm::Reloc::ROPI) - .Case("rwpi", llvm::Reloc::RWPI) - .Case("ropi-rwpi", llvm::Reloc::ROPI_RWPI) - .Case("dynamic-no-pic", llvm::Reloc::DynamicNoPIC) - .Default(None); - if (RM.hasValue()) - return *RM; - Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Value; - } - return llvm::Reloc::PIC_; -} - /// Create a new Regex instance out of the string value in \p RpassArg. /// It returns a pointer to the newly generated Regex instance. static std::shared_ptr @@ -927,7 +966,6 @@ Opts.StrictVTablePointers = Args.hasArg(OPT_fstrict_vtable_pointers); Opts.ForceEmitVTables = Args.hasArg(OPT_fforce_emit_vtables); Opts.UnwindTables = Args.hasArg(OPT_munwind_tables); - Opts.RelocationModel = getRelocModel(Args, Diags); Opts.ThreadModel = std::string(Args.getLastArgValue(OPT_mthread_model, "posix")); if (Opts.ThreadModel != "posix" && Opts.ThreadModel != "single") @@ -2107,7 +2145,6 @@ Opts.AddPrebuiltModulePath(A->getValue()); Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash); Opts.ModulesHashContent = Args.hasArg(OPT_fmodules_hash_content); - Opts.ModulesStrictContextHash = Args.hasArg(OPT_fmodules_strict_context_hash); Opts.ModulesValidateDiagnosticOptions = !Args.hasArg(OPT_fmodules_disable_diagnostic_validation); Opts.ImplicitModuleMaps = Args.hasArg(OPT_fimplicit_module_maps); @@ -3609,11 +3646,6 @@ Opts.FeaturesAsWritten = Args.getAllArgValues(OPT_target_feature); Opts.LinkerVersion = std::string(Args.getLastArgValue(OPT_target_linker_version)); - Opts.Triple = std::string(Args.getLastArgValue(OPT_triple)); - // Use the default target triple if unspecified. - if (Opts.Triple.empty()) - Opts.Triple = llvm::sys::getDefaultTargetTriple(); - Opts.Triple = llvm::Triple::normalize(Opts.Triple); Opts.OpenCLExtensionsAsWritten = Args.getAllArgValues(OPT_cl_ext_EQ); Opts.ForceEnableInt128 = Args.hasArg(OPT_fforce_enable_int128); Opts.NVPTXUseShortPointers = Args.hasFlag( @@ -3628,6 +3660,31 @@ } } +bool CompilerInvocation::parseSimpleArgs(const ArgList &Args, + DiagnosticsEngine &Diags) { +#define OPTION_WITH_MARSHALLING_FLAG(PREFIX_TYPE, NAME, ID, KIND, GROUP, \ + ALIAS, ALIASARGS, FLAGS, PARAM, HELPTEXT, \ + METAVAR, VALUES, SPELLING, ALWAYS_EMIT, \ + KEYPATH, DEFAULT_VALUE, IS_POSITIVE) \ + this->KEYPATH = Args.hasArg(OPT_##ID) && IS_POSITIVE; + +#define OPTION_WITH_MARSHALLING_STRING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + TYPE, NORMALIZER, DENORMALIZER, TABLE_INDEX) \ + { \ + if (auto MaybeValue = NORMALIZER(OPT_##ID, TABLE_INDEX, Args, Diags)) \ + this->KEYPATH = static_cast(*MaybeValue); \ + else \ + this->KEYPATH = DEFAULT_VALUE; \ + } + +#include "clang/Driver/Options.inc" +#undef OPTION_WITH_MARSHALLING_STRING +#undef OPTION_WITH_MARSHALLING_FLAG + return true; +} + bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ArrayRef CommandLineArgs, DiagnosticsEngine &Diags, @@ -3661,6 +3718,7 @@ Success = false; } + Success &= Res.parseSimpleArgs(Args, Diags); Success &= ParseAnalyzerArgs(*Res.getAnalyzerOpts(), Args, Diags); Success &= ParseMigratorArgs(Res.getMigratorOpts(), Args); ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), Args); @@ -3868,6 +3926,33 @@ return llvm::APInt(64, code).toString(36, /*Signed=*/false); } +void CompilerInvocation::generateCC1CommandLine( + SmallVectorImpl &Args, StringAllocator SA) const { +#define OPTION_WITH_MARSHALLING_FLAG(PREFIX_TYPE, NAME, ID, KIND, GROUP, \ + ALIAS, ALIASARGS, FLAGS, PARAM, HELPTEXT, \ + METAVAR, VALUES, SPELLING, ALWAYS_EMIT, \ + KEYPATH, DEFAULT_VALUE, IS_POSITIVE) \ + if (FLAGS & options::CC1Option && \ + (ALWAYS_EMIT || this->KEYPATH != DEFAULT_VALUE)) \ + Args.push_back(SPELLING); + +#define OPTION_WITH_MARSHALLING_STRING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + NORMALIZER_RET_TY, NORMALIZER, DENORMALIZER, TABLE_INDEX) \ + if ((FLAGS & options::CC1Option) && \ + (ALWAYS_EMIT || this->KEYPATH != DEFAULT_VALUE)) { \ + if (Option::KIND##Class == Option::SeparateClass) { \ + Args.push_back(SPELLING); \ + Args.push_back(DENORMALIZER(SA, TABLE_INDEX, this->KEYPATH)); \ + } \ + } + +#include "clang/Driver/Options.inc" +#undef OPTION_WITH_MARSHALLING_STRING +#undef OPTION_WITH_MARSHALLING_FLAG +} + namespace clang { IntrusiveRefCntPtr diff --git a/clang/unittests/Frontend/CMakeLists.txt b/clang/unittests/Frontend/CMakeLists.txt --- a/clang/unittests/Frontend/CMakeLists.txt +++ b/clang/unittests/Frontend/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_unittest(FrontendTests ASTUnitTest.cpp + CompilerInvocationTest.cpp CompilerInstanceTest.cpp FixedPointString.cpp FrontendActionTest.cpp diff --git a/clang/unittests/Frontend/CompilerInvocationTest.cpp b/clang/unittests/Frontend/CompilerInvocationTest.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/Frontend/CompilerInvocationTest.cpp @@ -0,0 +1,118 @@ +//===- unittests/Frontend/CompilerInvocationTest.cpp - CI tests //---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/CompilerInstance.h" +#include "llvm/Support/Host.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +using ::testing::Contains; +using ::testing::Each; +using ::testing::StrEq; +using ::testing::StrNe; + +namespace { + +class CC1CommandLineGenerationTest : public ::testing::Test { +public: + IntrusiveRefCntPtr Diags; + SmallVector GeneratedArgs; + SmallVector GeneratedArgsStorage; + + const char *operator()(const Twine &Arg) { + return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); + } + + CC1CommandLineGenerationTest() + : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions())) {} +}; + +TEST_F(CC1CommandLineGenerationTest, CanGenerateCC1CommandLineFlag) { + const char *Args[] = {"clang", "-xc++", "-fmodules-strict-context-hash", "-"}; + + CompilerInvocation CInvok; + CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); + + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash"))); +} + +TEST_F(CC1CommandLineGenerationTest, CanGenerateCC1CommandLineSeparate) { + const char *TripleCStr = "i686-apple-darwin9"; + const char *Args[] = {"clang", "-xc++", "-triple", TripleCStr, "-"}; + + CompilerInvocation CInvok; + CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); + + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr))); +} + +TEST_F(CC1CommandLineGenerationTest, + CanGenerateCC1CommandLineSeparateRequiredPresent) { + const std::string DefaultTriple = + llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); + const char *Args[] = {"clang", "-xc++", "-triple", DefaultTriple.c_str(), + "-"}; + + CompilerInvocation CInvok; + CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); + + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + // Triple should always be emitted even if it is the default + ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); +} + +TEST_F(CC1CommandLineGenerationTest, + CanGenerateCC1CommandLineSeparateRequiredAbsent) { + const std::string DefaultTriple = + llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); + const char *Args[] = {"clang", "-xc++", "-"}; + + CompilerInvocation CInvok; + CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); + + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + // Triple should always be emitted even if it is the default + ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); +} + +TEST_F(CC1CommandLineGenerationTest, CanGenerateCC1CommandLineSeparateEnum) { + const char *RelocationModelCStr = "static"; + const char *Args[] = {"clang", "-xc++", "-mrelocation-model", + RelocationModelCStr, "-"}; + + CompilerInvocation CInvok; + CompilerInvocation::CreateFromArgs(CInvok, Args, *Diags); + + CInvok.generateCC1CommandLine(GeneratedArgs, *this); + + // Non default relocation model + ASSERT_THAT(GeneratedArgs, Contains(StrEq(RelocationModelCStr))); + GeneratedArgs.clear(); + + RelocationModelCStr = "pic"; + Args[3] = RelocationModelCStr; + + CompilerInvocation CInvok1; + CompilerInvocation::CreateFromArgs(CInvok1, Args, *Diags); + + CInvok1.generateCC1CommandLine(GeneratedArgs, *this); + ASSERT_THAT(GeneratedArgs, Each(StrNe(RelocationModelCStr))); +} + +} // anonymous namespace diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td --- a/llvm/include/llvm/Option/OptParser.td +++ b/llvm/include/llvm/Option/OptParser.td @@ -97,6 +97,18 @@ OptionGroup Group = ?; Option Alias = ?; list AliasArgs = []; + string MarshallingKind = ?; + code KeyPath = ?; + code DefaultValue = ?; + bit ShouldAlwaysEmit = 0; + // Used by the Flag option kind. + bit IsPositive = 1; + // Used by the String option kind. + code NormalizerRetTy = ?; + code NormalizedValuesScope = ""; + code Normalizer = ""; + code Denormalizer = ""; + list NormalizedValues = ?; } // Helpers for defining options. @@ -130,6 +142,37 @@ class Values { string Values = value; } class ValuesCode { code ValuesCode = valuecode; } +// Helpers for defining marshalling information. + +class MarshallingInfo { + code KeyPath = keypath; + code DefaultValue = defaultvalue; +} +class MarshallingInfoString + : MarshallingInfo { + string MarshallingKind = "string"; + code NormalizerRetTy = normalizerretty; +} + +class MarshallingInfoFlag + : MarshallingInfo { + string MarshallingKind = "flag"; +} + +// Mixins for additional marshalling attributes. + +class IsNegative { bit IsPositive = 0; } +class AlwaysEmit { bit ShouldAlwaysEmit = 1; } +class Normalizer { code Normalizer = normalizer; } +class Denormalizer { code Denormalizer = denormalizer; } +class NormalizedValuesScope { code NormalizedValuesScope = scope; } +class NormalizedValues definitions> { list NormalizedValues = definitions; } +class DenormalizeString { code Denormalizer = "denormalizeString"; } +class AutoNormalizeEnum { + code Normalizer = "normalizeSimpleEnum"; + code Denormalizer = "denormalizeSimpleEnum"; +} + // Predefined options. // FIXME: Have generator validate that these appear in correct position (and diff --git a/llvm/utils/TableGen/OptParserEmitter.cpp b/llvm/utils/TableGen/OptParserEmitter.cpp --- a/llvm/utils/TableGen/OptParserEmitter.cpp +++ b/llvm/utils/TableGen/OptParserEmitter.cpp @@ -10,11 +10,13 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include #include #include +#include using namespace llvm; @@ -33,6 +35,210 @@ return OS; } +static const std::string getOptionSpelling(const Record &R, + size_t &PrefixLength) { + std::vector Prefixes = R.getValueAsListOfStrings("Prefixes"); + StringRef Name = R.getValueAsString("Name"); + if (Prefixes.empty()) { + PrefixLength = 0; + return Name.str(); + } + PrefixLength = Prefixes[0].size(); + return (Twine(Prefixes[0]) + Twine(Name)).str(); +} + +static const std::string getOptionSpelling(const Record &R) { + size_t PrefixLength; + return getOptionSpelling(R, PrefixLength); +} + +static void emitNameUsingSpelling(raw_ostream &OS, const Record &R) { + size_t PrefixLength; + OS << "&"; + write_cstring(OS, StringRef(getOptionSpelling(R, PrefixLength))); + OS << "[" << PrefixLength << "]"; +} + +class MarshallingKindInfo { +public: + const Record &R; + const char *MacroName; + bool ShouldAlwaysEmit; + StringRef KeyPath; + StringRef DefaultValue; + StringRef NormalizedValuesScope; + + void emit(raw_ostream &OS) const { + write_cstring(OS, StringRef(getOptionSpelling(R))); + OS << ", "; + OS << ShouldAlwaysEmit; + OS << ", "; + OS << KeyPath; + OS << ", "; + emitScopedNormalizedValue(OS, DefaultValue); + OS << ", "; + emitSpecific(OS); + } + + virtual Optional emitValueTable(raw_ostream &OS) const { + return None; + } + + virtual ~MarshallingKindInfo() = default; + + static std::unique_ptr create(const Record &R); + +protected: + void emitScopedNormalizedValue(raw_ostream &OS, + StringRef NormalizedValue) const { + if (!NormalizedValuesScope.empty()) + OS << NormalizedValuesScope << "::"; + OS << NormalizedValue; + } + + virtual void emitSpecific(raw_ostream &OS) const = 0; + MarshallingKindInfo(const Record &R, const char *MacroName) + : R(R), MacroName(MacroName) {} +}; + +class MarshallingFlagInfo final : public MarshallingKindInfo { +public: + bool IsPositive; + + void emitSpecific(raw_ostream &OS) const override { OS << IsPositive; } + + static std::unique_ptr create(const Record &R) { + std::unique_ptr Ret(new MarshallingFlagInfo(R)); + Ret->IsPositive = R.getValueAsBit("IsPositive"); + return Ret; + } + +private: + MarshallingFlagInfo(const Record &R) + : MarshallingKindInfo(R, "OPTION_WITH_MARSHALLING_FLAG") {} +}; + +class MarshallingStringInfo final : public MarshallingKindInfo { +public: + StringRef NormalizerRetTy; + StringRef Normalizer; + StringRef Denormalizer; + int TableIndex = -1; + std::vector Values; + std::vector NormalizedValues; + std::string ValueTableName; + + static constexpr const char *ValueTablePreamble = R"( +struct SimpleEnumValue { + const char *Name; + unsigned Value; +}; + +struct SimpleEnumValueTable { + const SimpleEnumValue *Table; + unsigned Size; +}; +)"; + + static constexpr const char *ValueTablesDecl = + "static const SimpleEnumValueTable SimpleEnumValueTables[] = "; + + void emitSpecific(raw_ostream &OS) const override { + emitScopedNormalizedValue(OS, NormalizerRetTy); + OS << ", "; + OS << Normalizer; + OS << ", "; + OS << Denormalizer; + OS << ", "; + OS << TableIndex; + } + + Optional emitValueTable(raw_ostream &OS) const override { + if (TableIndex == -1) + return {}; + OS << "static const SimpleEnumValue " << ValueTableName << "[] = {\n"; + for (unsigned I = 0, E = Values.size(); I != E; ++I) { + OS << "{"; + write_cstring(OS, Values[I]); + OS << ","; + OS << "static_cast("; + emitScopedNormalizedValue(OS, NormalizedValues[I]); + OS << ")},"; + } + OS << "};\n"; + return StringRef(ValueTableName); + } + + static std::unique_ptr create(const Record &R) { + assert(!isa(R.getValueInit("NormalizerRetTy")) && + "String options must have a type"); + + std::unique_ptr Ret(new MarshallingStringInfo(R)); + Ret->NormalizerRetTy = R.getValueAsString("NormalizerRetTy"); + + Ret->Normalizer = R.getValueAsString("Normalizer"); + Ret->Denormalizer = R.getValueAsString("Denormalizer"); + + if (!isa(R.getValueInit("NormalizedValues"))) { + assert(!isa(R.getValueInit("Values")) && + "Cannot provide normalized values for value-less options"); + Ret->TableIndex = NextTableIndex++; + Ret->NormalizedValues = R.getValueAsListOfStrings("NormalizedValues"); + Ret->Values.reserve(Ret->NormalizedValues.size()); + Ret->ValueTableName = getOptionName(R) + "ValueTable"; + + StringRef ValuesStr = R.getValueAsString("Values"); + for (;;) { + size_t Idx = ValuesStr.find(','); + if (Idx == StringRef::npos) + break; + if (Idx > 0) + Ret->Values.push_back(ValuesStr.slice(0, Idx)); + ValuesStr = ValuesStr.slice(Idx + 1, StringRef::npos); + } + if (!ValuesStr.empty()) + Ret->Values.push_back(ValuesStr); + + assert(Ret->Values.size() == Ret->NormalizedValues.size() && + "The number of normalized values doesn't match the number of " + "values"); + } + + return Ret; + } + +private: + MarshallingStringInfo(const Record &R) + : MarshallingKindInfo(R, "OPTION_WITH_MARSHALLING_STRING") {} + + static size_t NextTableIndex; +}; + +size_t MarshallingStringInfo::NextTableIndex = 0; + +std::unique_ptr +MarshallingKindInfo::create(const Record &R) { + assert(!isa(R.getValueInit("KeyPath")) && + !isa(R.getValueInit("DefaultValue")) && + "Must provide at least a key-path and a default value for emitting " + "marshalling information"); + + std::unique_ptr Ret = nullptr; + StringRef MarshallingKindStr = R.getValueAsString("MarshallingKind"); + + if (MarshallingKindStr == "flag") + Ret = MarshallingFlagInfo::create(R); + else if (MarshallingKindStr == "string") + Ret = MarshallingStringInfo::create(R); + + Ret->ShouldAlwaysEmit = R.getValueAsBit("ShouldAlwaysEmit"); + Ret->KeyPath = R.getValueAsString("KeyPath"); + Ret->DefaultValue = R.getValueAsString("DefaultValue"); + if (!isa(R.getValueInit("NormalizedValuesScope"))) + Ret->NormalizedValuesScope = R.getValueAsString("NormalizedValuesScope"); + return Ret; +} + /// OptParserEmitter - This tablegen backend takes an input .td file /// describing a list of options and emits a data structure for parsing and /// working with those options when given an input command line. @@ -135,18 +341,14 @@ OS << "//////////\n"; OS << "// Options\n\n"; - for (unsigned I = 0, E = Opts.size(); I != E; ++I) { - const Record &R = *Opts[I]; - - // Start a single option entry. - OS << "OPTION("; + auto WriteOptRecordFields = [&](raw_ostream &OS, const Record &R) { // The option prefix; std::vector prf = R.getValueAsListOfStrings("Prefixes"); OS << Prefixes[PrefixKeyT(prf.begin(), prf.end())] << ", "; // The option string. - write_cstring(OS, R.getValueAsString("Name")); + emitNameUsingSpelling(OS, R); // The option identifier name. OS << ", " << getOptionName(R); @@ -223,11 +425,52 @@ write_cstring(OS, R.getValueAsString("Values")); else OS << "nullptr"; + }; + + std::vector> OptsWithMarshalling; + for (unsigned I = 0, E = Opts.size(); I != E; ++I) { + const Record &R = *Opts[I]; + // Start a single option entry. + OS << "OPTION("; + WriteOptRecordFields(OS, R); OS << ")\n"; + if (!isa(R.getValueInit("MarshallingKind"))) + OptsWithMarshalling.push_back(MarshallingKindInfo::create(R)); } OS << "#endif // OPTION\n"; + for (const auto &KindInfo : OptsWithMarshalling) { + OS << "#ifdef " << KindInfo->MacroName << "\n"; + OS << KindInfo->MacroName << "("; + WriteOptRecordFields(OS, KindInfo->R); + OS << ", "; + KindInfo->emit(OS); + OS << ")\n"; + OS << "#endif // " << KindInfo->MacroName << "\n"; + } + + OS << "\n"; + OS << "#ifdef SIMPLE_ENUM_VALUE_TABLE"; + OS << "\n"; + OS << MarshallingStringInfo::ValueTablePreamble; + std::vector ValueTableNames; + for (const auto &KindInfo : OptsWithMarshalling) + if (auto MaybeValueTableName = KindInfo->emitValueTable(OS)) + ValueTableNames.push_back(*MaybeValueTableName); + + OS << MarshallingStringInfo::ValueTablesDecl << "{"; + for (auto ValueTableName : ValueTableNames) + OS << "{" << ValueTableName << ", sizeof(" << ValueTableName + << ") / sizeof(SimpleEnumValue)" + << "},\n"; + OS << "};\n"; + OS << "static const unsigned SimpleEnumValueTablesSize = " + "sizeof(SimpleEnumValueTables) / sizeof(SimpleEnumValueTable);\n"; + + OS << "#endif // SIMPLE_ENUM_VALUE_TABLE\n"; + OS << "\n"; + OS << "\n"; OS << "#ifdef OPTTABLE_ARG_INIT\n"; OS << "//////////\n";