[clang] 5e8a246 - [clang][cli] Generate and round-trip Frontend options
Jan Svoboda via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 9 07:40:38 PST 2021
Author: Jan Svoboda
Date: 2021-02-09T16:40:30+01:00
New Revision: 5e8a246ac9966e6ca43e7bd84db6df78b27d2201
URL: https://github.com/llvm/llvm-project/commit/5e8a246ac9966e6ca43e7bd84db6df78b27d2201
DIFF: https://github.com/llvm/llvm-project/commit/5e8a246ac9966e6ca43e7bd84db6df78b27d2201.diff
LOG: [clang][cli] Generate and round-trip Frontend options
This patch implements generation of remaining frontend options and tests it by performing parse-generate-parse round trip.
Depends on D96269.
Reviewed By: dexonsmith
Differential Revision: https://reviews.llvm.org/D96155
Added:
Modified:
clang/include/clang/Driver/Options.td
clang/include/clang/Frontend/CommandLineSourceLoc.h
clang/include/clang/Serialization/ModuleFileExtension.h
clang/lib/Frontend/CompilerInvocation.cpp
clang/lib/Frontend/TestModuleFileExtension.cpp
clang/lib/Frontend/TestModuleFileExtension.h
Removed:
################################################################################
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 544f5771861a..0f0522410df6 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -248,7 +248,7 @@ class LangOpts<string base>
class TargetOpts<string base>
: KeyPathAndMacro<"TargetOpts->", base> {}
class FrontendOpts<string base>
- : KeyPathAndMacro<"FrontendOpts.", base> {}
+ : KeyPathAndMacro<"FrontendOpts.", base, "FRONTEND_"> {}
class PreprocessorOutputOpts<string base>
: KeyPathAndMacro<"PreprocessorOutputOpts.", base> {}
class DependencyOutputOpts<string base>
diff --git a/clang/include/clang/Frontend/CommandLineSourceLoc.h b/clang/include/clang/Frontend/CommandLineSourceLoc.h
index 0827433462e1..dfc4454b4baf 100644
--- a/clang/include/clang/Frontend/CommandLineSourceLoc.h
+++ b/clang/include/clang/Frontend/CommandLineSourceLoc.h
@@ -48,6 +48,13 @@ struct ParsedSourceLocation {
return PSL;
}
+
+ /// Serialize ParsedSourceLocation back to a string.
+ std::string ToString() const {
+ return (llvm::Twine(FileName == "<stdin>" ? "-" : FileName) + ":" +
+ Twine(Line) + ":" + Twine(Column))
+ .str();
+ }
};
/// A source range that has been parsed on the command line.
diff --git a/clang/include/clang/Serialization/ModuleFileExtension.h b/clang/include/clang/Serialization/ModuleFileExtension.h
index 63562c0d6bd2..e9ac4c22520e 100644
--- a/clang/include/clang/Serialization/ModuleFileExtension.h
+++ b/clang/include/clang/Serialization/ModuleFileExtension.h
@@ -60,9 +60,20 @@ class ModuleFileExtensionWriter;
/// custom writer that can then be accessed via a custom reader when
/// the module file or precompiled header is loaded.
class ModuleFileExtension {
+protected:
+ /// Discriminator for LLVM-style RTTI.
+ enum ModuleFileExtensionKind {
+ MFEK_Test,
+ };
+
+ const ModuleFileExtensionKind Kind;
public:
+ ModuleFileExtension(ModuleFileExtensionKind Kind) : Kind(Kind) {}
+
virtual ~ModuleFileExtension();
+ ModuleFileExtensionKind getKind() const { return Kind; }
+
/// Retrieves the metadata for this module file extension.
virtual ModuleFileExtensionMetadata getExtensionMetadata() const = 0;
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 9cdaab8804d8..928009ebf2f4 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -386,7 +386,7 @@ template <typename T> static T extractForwardValue(T KeyPath) {
template <typename T, typename U, U Value>
static T extractMaskValue(T KeyPath) {
- return KeyPath & Value;
+ return ((KeyPath & Value) == Value) ? Value : T();
}
#define PARSE_OPTION_WITH_MARSHALLING(ARGS, DIAGS, SUCCESS, ID, FLAGS, PARAM, \
@@ -2219,9 +2219,188 @@ static Optional<frontend::ActionKind> getFrontendAction(OptSpecifier &Opt) {
return None;
}
-static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
- DiagnosticsEngine &Diags, bool &IsHeaderFile) {
+/// Maps frontend action to command line option.
+static Optional<OptSpecifier>
+getProgramActionOpt(frontend::ActionKind ProgramAction) {
+ for (const auto &ActionOpt : getFrontendActionTable())
+ if (ActionOpt.first == ProgramAction)
+ return OptSpecifier(ActionOpt.second);
+
+ return None;
+}
+
+static void GenerateFrontendArgs(const FrontendOptions &Opts,
+ SmallVectorImpl<const char *> &Args,
+ CompilerInvocation::StringAllocator SA,
+ bool IsHeader) {
+ const FrontendOptions &FrontendOpts = Opts;
+#define FRONTEND_OPTION_WITH_MARSHALLING( \
+ PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
+ DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
+ MERGER, EXTRACTOR, TABLE_INDEX) \
+ GENERATE_OPTION_WITH_MARSHALLING( \
+ Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
+ IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX)
+#include "clang/Driver/Options.inc"
+#undef FRONTEND_OPTION_WITH_MARSHALLING
+
+ Optional<OptSpecifier> ProgramActionOpt =
+ getProgramActionOpt(Opts.ProgramAction);
+
+ // Generating a simple flag covers most frontend actions.
+ std::function<void()> GenerateProgramAction = [&]() {
+ GenerateArg(Args, *ProgramActionOpt, SA);
+ };
+
+ if (!ProgramActionOpt) {
+ // PluginAction is the only program action handled separately.
+ assert(Opts.ProgramAction == frontend::PluginAction &&
+ "Frontend action without option.");
+ GenerateProgramAction = [&]() {
+ GenerateArg(Args, OPT_plugin, Opts.ActionName, SA);
+ };
+ }
+
+ // FIXME: Simplify the complex 'AST dump' command line.
+ if (Opts.ProgramAction == frontend::ASTDump) {
+ GenerateProgramAction = [&]() {
+ // ASTDumpLookups, ASTDumpDeclTypes and ASTDumpFilter are generated via
+ // marshalling infrastructure.
+
+ if (Opts.ASTDumpFormat != ADOF_Default) {
+ StringRef Format;
+ switch (Opts.ASTDumpFormat) {
+ case ADOF_Default:
+ llvm_unreachable("Default AST dump format.");
+ case ADOF_JSON:
+ Format = "json";
+ break;
+ }
+
+ if (Opts.ASTDumpAll)
+ GenerateArg(Args, OPT_ast_dump_all_EQ, Format, SA);
+ if (Opts.ASTDumpDecls)
+ GenerateArg(Args, OPT_ast_dump_EQ, Format, SA);
+ } else {
+ if (Opts.ASTDumpAll)
+ GenerateArg(Args, OPT_ast_dump_all, SA);
+ if (Opts.ASTDumpDecls)
+ GenerateArg(Args, OPT_ast_dump, SA);
+ }
+ };
+ }
+
+ if (Opts.ProgramAction == frontend::FixIt && !Opts.FixItSuffix.empty()) {
+ GenerateProgramAction = [&]() {
+ GenerateArg(Args, OPT_fixit_EQ, Opts.FixItSuffix, SA);
+ };
+ }
+
+ GenerateProgramAction();
+
+ for (const auto &PluginArgs : Opts.PluginArgs)
+ for (const auto &PluginArg : PluginArgs.second)
+ GenerateArg(Args, OPT_plugin_arg, PluginArgs.first + PluginArg, SA);
+
+ for (const auto &Ext : Opts.ModuleFileExtensions) {
+ if (auto *TestExt = dyn_cast_or_null<TestModuleFileExtension>(Ext.get())) {
+ std::string Buffer;
+ llvm::raw_string_ostream OS(Buffer);
+ OS << *TestExt;
+ GenerateArg(Args, OPT_ftest_module_file_extension_EQ, OS.str(), SA);
+ }
+ }
+
+ if (!Opts.CodeCompletionAt.FileName.empty())
+ GenerateArg(Args, OPT_code_completion_at, Opts.CodeCompletionAt.ToString(),
+ SA);
+
+ for (const auto &Plugin : Opts.Plugins)
+ GenerateArg(Args, OPT_load, Plugin, SA);
+
+ // ASTDumpDecls and ASTDumpAll already handled with ProgramAction.
+
+ for (const auto &ModuleFile : Opts.ModuleFiles)
+ GenerateArg(Args, OPT_fmodule_file, ModuleFile, SA);
+
+ if (Opts.AuxTargetCPU.hasValue())
+ GenerateArg(Args, OPT_aux_target_cpu, *Opts.AuxTargetCPU, SA);
+
+ if (Opts.AuxTargetFeatures.hasValue())
+ for (const auto &Feature : *Opts.AuxTargetFeatures)
+ GenerateArg(Args, OPT_aux_target_feature, Feature, SA);
+
+ {
+ StringRef Preprocessed = Opts.DashX.isPreprocessed() ? "-cpp-output" : "";
+ StringRef ModuleMap =
+ Opts.DashX.getFormat() == InputKind::ModuleMap ? "-module-map" : "";
+ StringRef Header = IsHeader ? "-header" : "";
+
+ StringRef Lang;
+ switch (Opts.DashX.getLanguage()) {
+ case Language::C:
+ Lang = "c";
+ break;
+ case Language::OpenCL:
+ Lang = "cl";
+ break;
+ case Language::CUDA:
+ Lang = "cuda";
+ break;
+ case Language::HIP:
+ Lang = "hip";
+ break;
+ case Language::CXX:
+ Lang = "c++";
+ break;
+ case Language::ObjC:
+ Lang = "objective-c";
+ break;
+ case Language::ObjCXX:
+ Lang = "objective-c++";
+ break;
+ case Language::RenderScript:
+ Lang = "renderscript";
+ break;
+ case Language::Asm:
+ Lang = "assembler-with-cpp";
+ break;
+ case Language::Unknown:
+ assert(Opts.DashX.getFormat() == InputKind::Precompiled &&
+ "Generating -x argument for unknown language (not precompiled).");
+ Lang = "ast";
+ break;
+ case Language::LLVM_IR:
+ Lang = "ir";
+ break;
+ }
+
+ GenerateArg(Args, OPT_x, Lang + Header + ModuleMap + Preprocessed, SA);
+ }
+
+ // OPT_INPUT has a unique class, generate it directly.
+ for (const auto &Input : Opts.Inputs)
+ Args.push_back(SA(Input.getFile()));
+}
+
+static bool ParseFrontendArgsImpl(FrontendOptions &Opts, ArgList &Args,
+ DiagnosticsEngine &Diags,
+ bool &IsHeaderFile) {
+ FrontendOptions &FrontendOpts = Opts;
+ bool Success = true;
unsigned NumErrorsBefore = Diags.getNumErrors();
+#define FRONTEND_OPTION_WITH_MARSHALLING( \
+ PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
+ DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
+ MERGER, EXTRACTOR, TABLE_INDEX) \
+ PARSE_OPTION_WITH_MARSHALLING(Args, Diags, Success, ID, FLAGS, PARAM, \
+ SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \
+ IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
+ MERGER, TABLE_INDEX)
+#include "clang/Driver/Options.inc"
+#undef FRONTEND_OPTION_WITH_MARSHALLING
Opts.ProgramAction = frontend::ParseSyntaxOnly;
if (const Arg *A = Args.getLastArg(OPT_Action_Group)) {
@@ -2426,6 +2605,34 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
return Diags.getNumErrors() == NumErrorsBefore;
}
+static bool ParseFrontendArgs(CompilerInvocation &Res, FrontendOptions &Opts,
+ ArgList &Args, DiagnosticsEngine &Diags,
+ bool &IsHeaderFile) {
+ FrontendOptions DummyOpts;
+
+ return RoundTrip(
+ [&IsHeaderFile](CompilerInvocation &Res, ArgList &Args,
+ DiagnosticsEngine &Diags) {
+ // ParseFrontendArgsImpl handles frontend action without querying the
+ // options. Let's do it now so RoundTrip considers us responsible for
+ // generating it.
+ for (const auto &Pair : getFrontendActionTable())
+ Args.hasArg(Pair.second);
+
+ return ParseFrontendArgsImpl(Res.getFrontendOpts(), Args, Diags,
+ IsHeaderFile);
+ },
+ [&IsHeaderFile](CompilerInvocation &Res,
+ SmallVectorImpl<const char *> &Args,
+ CompilerInvocation::StringAllocator SA) {
+ GenerateFrontendArgs(Res.getFrontendOpts(), Args, SA, IsHeaderFile);
+ },
+ [&DummyOpts](CompilerInvocation &Res) {
+ std::swap(Res.getFrontendOpts(), DummyOpts);
+ },
+ Res, Args, Diags, "FrontendOptions");
+}
+
std::string CompilerInvocation::GetResourcesPath(const char *Argv0,
void *MainAddr) {
std::string ClangExecutable =
@@ -3967,7 +4174,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
}
Success &= ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags,
/*DefaultDiagColor=*/false);
- Success &= ParseFrontendArgs(Res.getFrontendOpts(), Args, Diags,
+ Success &= ParseFrontendArgs(Res, Res.getFrontendOpts(), Args, Diags,
LangOpts.IsHeaderFile);
// FIXME: We shouldn't have to pass the DashX option around here
InputKind DashX = Res.getFrontendOpts().DashX;
@@ -4187,6 +4394,7 @@ void CompilerInvocation::generateCC1CommandLine(
llvm::Triple T(TargetOpts->Triple);
GenerateAnalyzerArgs(*AnalyzerOpts, Args, SA);
+ GenerateFrontendArgs(FrontendOpts, Args, SA, LangOpts->IsHeaderFile);
GenerateHeaderSearchArgs(*HeaderSearchOpts, Args, SA);
GenerateLangArgs(*LangOpts, Args, SA, T);
GenerateCodeGenArgs(CodeGenOpts, Args, SA, T, FrontendOpts.OutputFile,
diff --git a/clang/lib/Frontend/TestModuleFileExtension.cpp b/clang/lib/Frontend/TestModuleFileExtension.cpp
index 354aa7f5cd3f..f7d3f15046fe 100644
--- a/clang/lib/Frontend/TestModuleFileExtension.cpp
+++ b/clang/lib/Frontend/TestModuleFileExtension.cpp
@@ -127,3 +127,10 @@ TestModuleFileExtension::createExtensionReader(
return std::unique_ptr<ModuleFileExtensionReader>(
new TestModuleFileExtension::Reader(this, Stream));
}
+
+llvm::raw_ostream &clang::operator<<(llvm::raw_ostream &OS,
+ const TestModuleFileExtension &Extension) {
+ return OS << Extension.BlockName << ":" << Extension.MajorVersion << ":"
+ << Extension.MinorVersion << ":" << Extension.Hashed << ":"
+ << Extension.UserInfo;
+}
diff --git a/clang/lib/Frontend/TestModuleFileExtension.h b/clang/lib/Frontend/TestModuleFileExtension.h
index 13e090783b11..df4b718a1f68 100644
--- a/clang/lib/Frontend/TestModuleFileExtension.h
+++ b/clang/lib/Frontend/TestModuleFileExtension.h
@@ -48,7 +48,8 @@ class TestModuleFileExtension : public ModuleFileExtension {
unsigned MinorVersion,
bool Hashed,
StringRef UserInfo)
- : BlockName(BlockName),
+ : ModuleFileExtension(ModuleFileExtensionKind::MFEK_Test),
+ BlockName(BlockName),
MajorVersion(MajorVersion), MinorVersion(MinorVersion),
Hashed(Hashed), UserInfo(UserInfo) { }
~TestModuleFileExtension() override;
@@ -64,6 +65,14 @@ class TestModuleFileExtension : public ModuleFileExtension {
createExtensionReader(const ModuleFileExtensionMetadata &Metadata,
ASTReader &Reader, serialization::ModuleFile &Mod,
const llvm::BitstreamCursor &Stream) override;
+
+ static bool classof(const ModuleFileExtension *E) {
+ return E->getKind() == MFEK_Test;
+ }
+
+ /// Serialize the extension.
+ friend llvm::raw_ostream &
+ operator<<(llvm::raw_ostream &OS, const TestModuleFileExtension &Extension);
};
} // end namespace clang
More information about the cfe-commits
mailing list