Path: blob/main/contrib/llvm-project/llvm/lib/TableGen/Main.cpp
35232 views
//===- Main.cpp - Top-Level TableGen implementation -----------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// TableGen is a tool which can be used to build up a description of something,9// then invoke one or more "tablegen backends" to emit information about the10// description in some predefined format. In practice, this is used by the LLVM11// code generators to automate generation of a code generator through a12// high-level description of the target.13//14//===----------------------------------------------------------------------===//1516#include "llvm/TableGen/Main.h"17#include "TGLexer.h"18#include "TGParser.h"19#include "llvm/ADT/StringRef.h"20#include "llvm/ADT/Twine.h"21#include "llvm/Support/CommandLine.h"22#include "llvm/Support/ErrorOr.h"23#include "llvm/Support/FileSystem.h"24#include "llvm/Support/MemoryBuffer.h"25#include "llvm/Support/SMLoc.h"26#include "llvm/Support/SourceMgr.h"27#include "llvm/Support/ToolOutputFile.h"28#include "llvm/Support/raw_ostream.h"29#include "llvm/TableGen/Error.h"30#include "llvm/TableGen/Record.h"31#include "llvm/TableGen/TableGenBackend.h"32#include <memory>33#include <string>34#include <system_error>35#include <utility>36using namespace llvm;3738static cl::opt<std::string>39OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"),40cl::init("-"));4142static cl::opt<std::string>43DependFilename("d",44cl::desc("Dependency filename"),45cl::value_desc("filename"),46cl::init(""));4748static cl::opt<std::string>49InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));5051static cl::list<std::string>52IncludeDirs("I", cl::desc("Directory of include files"),53cl::value_desc("directory"), cl::Prefix);5455static cl::list<std::string>56MacroNames("D", cl::desc("Name of the macro to be defined"),57cl::value_desc("macro name"), cl::Prefix);5859static cl::opt<bool>60WriteIfChanged("write-if-changed", cl::desc("Only write output if it changed"));6162static cl::opt<bool>63TimePhases("time-phases", cl::desc("Time phases of parser and backend"));6465static cl::opt<bool> NoWarnOnUnusedTemplateArgs(66"no-warn-on-unused-template-args",67cl::desc("Disable unused template argument warnings."));6869static int reportError(const char *ProgName, Twine Msg) {70errs() << ProgName << ": " << Msg;71errs().flush();72return 1;73}7475/// Create a dependency file for `-d` option.76///77/// This functionality is really only for the benefit of the build system.78/// It is similar to GCC's `-M*` family of options.79static int createDependencyFile(const TGParser &Parser, const char *argv0) {80if (OutputFilename == "-")81return reportError(argv0, "the option -d must be used together with -o\n");8283std::error_code EC;84ToolOutputFile DepOut(DependFilename, EC, sys::fs::OF_Text);85if (EC)86return reportError(argv0, "error opening " + DependFilename + ":" +87EC.message() + "\n");88DepOut.os() << OutputFilename << ":";89for (const auto &Dep : Parser.getDependencies()) {90DepOut.os() << ' ' << Dep;91}92DepOut.os() << "\n";93DepOut.keep();94return 0;95}9697int llvm::TableGenMain(const char *argv0,98std::function<TableGenMainFn> MainFn) {99RecordKeeper Records;100101if (TimePhases)102Records.startPhaseTiming();103104// Parse the input file.105106Records.startTimer("Parse, build records");107ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =108MemoryBuffer::getFileOrSTDIN(InputFilename, /*IsText=*/true);109if (std::error_code EC = FileOrErr.getError())110return reportError(argv0, "Could not open input file '" + InputFilename +111"': " + EC.message() + "\n");112113Records.saveInputFilename(InputFilename);114115// Tell SrcMgr about this buffer, which is what TGParser will pick up.116SrcMgr.AddNewSourceBuffer(std::move(*FileOrErr), SMLoc());117118// Record the location of the include directory so that the lexer can find119// it later.120SrcMgr.setIncludeDirs(IncludeDirs);121122TGParser Parser(SrcMgr, MacroNames, Records, NoWarnOnUnusedTemplateArgs);123124if (Parser.ParseFile())125return 1;126Records.stopTimer();127128// Write output to memory.129Records.startBackendTimer("Backend overall");130std::string OutString;131raw_string_ostream Out(OutString);132unsigned status = 0;133TableGen::Emitter::FnT ActionFn = TableGen::Emitter::Action->getValue();134if (ActionFn)135ActionFn(Records, Out);136else if (MainFn)137status = MainFn(Out, Records);138else139return 1;140Records.stopBackendTimer();141if (status)142return 1;143144// Always write the depfile, even if the main output hasn't changed.145// If it's missing, Ninja considers the output dirty. If this was below146// the early exit below and someone deleted the .inc.d file but not the .inc147// file, tablegen would never write the depfile.148if (!DependFilename.empty()) {149if (int Ret = createDependencyFile(Parser, argv0))150return Ret;151}152153Records.startTimer("Write output");154bool WriteFile = true;155if (WriteIfChanged) {156// Only updates the real output file if there are any differences.157// This prevents recompilation of all the files depending on it if there158// aren't any.159if (auto ExistingOrErr =160MemoryBuffer::getFile(OutputFilename, /*IsText=*/true))161if (std::move(ExistingOrErr.get())->getBuffer() == OutString)162WriteFile = false;163}164if (WriteFile) {165std::error_code EC;166ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_Text);167if (EC)168return reportError(argv0, "error opening " + OutputFilename + ": " +169EC.message() + "\n");170OutFile.os() << OutString;171if (ErrorsPrinted == 0)172OutFile.keep();173}174175Records.stopTimer();176Records.stopPhaseTiming();177178if (ErrorsPrinted > 0)179return reportError(argv0, Twine(ErrorsPrinted) + " errors.\n");180return 0;181}182183184