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/TGTimer.h" |
33 | #include "llvm/TableGen/TableGenBackend.h" |
34 | #include <memory> |
35 | #include <string> |
36 | #include <system_error> |
37 | #include <utility> |
38 | using namespace llvm; |
39 | |
40 | static cl::opt<std::string> |
41 | OutputFilename("o" , cl::desc("Output filename" ), cl::value_desc("filename" ), |
42 | cl::init(Val: "-" )); |
43 | |
44 | static cl::opt<std::string> |
45 | DependFilename("d" , |
46 | cl::desc("Dependency filename" ), |
47 | cl::value_desc("filename" ), |
48 | cl::init(Val: "" )); |
49 | |
50 | static cl::opt<std::string> |
51 | InputFilename(cl::Positional, cl::desc("<input file>" ), cl::init(Val: "-" )); |
52 | |
53 | static cl::list<std::string> |
54 | IncludeDirs("I" , cl::desc("Directory of include files" ), |
55 | cl::value_desc("directory" ), cl::Prefix); |
56 | |
57 | static cl::list<std::string> |
58 | MacroNames("D" , cl::desc("Name of the macro to be defined" ), |
59 | cl::value_desc("macro name" ), cl::Prefix); |
60 | |
61 | static cl::opt<bool> |
62 | WriteIfChanged("write-if-changed" , cl::desc("Only write output if it changed" )); |
63 | |
64 | static cl::opt<bool> |
65 | TimePhases("time-phases" , cl::desc("Time phases of parser and backend" )); |
66 | |
67 | namespace llvm { |
68 | cl::opt<bool> EmitLongStrLiterals( |
69 | "long-string-literals" , |
70 | cl::desc("when emitting large string tables, prefer string literals over " |
71 | "comma-separated char literals. This can be a readability and " |
72 | "compile-time performance win, but upsets some compilers" ), |
73 | cl::Hidden, cl::init(Val: true)); |
74 | } // end namespace llvm |
75 | |
76 | static cl::opt<bool> NoWarnOnUnusedTemplateArgs( |
77 | "no-warn-on-unused-template-args" , |
78 | cl::desc("Disable unused template argument warnings." )); |
79 | |
80 | static int reportError(const char *ProgName, Twine Msg) { |
81 | errs() << ProgName << ": " << Msg; |
82 | errs().flush(); |
83 | return 1; |
84 | } |
85 | |
86 | /// Create a dependency file for `-d` option. |
87 | /// |
88 | /// This functionality is really only for the benefit of the build system. |
89 | /// It is similar to GCC's `-M*` family of options. |
90 | static int createDependencyFile(const TGParser &Parser, const char *argv0) { |
91 | if (OutputFilename == "-" ) |
92 | return reportError(ProgName: argv0, Msg: "the option -d must be used together with -o\n" ); |
93 | |
94 | std::error_code EC; |
95 | ToolOutputFile DepOut(DependFilename, EC, sys::fs::OF_Text); |
96 | if (EC) |
97 | return reportError(ProgName: argv0, Msg: "error opening " + DependFilename + ":" + |
98 | EC.message() + "\n" ); |
99 | DepOut.os() << OutputFilename << ":" ; |
100 | for (const auto &Dep : Parser.getDependencies()) { |
101 | DepOut.os() << ' ' << Dep; |
102 | } |
103 | DepOut.os() << "\n" ; |
104 | DepOut.keep(); |
105 | return 0; |
106 | } |
107 | |
108 | int llvm::TableGenMain(const char *argv0, |
109 | std::function<TableGenMainFn> MainFn) { |
110 | RecordKeeper Records; |
111 | TGTimer &Timer = Records.getTimer(); |
112 | |
113 | if (TimePhases) |
114 | Timer.startPhaseTiming(); |
115 | |
116 | // Parse the input file. |
117 | |
118 | Timer.startTimer(Name: "Parse, build records" ); |
119 | ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = |
120 | MemoryBuffer::getFileOrSTDIN(Filename: InputFilename, /*IsText=*/true); |
121 | if (std::error_code EC = FileOrErr.getError()) |
122 | return reportError(ProgName: argv0, Msg: "Could not open input file '" + InputFilename + |
123 | "': " + EC.message() + "\n" ); |
124 | |
125 | Records.saveInputFilename(Filename: InputFilename); |
126 | |
127 | // Tell SrcMgr about this buffer, which is what TGParser will pick up. |
128 | SrcMgr.AddNewSourceBuffer(F: std::move(*FileOrErr), IncludeLoc: SMLoc()); |
129 | |
130 | // Record the location of the include directory so that the lexer can find |
131 | // it later. |
132 | SrcMgr.setIncludeDirs(IncludeDirs); |
133 | |
134 | TGParser Parser(SrcMgr, MacroNames, Records, NoWarnOnUnusedTemplateArgs); |
135 | |
136 | if (Parser.ParseFile()) |
137 | return 1; |
138 | Timer.stopTimer(); |
139 | |
140 | // Return early if any other errors were generated during parsing |
141 | // (e.g., assert failures). |
142 | if (ErrorsPrinted > 0) |
143 | return reportError(ProgName: argv0, Msg: Twine(ErrorsPrinted) + " errors.\n" ); |
144 | |
145 | // Write output to memory. |
146 | Timer.startBackendTimer(Name: "Backend overall" ); |
147 | std::string OutString; |
148 | raw_string_ostream Out(OutString); |
149 | unsigned status = 0; |
150 | // ApplyCallback will return true if it did not apply any callback. In that |
151 | // case, attempt to apply the MainFn. |
152 | if (TableGen::Emitter::ApplyCallback(Records, OS&: Out)) |
153 | status = MainFn ? MainFn(Out, Records) : 1; |
154 | Timer.stopBackendTimer(); |
155 | if (status) |
156 | return 1; |
157 | |
158 | // Always write the depfile, even if the main output hasn't changed. |
159 | // If it's missing, Ninja considers the output dirty. If this was below |
160 | // the early exit below and someone deleted the .inc.d file but not the .inc |
161 | // file, tablegen would never write the depfile. |
162 | if (!DependFilename.empty()) { |
163 | if (int Ret = createDependencyFile(Parser, argv0)) |
164 | return Ret; |
165 | } |
166 | |
167 | Timer.startTimer(Name: "Write output" ); |
168 | bool WriteFile = true; |
169 | if (WriteIfChanged) { |
170 | // Only updates the real output file if there are any differences. |
171 | // This prevents recompilation of all the files depending on it if there |
172 | // aren't any. |
173 | if (auto ExistingOrErr = |
174 | MemoryBuffer::getFile(Filename: OutputFilename, /*IsText=*/true)) |
175 | if (std::move(ExistingOrErr.get())->getBuffer() == OutString) |
176 | WriteFile = false; |
177 | } |
178 | if (WriteFile) { |
179 | std::error_code EC; |
180 | ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_Text); |
181 | if (EC) |
182 | return reportError(ProgName: argv0, Msg: "error opening " + OutputFilename + ": " + |
183 | EC.message() + "\n" ); |
184 | OutFile.os() << OutString; |
185 | if (ErrorsPrinted == 0) |
186 | OutFile.keep(); |
187 | } |
188 | |
189 | Timer.stopTimer(); |
190 | Timer.stopPhaseTiming(); |
191 | |
192 | if (ErrorsPrinted > 0) |
193 | return reportError(ProgName: argv0, Msg: Twine(ErrorsPrinted) + " errors.\n" ); |
194 | return 0; |
195 | } |
196 | |