[PAC][lld][AArch64][ELF] Support signed GOT with tiny code model (#113816)
[llvm-project.git] / llvm / tools / llvm-cgdata / llvm-cgdata.cpp
blob9e3800f5bfbbc61dfd074c551b2d0cb4902f1199
1 //===-- llvm-cgdata.cpp - LLVM CodeGen Data Tool --------------------------===//
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 // llvm-cgdata parses raw codegen data embedded in compiled binary files, and
10 // merges them into a single .cgdata file. It can also inspect and maninuplate
11 // a .cgdata file. This .cgdata can contain various codegen data like outlining
12 // information, and it can be used to optimize the code in the subsequent build.
14 //===----------------------------------------------------------------------===//
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/CGData/CodeGenDataReader.h"
17 #include "llvm/CGData/CodeGenDataWriter.h"
18 #include "llvm/IR/LLVMContext.h"
19 #include "llvm/Object/Archive.h"
20 #include "llvm/Object/Binary.h"
21 #include "llvm/Option/ArgList.h"
22 #include "llvm/Option/Option.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/LLVMDriver.h"
25 #include "llvm/Support/Path.h"
26 #include "llvm/Support/VirtualFileSystem.h"
27 #include "llvm/Support/WithColor.h"
28 #include "llvm/Support/raw_ostream.h"
30 using namespace llvm;
31 using namespace llvm::object;
33 enum CGDataFormat {
34 Invalid,
35 Text,
36 Binary,
39 enum CGDataAction {
40 Convert,
41 Merge,
42 Show,
45 // Command-line option boilerplate.
46 namespace {
47 enum ID {
48 OPT_INVALID = 0, // This is not an option ID.
49 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
50 #include "Opts.inc"
51 #undef OPTION
54 #define OPTTABLE_STR_TABLE_CODE
55 #include "Opts.inc"
56 #undef OPTTABLE_STR_TABLE_CODE
58 #define OPTTABLE_PREFIXES_TABLE_CODE
59 #include "Opts.inc"
60 #undef OPTTABLE_PREFIXES_TABLE_CODE
62 using namespace llvm::opt;
63 static constexpr opt::OptTable::Info InfoTable[] = {
64 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
65 #include "Opts.inc"
66 #undef OPTION
69 class CGDataOptTable : public opt::GenericOptTable {
70 public:
71 CGDataOptTable()
72 : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
74 } // end anonymous namespace
76 // Options
77 static StringRef ToolName;
78 static StringRef OutputFilename = "-";
79 static StringRef Filename;
80 static bool ShowCGDataVersion;
81 static bool SkipTrim;
82 static CGDataAction Action;
83 static std::optional<CGDataFormat> OutputFormat;
84 static std::vector<std::string> InputFilenames;
86 static void exitWithError(Twine Message, std::string Whence = "",
87 std::string Hint = "") {
88 WithColor::error();
89 if (!Whence.empty())
90 errs() << Whence << ": ";
91 errs() << Message << "\n";
92 if (!Hint.empty())
93 WithColor::note() << Hint << "\n";
94 ::exit(1);
97 static void exitWithError(Error E, StringRef Whence = "") {
98 if (E.isA<CGDataError>()) {
99 handleAllErrors(std::move(E), [&](const CGDataError &IPE) {
100 exitWithError(IPE.message(), std::string(Whence));
102 return;
105 exitWithError(toString(std::move(E)), std::string(Whence));
108 static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
109 exitWithError(EC.message(), std::string(Whence));
112 static int convert_main(int argc, const char *argv[]) {
113 std::error_code EC;
114 raw_fd_ostream OS(OutputFilename, EC,
115 OutputFormat == CGDataFormat::Text
116 ? sys::fs::OF_TextWithCRLF
117 : sys::fs::OF_None);
118 if (EC)
119 exitWithErrorCode(EC, OutputFilename);
121 auto FS = vfs::getRealFileSystem();
122 auto ReaderOrErr = CodeGenDataReader::create(Filename, *FS);
123 if (Error E = ReaderOrErr.takeError())
124 exitWithError(std::move(E), Filename);
126 CodeGenDataWriter Writer;
127 auto Reader = ReaderOrErr->get();
128 if (Reader->hasOutlinedHashTree()) {
129 OutlinedHashTreeRecord Record(Reader->releaseOutlinedHashTree());
130 Writer.addRecord(Record);
132 if (Reader->hasStableFunctionMap()) {
133 StableFunctionMapRecord Record(Reader->releaseStableFunctionMap());
134 Writer.addRecord(Record);
137 if (OutputFormat == CGDataFormat::Text) {
138 if (Error E = Writer.writeText(OS))
139 exitWithError(std::move(E));
140 } else {
141 if (Error E = Writer.write(OS))
142 exitWithError(std::move(E));
145 return 0;
148 static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
149 OutlinedHashTreeRecord &GlobalOutlineRecord,
150 StableFunctionMapRecord &GlobalFunctionMapRecord);
152 static bool handleArchive(StringRef Filename, Archive &Arch,
153 OutlinedHashTreeRecord &GlobalOutlineRecord,
154 StableFunctionMapRecord &GlobalFunctionMapRecord) {
155 bool Result = true;
156 Error Err = Error::success();
157 for (const auto &Child : Arch.children(Err)) {
158 auto BuffOrErr = Child.getMemoryBufferRef();
159 if (Error E = BuffOrErr.takeError())
160 exitWithError(std::move(E), Filename);
161 auto NameOrErr = Child.getName();
162 if (Error E = NameOrErr.takeError())
163 exitWithError(std::move(E), Filename);
164 std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();
165 Result &= handleBuffer(Name, BuffOrErr.get(), GlobalOutlineRecord,
166 GlobalFunctionMapRecord);
168 if (Err)
169 exitWithError(std::move(Err), Filename);
170 return Result;
173 static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
174 OutlinedHashTreeRecord &GlobalOutlineRecord,
175 StableFunctionMapRecord &GlobalFunctionMapRecord) {
176 Expected<std::unique_ptr<object::Binary>> BinOrErr =
177 object::createBinary(Buffer);
178 if (Error E = BinOrErr.takeError())
179 exitWithError(std::move(E), Filename);
181 bool Result = true;
182 if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) {
183 if (Error E = CodeGenDataReader::mergeFromObjectFile(
184 Obj, GlobalOutlineRecord, GlobalFunctionMapRecord))
185 exitWithError(std::move(E), Filename);
186 } else if (auto *Arch = dyn_cast<Archive>(BinOrErr->get())) {
187 Result &= handleArchive(Filename, *Arch, GlobalOutlineRecord,
188 GlobalFunctionMapRecord);
189 } else {
190 // TODO: Support for the MachO universal binary format.
191 errs() << "Error: unsupported binary file: " << Filename << "\n";
192 Result = false;
195 return Result;
198 static bool handleFile(StringRef Filename,
199 OutlinedHashTreeRecord &GlobalOutlineRecord,
200 StableFunctionMapRecord &GlobalFunctionMapRecord) {
201 ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
202 MemoryBuffer::getFileOrSTDIN(Filename);
203 if (std::error_code EC = BuffOrErr.getError())
204 exitWithErrorCode(EC, Filename);
205 return handleBuffer(Filename, *BuffOrErr.get(), GlobalOutlineRecord,
206 GlobalFunctionMapRecord);
209 static int merge_main(int argc, const char *argv[]) {
210 bool Result = true;
211 OutlinedHashTreeRecord GlobalOutlineRecord;
212 StableFunctionMapRecord GlobalFunctionMapRecord;
213 for (auto &Filename : InputFilenames)
214 Result &=
215 handleFile(Filename, GlobalOutlineRecord, GlobalFunctionMapRecord);
217 if (!Result)
218 exitWithError("failed to merge codegen data files.");
220 GlobalFunctionMapRecord.finalize(SkipTrim);
222 CodeGenDataWriter Writer;
223 if (!GlobalOutlineRecord.empty())
224 Writer.addRecord(GlobalOutlineRecord);
225 if (!GlobalFunctionMapRecord.empty())
226 Writer.addRecord(GlobalFunctionMapRecord);
228 std::error_code EC;
229 raw_fd_ostream OS(OutputFilename, EC,
230 OutputFormat == CGDataFormat::Text
231 ? sys::fs::OF_TextWithCRLF
232 : sys::fs::OF_None);
233 if (EC)
234 exitWithErrorCode(EC, OutputFilename);
236 if (OutputFormat == CGDataFormat::Text) {
237 if (Error E = Writer.writeText(OS))
238 exitWithError(std::move(E));
239 } else {
240 if (Error E = Writer.write(OS))
241 exitWithError(std::move(E));
244 return 0;
247 static int show_main(int argc, const char *argv[]) {
248 std::error_code EC;
249 raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
250 if (EC)
251 exitWithErrorCode(EC, OutputFilename);
253 auto FS = vfs::getRealFileSystem();
254 auto ReaderOrErr = CodeGenDataReader::create(Filename, *FS);
255 if (Error E = ReaderOrErr.takeError())
256 exitWithError(std::move(E), Filename);
258 auto Reader = ReaderOrErr->get();
259 if (ShowCGDataVersion)
260 OS << "Version: " << Reader->getVersion() << "\n";
262 if (Reader->hasOutlinedHashTree()) {
263 auto Tree = Reader->releaseOutlinedHashTree();
264 OS << "Outlined hash tree:\n";
265 OS << " Total Node Count: " << Tree->size() << "\n";
266 OS << " Terminal Node Count: " << Tree->size(/*GetTerminalCountOnly=*/true)
267 << "\n";
268 OS << " Depth: " << Tree->depth() << "\n";
270 if (Reader->hasStableFunctionMap()) {
271 auto Map = Reader->releaseStableFunctionMap();
272 OS << "Stable function map:\n";
273 OS << " Unique hash Count: " << Map->size() << "\n";
274 OS << " Total function Count: "
275 << Map->size(StableFunctionMap::TotalFunctionCount) << "\n";
276 OS << " Mergeable function Count: "
277 << Map->size(StableFunctionMap::MergeableFunctionCount) << "\n";
280 return 0;
283 static void parseArgs(int argc, char **argv) {
284 CGDataOptTable Tbl;
285 ToolName = argv[0];
286 llvm::BumpPtrAllocator A;
287 llvm::StringSaver Saver{A};
288 llvm::opt::InputArgList Args =
289 Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
290 llvm::errs() << Msg << '\n';
291 std::exit(1);
294 if (Args.hasArg(OPT_help)) {
295 Tbl.printHelp(
296 llvm::outs(),
297 "llvm-cgdata <action> [options] (<binary files>|<.cgdata file>)",
298 ToolName.str().c_str());
299 std::exit(0);
301 if (Args.hasArg(OPT_version)) {
302 cl::PrintVersionMessage();
303 std::exit(0);
306 ShowCGDataVersion = Args.hasArg(OPT_cgdata_version);
307 SkipTrim = Args.hasArg(OPT_skip_trim);
309 if (opt::Arg *A = Args.getLastArg(OPT_format)) {
310 StringRef OF = A->getValue();
311 OutputFormat = StringSwitch<CGDataFormat>(OF)
312 .Case("text", CGDataFormat::Text)
313 .Case("binary", CGDataFormat::Binary)
314 .Default(CGDataFormat::Invalid);
315 if (OutputFormat == CGDataFormat::Invalid)
316 exitWithError("unsupported format '" + OF + "'");
319 InputFilenames = Args.getAllArgValues(OPT_INPUT);
320 if (InputFilenames.empty())
321 exitWithError("No input file is specified.");
322 Filename = InputFilenames[0];
324 if (Args.hasArg(OPT_output)) {
325 OutputFilename = Args.getLastArgValue(OPT_output);
326 for (auto &Filename : InputFilenames)
327 if (Filename == OutputFilename)
328 exitWithError(
329 "Input file name cannot be the same as the output file name!\n");
332 opt::Arg *ActionArg = nullptr;
333 for (opt::Arg *Arg : Args.filtered(OPT_action_group)) {
334 if (ActionArg)
335 exitWithError("Only one action is allowed.");
336 ActionArg = Arg;
338 if (!ActionArg)
339 exitWithError("One action is required.");
341 switch (ActionArg->getOption().getID()) {
342 case OPT_show:
343 if (InputFilenames.size() != 1)
344 exitWithError("only one input file is allowed.");
345 Action = CGDataAction::Show;
346 break;
347 case OPT_convert:
348 // The default output format is text for convert.
349 if (!OutputFormat)
350 OutputFormat = CGDataFormat::Text;
351 if (InputFilenames.size() != 1)
352 exitWithError("only one input file is allowed.");
353 Action = CGDataAction::Convert;
354 break;
355 case OPT_merge:
356 // The default output format is binary for merge.
357 if (!OutputFormat)
358 OutputFormat = CGDataFormat::Binary;
359 Action = CGDataAction::Merge;
360 break;
361 default:
362 llvm_unreachable("unrecognized action");
366 int llvm_cgdata_main(int argc, char **argvNonConst, const llvm::ToolContext &) {
367 const char **argv = const_cast<const char **>(argvNonConst);
368 parseArgs(argc, argvNonConst);
370 switch (Action) {
371 case CGDataAction::Convert:
372 return convert_main(argc, argv);
373 case CGDataAction::Merge:
374 return merge_main(argc, argv);
375 case CGDataAction::Show:
376 return show_main(argc, argv);
379 llvm_unreachable("unrecognized action");