[clang] 29125dd - Start adding support for generating CC1 command lines from CompilerInvocation
Daniel Grumberg via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 24 10:05:19 PDT 2020
Author: Daniel Grumberg
Date: 2020-06-24T18:05:05+01:00
New Revision: 29125ddf1323951901184d2859274afdecac0327
URL: https://github.com/llvm/llvm-project/commit/29125ddf1323951901184d2859274afdecac0327
DIFF: https://github.com/llvm/llvm-project/commit/29125ddf1323951901184d2859274afdecac0327.diff
LOG: Start adding support for generating CC1 command lines from CompilerInvocation
This change includes the following:
- Add additional information in the relevant table-gen files to encode
the necessary information to automatically parse the argument into a
CompilerInvocation instance and to generate the appropriate command
line argument from a CompilerInvocation instance.
- Extend OptParserEmitter to emit the necessary macro tables as well as
constant tables to support parsing and generating command line
arguments for options that provide the necessary information.
- Port some options to use this new system for parsing and generating
command line arguments.
Differential Revision: https://reviews.llvm.org/D79796
Added:
clang/unittests/Frontend/CompilerInvocationTest.cpp
Modified:
clang/include/clang/Driver/CC1Options.td
clang/include/clang/Frontend/CompilerInvocation.h
clang/lib/Frontend/CompilerInvocation.cpp
clang/unittests/Frontend/CMakeLists.txt
llvm/include/llvm/Option/OptParser.td
llvm/utils/TableGen/OptParserEmitter.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td
index cf2235ed9274..bc1fd0b58d3e 100644
--- a/clang/include/clang/Driver/CC1Options.td
+++ b/clang/include/clang/Driver/CC1Options.td
@@ -23,7 +23,9 @@ def target_cpu : Separate<["-"], "target-cpu">,
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 @@ def msave_temp_labels : Flag<["-"], "msave-temp-labels">,
"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 @@ def fmodules_hash_content : Flag<["-"], "fmodules-hash-content">,
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<"<directory>">,
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
index e63408da8f5e..c723fc084c85 100644
--- a/clang/include/clang/Frontend/CompilerInvocation.h
+++ b/clang/include/clang/Frontend/CompilerInvocation.h
@@ -153,6 +153,8 @@ class CompilerInvocation : public CompilerInvocationBase {
/// 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<const char *> CommandLineArgs,
DiagnosticsEngine &Diags,
@@ -184,6 +186,18 @@ class CompilerInvocation : public CompilerInvocationBase {
/// identifying the conditions under which the module was built.
std::string getModuleHash() const;
+ using StringAllocator = llvm::function_ref<const char *(const llvm::Twine &)>;
+ /// 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<const char *> &Args,
+ StringAllocator SA) const;
+
/// @}
/// @name Option Subgroups
/// @{
@@ -222,6 +236,16 @@ class CompilerInvocation : public CompilerInvocationBase {
}
/// @}
+
+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<llvm::vfs::FileSystem>
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 28c4fda02a7d..debd793046b9 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -117,6 +117,64 @@ CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &X)
CompilerInvocationBase::~CompilerInvocationBase() = default;
+//===----------------------------------------------------------------------===//
+// Normalizers
+//===----------------------------------------------------------------------===//
+
+#define SIMPLE_ENUM_VALUE_TABLE
+#include "clang/Driver/Options.inc"
+#undef SIMPLE_ENUM_VALUE_TABLE
+
+static llvm::Optional<unsigned> 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<std::string> 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 @@ static void ParseCommentArgs(CommentOptions &Opts, ArgList &Args) {
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<llvm::Optional<llvm::Reloc::Model>>(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<llvm::Regex>
@@ -927,7 +966,6 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
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 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
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 @@ static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args,
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 @@ static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args,
}
}
+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<TYPE>(*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<const char *> CommandLineArgs,
DiagnosticsEngine &Diags,
@@ -3661,6 +3718,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
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 @@ std::string CompilerInvocation::getModuleHash() const {
return llvm::APInt(64, code).toString(36, /*Signed=*/false);
}
+void CompilerInvocation::generateCC1CommandLine(
+ SmallVectorImpl<const char *> &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<llvm::vfs::FileSystem>
diff --git a/clang/unittests/Frontend/CMakeLists.txt b/clang/unittests/Frontend/CMakeLists.txt
index cde19e910614..d247089e9295 100644
--- a/clang/unittests/Frontend/CMakeLists.txt
+++ b/clang/unittests/Frontend/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
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
index 000000000000..ed82d678462f
--- /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<DiagnosticsEngine> Diags;
+ SmallVector<const char *, 32> GeneratedArgs;
+ SmallVector<std::string, 32> 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
index a68f17a8b10b..e32355444d7b 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -97,6 +97,18 @@ class Option<list<string> prefixes, string name, OptionKind kind> {
OptionGroup Group = ?;
Option Alias = ?;
list<string> 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<code> NormalizedValues = ?;
}
// Helpers for defining options.
@@ -130,6 +142,37 @@ class MetaVarName<string name> { string MetaVarName = name; }
class Values<string value> { string Values = value; }
class ValuesCode<code valuecode> { code ValuesCode = valuecode; }
+// Helpers for defining marshalling information.
+
+class MarshallingInfo<code keypath, code defaultvalue> {
+ code KeyPath = keypath;
+ code DefaultValue = defaultvalue;
+}
+class MarshallingInfoString<code keypath, code defaultvalue, code normalizerretty>
+ : MarshallingInfo<keypath, defaultvalue> {
+ string MarshallingKind = "string";
+ code NormalizerRetTy = normalizerretty;
+}
+
+class MarshallingInfoFlag<code keypath, code defaultvalue>
+ : MarshallingInfo<keypath, defaultvalue> {
+ string MarshallingKind = "flag";
+}
+
+// Mixins for additional marshalling attributes.
+
+class IsNegative { bit IsPositive = 0; }
+class AlwaysEmit { bit ShouldAlwaysEmit = 1; }
+class Normalizer<code normalizer> { code Normalizer = normalizer; }
+class Denormalizer<code denormalizer> { code Denormalizer = denormalizer; }
+class NormalizedValuesScope<code scope> { code NormalizedValuesScope = scope; }
+class NormalizedValues<list<code> definitions> { list<code> 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
index 92346ab9b111..251533a8d154 100644
--- 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 <cctype>
#include <cstring>
#include <map>
+#include <memory>
using namespace llvm;
@@ -33,6 +35,210 @@ static raw_ostream &write_cstring(raw_ostream &OS, llvm::StringRef Str) {
return OS;
}
+static const std::string getOptionSpelling(const Record &R,
+ size_t &PrefixLength) {
+ std::vector<StringRef> 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<StringRef> emitValueTable(raw_ostream &OS) const {
+ return None;
+ }
+
+ virtual ~MarshallingKindInfo() = default;
+
+ static std::unique_ptr<MarshallingKindInfo> 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<MarshallingKindInfo> create(const Record &R) {
+ std::unique_ptr<MarshallingFlagInfo> 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<StringRef> Values;
+ std::vector<StringRef> 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<StringRef> 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<unsigned>(";
+ emitScopedNormalizedValue(OS, NormalizedValues[I]);
+ OS << ")},";
+ }
+ OS << "};\n";
+ return StringRef(ValueTableName);
+ }
+
+ static std::unique_ptr<MarshallingKindInfo> create(const Record &R) {
+ assert(!isa<UnsetInit>(R.getValueInit("NormalizerRetTy")) &&
+ "String options must have a type");
+
+ std::unique_ptr<MarshallingStringInfo> Ret(new MarshallingStringInfo(R));
+ Ret->NormalizerRetTy = R.getValueAsString("NormalizerRetTy");
+
+ Ret->Normalizer = R.getValueAsString("Normalizer");
+ Ret->Denormalizer = R.getValueAsString("Denormalizer");
+
+ if (!isa<UnsetInit>(R.getValueInit("NormalizedValues"))) {
+ assert(!isa<UnsetInit>(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>
+MarshallingKindInfo::create(const Record &R) {
+ assert(!isa<UnsetInit>(R.getValueInit("KeyPath")) &&
+ !isa<UnsetInit>(R.getValueInit("DefaultValue")) &&
+ "Must provide at least a key-path and a default value for emitting "
+ "marshalling information");
+
+ std::unique_ptr<MarshallingKindInfo> 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<UnsetInit>(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 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
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<StringRef> 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 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
write_cstring(OS, R.getValueAsString("Values"));
else
OS << "nullptr";
+ };
+
+ std::vector<std::unique_ptr<MarshallingKindInfo>> 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<UnsetInit>(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<StringRef> 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";
More information about the cfe-commits
mailing list