1 //===-- llvm-cgdata.cpp - LLVM CodeGen Data Tool --------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
31 using namespace llvm::object
;
45 // Command-line option boilerplate.
48 OPT_INVALID
= 0, // This is not an option ID.
49 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
54 #define OPTTABLE_STR_TABLE_CODE
56 #undef OPTTABLE_STR_TABLE_CODE
58 #define OPTTABLE_PREFIXES_TABLE_CODE
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__),
69 class CGDataOptTable
: public opt::GenericOptTable
{
72 : GenericOptTable(OptionStrTable
, OptionPrefixesTable
, InfoTable
) {}
74 } // end anonymous namespace
77 static StringRef ToolName
;
78 static StringRef OutputFilename
= "-";
79 static StringRef Filename
;
80 static bool ShowCGDataVersion
;
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
= "") {
90 errs() << Whence
<< ": ";
91 errs() << Message
<< "\n";
93 WithColor::note() << Hint
<< "\n";
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
));
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
[]) {
114 raw_fd_ostream
OS(OutputFilename
, EC
,
115 OutputFormat
== CGDataFormat::Text
116 ? sys::fs::OF_TextWithCRLF
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
));
141 if (Error E
= Writer
.write(OS
))
142 exitWithError(std::move(E
));
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
) {
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
);
169 exitWithError(std::move(Err
), Filename
);
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
);
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
);
190 // TODO: Support for the MachO universal binary format.
191 errs() << "Error: unsupported binary file: " << Filename
<< "\n";
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
[]) {
211 OutlinedHashTreeRecord GlobalOutlineRecord
;
212 StableFunctionMapRecord GlobalFunctionMapRecord
;
213 for (auto &Filename
: InputFilenames
)
215 handleFile(Filename
, GlobalOutlineRecord
, GlobalFunctionMapRecord
);
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
);
229 raw_fd_ostream
OS(OutputFilename
, EC
,
230 OutputFormat
== CGDataFormat::Text
231 ? sys::fs::OF_TextWithCRLF
234 exitWithErrorCode(EC
, OutputFilename
);
236 if (OutputFormat
== CGDataFormat::Text
) {
237 if (Error E
= Writer
.writeText(OS
))
238 exitWithError(std::move(E
));
240 if (Error E
= Writer
.write(OS
))
241 exitWithError(std::move(E
));
247 static int show_main(int argc
, const char *argv
[]) {
249 raw_fd_ostream
OS(OutputFilename
.data(), EC
, sys::fs::OF_TextWithCRLF
);
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)
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";
283 static void parseArgs(int argc
, char **argv
) {
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';
294 if (Args
.hasArg(OPT_help
)) {
297 "llvm-cgdata <action> [options] (<binary files>|<.cgdata file>)",
298 ToolName
.str().c_str());
301 if (Args
.hasArg(OPT_version
)) {
302 cl::PrintVersionMessage();
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
)
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
)) {
335 exitWithError("Only one action is allowed.");
339 exitWithError("One action is required.");
341 switch (ActionArg
->getOption().getID()) {
343 if (InputFilenames
.size() != 1)
344 exitWithError("only one input file is allowed.");
345 Action
= CGDataAction::Show
;
348 // The default output format is text for convert.
350 OutputFormat
= CGDataFormat::Text
;
351 if (InputFilenames
.size() != 1)
352 exitWithError("only one input file is allowed.");
353 Action
= CGDataAction::Convert
;
356 // The default output format is binary for merge.
358 OutputFormat
= CGDataFormat::Binary
;
359 Action
= CGDataAction::Merge
;
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
);
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");