TableGen: Introduce `llvm::TableGen::Emitter` to register backends
authorNAKAMURA Takumi <geek4civic@gmail.com>
Thu, 16 Feb 2023 23:24:07 +0000 (08:24 +0900)
committerNAKAMURA Takumi <geek4civic@gmail.com>
Tue, 21 Mar 2023 07:21:27 +0000 (16:21 +0900)
`Opt(flag, func, desc)` registers an option into `Action`.

`OptClass<EmitterC>` is also available if `EmitterC(RK).run(OS)` is capable.

`Action` is defined as `ManagedStatic<cl::opt>` to guarantee to be created
when each registration of emitter is invoked.

`llvm::TableGenMain(argv0, MainFn)` invokes `Action` instead of `MainFn`

Differential Revision: https://reviews.llvm.org/D144351

llvm/docs/TableGen/BackGuide.rst
llvm/include/llvm/TableGen/TableGenBackend.h
llvm/lib/TableGen/Main.cpp
llvm/lib/TableGen/TableGenBackend.cpp
llvm/lib/TableGen/TableGenBackendSkeleton.cpp

index 7da39bf..e1413c1 100644 (file)
@@ -452,20 +452,6 @@ The following steps are required to create a new backend for TableGen.
    one instance for Clang and another for LLVM. Or you may be building
    your own instance.
 
-#. Modify the selected ``tablegen.cpp`` to include your new backend.
-
-  a. Add the name to the enumerated type ``ActionType``.
-
-  #. Add a keyword to the ``ActionType`` command option using the
-     ``clEnumValN()`` function.
-
-  #. Add a case to the ``switch`` statement in the *xxx*\ ``TableGenMain()``
-     function. It should invoke the "main function" of your backend, which
-     in this case, according to convention, is named ``EmitAddressModes``.
-
-5. Add a declaration of your "main function" to the corresponding
-   ``TableGenBackends.h`` header file.
-
 #. Add your backend C++ file to the appropriate ``CMakeLists.txt`` file so
    that it will be built.
 
@@ -498,11 +484,14 @@ unit for writing a new TableGen backend. Here are a few notes on the file.
 * The ``run`` function should use the ``emitSourceFileHeader`` helper function
   to include a standard header in the emitted file.
 
-* The only function in the ``llvm`` namespace is the backend "main function."
-  In this example, it is named ``EmitAddressModes``. It creates an instance
-  of the ``AddressModesEmitter`` class, passing the ``RecordKeeper``
-  instance, then invokes the ``run`` function, passing the ``raw_ostream``
-  instance.
+* Register the class or the function as the command line option
+  with ``llvm/TableGen/TableGenBackend.h``.
+
+  * Use ``llvm::TableGen::Emitter::OptClass<AddressModesEmitter>``
+    if the class has the constructor ``(RK)`` and
+    the method ``run(OS)``.
+
+  * Otherwise, use ``llvm::TableGen::Emitter::Opt``.
 
 All the examples in the remainder of this document will assume the naming
 conventions used in the skeleton file.
index a426e42..09e60cb 100644 (file)
 #ifndef LLVM_TABLEGEN_TABLEGENBACKEND_H
 #define LLVM_TABLEGEN_TABLEGENBACKEND_H
 
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ManagedStatic.h"
+
 namespace llvm {
 
-class StringRef;
+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;
+
+struct Opt {
+  Opt(StringRef Name, FnT CB, StringRef Desc, bool ByDefault = false) {
+    if (ByDefault)
+      Action->setInitialValue(CB);
+    Action->getParser().addLiteralOption(Name, CB, Desc);
+  }
+};
+
+template <class EmitterC> class OptClass : Opt {
+  static void run(RecordKeeper &RK, raw_ostream &OS) { EmitterC(RK).run(OS); }
+
+public:
+  OptClass(StringRef Name, StringRef Desc) : Opt(Name, run, Desc) {}
+};
+
+} // namespace TableGen::Emitter
+
 /// emitSourceFileHeader - Output an LLVM style file header to the specified
 /// raw_ostream.
 void emitSourceFileHeader(StringRef Desc, raw_ostream &OS);
index ee72b4b..9aee1f8 100644 (file)
@@ -29,6 +29,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/TableGen/Error.h"
 #include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
 #include <memory>
 #include <string>
 #include <system_error>
@@ -131,7 +132,10 @@ int llvm::TableGenMain(const char *argv0,
   std::string OutString;
   raw_string_ostream Out(OutString);
   unsigned status = 0;
-  if (MainFn)
+  TableGen::Emitter::FnT ActionFn = TableGen::Emitter::Action->getValue();
+  if (ActionFn)
+    ActionFn(Records, Out);
+  else if (MainFn)
     status = MainFn(Out, Records);
   else
     return 1;
index 252f126..135ec64 100644 (file)
 #include "llvm/TableGen/TableGenBackend.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/raw_ostream.h"
+#include <algorithm>
 #include <cassert>
+#include <cstddef>
 
 using namespace llvm;
 
 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:"));
+}
+} // namespace llvm::TableGen::Emitter
+
 static void printLine(raw_ostream &OS, const Twine &Prefix, char Fill,
                       StringRef Suffix) {
   size_t Pos = (size_t)OS.tell();
index 0ba00c8..2fde4a6 100644 (file)
@@ -46,14 +46,20 @@ void SkeletonEmitter::run(raw_ostream &OS) {
   (void)Records; // To suppress unused variable warning; remove on use.
 }
 
-namespace llvm {
+// Choose either option A or B.
 
-// The only thing that should be in the llvm namespace is the
-// emitter entry point function.
+//===----------------------------------------------------------------------===//
+// Option A: Register the backed as class <SkeletonEmitter>
+static TableGen::Emitter::OptClass<SkeletonEmitter>
+    X("gen-skeleton-class", "Generate example skeleton class");
 
-void EmitSkeleton(RecordKeeper &RK, raw_ostream &OS) {
+//===----------------------------------------------------------------------===//
+// Option B: Register "EmitSkeleton" directly
+// The emitter entry may be private scope.
+static void EmitSkeleton(RecordKeeper &RK, raw_ostream &OS) {
   // Instantiate the emitter class and invoke run().
   SkeletonEmitter(RK).run(OS);
 }
 
-} // namespace llvm
+static TableGen::Emitter::Opt Y("gen-skeleton-entry", EmitSkeleton,
+                                "Generate example skeleton entry");