brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.0 KiB · ea89c4d Raw
388 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// llvm-cgdata parses raw codegen data embedded in compiled binary files, and10// merges them into a single .cgdata file. It can also inspect and maninuplate11// a .cgdata file. This .cgdata can contain various codegen data like outlining12// information, and it can be used to optimize the code in the subsequent build.13//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"29 30using namespace llvm;31using namespace llvm::object;32 33enum CGDataFormat {34  Invalid,35  Text,36  Binary,37};38 39enum CGDataAction {40  Convert,41  Merge,42  Show,43};44 45// Command-line option boilerplate.46namespace {47enum 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 OPTION52};53 54#define OPTTABLE_STR_TABLE_CODE55#include "Opts.inc"56#undef OPTTABLE_STR_TABLE_CODE57 58#define OPTTABLE_PREFIXES_TABLE_CODE59#include "Opts.inc"60#undef OPTTABLE_PREFIXES_TABLE_CODE61 62using namespace llvm::opt;63static constexpr opt::OptTable::Info InfoTable[] = {64#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),65#include "Opts.inc"66#undef OPTION67};68 69class CGDataOptTable : public opt::GenericOptTable {70public:71  CGDataOptTable()72      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}73};74} // end anonymous namespace75 76// Options77static StringRef ToolName;78static std::string OutputFilename = "-";79static std::string Filename;80static bool ShowCGDataVersion;81static bool SkipTrim;82static CGDataAction Action;83static std::optional<CGDataFormat> OutputFormat;84static std::vector<std::string> InputFilenames;85 86namespace llvm {87extern cl::opt<bool> IndexedCodeGenDataLazyLoading;88} // end namespace llvm89 90static void exitWithError(Twine Message, StringRef Whence = "",91                          StringRef Hint = "") {92  WithColor::error();93  if (!Whence.empty())94    errs() << Whence << ": ";95  errs() << Message << "\n";96  if (!Hint.empty())97    WithColor::note() << Hint << "\n";98  ::exit(1);99}100 101static void exitWithError(Error E, StringRef Whence = "") {102  if (E.isA<CGDataError>()) {103    handleAllErrors(std::move(E), [&](const CGDataError &IPE) {104      exitWithError(IPE.message(), Whence);105    });106    return;107  }108 109  exitWithError(toString(std::move(E)), Whence);110}111 112static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {113  exitWithError(EC.message(), Whence);114}115 116static int convert_main(int argc, const char *argv[]) {117  std::error_code EC;118  raw_fd_ostream OS(OutputFilename, EC,119                    OutputFormat == CGDataFormat::Text120                        ? sys::fs::OF_TextWithCRLF121                        : sys::fs::OF_None);122  if (EC)123    exitWithErrorCode(EC, OutputFilename);124 125  auto FS = vfs::getRealFileSystem();126  auto ReaderOrErr = CodeGenDataReader::create(Filename, *FS);127  if (Error E = ReaderOrErr.takeError())128    exitWithError(std::move(E), Filename);129 130  CodeGenDataWriter Writer;131  auto Reader = ReaderOrErr->get();132  if (Reader->hasOutlinedHashTree()) {133    OutlinedHashTreeRecord Record(Reader->releaseOutlinedHashTree());134    Writer.addRecord(Record);135  }136  if (Reader->hasStableFunctionMap()) {137    StableFunctionMapRecord Record(Reader->releaseStableFunctionMap());138    Writer.addRecord(Record);139  }140 141  if (OutputFormat == CGDataFormat::Text) {142    if (Error E = Writer.writeText(OS))143      exitWithError(std::move(E));144  } else {145    if (Error E = Writer.write(OS))146      exitWithError(std::move(E));147  }148 149  return 0;150}151 152static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,153                         OutlinedHashTreeRecord &GlobalOutlineRecord,154                         StableFunctionMapRecord &GlobalFunctionMapRecord);155 156static bool handleArchive(StringRef Filename, Archive &Arch,157                          OutlinedHashTreeRecord &GlobalOutlineRecord,158                          StableFunctionMapRecord &GlobalFunctionMapRecord) {159  bool Result = true;160  Error Err = Error::success();161  for (const auto &Child : Arch.children(Err)) {162    auto BuffOrErr = Child.getMemoryBufferRef();163    if (Error E = BuffOrErr.takeError())164      exitWithError(std::move(E), Filename);165    auto NameOrErr = Child.getName();166    if (Error E = NameOrErr.takeError())167      exitWithError(std::move(E), Filename);168    std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();169    Result &= handleBuffer(Name, BuffOrErr.get(), GlobalOutlineRecord,170                           GlobalFunctionMapRecord);171  }172  if (Err)173    exitWithError(std::move(Err), Filename);174  return Result;175}176 177static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,178                         OutlinedHashTreeRecord &GlobalOutlineRecord,179                         StableFunctionMapRecord &GlobalFunctionMapRecord) {180  Expected<std::unique_ptr<object::Binary>> BinOrErr =181      object::createBinary(Buffer);182  if (Error E = BinOrErr.takeError())183    exitWithError(std::move(E), Filename);184 185  bool Result = true;186  if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) {187    if (Error E = CodeGenDataReader::mergeFromObjectFile(188            Obj, GlobalOutlineRecord, GlobalFunctionMapRecord))189      exitWithError(std::move(E), Filename);190  } else if (auto *Arch = dyn_cast<Archive>(BinOrErr->get())) {191    Result &= handleArchive(Filename, *Arch, GlobalOutlineRecord,192                            GlobalFunctionMapRecord);193  } else {194    // TODO: Support for the MachO universal binary format.195    errs() << "Error: unsupported binary file: " << Filename << "\n";196    Result = false;197  }198 199  return Result;200}201 202static bool handleFile(StringRef Filename,203                       OutlinedHashTreeRecord &GlobalOutlineRecord,204                       StableFunctionMapRecord &GlobalFunctionMapRecord) {205  ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =206      MemoryBuffer::getFileOrSTDIN(Filename);207  if (std::error_code EC = BuffOrErr.getError())208    exitWithErrorCode(EC, Filename);209  return handleBuffer(Filename, *BuffOrErr.get(), GlobalOutlineRecord,210                      GlobalFunctionMapRecord);211}212 213static int merge_main(int argc, const char *argv[]) {214  bool Result = true;215  OutlinedHashTreeRecord GlobalOutlineRecord;216  StableFunctionMapRecord GlobalFunctionMapRecord;217  for (auto &Filename : InputFilenames)218    Result &=219        handleFile(Filename, GlobalOutlineRecord, GlobalFunctionMapRecord);220 221  if (!Result)222    exitWithError("failed to merge codegen data files.");223 224  GlobalFunctionMapRecord.finalize(SkipTrim);225 226  CodeGenDataWriter Writer;227  if (!GlobalOutlineRecord.empty())228    Writer.addRecord(GlobalOutlineRecord);229  if (!GlobalFunctionMapRecord.empty())230    Writer.addRecord(GlobalFunctionMapRecord);231 232  std::error_code EC;233  raw_fd_ostream OS(OutputFilename, EC,234                    OutputFormat == CGDataFormat::Text235                        ? sys::fs::OF_TextWithCRLF236                        : sys::fs::OF_None);237  if (EC)238    exitWithErrorCode(EC, OutputFilename);239 240  if (OutputFormat == CGDataFormat::Text) {241    if (Error E = Writer.writeText(OS))242      exitWithError(std::move(E));243  } else {244    if (Error E = Writer.write(OS))245      exitWithError(std::move(E));246  }247 248  return 0;249}250 251static int show_main(int argc, const char *argv[]) {252  std::error_code EC;253  raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);254  if (EC)255    exitWithErrorCode(EC, OutputFilename);256 257  auto FS = vfs::getRealFileSystem();258  auto ReaderOrErr = CodeGenDataReader::create(Filename, *FS);259  if (Error E = ReaderOrErr.takeError())260    exitWithError(std::move(E), Filename);261 262  auto Reader = ReaderOrErr->get();263  if (ShowCGDataVersion)264    OS << "Version: " << Reader->getVersion() << "\n";265 266  if (Reader->hasOutlinedHashTree()) {267    auto Tree = Reader->releaseOutlinedHashTree();268    OS << "Outlined hash tree:\n";269    OS << "  Total Node Count: " << Tree->size() << "\n";270    OS << "  Terminal Node Count: " << Tree->size(/*GetTerminalCountOnly=*/true)271       << "\n";272    OS << "  Depth: " << Tree->depth() << "\n";273  }274  if (Reader->hasStableFunctionMap()) {275    auto Map = Reader->releaseStableFunctionMap();276    OS << "Stable function map:\n";277    OS << "  Unique hash Count: " << Map->size() << "\n";278    OS << "  Total function Count: "279       << Map->size(StableFunctionMap::TotalFunctionCount) << "\n";280    OS << "  Mergeable function Count: "281       << Map->size(StableFunctionMap::MergeableFunctionCount) << "\n";282  }283 284  return 0;285}286 287static void parseArgs(int argc, char **argv) {288  CGDataOptTable Tbl;289  ToolName = argv[0];290  llvm::BumpPtrAllocator A;291  llvm::StringSaver Saver{A};292  llvm::opt::InputArgList Args =293      Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {294        llvm::errs() << Msg << '\n';295        std::exit(1);296      });297 298  if (Args.hasArg(OPT_help)) {299    Tbl.printHelp(300        llvm::outs(),301        "llvm-cgdata <action> [options] (<binary files>|<.cgdata file>)",302        ToolName.str().c_str());303    std::exit(0);304  }305  if (Args.hasArg(OPT_version)) {306    cl::PrintVersionMessage();307    std::exit(0);308  }309 310  ShowCGDataVersion = Args.hasArg(OPT_cgdata_version);311  SkipTrim = Args.hasArg(OPT_skip_trim);312 313  if (opt::Arg *A = Args.getLastArg(OPT_format)) {314    StringRef OF = A->getValue();315    OutputFormat = StringSwitch<CGDataFormat>(OF)316                       .Case("text", CGDataFormat::Text)317                       .Case("binary", CGDataFormat::Binary)318                       .Default(CGDataFormat::Invalid);319    if (OutputFormat == CGDataFormat::Invalid)320      exitWithError("unsupported format '" + OF + "'");321  }322 323  InputFilenames = Args.getAllArgValues(OPT_INPUT);324  if (InputFilenames.empty())325    exitWithError("No input file is specified.");326  Filename = InputFilenames[0];327 328  if (Args.hasArg(OPT_output)) {329    OutputFilename = Args.getLastArgValue(OPT_output);330    for (auto &Filename : InputFilenames)331      if (Filename == OutputFilename)332        exitWithError(333            "Input file name cannot be the same as the output file name!\n");334  }335 336  opt::Arg *ActionArg = nullptr;337  for (opt::Arg *Arg : Args.filtered(OPT_action_group)) {338    if (ActionArg)339      exitWithError("Only one action is allowed.");340    ActionArg = Arg;341  }342  if (!ActionArg)343    exitWithError("One action is required.");344 345  switch (ActionArg->getOption().getID()) {346  case OPT_show:347    if (InputFilenames.size() != 1)348      exitWithError("only one input file is allowed.");349    Action = CGDataAction::Show;350    break;351  case OPT_convert:352    // The default output format is text for convert.353    if (!OutputFormat)354      OutputFormat = CGDataFormat::Text;355    if (InputFilenames.size() != 1)356      exitWithError("only one input file is allowed.");357    Action = CGDataAction::Convert;358    break;359  case OPT_merge:360    // The default output format is binary for merge.361    if (!OutputFormat)362      OutputFormat = CGDataFormat::Binary;363    Action = CGDataAction::Merge;364    break;365  default:366    llvm_unreachable("unrecognized action");367  }368 369  IndexedCodeGenDataLazyLoading =370      Args.hasArg(OPT_indexed_codegen_data_lazy_loading);371}372 373int llvm_cgdata_main(int argc, char **argvNonConst, const llvm::ToolContext &) {374  const char **argv = const_cast<const char **>(argvNonConst);375  parseArgs(argc, argvNonConst);376 377  switch (Action) {378  case CGDataAction::Convert:379    return convert_main(argc, argv);380  case CGDataAction::Merge:381    return merge_main(argc, argv);382  case CGDataAction::Show:383    return show_main(argc, argv);384  }385 386  llvm_unreachable("unrecognized action");387}388