xref: /freebsd-src/contrib/llvm-project/llvm/lib/TableGen/Main.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- Main.cpp - Top-Level TableGen implementation -----------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // TableGen is a tool which can be used to build up a description of something,
100b57cec5SDimitry Andric // then invoke one or more "tablegen backends" to emit information about the
110b57cec5SDimitry Andric // description in some predefined format.  In practice, this is used by the LLVM
120b57cec5SDimitry Andric // code generators to automate generation of a code generator through a
130b57cec5SDimitry Andric // high-level description of the target.
140b57cec5SDimitry Andric //
150b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include "llvm/TableGen/Main.h"
1806c3fb27SDimitry Andric #include "TGLexer.h"
190b57cec5SDimitry Andric #include "TGParser.h"
2006c3fb27SDimitry Andric #include "llvm/ADT/StringRef.h"
2106c3fb27SDimitry Andric #include "llvm/ADT/Twine.h"
220b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
2306c3fb27SDimitry Andric #include "llvm/Support/ErrorOr.h"
240b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
250b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
2606c3fb27SDimitry Andric #include "llvm/Support/SMLoc.h"
2706c3fb27SDimitry Andric #include "llvm/Support/SourceMgr.h"
280b57cec5SDimitry Andric #include "llvm/Support/ToolOutputFile.h"
2906c3fb27SDimitry Andric #include "llvm/Support/raw_ostream.h"
300b57cec5SDimitry Andric #include "llvm/TableGen/Error.h"
310b57cec5SDimitry Andric #include "llvm/TableGen/Record.h"
3206c3fb27SDimitry Andric #include "llvm/TableGen/TableGenBackend.h"
3306c3fb27SDimitry Andric #include <memory>
3406c3fb27SDimitry Andric #include <string>
350b57cec5SDimitry Andric #include <system_error>
3606c3fb27SDimitry Andric #include <utility>
370b57cec5SDimitry Andric using namespace llvm;
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric static cl::opt<std::string>
400b57cec5SDimitry Andric OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"),
410b57cec5SDimitry Andric                cl::init("-"));
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric static cl::opt<std::string>
440b57cec5SDimitry Andric DependFilename("d",
450b57cec5SDimitry Andric                cl::desc("Dependency filename"),
460b57cec5SDimitry Andric                cl::value_desc("filename"),
470b57cec5SDimitry Andric                cl::init(""));
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric static cl::opt<std::string>
500b57cec5SDimitry Andric InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric static cl::list<std::string>
530b57cec5SDimitry Andric IncludeDirs("I", cl::desc("Directory of include files"),
540b57cec5SDimitry Andric             cl::value_desc("directory"), cl::Prefix);
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric static cl::list<std::string>
570b57cec5SDimitry Andric MacroNames("D", cl::desc("Name of the macro to be defined"),
580b57cec5SDimitry Andric             cl::value_desc("macro name"), cl::Prefix);
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric static cl::opt<bool>
610b57cec5SDimitry Andric WriteIfChanged("write-if-changed", cl::desc("Only write output if it changed"));
620b57cec5SDimitry Andric 
63e8d8bef9SDimitry Andric static cl::opt<bool>
64e8d8bef9SDimitry Andric TimePhases("time-phases", cl::desc("Time phases of parser and backend"));
65e8d8bef9SDimitry Andric 
66349cc55cSDimitry Andric static cl::opt<bool> NoWarnOnUnusedTemplateArgs(
67349cc55cSDimitry Andric     "no-warn-on-unused-template-args",
68349cc55cSDimitry Andric     cl::desc("Disable unused template argument warnings."));
69349cc55cSDimitry Andric 
700b57cec5SDimitry Andric static int reportError(const char *ProgName, Twine Msg) {
710b57cec5SDimitry Andric   errs() << ProgName << ": " << Msg;
720b57cec5SDimitry Andric   errs().flush();
730b57cec5SDimitry Andric   return 1;
740b57cec5SDimitry Andric }
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric /// Create a dependency file for `-d` option.
770b57cec5SDimitry Andric ///
780b57cec5SDimitry Andric /// This functionality is really only for the benefit of the build system.
790b57cec5SDimitry Andric /// It is similar to GCC's `-M*` family of options.
800b57cec5SDimitry Andric static int createDependencyFile(const TGParser &Parser, const char *argv0) {
810b57cec5SDimitry Andric   if (OutputFilename == "-")
820b57cec5SDimitry Andric     return reportError(argv0, "the option -d must be used together with -o\n");
830b57cec5SDimitry Andric 
840b57cec5SDimitry Andric   std::error_code EC;
85fe6060f1SDimitry Andric   ToolOutputFile DepOut(DependFilename, EC, sys::fs::OF_Text);
860b57cec5SDimitry Andric   if (EC)
870b57cec5SDimitry Andric     return reportError(argv0, "error opening " + DependFilename + ":" +
880b57cec5SDimitry Andric                                   EC.message() + "\n");
890b57cec5SDimitry Andric   DepOut.os() << OutputFilename << ":";
900b57cec5SDimitry Andric   for (const auto &Dep : Parser.getDependencies()) {
91480093f4SDimitry Andric     DepOut.os() << ' ' << Dep;
920b57cec5SDimitry Andric   }
930b57cec5SDimitry Andric   DepOut.os() << "\n";
940b57cec5SDimitry Andric   DepOut.keep();
950b57cec5SDimitry Andric   return 0;
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric 
9806c3fb27SDimitry Andric int llvm::TableGenMain(const char *argv0,
9906c3fb27SDimitry Andric                        std::function<TableGenMainFn> MainFn) {
1000b57cec5SDimitry Andric   RecordKeeper Records;
1010b57cec5SDimitry Andric 
102e8d8bef9SDimitry Andric   if (TimePhases)
103e8d8bef9SDimitry Andric     Records.startPhaseTiming();
104e8d8bef9SDimitry Andric 
1050b57cec5SDimitry Andric   // Parse the input file.
106e8d8bef9SDimitry Andric 
107e8d8bef9SDimitry Andric   Records.startTimer("Parse, build records");
1080b57cec5SDimitry Andric   ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
109fe6060f1SDimitry Andric       MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true);
1100b57cec5SDimitry Andric   if (std::error_code EC = FileOrErr.getError())
1110b57cec5SDimitry Andric     return reportError(argv0, "Could not open input file '" + InputFilename +
1120b57cec5SDimitry Andric                                   "': " + EC.message() + "\n");
1130b57cec5SDimitry Andric 
114e8d8bef9SDimitry Andric   Records.saveInputFilename(InputFilename);
115e8d8bef9SDimitry Andric 
1160b57cec5SDimitry Andric   // Tell SrcMgr about this buffer, which is what TGParser will pick up.
1170b57cec5SDimitry Andric   SrcMgr.AddNewSourceBuffer(std::move(*FileOrErr), SMLoc());
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric   // Record the location of the include directory so that the lexer can find
1200b57cec5SDimitry Andric   // it later.
1210b57cec5SDimitry Andric   SrcMgr.setIncludeDirs(IncludeDirs);
1220b57cec5SDimitry Andric 
123349cc55cSDimitry Andric   TGParser Parser(SrcMgr, MacroNames, Records, NoWarnOnUnusedTemplateArgs);
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric   if (Parser.ParseFile())
1260b57cec5SDimitry Andric     return 1;
127e8d8bef9SDimitry Andric   Records.stopTimer();
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   // Write output to memory.
130e8d8bef9SDimitry Andric   Records.startBackendTimer("Backend overall");
1310b57cec5SDimitry Andric   std::string OutString;
1320b57cec5SDimitry Andric   raw_string_ostream Out(OutString);
13306c3fb27SDimitry Andric   unsigned status = 0;
13406c3fb27SDimitry Andric   TableGen::Emitter::FnT ActionFn = TableGen::Emitter::Action->getValue();
13506c3fb27SDimitry Andric   if (ActionFn)
13606c3fb27SDimitry Andric     ActionFn(Records, Out);
13706c3fb27SDimitry Andric   else if (MainFn)
13806c3fb27SDimitry Andric     status = MainFn(Out, Records);
13906c3fb27SDimitry Andric   else
14006c3fb27SDimitry Andric     return 1;
141e8d8bef9SDimitry Andric   Records.stopBackendTimer();
142e8d8bef9SDimitry Andric   if (status)
1430b57cec5SDimitry Andric     return 1;
1440b57cec5SDimitry Andric 
1450b57cec5SDimitry Andric   // Always write the depfile, even if the main output hasn't changed.
1460b57cec5SDimitry Andric   // If it's missing, Ninja considers the output dirty.  If this was below
1470b57cec5SDimitry Andric   // the early exit below and someone deleted the .inc.d file but not the .inc
1480b57cec5SDimitry Andric   // file, tablegen would never write the depfile.
1490b57cec5SDimitry Andric   if (!DependFilename.empty()) {
1500b57cec5SDimitry Andric     if (int Ret = createDependencyFile(Parser, argv0))
1510b57cec5SDimitry Andric       return Ret;
1520b57cec5SDimitry Andric   }
1530b57cec5SDimitry Andric 
154e8d8bef9SDimitry Andric   Records.startTimer("Write output");
155e8d8bef9SDimitry Andric   bool WriteFile = true;
1560b57cec5SDimitry Andric   if (WriteIfChanged) {
1570b57cec5SDimitry Andric     // Only updates the real output file if there are any differences.
1580b57cec5SDimitry Andric     // This prevents recompilation of all the files depending on it if there
1590b57cec5SDimitry Andric     // aren't any.
160fe6060f1SDimitry Andric     if (auto ExistingOrErr =
161fe6060f1SDimitry Andric             MemoryBuffer::getFile(OutputFilename, /*IsText=*/true))
162*0fca6ea1SDimitry Andric       if (std::move(ExistingOrErr.get())->getBuffer() == OutString)
163e8d8bef9SDimitry Andric         WriteFile = false;
1640b57cec5SDimitry Andric   }
165e8d8bef9SDimitry Andric   if (WriteFile) {
1660b57cec5SDimitry Andric     std::error_code EC;
167fe6060f1SDimitry Andric     ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_Text);
1680b57cec5SDimitry Andric     if (EC)
1690b57cec5SDimitry Andric       return reportError(argv0, "error opening " + OutputFilename + ": " +
1700b57cec5SDimitry Andric                                     EC.message() + "\n");
171*0fca6ea1SDimitry Andric     OutFile.os() << OutString;
172e8d8bef9SDimitry Andric     if (ErrorsPrinted == 0)
173e8d8bef9SDimitry Andric       OutFile.keep();
174e8d8bef9SDimitry Andric   }
175e8d8bef9SDimitry Andric 
176e8d8bef9SDimitry Andric   Records.stopTimer();
177e8d8bef9SDimitry Andric   Records.stopPhaseTiming();
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric   if (ErrorsPrinted > 0)
1800b57cec5SDimitry Andric     return reportError(argv0, Twine(ErrorsPrinted) + " errors.\n");
1810b57cec5SDimitry Andric   return 0;
1820b57cec5SDimitry Andric }
183