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