[llvm] [CGData] Declare command line options in TableGen, one struct per library (PR #226087)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 24 02:18:52 PDT 2026
https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/226087
Implement the first step of
https://discourse.llvm.org/t/rfc-declare-library-command-line-options-in-tablegen-one-struct-per-library/91877
and migrate LLVMCGData's 13 cl::opts.
A .td with an `OptionsStruct` def declares a library's options with
`BoolField` (`-x`, `-no-x`, `-x=<bool>`) and `ValueField` (`-x=v`,
`-x v`). `-gen-opt-parser-defs` generates a struct with one member per
option, a `Global` instance, the option table, and `apply(const Arg &)`.
`cl::ParseCommandLineOptions` keeps owning argv: a static
`opt::RegisterLibraryOptions<T>` registers the struct as a
`cl::LibraryOptions`, and an argument naming none of cl::'s options is
dispatched to the library that declares it. `-help-hidden` lists library
options, `ResetAllOptionOccurrences` restores their defaults, and a name
shared with a cl::opt or another library is a fatal error.
CGDataOptions is private to lib/CGData:
* LTO calls `cgdata::thinLTOTwoRounds()` instead of declaring
`extern cl::opt<bool> CodeGenDataThinLTOTwoRounds`.
* llvm-cgdata passes `--indexed-codegen-data-lazy-loading` to
`CodeGenDataReader::create` instead of assigning the cl::opt exported
from CodeGenDataReader.h.
>From f1fd167eda2fdab5a7bf158a89e8ec4916396987 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Thu, 24 Sep 2026 00:42:37 -0700
Subject: [PATCH] [CGData] Declare command line options in TableGen, one struct
per library
Implement the first step of
https://discourse.llvm.org/t/rfc-declare-library-command-line-options-in-tablegen-one-struct-per-library/91877
and migrate LLVMCGData's 13 cl::opts.
A .td with an `OptionsStruct` def declares a library's options with
`BoolField` (`-x`, `-no-x`, `-x=<bool>`) and `ValueField` (`-x=v`,
`-x v`). `-gen-opt-parser-defs` generates a struct with one member per
option, a `Global` instance, the option table, and `apply(const Arg &)`.
`cl::ParseCommandLineOptions` keeps owning argv: a static
`opt::RegisterLibraryOptions<T>` registers the struct as a
`cl::LibraryOptions`, and an argument naming none of cl::'s options is
dispatched to the library that declares it. `-help-hidden` lists library
options, `ResetAllOptionOccurrences` restores their defaults, and a name
shared with a cl::opt or another library is a fatal error.
CGDataOptions is private to lib/CGData:
* LTO calls `cgdata::thinLTOTwoRounds()` instead of declaring
`extern cl::opt<bool> CodeGenDataThinLTOTwoRounds`.
* llvm-cgdata passes `--indexed-codegen-data-lazy-loading` to
`CodeGenDataReader::create` instead of assigning the cl::opt exported
from CodeGenDataReader.h.
---
llvm/docs/CommandLine.md | 33 +++++
llvm/include/llvm/CGData/CodeGenData.h | 4 +
llvm/include/llvm/CGData/CodeGenDataReader.h | 16 +--
llvm/include/llvm/Option/LibraryOptions.h | 96 +++++++++++++
llvm/include/llvm/Option/OptParser.td | 36 +++++
llvm/include/llvm/Support/CommandLine.h | 28 ++++
llvm/lib/CGData/CGDataOptions.cpp | 15 +++
llvm/lib/CGData/CGDataOptions.h | 17 +++
llvm/lib/CGData/CGDataOptions.td | 51 +++++++
llvm/lib/CGData/CMakeLists.txt | 7 +
llvm/lib/CGData/CodeGenData.cpp | 31 ++---
llvm/lib/CGData/CodeGenDataReader.cpp | 29 ++--
llvm/lib/CGData/StableFunctionMap.cpp | 57 ++------
llvm/lib/LTO/LTO.cpp | 3 +-
llvm/lib/Option/CMakeLists.txt | 1 +
llvm/lib/Option/LibraryOptions.cpp | 52 +++++++
llvm/lib/Support/CommandLine.cpp | 82 ++++++++++-
llvm/tools/llvm-cgdata/llvm-cgdata.cpp | 8 +-
llvm/unittests/Option/CMakeLists.txt | 4 +
llvm/unittests/Option/LibraryOptionsTest.cpp | 111 +++++++++++++++
llvm/unittests/Option/LibraryOpts.td | 16 +++
llvm/unittests/Support/CommandLineTest.cpp | 127 ++++++++++++++++++
llvm/utils/TableGen/OptionParserEmitter.cpp | 125 ++++++++++++++++-
.../gn/secondary/llvm/lib/CGData/BUILD.gn | 10 ++
.../gn/secondary/llvm/lib/Option/BUILD.gn | 1 +
.../secondary/llvm/unittests/Option/BUILD.gn | 7 +
.../llvm-project-overlay/llvm/BUILD.bazel | 16 ++-
.../llvm/unittests/BUILD.bazel | 11 ++
28 files changed, 889 insertions(+), 105 deletions(-)
create mode 100644 llvm/include/llvm/Option/LibraryOptions.h
create mode 100644 llvm/lib/CGData/CGDataOptions.cpp
create mode 100644 llvm/lib/CGData/CGDataOptions.h
create mode 100644 llvm/lib/CGData/CGDataOptions.td
create mode 100644 llvm/lib/Option/LibraryOptions.cpp
create mode 100644 llvm/unittests/Option/LibraryOptionsTest.cpp
create mode 100644 llvm/unittests/Option/LibraryOpts.td
diff --git a/llvm/docs/CommandLine.md b/llvm/docs/CommandLine.md
index 3dd499e2a2f6c..d8614e394109d 100644
--- a/llvm/docs/CommandLine.md
+++ b/llvm/docs/CommandLine.md
@@ -1680,3 +1680,36 @@ TODO: complete this section
:::{todo}
TODO: fill in this section
:::
+
+## Declaring a Library's Options in TableGen
+
+A library can declare its options in a `.td` file instead of as `cl::opt`
+globals. `llvm-tblgen -gen-opt-parser-defs` generates a struct with a member
+per option, the table that parses them, and the hooks through which
+`cl::ParseCommandLineOptions` parses them and `-help-hidden` lists them.
+
+```text
+include "llvm/Option/OptParser.td"
+
+def FooOptions : OptionsStruct;
+// The spellings of FooMode, a C++ enumeration declared elsewhere.
+def FooMode : OptionEnum<"FooMode", [EnumMember<"Fast", "fast">,
+ EnumMember<"Small", "small">]>;
+
+defm Enable : BoolField<"foo-enable", "1", "Enable foo">;
+defm Threshold : ValueField<"foo-threshold", "unsigned", "8", "The threshold">;
+defm Mode : EnumField<"foo-mode", FooMode, "FooMode::Fast", "Foo's mode">;
+```
+
+The `defm` name is the member name. A `BoolField` is set by `-x`, `-no-x`, or
+`-x=true|false|1|0`; a `ValueField` of an integer type, `double`,
+or `std::string` by `-x=value` or `-x value`. Both accept `--` for `-`.
+
+The header declares the struct after including what the member defaults need,
+and one source file defines it and registers it with `cl::`.
+
+The library then lists `XXOptionsTableGen` under `DEPENDS` and `Option`
+under `LINK_COMPONENTS`. Code reads `XXOptions::Global.CodeGenDataGenerate`,
+the instance the command line sets. Keep the header in `lib/`, as private as the
+`static cl::opt` it replaces; another library that needs a value calls a
+function or takes a parameter.
diff --git a/llvm/include/llvm/CGData/CodeGenData.h b/llvm/include/llvm/CGData/CodeGenData.h
index e44497a408245..cd1d849fdc155 100644
--- a/llvm/include/llvm/CGData/CodeGenData.h
+++ b/llvm/include/llvm/CGData/CodeGenData.h
@@ -242,6 +242,10 @@ struct StreamCacheData {
}
};
+/// Returns whether ThinLTO runs two rounds of code generation
+/// (-codegen-data-thinlto-two-rounds).
+LLVM_ABI bool thinLTOTwoRounds();
+
/// Save \p TheModule before the first codegen round.
/// \p Task represents the partition number in the parallel code generation
/// process. \p AddStream is the callback used to add the serialized module to
diff --git a/llvm/include/llvm/CGData/CodeGenDataReader.h b/llvm/include/llvm/CGData/CodeGenDataReader.h
index a656147a48610..94f7af2de1701 100644
--- a/llvm/include/llvm/CGData/CodeGenDataReader.h
+++ b/llvm/include/llvm/CGData/CodeGenDataReader.h
@@ -16,7 +16,6 @@
#include "llvm/CGData/CodeGenData.h"
#include "llvm/CGData/OutlinedHashTreeRecord.h"
#include "llvm/CGData/StableFunctionMapRecord.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/VirtualFileSystem.h"
@@ -50,14 +49,15 @@ class CodeGenDataReader {
}
/// Factory method to create an appropriately typed reader for the given
- /// codegen data file path and file system.
+ /// codegen data file path and file system. \p LazyLoading defers
+ /// deserializing the stable function map of indexed codegen data.
LLVM_ABI static Expected<std::unique_ptr<CodeGenDataReader>>
- create(const Twine &Path, vfs::FileSystem &FS);
+ create(const Twine &Path, vfs::FileSystem &FS, bool LazyLoading = false);
/// Factory method to create an appropriately typed reader for the given
/// memory buffer.
LLVM_ABI static Expected<std::unique_ptr<CodeGenDataReader>>
- create(std::unique_ptr<MemoryBuffer> Buffer);
+ create(std::unique_ptr<MemoryBuffer> Buffer, bool LazyLoading = false);
/// Extract the cgdata embedded in sections from the given object file and
/// merge them into the GlobalOutlineRecord. This is a static helper that
@@ -100,17 +100,17 @@ class CodeGenDataReader {
Error success() { return error(cgdata_error::success); }
};
-LLVM_ABI extern cl::opt<bool> IndexedCodeGenDataLazyLoading;
-
class LLVM_ABI IndexedCodeGenDataReader : public CodeGenDataReader {
/// The codegen data file contents.
std::unique_ptr<MemoryBuffer> DataBuffer;
/// The header
IndexedCGData::Header Header;
+ bool LazyLoading;
public:
- IndexedCodeGenDataReader(std::unique_ptr<MemoryBuffer> DataBuffer)
- : DataBuffer(std::move(DataBuffer)) {}
+ IndexedCodeGenDataReader(std::unique_ptr<MemoryBuffer> DataBuffer,
+ bool LazyLoading = false)
+ : DataBuffer(std::move(DataBuffer)), LazyLoading(LazyLoading) {}
IndexedCodeGenDataReader(const IndexedCodeGenDataReader &) = delete;
IndexedCodeGenDataReader &
operator=(const IndexedCodeGenDataReader &) = delete;
diff --git a/llvm/include/llvm/Option/LibraryOptions.h b/llvm/include/llvm/Option/LibraryOptions.h
new file mode 100644
index 0000000000000..2ddcb34b3323b
--- /dev/null
+++ b/llvm/include/llvm/Option/LibraryOptions.h
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Support for the options struct that -gen-opt-parser-defs generates from an
+// OptionsStruct def. See "Declaring a Library's Options in TableGen" in
+// llvm/docs/CommandLine.md.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OPTION_LIBRARYOPTIONS_H
+#define LLVM_OPTION_LIBRARYOPTIONS_H
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compiler.h"
+#include <string>
+#include <type_traits>
+
+namespace llvm {
+namespace opt {
+
+// Each accepts the spellings cl::opt accepts for the type.
+inline bool parseArgValue(StringRef S, bool &V) {
+ if (S == "true" || S == "1")
+ V = true;
+ else if (S == "false" || S == "0")
+ V = false;
+ else
+ return false;
+ return true;
+}
+
+inline bool parseArgValue(StringRef S, std::string &V) {
+ V = S.str();
+ return true;
+}
+
+template <typename T>
+std::enable_if_t<std::is_arithmetic_v<T> && !std::is_same_v<T, bool>, bool>
+parseArgValue(StringRef S, T &V) {
+ if constexpr (std::is_floating_point_v<T>)
+ return to_float(S, V);
+ else
+ return to_integer(S, V);
+}
+
+/// An OptTable with a public constructor, shared by every options struct.
+class LLVM_ABI LibraryOptTable : public OptTable {
+public:
+ explicit LibraryOptTable(const Tables &T) : OptTable(T) {}
+ ~LibraryOptTable() override;
+};
+
+/// Connects an options struct to cl::ParseCommandLineOptions.
+class LLVM_ABI LibraryOptionsParser final : public cl::LibraryOptions {
+public:
+ using ApplyFn = bool (*)(const Arg &);
+ using TableFn = const OptTable &(*)();
+ LibraryOptionsParser(TableFn Table, ApplyFn Apply, void (*Reset)())
+ : Table(Table), Apply(Apply), Reset(Reset) {}
+
+ void forEachOption(
+ function_ref<void(StringRef, StringRef, StringRef)> Fn) const override;
+ Error parse(ArrayRef<const char *> Args, unsigned &Consumed) override;
+ void reset() override { Reset(); }
+
+private:
+ TableFn Table;
+ ApplyFn Apply;
+ void (*Reset)();
+};
+
+/// Registers T::Global with cl::ParseCommandLineOptions. The library owning T
+/// defines one static instance in the file that includes the struct's
+/// definitions.
+template <typename T> class RegisterLibraryOptions {
+ LibraryOptionsParser Parser{T::optTable,
+ [](const Arg &A) { return T::Global.apply(A); },
+ [] { T::Global = T(); }};
+
+public:
+ RegisterLibraryOptions() { cl::addLibraryOptions(Parser); }
+};
+
+} // namespace opt
+} // namespace llvm
+
+#endif // LLVM_OPTION_LIBRARYOPTIONS_H
diff --git a/llvm/include/llvm/Option/OptParser.td b/llvm/include/llvm/Option/OptParser.td
index 7adb2cf6fa5de..d5fe98430b5a6 100644
--- a/llvm/include/llvm/Option/OptParser.td
+++ b/llvm/include/llvm/Option/OptParser.td
@@ -136,6 +136,13 @@ class Option<list<string> prefixes, string name, OptionKind kind,
code Denormalizer = "";
list<code> NormalizedValues = ?;
list<SubCommand> SubCommands = subcommands;
+ // For an option of an OptionsStruct: the member it sets, the member's type
+ // and default value, and the value a Flag assigns. Other kinds parse their
+ // argument.
+ string FieldName = ?;
+ string FieldType = ?;
+ code FieldDefault = ?;
+ code FieldValue = ?;
}
// Helpers for defining options.
@@ -271,6 +278,35 @@ class Denormalizer<code denormalizer> { code Denormalizer = denormalizer; }
class NormalizedValuesScope<code scope> { code NormalizedValuesScope = scope; }
class NormalizedValues<list<code> definitions> { list<code> NormalizedValues = definitions; }
+// Declaring a library's options as the members of a struct. A .td with an
+// OptionsStruct def generates a struct named after the def, with one member per
+// BoolField or ValueField. Every option is hidden from -help.
+
+class OptionsStruct;
+
+class Field<string name, string type, code default> {
+ string FieldName = name;
+ string FieldType = type;
+ code FieldDefault = default;
+}
+class FieldValue<code value> { code FieldValue = value; }
+
+// A bool member set by -<name>, -no-<name>, or -<name>=<true|false|1|0>.
+multiclass BoolField<string name, code default, string help> {
+ def NAME : Flag<["-", "--"], name>, Field<NAME, "bool", default>,
+ FieldValue<"true">, HelpText<help>, Flags<[HelpHidden]>;
+ def no_#NAME : Flag<["-", "--"], "no-"#name>, Field<NAME, "bool", default>,
+ FieldValue<"false">;
+ def NAME#_EQ : Joined<["-", "--"], name#"=">, Field<NAME, "bool", default>;
+}
+
+// A member of the C++ type <type> set by -<name>=<value> or -<name> <value>.
+multiclass ValueField<string name, string type, code default, string help> {
+ def NAME#_EQ : Joined<["-", "--"], name#"=">, Field<NAME, type, default>,
+ HelpText<help>, Flags<[HelpHidden]>;
+ def NAME : Separate<["-", "--"], name>, Alias<!cast<Option>(NAME#"_EQ")>;
+}
+
// Predefined options.
// FIXME: Have generator validate that these appear in correct position (and
diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h
index 15fbb8c56a0b0..c8917a75a291d 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -44,6 +44,7 @@ namespace llvm {
class StringSaver;
class ElementCount;
+class Error;
/// This namespace contains all of the command line option processing machinery.
/// It is intentionally a short name to make qualified usage concise.
@@ -2316,6 +2317,33 @@ LLVM_ABI void ResetCommandLineParser();
/// Parses `Arg` into the option handler `Handler`.
LLVM_ABI bool ProvidePositionalOption(Option *Handler, StringRef Arg, int i);
+/// The options of a library that declares them in TableGen rather than as
+/// cl::opt (see llvm/Option/LibraryOptions.h). ParseCommandLineOptions hands
+/// every argument naming one of them to parse().
+class LLVM_ABI LibraryOptions {
+public:
+ /// Calls \p Fn with the spelling of each option without its prefix (e.g.
+ /// "x" or "x="), its metavariable, and its help text.
+ virtual void forEachOption(
+ function_ref<void(StringRef Spelling, StringRef MetaVar, StringRef Help)>
+ Fn) const = 0;
+
+ /// Parses the option spelled by Args[0], which may take Args[1] as its
+ /// value, and sets \p Consumed to the number of arguments it spans.
+ virtual Error parse(ArrayRef<const char *> Args, unsigned &Consumed) = 0;
+
+ /// Restores the default values.
+ virtual void reset() = 0;
+
+protected:
+ // Registrations are static and never destroyed through this class.
+ ~LibraryOptions() = default;
+};
+
+/// Makes ParseCommandLineOptions recognize \p L's options. A name that is also
+/// a cl::opt or belongs to another library is a fatal error.
+LLVM_ABI void addLibraryOptions(LibraryOptions &L);
+
} // end namespace cl
} // end namespace llvm
diff --git a/llvm/lib/CGData/CGDataOptions.cpp b/llvm/lib/CGData/CGDataOptions.cpp
new file mode 100644
index 0000000000000..5154719dcb3f0
--- /dev/null
+++ b/llvm/lib/CGData/CGDataOptions.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "CGDataOptions.h"
+#include "llvm/Option/LibraryOptions.h"
+
+#define OPTIONS_STRUCT_DEFS
+#include "CGDataOptions.inc"
+
+static llvm::opt::RegisterLibraryOptions<llvm::CGDataOptions> Registration;
diff --git a/llvm/lib/CGData/CGDataOptions.h b/llvm/lib/CGData/CGDataOptions.h
new file mode 100644
index 0000000000000..b2430ad000f3a
--- /dev/null
+++ b/llvm/lib/CGData/CGDataOptions.h
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_CGDATA_CGDATAOPTIONS_H
+#define LLVM_LIB_CGDATA_CGDATAOPTIONS_H
+
+#include <limits>
+
+#define OPTIONS_STRUCT_DECL
+#include "CGDataOptions.inc"
+
+#endif // LLVM_LIB_CGDATA_CGDATAOPTIONS_H
diff --git a/llvm/lib/CGData/CGDataOptions.td b/llvm/lib/CGData/CGDataOptions.td
new file mode 100644
index 0000000000000..b46293d43edb1
--- /dev/null
+++ b/llvm/lib/CGData/CGDataOptions.td
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "llvm/Option/OptParser.td"
+
+def CGDataOptions : OptionsStruct;
+
+defm CodeGenDataGenerate : BoolField<"codegen-data-generate", "false",
+ "Emit CodeGen Data into custom sections">;
+defm CodeGenDataThinLTOTwoRounds : BoolField<"codegen-data-thinlto-two-rounds",
+ "false", "Enable two-round ThinLTO code generation. The first round emits "
+ "codegen data, while the second round uses the emitted codegen data for "
+ "further optimizations.">;
+defm CodeGenDataUsePath : ValueField<"codegen-data-use-path", "std::string",
+ "", "File path to where .cgdata file is read">;
+defm GlobalMergingCallOverhead : ValueField<"global-merging-call-overhead",
+ "double", "1.0", "The overhead cost associated with each function call when "
+ "merging functions.">;
+defm GlobalMergingExtraThreshold : ValueField<"global-merging-extra-threshold",
+ "double", "0.0", "An additional cost threshold that must be exceeded for "
+ "merging to be considered beneficial.">;
+defm GlobalMergingInstOverhead : ValueField<"global-merging-inst-overhead",
+ "double", "1.2", "The overhead cost associated with each instruction when "
+ "lowering to machine instruction.">;
+defm GlobalMergingMaxParams : ValueField<"global-merging-max-params",
+ "unsigned", "std::numeric_limits<unsigned>::max()",
+ "The maximum number of parameters allowed when merging functions.">;
+defm GlobalMergingMinInstrs : ValueField<"global-merging-min-instrs",
+ "unsigned", "1",
+ "The minimum instruction count required when merging functions.">;
+defm GlobalMergingMinMerges : ValueField<"global-merging-min-merges",
+ "unsigned", "2", "Minimum number of similar functions with the same hash "
+ "required for merging.">;
+defm GlobalMergingParamOverhead : ValueField<"global-merging-param-overhead",
+ "double", "2.0", "The overhead cost associated with each parameter when "
+ "merging functions.">;
+defm GlobalMergingSkipNoParams : BoolField<"global-merging-skip-no-params",
+ "true", "Skip merging functions with no parameters.">;
+defm IndexedCodeGenDataLazyLoading : BoolField<
+ "indexed-codegen-data-lazy-loading", "false", "Lazily load indexed "
+ "CodeGenData. Enable to save memory and time for final consumption of the "
+ "indexed CodeGenData in production.">;
+defm IndexedCodeGenDataReadFunctionMapNames : BoolField<
+ "indexed-codegen-data-read-function-map-names", "true", "Read function map "
+ "names in indexed CodeGenData. Can be disabled to save memory and time for "
+ "final consumption of the indexed CodeGenData in production.">;
diff --git a/llvm/lib/CGData/CMakeLists.txt b/llvm/lib/CGData/CMakeLists.txt
index 003173139f36c..6c31d76331f6f 100644
--- a/llvm/lib/CGData/CMakeLists.txt
+++ b/llvm/lib/CGData/CMakeLists.txt
@@ -1,4 +1,9 @@
+set(LLVM_TARGET_DEFINITIONS CGDataOptions.td)
+tablegen(LLVM CGDataOptions.inc -gen-opt-parser-defs)
+add_public_tablegen_target(CGDataOptionsTableGen)
+
add_llvm_component_library(LLVMCGData
+ CGDataOptions.cpp
CodeGenData.cpp
CodeGenDataReader.cpp
CodeGenDataWriter.cpp
@@ -11,6 +16,7 @@ add_llvm_component_library(LLVMCGData
${LLVM_MAIN_INCLUDE_DIR}/llvm/CGData
DEPENDS
+ CGDataOptionsTableGen
intrinsics_gen
LINK_COMPONENTS
@@ -19,4 +25,5 @@ add_llvm_component_library(LLVMCGData
Core
Support
Object
+ Option
)
diff --git a/llvm/lib/CGData/CodeGenData.cpp b/llvm/lib/CGData/CodeGenData.cpp
index 7900dc7653c03..0b253d1504e50 100644
--- a/llvm/lib/CGData/CodeGenData.cpp
+++ b/llvm/lib/CGData/CodeGenData.cpp
@@ -11,13 +11,13 @@
//
//===----------------------------------------------------------------------===//
+#include "CGDataOptions.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/CGData/CodeGenDataReader.h"
#include "llvm/CGData/OutlinedHashTreeRecord.h"
#include "llvm/CGData/StableFunctionMapRecord.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Caching.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/WithColor.h"
#define DEBUG_TYPE "cg-data"
@@ -25,21 +25,6 @@
using namespace llvm;
using namespace cgdata;
-static cl::opt<bool>
- CodeGenDataGenerate("codegen-data-generate", cl::init(false), cl::Hidden,
- cl::desc("Emit CodeGen Data into custom sections"));
-static cl::opt<std::string>
- CodeGenDataUsePath("codegen-data-use-path", cl::init(""), cl::Hidden,
- cl::desc("File path to where .cgdata file is read"));
-
-namespace llvm {
-cl::opt<bool> CodeGenDataThinLTOTwoRounds(
- "codegen-data-thinlto-two-rounds", cl::init(false), cl::Hidden,
- cl::desc("Enable two-round ThinLTO code generation. The first round "
- "emits codegen data, while the second round uses the emitted "
- "codegen data for further optimizations."));
-} // end namespace llvm
-
static std::string getCGDataErrString(cgdata_error Err,
const std::string &ErrMsg = "") {
std::string Msg;
@@ -123,6 +108,10 @@ const char *CodeGenDataSectNamePrefix[] = {
} // namespace
+bool llvm::cgdata::thinLTOTwoRounds() {
+ return CGDataOptions::Global.CodeGenDataThinLTOTwoRounds;
+}
+
namespace llvm {
std::string getCodeGenDataSectionName(CGDataSectKind CGSK,
@@ -148,17 +137,19 @@ CodeGenData &CodeGenData::getInstance() {
std::call_once(CodeGenData::OnceFlag, []() {
Instance = std::unique_ptr<CodeGenData>(new CodeGenData());
- if (CodeGenDataGenerate || CodeGenDataThinLTOTwoRounds)
+ const CGDataOptions &Opts = CGDataOptions::Global;
+ if (Opts.CodeGenDataGenerate || Opts.CodeGenDataThinLTOTwoRounds)
Instance->EmitCGData = true;
- else if (!CodeGenDataUsePath.empty()) {
+ else if (!Opts.CodeGenDataUsePath.empty()) {
// Initialize the global CGData if the input file name is given.
// We do not error-out when failing to parse the input file.
// Instead, just emit an warning message and fall back as if no CGData
// were available.
auto FS = vfs::getRealFileSystem();
- auto ReaderOrErr = CodeGenDataReader::create(CodeGenDataUsePath, *FS);
+ auto ReaderOrErr = CodeGenDataReader::create(
+ Opts.CodeGenDataUsePath, *FS, Opts.IndexedCodeGenDataLazyLoading);
if (Error E = ReaderOrErr.takeError()) {
- warn(std::move(E), CodeGenDataUsePath);
+ warn(std::move(E), Opts.CodeGenDataUsePath);
return;
}
// Publish each CGData based on the data type in the header.
diff --git a/llvm/lib/CGData/CodeGenDataReader.cpp b/llvm/lib/CGData/CodeGenDataReader.cpp
index aeb4a4d0d4b94..64bcdc9fb5595 100644
--- a/llvm/lib/CGData/CodeGenDataReader.cpp
+++ b/llvm/lib/CGData/CodeGenDataReader.cpp
@@ -11,29 +11,17 @@
//===----------------------------------------------------------------------===//
#include "llvm/CGData/CodeGenDataReader.h"
+#include "CGDataOptions.h"
#include "llvm/CGData/OutlinedHashTreeRecord.h"
#include "llvm/Object/ObjectFile.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#define DEBUG_TYPE "cg-data-reader"
using namespace llvm;
-static cl::opt<bool> IndexedCodeGenDataReadFunctionMapNames(
- "indexed-codegen-data-read-function-map-names", cl::init(true), cl::Hidden,
- cl::desc("Read function map names in indexed CodeGenData. Can be "
- "disabled to save memory and time for final consumption of the "
- "indexed CodeGenData in production."));
-
namespace llvm {
-cl::opt<bool> IndexedCodeGenDataLazyLoading(
- "indexed-codegen-data-lazy-loading", cl::init(false), cl::Hidden,
- cl::desc(
- "Lazily load indexed CodeGenData. Enable to save memory and time "
- "for final consumption of the indexed CodeGenData in production."));
-
static Expected<std::unique_ptr<MemoryBuffer>>
setupMemoryBuffer(const Twine &Filename, vfs::FileSystem &FS) {
auto BufferOrErr = Filename.str() == "-" ? MemoryBuffer::getSTDIN()
@@ -123,8 +111,8 @@ Error IndexedCodeGenDataReader::read() {
if (Ptr >= End)
return error(cgdata_error::eof);
FunctionMapRecord.setReadStableFunctionMapNames(
- IndexedCodeGenDataReadFunctionMapNames);
- if (IndexedCodeGenDataLazyLoading)
+ CGDataOptions::Global.IndexedCodeGenDataReadFunctionMapNames);
+ if (LazyLoading)
FunctionMapRecord.lazyDeserialize(std::move(SharedDataBuffer),
Header.StableFunctionMapOffset);
else
@@ -135,23 +123,26 @@ Error IndexedCodeGenDataReader::read() {
}
Expected<std::unique_ptr<CodeGenDataReader>>
-CodeGenDataReader::create(const Twine &Path, vfs::FileSystem &FS) {
+CodeGenDataReader::create(const Twine &Path, vfs::FileSystem &FS,
+ bool LazyLoading) {
// Set up the buffer to read.
auto BufferOrError = setupMemoryBuffer(Path, FS);
if (Error E = BufferOrError.takeError())
return std::move(E);
- return CodeGenDataReader::create(std::move(BufferOrError.get()));
+ return CodeGenDataReader::create(std::move(BufferOrError.get()), LazyLoading);
}
Expected<std::unique_ptr<CodeGenDataReader>>
-CodeGenDataReader::create(std::unique_ptr<MemoryBuffer> Buffer) {
+CodeGenDataReader::create(std::unique_ptr<MemoryBuffer> Buffer,
+ bool LazyLoading) {
if (Buffer->getBufferSize() == 0)
return make_error<CGDataError>(cgdata_error::empty_cgdata);
std::unique_ptr<CodeGenDataReader> Reader;
// Create the reader.
if (IndexedCodeGenDataReader::hasFormat(*Buffer))
- Reader = std::make_unique<IndexedCodeGenDataReader>(std::move(Buffer));
+ Reader = std::make_unique<IndexedCodeGenDataReader>(std::move(Buffer),
+ LazyLoading);
else if (TextCodeGenDataReader::hasFormat(*Buffer))
Reader = std::make_unique<TextCodeGenDataReader>(std::move(Buffer));
else
diff --git a/llvm/lib/CGData/StableFunctionMap.cpp b/llvm/lib/CGData/StableFunctionMap.cpp
index d0fae3a9d32f0..2ccfe58dc913d 100644
--- a/llvm/lib/CGData/StableFunctionMap.cpp
+++ b/llvm/lib/CGData/StableFunctionMap.cpp
@@ -14,54 +14,15 @@
//===----------------------------------------------------------------------===//
#include "llvm/CGData/StableFunctionMap.h"
+#include "CGDataOptions.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/CGData/StableFunctionMapRecord.h"
-#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#define DEBUG_TYPE "stable-function-map"
using namespace llvm;
-static cl::opt<unsigned>
- GlobalMergingMinMerges("global-merging-min-merges",
- cl::desc("Minimum number of similar functions with "
- "the same hash required for merging."),
- cl::init(2), cl::Hidden);
-static cl::opt<unsigned> GlobalMergingMinInstrs(
- "global-merging-min-instrs",
- cl::desc("The minimum instruction count required when merging functions."),
- cl::init(1), cl::Hidden);
-static cl::opt<unsigned> GlobalMergingMaxParams(
- "global-merging-max-params",
- cl::desc(
- "The maximum number of parameters allowed when merging functions."),
- cl::init(std::numeric_limits<unsigned>::max()), cl::Hidden);
-static cl::opt<bool> GlobalMergingSkipNoParams(
- "global-merging-skip-no-params",
- cl::desc("Skip merging functions with no parameters."), cl::init(true),
- cl::Hidden);
-static cl::opt<double> GlobalMergingInstOverhead(
- "global-merging-inst-overhead",
- cl::desc("The overhead cost associated with each instruction when lowering "
- "to machine instruction."),
- cl::init(1.2), cl::Hidden);
-static cl::opt<double> GlobalMergingParamOverhead(
- "global-merging-param-overhead",
- cl::desc("The overhead cost associated with each parameter when merging "
- "functions."),
- cl::init(2.0), cl::Hidden);
-static cl::opt<double>
- GlobalMergingCallOverhead("global-merging-call-overhead",
- cl::desc("The overhead cost associated with each "
- "function call when merging functions."),
- cl::init(1.0), cl::Hidden);
-static cl::opt<double> GlobalMergingExtraThreshold(
- "global-merging-extra-threshold",
- cl::desc("An additional cost threshold that must be exceeded for merging "
- "to be considered beneficial."),
- cl::init(0.0), cl::Hidden);
-
unsigned StableFunctionMap::getIdOrCreateForName(StringRef Name) {
auto It = NameToId.find(Name);
if (It != NameToId.end())
@@ -201,12 +162,13 @@ removeIdenticalIndexPair(StableFunctionMap::StableFunctionEntries &SFS) {
}
static bool isProfitable(const StableFunctionMap::StableFunctionEntries &SFS) {
+ const CGDataOptions &Opts = CGDataOptions::Global;
unsigned StableFunctionCount = SFS.size();
- if (StableFunctionCount < GlobalMergingMinMerges)
+ if (StableFunctionCount < Opts.GlobalMergingMinMerges)
return false;
unsigned InstCount = SFS[0]->InstCount;
- if (InstCount < GlobalMergingMinInstrs)
+ if (InstCount < Opts.GlobalMergingMinInstrs)
return false;
double Cost = 0.0;
@@ -216,21 +178,22 @@ static bool isProfitable(const StableFunctionMap::StableFunctionEntries &SFS) {
for (auto &[IndexPair, Hash] : *SF->IndexOperandHashMap)
UniqueHashVals.insert(Hash);
unsigned ParamCount = UniqueHashVals.size();
- if (ParamCount > GlobalMergingMaxParams)
+ if (ParamCount > Opts.GlobalMergingMaxParams)
return false;
// Theoretically, if ParamCount is 0, it results in identical code folding
// (ICF), which we can skip merging here since the linker already handles
// ICF. This pass would otherwise introduce unnecessary thunks that are
// merely direct jumps. However, enabling this could be beneficial depending
// on downstream passes, so we provide an option for it.
- if (GlobalMergingSkipNoParams && ParamCount == 0)
+ if (Opts.GlobalMergingSkipNoParams && ParamCount == 0)
return false;
- Cost += ParamCount * GlobalMergingParamOverhead + GlobalMergingCallOverhead;
+ Cost += ParamCount * Opts.GlobalMergingParamOverhead +
+ Opts.GlobalMergingCallOverhead;
}
- Cost += GlobalMergingExtraThreshold;
+ Cost += Opts.GlobalMergingExtraThreshold;
double Benefit =
- InstCount * (StableFunctionCount - 1) * GlobalMergingInstOverhead;
+ InstCount * (StableFunctionCount - 1) * Opts.GlobalMergingInstOverhead;
bool Result = Benefit > Cost;
LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS[0]->Hash << ", "
<< "StableFunctionCount = " << StableFunctionCount
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 4594c52fb5f6e..5f6edf88ce3e4 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -108,7 +108,6 @@ static cl::opt<bool>
DumpThinCGSCCs("dump-thin-cg-sccs", cl::init(false), cl::Hidden,
cl::desc("Dump the SCCs in the ThinLTO index's callgraph"));
namespace llvm {
-extern cl::opt<bool> CodeGenDataThinLTOTwoRounds;
extern cl::opt<bool> ForceImportAll;
extern cl::opt<bool> AlwaysRenamePromotedLocals;
} // end namespace llvm
@@ -2297,7 +2296,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
return BackendProcess->wait();
};
- if (!CodeGenDataThinLTOTwoRounds) {
+ if (!cgdata::thinLTOTwoRounds()) {
std::unique_ptr<ThinBackendProc> BackendProc =
ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
AddStream, Cache, BitcodeLibFuncs);
diff --git a/llvm/lib/Option/CMakeLists.txt b/llvm/lib/Option/CMakeLists.txt
index dc7669c521115..3106bcfa3878a 100644
--- a/llvm/lib/Option/CMakeLists.txt
+++ b/llvm/lib/Option/CMakeLists.txt
@@ -1,6 +1,7 @@
add_llvm_component_library(LLVMOption
Arg.cpp
ArgList.cpp
+ LibraryOptions.cpp
Option.cpp
OptTable.cpp
diff --git a/llvm/lib/Option/LibraryOptions.cpp b/llvm/lib/Option/LibraryOptions.cpp
new file mode 100644
index 0000000000000..62d87686e88cd
--- /dev/null
+++ b/llvm/lib/Option/LibraryOptions.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "llvm/Option/LibraryOptions.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Error.h"
+
+using namespace llvm;
+using namespace llvm::opt;
+
+LibraryOptTable::~LibraryOptTable() = default;
+
+void LibraryOptionsParser::forEachOption(
+ function_ref<void(StringRef, StringRef, StringRef)> Fn) const {
+ const OptTable &T = Table();
+ for (unsigned ID = 1, E = T.getNumOptions(); ID <= E; ++ID) {
+ unsigned Kind = T.getOptionKind(ID);
+ if (Kind != Option::FlagClass && Kind != Option::JoinedClass &&
+ Kind != Option::SeparateClass)
+ continue;
+ StringRef MetaVar = T.getOptionMetaVar(ID);
+ if (MetaVar.empty() && Kind == Option::JoinedClass)
+ MetaVar = "<value>";
+ Fn(T.getOptionName(ID), MetaVar, T.getOptionHelpText(ID));
+ }
+}
+
+Error LibraryOptionsParser::parse(ArrayRef<const char *> Args,
+ unsigned &Consumed) {
+ // An option takes at most one separate value.
+ Args = Args.take_front(2);
+ InputArgList List(Args.begin(), Args.end());
+ Consumed = 0;
+ std::unique_ptr<Arg> A = Table().ParseOneArg(List, Consumed);
+ Consumed = std::min<unsigned>(Consumed, Args.size());
+ if (!A)
+ return createStringError("option '" + Twine(Args[0]) +
+ "' requires an argument");
+ if (A->getOption().getKind() == Option::UnknownClass)
+ return createStringError("unknown argument '" + Twine(Args[0]) + "'");
+ if (!Apply(*A))
+ return createStringError("invalid value '" + Twine(A->getValue()) +
+ "' in '" + A->getAsString(List) + "'");
+ return Error::success();
+}
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index 70eca48ee6635..e32ca238ee538 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -182,6 +182,12 @@ class CommandLineParser {
// This collects the different subcommands that have been registered.
SmallPtrSet<SubCommand *, 4> RegisteredSubCommands;
+ // Libraries whose options are declared in TableGen, and an index from their
+ // option names. Libraries[NumIndexedLibraries:] are not indexed yet.
+ SmallVector<LibraryOptions *, 0> Libraries;
+ DenseMap<StringRef, LibraryOptions *> LibraryIndex;
+ size_t NumIndexedLibraries = 0;
+
CommandLineParser() { registerSubCommand(&SubCommand::getTopLevel()); }
void ResetAllOptionOccurrences();
@@ -228,7 +234,8 @@ class CommandLineParser {
bool HadErrors = false;
if (O->hasArgStr()) {
// Add argument to the argument map!
- if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) {
+ if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second ||
+ LibraryIndex.contains(O->ArgStr)) {
errs() << ProgramName << ": CommandLine Error: Option '" << O->ArgStr
<< "' registered more than once!\n";
HadErrors = true;
@@ -299,7 +306,7 @@ class CommandLineParser {
if (hasOptions(*S))
return true;
}
- return false;
+ return !Libraries.empty();
}
bool hasNamedSubCommands() const {
@@ -328,6 +335,34 @@ class CommandLineParser {
void printOptionValues();
+ void indexLibraryOptions() {
+ bool HadErrors = false;
+ for (; NumIndexedLibraries != Libraries.size(); ++NumIndexedLibraries) {
+ LibraryOptions *L = Libraries[NumIndexedLibraries];
+ L->forEachOption([&](StringRef Spelling, StringRef, StringRef) {
+ StringRef Name = Spelling.rtrim('=');
+ auto [It, Inserted] = LibraryIndex.try_emplace(Name, L);
+ if (Inserted ? none_of(RegisteredSubCommands,
+ [&](SubCommand *SC) {
+ return SC->OptionsMap.contains(Name);
+ })
+ : It->second == L)
+ return;
+ errs() << ProgramName << ": CommandLine Error: Option '" << Name
+ << "' registered more than once!\n";
+ HadErrors = true;
+ });
+ }
+ if (HadErrors)
+ report_fatal_error("inconsistency in registered CommandLine options");
+ }
+
+ // A library loaded while parsing (e.g. a pass plugin) may define the name.
+ LibraryOptions *lookupLibraryOption(StringRef Name) {
+ indexLibraryOptions();
+ return LibraryIndex.lookup(Name);
+ }
+
void registerCategory(OptionCategory *cat) {
assert(count_if(RegisteredOptionCategories,
[cat](const OptionCategory *Category) {
@@ -380,6 +415,9 @@ class CommandLineParser {
ResetAllOptionOccurrences();
RegisteredSubCommands.clear();
+ Libraries.clear();
+ LibraryIndex.clear();
+ NumIndexedLibraries = 0;
SubCommand::getTopLevel().reset();
SubCommand::getAll().reset();
@@ -1468,6 +1506,8 @@ void CommandLineParser::ResetAllOptionOccurrences() {
if (SC->ConsumeAfterOpt)
SC->ConsumeAfterOpt->reset();
}
+ for (LibraryOptions *L : Libraries)
+ L->reset();
}
bool CommandLineParser::ParseCommandLineOptions(
@@ -1501,6 +1541,7 @@ bool CommandLineParser::ParseCommandLineOptions(
// Copy the program name into ProgName, making sure not to overflow it.
ProgramName = std::string(sys::path::filename(StringRef(argv[0])));
+ indexLibraryOptions();
// Check out the positional arguments to collect information about them.
unsigned NumPositionalRequired = 0;
@@ -1651,6 +1692,18 @@ bool CommandLineParser::ParseCommandLineOptions(
Handler = LookupLongOption(SubCommand::getTopLevel(), ArgName, Value,
LongOptionsUseDoubleDash, HaveDoubleDash);
+ if (!Handler && (!LongOptionsUseDoubleDash || HaveDoubleDash)) {
+ if (LibraryOptions *L = lookupLibraryOption(ArgName.split('=').first)) {
+ unsigned N = 1;
+ if (Error E = L->parse(ArrayRef(argv + i, argv + argc), N)) {
+ *Errs << ProgramName << ": " << toString(std::move(E)) << '\n';
+ ErrorParsing = true;
+ }
+ i += N - 1;
+ continue;
+ }
+ }
+
// Check to see if this "option" is really a prefixed or grouped argument.
if (!Handler && !(LongOptionsUseDoubleDash && HaveDoubleDash))
Handler = HandlePrefixedOrGroupedOption(ArgName, Value, ErrorParsing,
@@ -2440,9 +2493,29 @@ class HelpPrinter {
for (const auto &Opt : Opts)
MaxArgLen = std::max(MaxArgLen, Opt.second->getOptionWidth());
+ // Library options are all hidden.
+ SmallVector<std::pair<std::string, StringRef>, 0> LibraryOpts;
+ if (ShowHidden)
+ for (LibraryOptions *L : globalParser().Libraries)
+ L->forEachOption(
+ [&](StringRef Spelling, StringRef MetaVar, StringRef Help) {
+ if (!Help.empty())
+ LibraryOpts.emplace_back((Spelling + MetaVar).str(), Help);
+ });
+ llvm::sort(LibraryOpts);
+ for (const auto &[Name, Help] : LibraryOpts)
+ MaxArgLen = std::max(MaxArgLen, argPlusPrefixesSize(Name));
+
outs() << "OPTIONS:\n";
printOptions(Opts, MaxArgLen);
+ if (!LibraryOpts.empty())
+ outs() << "\nLibrary options:\n\n";
+ for (const auto &[Name, Help] : LibraryOpts) {
+ outs() << PrintArg(Name);
+ Option::printHelpStr(Help, MaxArgLen, argPlusPrefixesSize(Name));
+ }
+
// Print any extra help the user has declared.
for (const auto &I : globalParser().MoreHelp)
outs() << I;
@@ -2867,6 +2940,11 @@ void cl::HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories,
}
void cl::ResetCommandLineParser() { globalParser().reset(); }
+
+void cl::addLibraryOptions(LibraryOptions &L) {
+ globalParser().Libraries.push_back(&L);
+}
+
void cl::ResetAllOptionOccurrences() {
globalParser().ResetAllOptionOccurrences();
}
diff --git a/llvm/tools/llvm-cgdata/llvm-cgdata.cpp b/llvm/tools/llvm-cgdata/llvm-cgdata.cpp
index f06119a24e6ff..9cb7b1657aa0e 100644
--- a/llvm/tools/llvm-cgdata/llvm-cgdata.cpp
+++ b/llvm/tools/llvm-cgdata/llvm-cgdata.cpp
@@ -67,6 +67,7 @@ static std::string OutputFilename = "-";
static std::string Filename;
static bool ShowCGDataVersion;
static bool SkipTrim;
+static bool LazyLoading;
static CGDataAction Action;
static std::optional<CGDataFormat> OutputFormat;
static std::vector<std::string> InputFilenames;
@@ -107,7 +108,7 @@ static int convert_main(int argc, const char *argv[]) {
exitWithErrorCode(EC, OutputFilename);
auto FS = vfs::getRealFileSystem();
- auto ReaderOrErr = CodeGenDataReader::create(Filename, *FS);
+ auto ReaderOrErr = CodeGenDataReader::create(Filename, *FS, LazyLoading);
if (Error E = ReaderOrErr.takeError())
exitWithError(std::move(E), Filename);
@@ -239,7 +240,7 @@ static int show_main(int argc, const char *argv[]) {
exitWithErrorCode(EC, OutputFilename);
auto FS = vfs::getRealFileSystem();
- auto ReaderOrErr = CodeGenDataReader::create(Filename, *FS);
+ auto ReaderOrErr = CodeGenDataReader::create(Filename, *FS, LazyLoading);
if (Error E = ReaderOrErr.takeError())
exitWithError(std::move(E), Filename);
@@ -350,8 +351,7 @@ static void parseArgs(int argc, char **argv) {
llvm_unreachable("unrecognized action");
}
- IndexedCodeGenDataLazyLoading =
- Args.hasArg(OPT_indexed_codegen_data_lazy_loading);
+ LazyLoading = Args.hasArg(OPT_indexed_codegen_data_lazy_loading);
}
int llvm_cgdata_main(int argc, char **argvNonConst, const llvm::ToolContext &) {
diff --git a/llvm/unittests/Option/CMakeLists.txt b/llvm/unittests/Option/CMakeLists.txt
index 5fefb5e85afde..2c48a7e8ad4b3 100644
--- a/llvm/unittests/Option/CMakeLists.txt
+++ b/llvm/unittests/Option/CMakeLists.txt
@@ -3,6 +3,9 @@ set(LLVM_LINK_COMPONENTS
Support
)
+set(LLVM_TARGET_DEFINITIONS LibraryOpts.td)
+tablegen(LLVM LibraryOpts.inc -gen-opt-parser-defs)
+
set(LLVM_TARGET_DEFINITIONS Opts.td)
tablegen(LLVM Opts.inc -gen-opt-parser-defs)
@@ -12,6 +15,7 @@ tablegen(LLVM SubCommandOpts.inc -gen-opt-parser-defs)
add_public_tablegen_target(OptsTestTableGen)
add_llvm_unittest(OptionTests
+ LibraryOptionsTest.cpp
OptionParsingTest.cpp
OptionMarshallingTest.cpp
OptionSubCommandsTest.cpp
diff --git a/llvm/unittests/Option/LibraryOptionsTest.cpp b/llvm/unittests/Option/LibraryOptionsTest.cpp
new file mode 100644
index 0000000000000..6dde8550c4d8b
--- /dev/null
+++ b/llvm/unittests/Option/LibraryOptionsTest.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#define OPTIONS_STRUCT_DECL
+#include "LibraryOpts.inc"
+
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/LibraryOptions.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#define OPTIONS_STRUCT_DEFS
+#include "LibraryOpts.inc"
+
+using namespace llvm;
+
+namespace {
+
+// The struct -gen-opt-parser-defs generates: every spelling sets its member.
+TEST(LibraryOptionsTest, Apply) {
+ TestLibraryOptions O;
+ EXPECT_FALSE(O.Enable);
+ EXPECT_EQ(O.Count, 3u);
+ EXPECT_EQ(O.Ratio, 0.5);
+ EXPECT_EQ(O.Path, "p");
+
+ auto Apply = [&](std::initializer_list<const char *> Argv) {
+ unsigned MissingIndex, MissingCount;
+ opt::InputArgList Args = TestLibraryOptions::optTable().ParseArgs(
+ Argv, MissingIndex, MissingCount);
+ std::vector<bool> Applied;
+ for (const opt::Arg *A : Args)
+ Applied.push_back(O.apply(*A));
+ return Applied;
+ };
+ EXPECT_THAT(Apply({"-lib-enable", "--lib-count=7", "-lib-ratio", "0.25",
+ "-lib-path=a=b"}),
+ testing::Each(true));
+ EXPECT_TRUE(O.Enable);
+ EXPECT_EQ(O.Count, 7u);
+ EXPECT_EQ(O.Ratio, 0.25);
+ EXPECT_EQ(O.Path, "a=b");
+ EXPECT_THAT(Apply({"-no-lib-enable"}), testing::Each(true));
+ EXPECT_FALSE(O.Enable);
+ EXPECT_THAT(Apply({"-lib-enable=1"}), testing::Each(true));
+ EXPECT_TRUE(O.Enable);
+
+ // A rejected value leaves the member unchanged.
+ EXPECT_THAT(Apply({"-lib-enable=2", "-lib-count=x", "-lib-ratio=y"}),
+ testing::Each(false));
+ EXPECT_TRUE(O.Enable);
+ EXPECT_EQ(O.Count, 7u);
+ EXPECT_EQ(O.Ratio, 0.25);
+}
+
+// What cl:: sees of the struct, without cl::.
+TEST(LibraryOptionsTest, Parser) {
+ opt::LibraryOptionsParser P(
+ TestLibraryOptions::optTable,
+ [](const opt::Arg &A) { return TestLibraryOptions::Global.apply(A); },
+ [] { TestLibraryOptions::Global = TestLibraryOptions(); });
+
+ std::vector<std::string> Rows;
+ P.forEachOption([&](StringRef Spelling, StringRef MetaVar, StringRef Help) {
+ Rows.push_back((Spelling + "|" + MetaVar + "|" + Help).str());
+ });
+ EXPECT_THAT(Rows, testing::ElementsAre(
+ "lib-count=|<value>|An unsigned", "lib-count||",
+ "lib-enable=|<value>|", "lib-enable||A bool",
+ "lib-path=|<value>|A string", "lib-path||",
+ "lib-ratio=|<value>|A double", "lib-ratio||",
+ "no-lib-enable||"));
+
+ auto Parse = [&](std::initializer_list<const char *> Argv) {
+ unsigned Consumed = 0;
+ std::string Err = toString(P.parse(Argv, Consumed));
+ return std::to_string(Consumed) + " " + Err;
+ };
+ EXPECT_EQ(Parse({"-lib-count", "5", "-lib-enable"}), "2 ");
+ EXPECT_EQ(TestLibraryOptions::Global.Count, 5u);
+ EXPECT_EQ(Parse({"-lib-count=x", "-lib-enable"}),
+ "1 invalid value 'x' in '-lib-count=x'");
+ EXPECT_EQ(Parse({"-lib-count"}),
+ "1 option '-lib-count' requires an argument");
+ EXPECT_EQ(Parse({"-no-lib-enable=1"}),
+ "1 unknown argument '-no-lib-enable=1'");
+ P.reset();
+ EXPECT_EQ(TestLibraryOptions::Global.Count, 3u);
+}
+
+// A static RegisterLibraryOptions connects the struct's Global to cl::.
+TEST(LibraryOptionsTest, Register) {
+ cl::ResetCommandLineParser();
+ opt::RegisterLibraryOptions<TestLibraryOptions> Registration;
+ const TestLibraryOptions &G = TestLibraryOptions::Global;
+ const char *Args[] = {"prog", "-lib-count", "5", "-lib-enable"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(Args), Args, "", &nulls()));
+ EXPECT_EQ(G.Count, 5u);
+ EXPECT_TRUE(G.Enable);
+ cl::ResetAllOptionOccurrences();
+ EXPECT_EQ(G.Count, 3u);
+ EXPECT_FALSE(G.Enable);
+ cl::ResetCommandLineParser();
+}
+
+} // namespace
diff --git a/llvm/unittests/Option/LibraryOpts.td b/llvm/unittests/Option/LibraryOpts.td
new file mode 100644
index 0000000000000..8efd1e3059949
--- /dev/null
+++ b/llvm/unittests/Option/LibraryOpts.td
@@ -0,0 +1,16 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "llvm/Option/OptParser.td"
+
+def TestLibraryOptions : OptionsStruct;
+
+defm Enable : BoolField<"lib-enable", "false", "A bool">;
+defm Count : ValueField<"lib-count", "unsigned", "3", "An unsigned">;
+defm Ratio : ValueField<"lib-ratio", "double", "0.5", "A double">;
+defm Path : ValueField<"lib-path", "std::string", "\"p\"", "A string">;
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp
index 6af5ec7ccc774..ce5b2609fd2d6 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -2465,4 +2465,131 @@ TEST(CommandLineTest, HelpWithEmptyCategory) {
cl::ResetCommandLineParser();
}
+// Records the arguments it is handed. -library-int takes a separate value and
+// rejects "bad".
+struct TestLibrary final : cl::LibraryOptions {
+ std::vector<std::string> Args;
+ void forEachOption(
+ function_ref<void(StringRef, StringRef, StringRef)> Fn) const override {
+ Fn("library-flag", "", "A flag");
+ Fn("library-int", " <int>", "");
+ }
+ Error parse(ArrayRef<const char *> Argv, unsigned &Consumed) override {
+ Consumed = 1;
+ Args.push_back(Argv[0]);
+ if (StringRef(Argv[0]).ltrim('-') != "library-int")
+ return Error::success();
+ if (Argv.size() < 2)
+ return createStringError("missing value");
+ Consumed = 2;
+ Args.push_back(Argv[1]);
+ if (Args.back() == "bad")
+ return createStringError("bad value");
+ return Error::success();
+ }
+ void reset() override { Args.clear(); }
+};
+
+TEST(CommandLineTest, LibraryOptions) {
+ cl::ResetCommandLineParser();
+ TestLibrary L;
+ cl::addLibraryOptions(L);
+
+ // A library's options suffice to parse.
+ const char *Only[] = {"prog", "-library-flag"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(Only), Only, StringRef(),
+ &llvm::nulls()));
+ EXPECT_THAT(L.Args, testing::ElementsAre("-library-flag"));
+ cl::ResetAllOptionOccurrences();
+ EXPECT_TRUE(L.Args.empty());
+
+ StackOption<int> Local("local");
+ const char *Args[] = {"prog", "--library-int", "4", "-local=3",
+ "-library-flag"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(Args), Args, StringRef(),
+ &llvm::nulls()));
+ EXPECT_EQ(Local, 3);
+ EXPECT_THAT(L.Args,
+ testing::ElementsAre("--library-int", "4", "-library-flag"));
+ cl::ResetAllOptionOccurrences();
+
+ // A rejected value is still consumed.
+ std::string Errs;
+ raw_string_ostream OS(Errs);
+ const char *Bad[] = {"prog", "-library-int", "bad", "-local=1",
+ "-library-int"};
+ EXPECT_FALSE(
+ cl::ParseCommandLineOptions(std::size(Bad), Bad, StringRef(), &OS));
+ EXPECT_EQ(Local, 1);
+ EXPECT_EQ(Errs, "prog: bad value\nprog: missing value\n");
+ cl::ResetCommandLineParser();
+}
+
+TEST(CommandLineTest, LibraryOptionsHelp) {
+ cl::ResetCommandLineParser();
+ TestLibrary L;
+ cl::addLibraryOptions(L);
+ StackOption<int> Local("local", cl::desc("A cl::opt"));
+ const char *Args[] = {"prog"};
+ cl::ParseCommandLineOptions(std::size(Args), Args, StringRef(),
+ &llvm::nulls());
+
+ // Library options are listed only by -help-hidden, aligned with cl::'s.
+ // Options without help text are not listed.
+ std::string Hidden = interceptStdout(
+ [] { cl::PrintHelpMessage(/*Hidden=*/true, /*Categorized=*/false); });
+ auto HelpColumn = [&](StringRef Line) {
+ size_t Begin = Hidden.find(("\n" + Line).str());
+ return Begin == std::string::npos ? Begin
+ : Hidden.find(" - ", Begin) - Begin;
+ };
+ EXPECT_NE(std::string::npos,
+ Hidden.find("\nLibrary options:\n\n --library-flag "))
+ << Hidden;
+ EXPECT_NE(std::string::npos, HelpColumn(" --local="));
+ EXPECT_EQ(HelpColumn(" --local="), HelpColumn(" --library-flag "));
+ EXPECT_EQ(std::string::npos, Hidden.find("library-int"));
+ std::string Visible = interceptStdout(
+ [] { cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/false); });
+ EXPECT_EQ(std::string::npos, Visible.find("library"));
+ cl::ResetCommandLineParser();
+}
+
+// A library registered while parsing, as a plugin that an argument loads
+// does, serves the arguments after it.
+TEST(CommandLineTest, LibraryOptionsRegisteredWhileParsing) {
+ cl::ResetCommandLineParser();
+ TestLibrary L;
+ StackOption<std::string> Load("load", cl::callback([&](const std::string &) {
+ cl::addLibraryOptions(L);
+ }));
+
+ const char *Args[] = {"prog", "-load=plugin", "-library-flag"};
+ EXPECT_TRUE(cl::ParseCommandLineOptions(std::size(Args), Args, StringRef(),
+ &llvm::nulls()));
+ EXPECT_THAT(L.Args, testing::ElementsAre("-library-flag"));
+ cl::ResetCommandLineParser();
+}
+
+#if GTEST_HAS_DEATH_TEST
+TEST(CommandLineTest, LibraryOptionsConflict) {
+ cl::ResetCommandLineParser();
+ TestLibrary L;
+ cl::addLibraryOptions(L);
+ const char *Args[] = {"prog"};
+ // A cl::opt registered before the library is indexed.
+ EXPECT_DEATH(
+ {
+ StackOption<int> Clash("library-int");
+ cl::ParseCommandLineOptions(std::size(Args), Args);
+ },
+ "Option 'library-int' registered more than once");
+ // A cl::opt registered after.
+ cl::ParseCommandLineOptions(std::size(Args), Args);
+ EXPECT_DEATH(StackOption<int>("library-flag"),
+ "Option 'library-flag' registered more than once");
+ cl::ResetCommandLineParser();
+}
+#endif
+
} // anonymous namespace
diff --git a/llvm/utils/TableGen/OptionParserEmitter.cpp b/llvm/utils/TableGen/OptionParserEmitter.cpp
index 2360d55267625..8e55446b52110 100644
--- a/llvm/utils/TableGen/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/OptionParserEmitter.cpp
@@ -11,6 +11,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/raw_ostream.h"
@@ -228,6 +229,108 @@ static MarshallingInfo createMarshallingInfo(const Record &R) {
return Ret;
}
+// Emits the struct an OptionsStruct def declares: its declaration under
+// OPTIONS_STRUCT_DECL, and under OPTIONS_STRUCT_DEFS the global instance, the
+// option table, and apply(), which sets the member an argument names.
+static void emitOptionsStruct(const Record &Struct,
+ ArrayRef<const Record *> Groups,
+ ArrayRef<const Record *> Opts, raw_ostream &OS) {
+ struct Member {
+ StringRef Name, Type, Default, HelpText;
+ };
+ std::vector<const Record *> Fields;
+ for (const Record *R : Opts) {
+ StringRef Kind = R->getValueAsDef("Kind")->getValueAsString("Name");
+ if (isa<UnsetInit>(R->getValueInit("FieldName"))) {
+ if (Kind != "Input" && Kind != "Unknown" &&
+ isa<UnsetInit>(R->getValueInit("Alias")))
+ PrintFatalError(R->getLoc(), "an option of an OptionsStruct must be "
+ "declared with BoolField or ValueField");
+ continue;
+ }
+ bool HasValue = !isa<UnsetInit>(R->getValueInit("FieldValue"));
+ if ((Kind == "Flag") != HasValue ||
+ (Kind != "Flag" && Kind != "Joined" && Kind != "Separate"))
+ PrintFatalError(R->getLoc(), "a member is set by a Flag with a "
+ "FieldValue, or by a Joined or Separate");
+ Fields.push_back(R);
+ }
+ // Members in declaration order.
+ std::vector<const Record *> ByID = Fields;
+ llvm::sort(ByID, [](const Record *A, const Record *B) {
+ return A->getID() < B->getID();
+ });
+ std::vector<Member> Members;
+ StringMap<unsigned> MemberIndex;
+ for (const Record *R : ByID) {
+ Member M{R->getValueAsString("FieldName"), R->getValueAsString("FieldType"),
+ R->getValueAsString("FieldDefault"),
+ getOptionalString(*R, "HelpText")};
+ auto [It, Inserted] = MemberIndex.try_emplace(M.Name, Members.size());
+ if (Inserted) {
+ Members.push_back(M);
+ continue;
+ }
+ Member &Prev = Members[It->second];
+ if (Prev.Type != M.Type || Prev.Default != M.Default)
+ PrintFatalError(R->getLoc(), "member '" + M.Name +
+ "' is declared with a different type "
+ "or default");
+ if (Prev.HelpText.empty())
+ Prev.HelpText = M.HelpText;
+ }
+
+ StringRef Name = Struct.getName();
+ OS << "\n#ifdef OPTIONS_STRUCT_DECL\n#undef OPTIONS_STRUCT_DECL\n";
+ OS << "#include <string>\n\n";
+ OS << "namespace llvm {\nnamespace opt {\nclass Arg;\n"
+ "class OptTable;\n} // namespace opt\n\n";
+ OS << "struct " << Name << " {\n";
+ for (const Member &M : Members) {
+ if (!M.HelpText.empty()) {
+ std::string Help = M.HelpText.str();
+ llvm::replace(Help, '\n', ' ');
+ OS << " /// " << Help << "\n";
+ }
+ OS << " " << M.Type << " " << M.Name << "{" << M.Default << "};\n";
+ }
+ OS << "\n /// The instance cl::ParseCommandLineOptions sets.\n";
+ OS << " static " << Name << " Global;\n\n";
+ OS << " static const opt::OptTable &optTable();\n";
+ OS << " /// Sets the member that \\p A names. Returns false if the value is "
+ "invalid.\n";
+ OS << " bool apply(const opt::Arg &A);\n";
+ OS << "};\n} // namespace llvm\n";
+ OS << "#endif // OPTIONS_STRUCT_DECL\n";
+
+ std::string Qualified = ("llvm::" + Name).str();
+ OS << "\n#ifdef OPTIONS_STRUCT_DEFS\n#undef OPTIONS_STRUCT_DEFS\n";
+ OS << Qualified << " " << Qualified << "::Global;\n\n";
+ OS << "namespace {\nenum ID : unsigned {\n OPT_INVALID = 0,\n";
+ for (const Record *R : Groups)
+ OS << " OPT_" << getOptionName(*R) << ",\n";
+ for (const Record *R : Opts)
+ OS << " OPT_" << getOptionName(*R) << ",\n";
+ OS << "};\n} // namespace\n\n";
+ OS << "const llvm::opt::OptTable &" << Qualified << "::optTable() {\n";
+ OS << " static const llvm::opt::LibraryOptTable T(optionTables());\n";
+ OS << " return T;\n}\n\n";
+ OS << "bool " << Qualified << "::apply(const llvm::opt::Arg &A) {\n";
+ OS << " switch (A.getOption().getID()) {\n";
+ for (const Record *R : Fields) {
+ OS << " case OPT_" << getOptionName(*R) << ":\n";
+ StringRef Member = R->getValueAsString("FieldName");
+ if (isa<UnsetInit>(R->getValueInit("FieldValue")))
+ OS << " return llvm::opt::parseArgValue(A.getValue(), " << Member
+ << ");\n";
+ else
+ OS << " " << Member << " = " << R->getValueAsString("FieldValue")
+ << ";\n return true;\n";
+ }
+ OS << " }\n llvm_unreachable(\"option without a member\");\n}\n";
+ OS << "#endif // OPTIONS_STRUCT_DEFS\n";
+}
+
/// OptionParserEmitter - 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.
@@ -241,6 +344,11 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
std::vector<const Record *> SubCommands =
Records.getAllDerivedDefinitions("SubCommand");
+ ArrayRef<const Record *> Structs =
+ Records.getAllDerivedDefinitions("OptionsStruct");
+ if (Structs.size() > 1)
+ PrintFatalError(Structs[1]->getLoc(), "only one OptionsStruct is allowed");
+
emitSourceFileHeader("Option Parsing Definitions", OS);
// Generate prefix groups.
@@ -324,9 +432,17 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
OS << "/////////\n";
OS << "// Tables\n\n";
- OS << "#ifdef OPTTABLE_CODE\n";
+ // An OptionsStruct's definitions use the tables.
+ OS << (Structs.empty()
+ ? "#ifdef OPTTABLE_CODE\n"
+ : "#if defined(OPTTABLE_CODE) || defined(OPTIONS_STRUCT_DEFS)\n");
// A function rather than an object: the object needs dynamic relocations.
OS << "static llvm::opt::OptTable::Tables optionTables() {\n";
+ // An OptionsStruct's .cpp has no using-directive for llvm::opt.
+ if (!Structs.empty())
+ OS << " using llvm::opt::DefaultVis, llvm::opt::HelpHidden,\n"
+ " llvm::opt::RenderAsInput, llvm::opt::RenderJoined,\n"
+ " llvm::opt::RenderSeparate;\n";
Table.EmitStringTableDef(OS, "OptionStrTable");
OS << "\n";
@@ -484,7 +600,12 @@ static void emitOptionParser(const RecordKeeper &Records, raw_ostream &OS) {
<< ", OptionSubCommandIDsTable};\n";
OS << "}\n";
OS << "#undef OPTTABLE_CODE\n";
- OS << "#endif // OPTTABLE_CODE\n\n";
+ OS << (Structs.empty()
+ ? "#endif // OPTTABLE_CODE\n\n"
+ : "#endif // OPTTABLE_CODE || OPTIONS_STRUCT_DEFS\n\n");
+
+ if (!Structs.empty())
+ emitOptionsStruct(*Structs[0], Groups, Opts, OS);
// Dump ValuesCode.
OS << "/////////\n";
diff --git a/llvm/utils/gn/secondary/llvm/lib/CGData/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/CGData/BUILD.gn
index 9947c68a319d8..cf2b6997540bf 100644
--- a/llvm/utils/gn/secondary/llvm/lib/CGData/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/lib/CGData/BUILD.gn
@@ -1,10 +1,20 @@
+import("//llvm/utils/TableGen/tablegen.gni")
+
+tablegen("CGDataOptions") {
+ visibility = [ ":CGData" ]
+ args = [ "-gen-opt-parser-defs" ]
+}
+
static_library("CGData") {
output_name = "LLVMCGData"
deps = [
+ ":CGDataOptions",
"//llvm/lib/IR",
+ "//llvm/lib/Option",
"//llvm/lib/Support",
]
sources = [
+ "CGDataOptions.cpp",
"CodeGenData.cpp",
"CodeGenDataReader.cpp",
"CodeGenDataWriter.cpp",
diff --git a/llvm/utils/gn/secondary/llvm/lib/Option/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Option/BUILD.gn
index 2d1cf272865d0..b193227d160e5 100644
--- a/llvm/utils/gn/secondary/llvm/lib/Option/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/lib/Option/BUILD.gn
@@ -4,6 +4,7 @@ static_library("Option") {
sources = [
"Arg.cpp",
"ArgList.cpp",
+ "LibraryOptions.cpp",
"OptTable.cpp",
"Option.cpp",
]
diff --git a/llvm/utils/gn/secondary/llvm/unittests/Option/BUILD.gn b/llvm/utils/gn/secondary/llvm/unittests/Option/BUILD.gn
index 759fd6e40fa9f..4694b886c50c7 100644
--- a/llvm/utils/gn/secondary/llvm/unittests/Option/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/unittests/Option/BUILD.gn
@@ -1,6 +1,11 @@
import("//llvm/utils/TableGen/tablegen.gni")
import("//third-party/unittest/unittest.gni")
+tablegen("LibraryOpts") {
+ visibility = [ ":OptionTests" ]
+ args = [ "-gen-opt-parser-defs" ]
+}
+
tablegen("Opts") {
visibility = [ ":OptionTests" ]
args = [ "-gen-opt-parser-defs" ]
@@ -13,12 +18,14 @@ tablegen("SubCommandOpts") {
unittest("OptionTests") {
deps = [
+ ":LibraryOpts",
":Opts",
":SubCommandOpts",
"//llvm/lib/Option",
"//llvm/lib/Support",
]
sources = [
+ "LibraryOptionsTest.cpp",
"OptionMarshallingTest.cpp",
"OptionParsingTest.cpp",
"OptionSubCommandsTest.cpp",
diff --git a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
index b683f86ca3863..2680e426957a8 100644
--- a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
@@ -2361,9 +2361,21 @@ cc_library(
],
)
+gentbl_cc_library(
+ name = "CGDataOptionsTableGen",
+ strip_include_prefix = "lib/CGData",
+ tbl_outs = {"lib/CGData/CGDataOptions.inc": ["-gen-opt-parser-defs"]},
+ tblgen = ":llvm-tblgen",
+ td_file = "lib/CGData/CGDataOptions.td",
+ deps = [":OptParserTdFiles"],
+)
+
cc_library(
name = "CGData",
- srcs = glob(["lib/CGData/**/*.cpp"]),
+ srcs = glob([
+ "lib/CGData/**/*.cpp",
+ "lib/CGData/**/*.h",
+ ]),
hdrs = glob([
"include/llvm/CGData/**/*.h",
"include/llvm/CGData/**/*.inc",
@@ -2372,9 +2384,11 @@ cc_library(
deps = [
":BitReader",
":BitWriter",
+ ":CGDataOptionsTableGen",
":Core",
":Object",
":ObjectYAML",
+ ":Option",
":Support",
":TargetParser",
],
diff --git a/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
index 90a6760e9fb74..4ab839839856c 100644
--- a/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
@@ -644,6 +644,15 @@ cc_test(
],
)
+gentbl_cc_library(
+ name = "library_opts_tests_gen",
+ strip_include_prefix = "Option",
+ tbl_outs = {"Option/LibraryOpts.inc": ["-gen-opt-parser-defs"]},
+ tblgen = "//llvm:llvm-tblgen",
+ td_file = "Option/LibraryOpts.td",
+ deps = ["//llvm:OptParserTdFiles"],
+)
+
gentbl_cc_library(
name = "option_tests_gen",
strip_include_prefix = "Option",
@@ -682,10 +691,12 @@ cc_test(
allow_empty = False,
),
deps = [
+ ":library_opts_tests_gen",
":option_tests_gen",
":sub_command_opts_tests_gen",
"//llvm:Option",
"//llvm:Support",
+ "//third-party/unittest:gmock",
"//third-party/unittest:gtest",
"//third-party/unittest:gtest_main",
],
More information about the llvm-commits
mailing list