1 | //===- Main.cpp - Top-Level TableGen implementation -----------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // TableGen is a tool which can be used to build up a description of something, |
10 | // then invoke one or more "tablegen backends" to emit information about the |
11 | // description in some predefined format. In practice, this is used by the LLVM |
12 | // code generators to automate generation of a code generator through a |
13 | // high-level description of the target. |
14 | // |
15 | //===----------------------------------------------------------------------===// |
16 | |
17 | #include "llvm/TableGen/Main.h" |
18 | #include "TGLexer.h" |
19 | #include "TGParser.h" |
20 | #include "llvm/ADT/StringRef.h" |
21 | #include "llvm/ADT/Twine.h" |
22 | #include "llvm/Support/CommandLine.h" |
23 | #include "llvm/Support/ErrorOr.h" |
24 | #include "llvm/Support/FileSystem.h" |
25 | #include "llvm/Support/MemoryBuffer.h" |
26 | #include "llvm/Support/SMLoc.h" |
27 | #include "llvm/Support/SourceMgr.h" |
28 | #include "llvm/Support/ToolOutputFile.h" |
29 | #include "llvm/Support/raw_ostream.h" |
30 | #include "llvm/TableGen/Error.h" |
31 | #include "llvm/TableGen/Record.h" |
32 | #include "llvm/TableGen/TableGenBackend.h" |
33 | #include <memory> |
34 | #include <string> |
35 | #include <system_error> |
36 | #include <utility> |
37 | using namespace llvm; |
38 | |
39 | static cl::opt<std::string> |
40 | OutputFilename("o" , cl::desc("Output filename" ), cl::value_desc("filename" ), |
41 | cl::init(Val: "-" )); |
42 | |
43 | static cl::opt<std::string> |
44 | DependFilename("d" , |
45 | cl::desc("Dependency filename" ), |
46 | cl::value_desc("filename" ), |
47 | cl::init(Val: "" )); |
48 | |
49 | static cl::opt<std::string> |
50 | InputFilename(cl::Positional, cl::desc("<input file>" ), cl::init(Val: "-" )); |
51 | |
52 | static cl::list<std::string> |
53 | IncludeDirs("I" , cl::desc("Directory of include files" ), |
54 | cl::value_desc("directory" ), cl::Prefix); |
55 | |
56 | static cl::list<std::string> |
57 | MacroNames("D" , cl::desc("Name of the macro to be defined" ), |
58 | cl::value_desc("macro name" ), cl::Prefix); |
59 | |
60 | static cl::opt<bool> |
61 | WriteIfChanged("write-if-changed" , cl::desc("Only write output if it changed" )); |
62 | |
63 | static cl::opt<bool> |
64 | TimePhases("time-phases" , cl::desc("Time phases of parser and backend" )); |
65 | |
66 | static cl::opt<bool> NoWarnOnUnusedTemplateArgs( |
67 | "no-warn-on-unused-template-args" , |
68 | cl::desc("Disable unused template argument warnings." )); |
69 | |
70 | static int reportError(const char *ProgName, Twine Msg) { |
71 | errs() << ProgName << ": " << Msg; |
72 | errs().flush(); |
73 | return 1; |
74 | } |
75 | |
76 | /// Create a dependency file for `-d` option. |
77 | /// |
78 | /// This functionality is really only for the benefit of the build system. |
79 | /// It is similar to GCC's `-M*` family of options. |
80 | static int createDependencyFile(const TGParser &Parser, const char *argv0) { |
81 | if (OutputFilename == "-" ) |
82 | return reportError(ProgName: argv0, Msg: "the option -d must be used together with -o\n" ); |
83 | |
84 | std::error_code EC; |
85 | ToolOutputFile DepOut(DependFilename, EC, sys::fs::OF_Text); |
86 | if (EC) |
87 | return reportError(ProgName: argv0, Msg: "error opening " + DependFilename + ":" + |
88 | EC.message() + "\n" ); |
89 | DepOut.os() << OutputFilename << ":" ; |
90 | for (const auto &Dep : Parser.getDependencies()) { |
91 | DepOut.os() << ' ' << Dep; |
92 | } |
93 | DepOut.os() << "\n" ; |
94 | DepOut.keep(); |
95 | return 0; |
96 | } |
97 | |
98 | int llvm::TableGenMain(const char *argv0, |
99 | std::function<TableGenMainFn> MainFn) { |
100 | RecordKeeper Records; |
101 | |
102 | if (TimePhases) |
103 | Records.startPhaseTiming(); |
104 | |
105 | // Parse the input file. |
106 | |
107 | Records.startTimer(Name: "Parse, build records" ); |
108 | ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = |
109 | MemoryBuffer::getFileOrSTDIN(Filename: InputFilename, /*IsText=*/true); |
110 | if (std::error_code EC = FileOrErr.getError()) |
111 | return reportError(ProgName: argv0, Msg: "Could not open input file '" + InputFilename + |
112 | "': " + EC.message() + "\n" ); |
113 | |
114 | Records.saveInputFilename(Filename: InputFilename); |
115 | |
116 | // Tell SrcMgr about this buffer, which is what TGParser will pick up. |
117 | SrcMgr.AddNewSourceBuffer(F: std::move(*FileOrErr), IncludeLoc: SMLoc()); |
118 | |
119 | // Record the location of the include directory so that the lexer can find |
120 | // it later. |
121 | SrcMgr.setIncludeDirs(IncludeDirs); |
122 | |
123 | TGParser Parser(SrcMgr, MacroNames, Records, NoWarnOnUnusedTemplateArgs); |
124 | |
125 | if (Parser.ParseFile()) |
126 | return 1; |
127 | Records.stopTimer(); |
128 | |
129 | // Write output to memory. |
130 | Records.startBackendTimer(Name: "Backend overall" ); |
131 | std::string OutString; |
132 | raw_string_ostream Out(OutString); |
133 | unsigned status = 0; |
134 | TableGen::Emitter::FnT ActionFn = TableGen::Emitter::Action->getValue(); |
135 | if (ActionFn) |
136 | ActionFn(Records, Out); |
137 | else if (MainFn) |
138 | status = MainFn(Out, Records); |
139 | else |
140 | return 1; |
141 | Records.stopBackendTimer(); |
142 | if (status) |
143 | return 1; |
144 | |
145 | // Always write the depfile, even if the main output hasn't changed. |
146 | // If it's missing, Ninja considers the output dirty. If this was below |
147 | // the early exit below and someone deleted the .inc.d file but not the .inc |
148 | // file, tablegen would never write the depfile. |
149 | if (!DependFilename.empty()) { |
150 | if (int Ret = createDependencyFile(Parser, argv0)) |
151 | return Ret; |
152 | } |
153 | |
154 | Records.startTimer(Name: "Write output" ); |
155 | bool WriteFile = true; |
156 | if (WriteIfChanged) { |
157 | // Only updates the real output file if there are any differences. |
158 | // This prevents recompilation of all the files depending on it if there |
159 | // aren't any. |
160 | if (auto ExistingOrErr = |
161 | MemoryBuffer::getFile(Filename: OutputFilename, /*IsText=*/true)) |
162 | if (std::move(ExistingOrErr.get())->getBuffer() == OutString) |
163 | WriteFile = false; |
164 | } |
165 | if (WriteFile) { |
166 | std::error_code EC; |
167 | ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_Text); |
168 | if (EC) |
169 | return reportError(ProgName: argv0, Msg: "error opening " + OutputFilename + ": " + |
170 | EC.message() + "\n" ); |
171 | OutFile.os() << OutString; |
172 | if (ErrorsPrinted == 0) |
173 | OutFile.keep(); |
174 | } |
175 | |
176 | Records.stopTimer(); |
177 | Records.stopPhaseTiming(); |
178 | |
179 | if (ErrorsPrinted > 0) |
180 | return reportError(ProgName: argv0, Msg: Twine(ErrorsPrinted) + " errors.\n" ); |
181 | return 0; |
182 | } |
183 | |