[llvm] [TableGen] Allow emitter actions to use `const RecordKeeper &` (PR #104716)
Rahul Joshi via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 20 10:54:07 PDT 2024
https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/104716
>From 45fc41b2ec6a6fe3668fc9f42ccb8761c592a389 Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Sun, 18 Aug 2024 09:06:16 -0700
Subject: [PATCH] [TableGen] Allow emitter callback to use `const RecordKeeper
&`
- Refactor TableGen backend options to allow specifying a callback
function that takes a const reference to `RecordKeeper`. This
will enable gradual migration of code to use const references
and pointers to `RecordKeeper` and `Record` in the TableGen
backend. Add support for both forms of the callback to both
`Opt` and `OptClass`.
- Refactor handling of the callback command line options. Move
variable for the command line option from the header to the
CPP file, and add a function `ApplyCallback` to apply the
selected callback.
- Change callbacks in TableGen.cpp to take const reference. They
use the `Opt` class to define their callbacks.
- Change IntrinsicEmitter to use the `OptClass` to define its
callbacks. It already uses a const reference.
- Change global variables in TableGen.cpp to be static instead of
being in an anonymous namespace per LLVM coding standards.
---
llvm/include/llvm/TableGen/TableGenBackend.h | 37 ++++++-----
llvm/lib/TableGen/Main.cpp | 11 ++--
llvm/lib/TableGen/TableGenBackend.cpp | 64 ++++++++++++++++++--
llvm/utils/TableGen/DisassemblerEmitter.cpp | 1 +
llvm/utils/TableGen/IntrinsicEmitter.cpp | 24 ++++----
llvm/utils/TableGen/TableGen.cpp | 25 ++++----
6 files changed, 109 insertions(+), 53 deletions(-)
diff --git a/llvm/include/llvm/TableGen/TableGenBackend.h b/llvm/include/llvm/TableGen/TableGenBackend.h
index 9c5a785f45a403..83fe8a1fe8edcd 100644
--- a/llvm/include/llvm/TableGen/TableGenBackend.h
+++ b/llvm/include/llvm/TableGen/TableGenBackend.h
@@ -14,8 +14,6 @@
#define LLVM_TABLEGEN_TABLEGENBACKEND_H
#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/ManagedStatic.h"
#include "llvm/TableGen/Record.h"
namespace llvm {
@@ -24,29 +22,38 @@ class RecordKeeper;
class raw_ostream;
namespace TableGen::Emitter {
-using FnT = void (*)(RecordKeeper &Records, raw_ostream &OS);
-
-struct OptCreatorT {
- static void *call();
-};
-
-extern ManagedStatic<cl::opt<FnT>, OptCreatorT> Action;
+// Support const and non-const forms of callback functions.
+using FnNonConstT = void (*)(RecordKeeper &Records, raw_ostream &OS);
+using FnConstT = void (*)(const RecordKeeper &Records, raw_ostream &OS);
+/// Creating an `Opt` object registers the command line option \p Name with
+/// TableGen backend and associates the callback \p CB with that option. If
+/// \p ByDefault is true, then that callback is applied by default if no
+/// command line option was specified.
struct Opt {
- Opt(StringRef Name, FnT CB, StringRef Desc, bool ByDefault = false) {
- if (ByDefault)
- Action->setInitialValue(CB);
- Action->getParser().addLiteralOption(Name, CB, Desc);
- }
+ Opt(StringRef Name, FnNonConstT CB, StringRef Desc, bool ByDefault = false);
+ Opt(StringRef Name, FnConstT CB, StringRef Desc, bool ByDefault = false);
};
+/// Convienence wrapper around `Opt` that registers `EmitterClass::run` as the
+/// callback.
template <class EmitterC> class OptClass : Opt {
- static void run(RecordKeeper &RK, raw_ostream &OS) { EmitterC(RK).run(OS); }
+ static constexpr bool UsesConstRef =
+ std::is_invocable_v<decltype(&EmitterC::run), const RecordKeeper &,
+ raw_ostream>;
+ using RKType =
+ std::conditional_t<UsesConstRef, const RecordKeeper &, RecordKeeper &>;
+
+ static void run(RKType RK, raw_ostream &OS) { EmitterC(RK).run(OS); }
public:
OptClass(StringRef Name, StringRef Desc) : Opt(Name, run, Desc) {}
};
+/// Apply callback for any command line option registered above. Returns false
+/// is no callback was applied.
+bool ApplyCallback(RecordKeeper &Records, raw_ostream &OS);
+
} // namespace TableGen::Emitter
/// emitSourceFileHeader - Output an LLVM style file header to the specified
diff --git a/llvm/lib/TableGen/Main.cpp b/llvm/lib/TableGen/Main.cpp
index 841fa7c3f3690b..a4c41223c07620 100644
--- a/llvm/lib/TableGen/Main.cpp
+++ b/llvm/lib/TableGen/Main.cpp
@@ -131,13 +131,10 @@ int llvm::TableGenMain(const char *argv0,
std::string OutString;
raw_string_ostream Out(OutString);
unsigned status = 0;
- TableGen::Emitter::FnT ActionFn = TableGen::Emitter::Action->getValue();
- if (ActionFn)
- ActionFn(Records, Out);
- else if (MainFn)
- status = MainFn(Out, Records);
- else
- return 1;
+ // ApplyCallback will return true if it did not apply any callback. In that
+ // case, attempt to apply the MainFn.
+ if (TableGen::Emitter::ApplyCallback(Records, Out))
+ status = MainFn ? MainFn(Out, Records) : 1;
Records.stopBackendTimer();
if (status)
return 1;
diff --git a/llvm/lib/TableGen/TableGenBackend.cpp b/llvm/lib/TableGen/TableGenBackend.cpp
index 035abe936e114d..6e054f59c90e2b 100644
--- a/llvm/lib/TableGen/TableGenBackend.cpp
+++ b/llvm/lib/TableGen/TableGenBackend.cpp
@@ -12,22 +12,76 @@
#include "llvm/TableGen/TableGenBackend.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
+#include <variant>
using namespace llvm;
+using namespace TableGen::Emitter;
const size_t MAX_LINE_LEN = 80U;
-namespace llvm::TableGen::Emitter {
-ManagedStatic<cl::opt<FnT>, OptCreatorT> Action;
-void *OptCreatorT::call() {
- return new cl::opt<FnT>(cl::desc("Action to perform:"));
+using FnT = std::variant<FnNonConstT, FnConstT>;
+
+// CommandLine options of class type are not directly supported with some
+// specific exceptions like std::string which are safe to copy. In our case,
+// the `FnT` variant object is also safe to copy. So provide a specialization
+// of `OptionValue` for `FnT` type that stores it as a copy. This is essentially
+// similar to OptionValue<std::string> specialization for strings.
+template <> struct cl::OptionValue<FnT> final : cl::OptionValueCopy<FnT> {
+ OptionValue() = default;
+
+ OptionValue(const FnT &V) { this->setValue(V); }
+
+ OptionValue<FnT> &operator=(const FnT &V) {
+ setValue(V);
+ return *this;
+ }
+
+private:
+ void anchor() override {}
+};
+
+namespace {
+struct OptCreatorT {
+ static void *call() {
+ return new cl::opt<FnT>(cl::desc("Action to perform:"));
+ }
+};
+} // namespace
+
+static ManagedStatic<cl::opt<FnT>, OptCreatorT> CallbackFunction;
+
+Opt::Opt(StringRef Name, FnNonConstT CB, StringRef Desc, bool ByDefault) {
+ if (ByDefault)
+ CallbackFunction->setInitialValue(CB);
+ CallbackFunction->getParser().addLiteralOption(Name, FnT(CB), Desc);
+}
+
+Opt::Opt(StringRef Name, FnConstT CB, StringRef Desc, bool ByDefault) {
+ if (ByDefault)
+ CallbackFunction->setInitialValue(CB);
+ CallbackFunction->getParser().addLiteralOption(Name, FnT(CB), Desc);
+}
+
+/// Apply callback specified on the command line. Returns false is no callback
+/// was applied.
+bool llvm::TableGen::Emitter::ApplyCallback(RecordKeeper &Records,
+ raw_ostream &OS) {
+ const FnT &CallBackFn = *CallbackFunction;
+ if (auto *Fn = std::get_if<FnNonConstT>(&CallBackFn); Fn && *Fn)
+ (*Fn)(Records, OS);
+ else if (auto *Fn = std::get_if<FnConstT>(&CallBackFn); Fn && *Fn)
+ (*Fn)(Records, OS);
+ else
+ return true;
+ return false;
}
-} // namespace llvm::TableGen::Emitter
static void printLine(raw_ostream &OS, const Twine &Prefix, char Fill,
StringRef Suffix) {
diff --git a/llvm/utils/TableGen/DisassemblerEmitter.cpp b/llvm/utils/TableGen/DisassemblerEmitter.cpp
index d41750075b41f2..f2c25d38ad2a7d 100644
--- a/llvm/utils/TableGen/DisassemblerEmitter.cpp
+++ b/llvm/utils/TableGen/DisassemblerEmitter.cpp
@@ -11,6 +11,7 @@
#include "WebAssemblyDisassemblerEmitter.h"
#include "X86DisassemblerTables.h"
#include "X86RecognizableInstr.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
diff --git a/llvm/utils/TableGen/IntrinsicEmitter.cpp b/llvm/utils/TableGen/IntrinsicEmitter.cpp
index b1307153e9109b..69c28fa168cb69 100644
--- a/llvm/utils/TableGen/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/IntrinsicEmitter.cpp
@@ -63,6 +63,14 @@ class IntrinsicEmitter {
void EmitIntrinsicToBuiltinMap(const CodeGenIntrinsicTable &Ints,
bool IsClang, raw_ostream &OS);
};
+
+// Helper class to use with `TableGen::Emitter::OptClass`.
+template <bool Enums> class IntrinsicEmitterOpt : public IntrinsicEmitter {
+public:
+ IntrinsicEmitterOpt(const RecordKeeper &R) : IntrinsicEmitter(R) {}
+ void run(raw_ostream &OS) { IntrinsicEmitter::run(OS, Enums); }
+};
+
} // End anonymous namespace
//===----------------------------------------------------------------------===//
@@ -681,16 +689,8 @@ void IntrinsicEmitter::EmitIntrinsicToBuiltinMap(
OS << "#endif\n\n";
}
-static void EmitIntrinsicEnums(RecordKeeper &RK, raw_ostream &OS) {
- IntrinsicEmitter(RK).run(OS, /*Enums=*/true);
-}
-
-static TableGen::Emitter::Opt X("gen-intrinsic-enums", EmitIntrinsicEnums,
- "Generate intrinsic enums");
-
-static void EmitIntrinsicImpl(RecordKeeper &RK, raw_ostream &OS) {
- IntrinsicEmitter(RK).run(OS, /*Enums=*/false);
-}
+static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/true>>
+ X("gen-intrinsic-enums", "Generate intrinsic enums");
-static TableGen::Emitter::Opt Y("gen-intrinsic-impl", EmitIntrinsicImpl,
- "Generate intrinsic implementation code");
+static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/false>>
+ Y("gen-intrinsic-impl", "Generate intrinsic implementation code");
diff --git a/llvm/utils/TableGen/TableGen.cpp b/llvm/utils/TableGen/TableGen.cpp
index b2ed48cffe6be5..496e901f370be6 100644
--- a/llvm/utils/TableGen/TableGen.cpp
+++ b/llvm/utils/TableGen/TableGen.cpp
@@ -33,50 +33,47 @@ cl::opt<bool> EmitLongStrLiterals(
cl::Hidden, cl::init(true));
} // end namespace llvm
-namespace {
+static cl::OptionCategory PrintEnumsCat("Options for -print-enums");
+static cl::opt<std::string> Class("class",
+ cl::desc("Print Enum list for this class"),
+ cl::value_desc("class name"),
+ cl::cat(PrintEnumsCat));
-cl::OptionCategory PrintEnumsCat("Options for -print-enums");
-cl::opt<std::string> Class("class", cl::desc("Print Enum list for this class"),
- cl::value_desc("class name"),
- cl::cat(PrintEnumsCat));
-
-void PrintRecords(RecordKeeper &Records, raw_ostream &OS) {
+static void PrintRecords(const RecordKeeper &Records, raw_ostream &OS) {
OS << Records; // No argument, dump all contents
}
-void PrintEnums(RecordKeeper &Records, raw_ostream &OS) {
+static void PrintEnums(const RecordKeeper &Records, raw_ostream &OS) {
for (Record *Rec : Records.getAllDerivedDefinitions(Class))
OS << Rec->getName() << ", ";
OS << "\n";
}
-void PrintSets(RecordKeeper &Records, raw_ostream &OS) {
+static void PrintSets(const RecordKeeper &Records, raw_ostream &OS) {
SetTheory Sets;
Sets.addFieldExpander("Set", "Elements");
for (Record *Rec : Records.getAllDerivedDefinitions("Set")) {
OS << Rec->getName() << " = [";
const std::vector<Record *> *Elts = Sets.expand(Rec);
assert(Elts && "Couldn't expand Set instance");
- for (Record *Elt : *Elts)
+ for (const Record *Elt : *Elts)
OS << ' ' << Elt->getName();
OS << " ]\n";
}
}
-TableGen::Emitter::Opt X[] = {
+static llvm::TableGen::Emitter::Opt X[] = {
{"print-records", PrintRecords, "Print all records to stdout (default)",
true},
{"print-detailed-records", EmitDetailedRecords,
"Print full details of all records to stdout"},
- {"null-backend", [](RecordKeeper &Records, raw_ostream &OS) {},
+ {"null-backend", [](const RecordKeeper &Records, raw_ostream &OS) {},
"Do nothing after parsing (useful for timing)"},
{"dump-json", EmitJSON, "Dump all records as machine-readable JSON"},
{"print-enums", PrintEnums, "Print enum values for a class"},
{"print-sets", PrintSets, "Print expanded sets for testing DAG exprs"},
};
-} // namespace
-
int main(int argc, char **argv) {
InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv);
More information about the llvm-commits
mailing list