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
This commit is contained in:
@@ -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">,
|
||||
|
||||
@@ -153,6 +153,8 @@ public:
|
||||
/// 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 @@ public:
|
||||
/// 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 @@ public:
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
|
||||
add_clang_unittest(FrontendTests
|
||||
ASTUnitTest.cpp
|
||||
CompilerInvocationTest.cpp
|
||||
CompilerInstanceTest.cpp
|
||||
FixedPointString.cpp
|
||||
FrontendActionTest.cpp
|
||||
|
||||
118
clang/unittests/Frontend/CompilerInvocationTest.cpp
Normal file
118
clang/unittests/Frontend/CompilerInvocationTest.cpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user