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