brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.7 KiB · 0b606af Raw
369 lines · cpp
1//===-- llvm-tli-checker.cpp - Compare TargetLibraryInfo to SDK libraries -===//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#include "llvm/ADT/SmallString.h"10#include "llvm/ADT/StringMap.h"11#include "llvm/Analysis/TargetLibraryInfo.h"12#include "llvm/Config/llvm-config.h"13#include "llvm/Demangle/Demangle.h"14#include "llvm/Object/Archive.h"15#include "llvm/Object/ELFObjectFile.h"16#include "llvm/Option/ArgList.h"17#include "llvm/Option/Option.h"18#include "llvm/Support/FileSystem.h"19#include "llvm/Support/InitLLVM.h"20#include "llvm/Support/Path.h"21#include "llvm/Support/WithColor.h"22#include "llvm/TargetParser/Triple.h"23 24using namespace llvm;25using namespace llvm::object;26 27// Command-line option boilerplate.28namespace {29enum ID {30  OPT_INVALID = 0, // This is not an option ID.31#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),32#include "Opts.inc"33#undef OPTION34};35 36#define OPTTABLE_STR_TABLE_CODE37#include "Opts.inc"38#undef OPTTABLE_STR_TABLE_CODE39 40#define OPTTABLE_PREFIXES_TABLE_CODE41#include "Opts.inc"42#undef OPTTABLE_PREFIXES_TABLE_CODE43 44using namespace llvm::opt;45static constexpr opt::OptTable::Info InfoTable[] = {46#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),47#include "Opts.inc"48#undef OPTION49};50 51class TLICheckerOptTable : public opt::GenericOptTable {52public:53  TLICheckerOptTable()54      : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}55};56} // end anonymous namespace57 58// We have three levels of reporting.59enum class ReportKind {60  Error,       // For argument parsing errors.61  Summary,     // Report counts but not details.62  Discrepancy, // Report where TLI and the library differ.63  Full         // Report for every known-to-TLI function.64};65 66// Most of the ObjectFile interfaces return an Expected<T>, so make it easy67// to ignore errors.68template <typename T>69static T unwrapIgnoreError(Expected<T> E, T Default = T()) {70  if (E)71    return std::move(*E);72  // Sink the error and return a nothing value.73  consumeError(E.takeError());74  return Default;75}76 77static void fail(const Twine &Message) {78  WithColor::error() << Message << '\n';79  exit(EXIT_FAILURE);80}81 82// Some problem occurred with an archive member; complain and continue.83static void reportArchiveChildIssue(const object::Archive::Child &C, int Index,84                                    StringRef ArchiveFilename) {85  // First get the member name.86  std::string ChildName;87  Expected<StringRef> NameOrErr = C.getName();88  if (NameOrErr)89    ChildName = std::string(NameOrErr.get());90  else {91    // Ignore the name-fetch error, just report the index.92    consumeError(NameOrErr.takeError());93    ChildName = "<file index: " + std::to_string(Index) + ">";94  }95 96  WithColor::warning() << ArchiveFilename << "(" << ChildName97                       << "): member is not usable\n";98}99 100// Return Name, and if Name is mangled, append "aka" and the demangled name.101static raw_ostream &printPrintableName(raw_ostream &OS, StringRef Name) {102  OS << '\'' << Name << '\'';103 104  std::string DemangledName(demangle(Name));105  if (Name != DemangledName)106    OS << " aka " << DemangledName;107  return OS;108}109 110static void reportNumberOfEntries(const TargetLibraryInfo &TLI,111                                  StringRef TargetTriple) {112  unsigned NumAvailable = 0;113 114  // Assume this gets called after initialize(), so we have the above line of115  // output as a header.  So, for example, no need to repeat the triple.116  for (unsigned FI = LibFunc::Begin_LibFunc; FI != LibFunc::End_LibFunc; ++FI) {117    if (TLI.has(static_cast<LibFunc>(FI)))118      ++NumAvailable;119  }120 121  outs() << "TLI knows " << (LibFunc::End_LibFunc - LibFunc::Begin_LibFunc)122         << " symbols, " << NumAvailable << " available for '" << TargetTriple123         << "'\n";124}125 126static void dumpTLIEntries(const TargetLibraryInfo &TLI) {127  // Assume this gets called after initialize(), so we have the above line of128  // output as a header.  So, for example, no need to repeat the triple.129  for (unsigned FI = LibFunc::Begin_LibFunc; FI != LibFunc::End_LibFunc; ++FI) {130    LibFunc LF = static_cast<LibFunc>(FI);131    bool IsAvailable = TLI.has(LF);132    StringRef FuncName = TargetLibraryInfo::getStandardName(LF);133 134    outs() << (IsAvailable ? "    " : "not ") << "available: ";135    printPrintableName(outs(), FuncName) << '\n';136  }137}138 139// Store all the exported symbol names we found in the input libraries.140// We use a map to get hashed lookup speed; the bool is meaningless.141class SDKNameMap : public StringMap<bool> {142  void maybeInsertSymbol(const SymbolRef &S, const ObjectFile &O);143  void populateFromObject(ObjectFile *O);144  void populateFromArchive(Archive *A);145 146public:147  void populateFromFile(StringRef LibDir, StringRef LibName);148};149static SDKNameMap SDKNames;150 151// Insert defined global function symbols into the map if valid.152void SDKNameMap::maybeInsertSymbol(const SymbolRef &S, const ObjectFile &O) {153  SymbolRef::Type Type = unwrapIgnoreError(S.getType());154  uint32_t Flags = unwrapIgnoreError(S.getFlags());155  section_iterator Section = unwrapIgnoreError(S.getSection(),156                                               /*Default=*/O.section_end());157  bool IsRegularFunction = Type == SymbolRef::ST_Function &&158                           (Flags & SymbolRef::SF_Global) &&159                           Section != O.section_end();160  bool IsIFunc =161      Type == SymbolRef::ST_Other && (Flags & SymbolRef::SF_Indirect);162  if (IsRegularFunction || IsIFunc) {163    StringRef Name = unwrapIgnoreError(S.getName());164    insert({ Name, true });165  }166}167 168// Given an ObjectFile, extract the global function symbols.169void SDKNameMap::populateFromObject(ObjectFile *O) {170  // FIXME: Support other formats.171  if (!O->isELF()) {172    WithColor::warning() << O->getFileName()173                         << ": only ELF-format files are supported\n";174    return;175  }176  const auto *ELF = cast<ELFObjectFileBase>(O);177 178  if (ELF->getEType() == ELF::ET_REL) {179    for (const auto &S : ELF->symbols())180      maybeInsertSymbol(S, *O);181  } else {182    for (const auto &S : ELF->getDynamicSymbolIterators())183      maybeInsertSymbol(S, *O);184  }185}186 187// Unpack an archive and populate from the component object files.188// This roughly imitates dumpArchive() from llvm-objdump.cpp.189void SDKNameMap::populateFromArchive(Archive *A) {190  Error Err = Error::success();191  int Index = -1;192  for (const auto &C : A->children(Err)) {193    ++Index;194    Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();195    if (!ChildOrErr) {196      if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) {197        // Issue a generic warning.198        consumeError(std::move(E));199        reportArchiveChildIssue(C, Index, A->getFileName());200      }201      continue;202    }203    if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get()))204      populateFromObject(O);205    // Ignore non-object archive members.206  }207  if (Err)208    WithColor::defaultErrorHandler(std::move(Err));209}210 211// Unpack a library file and extract the global function names.212void SDKNameMap::populateFromFile(StringRef LibDir, StringRef LibName) {213  // Pick an arbitrary but reasonable default size.214  SmallString<255> Filepath(LibDir);215  sys::path::append(Filepath, LibName);216  if (!sys::fs::exists(Filepath)) {217    WithColor::warning() << StringRef(Filepath) << ": not found\n";218    return;219  }220  outs() << "\nLooking for symbols in '" << StringRef(Filepath) << "'\n";221  auto ExpectedBinary = createBinary(Filepath);222  if (!ExpectedBinary) {223    // FIXME: Report this better.224    WithColor::defaultWarningHandler(ExpectedBinary.takeError());225    return;226  }227  OwningBinary<Binary> OBinary = std::move(*ExpectedBinary);228  Binary &Binary = *OBinary.getBinary();229  size_t Precount = size();230  if (Archive *A = dyn_cast<Archive>(&Binary))231    populateFromArchive(A);232  else if (ObjectFile *O = dyn_cast<ObjectFile>(&Binary))233    populateFromObject(O);234  else {235    WithColor::warning() << StringRef(Filepath)236                         << ": not an archive or object file\n";237    return;238  }239  if (Precount == size())240    WithColor::warning() << StringRef(Filepath) << ": no symbols found\n";241  else242    outs() << "Found " << size() - Precount << " global function symbols in '"243           << StringRef(Filepath) << "'\n";244}245 246int main(int argc, char *argv[]) {247  InitLLVM X(argc, argv);248  BumpPtrAllocator A;249  StringSaver Saver(A);250  TLICheckerOptTable Tbl;251  opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,252                                         [&](StringRef Msg) { fail(Msg); });253 254  if (Args.hasArg(OPT_help)) {255    std::string Usage(argv[0]);256    Usage += " [options] library-file [library-file...]";257    Tbl.printHelp(outs(), Usage.c_str(),258                  "LLVM TargetLibraryInfo versus SDK checker");259    outs() << "\nPass @FILE as argument to read options or library names from "260              "FILE.\n";261    return 0;262  }263 264  StringRef TripleStr = Args.getLastArgValue(OPT_triple_EQ);265  Triple TargetTriple(TripleStr);266  TargetLibraryInfoImpl TLII(TargetTriple);267  TargetLibraryInfo TLI(TLII);268 269  reportNumberOfEntries(TLI, TripleStr);270 271  // --dump-tli doesn't require any input files.272  if (Args.hasArg(OPT_dump_tli)) {273    dumpTLIEntries(TLI);274    return 0;275  }276 277  std::vector<std::string> LibList = Args.getAllArgValues(OPT_INPUT);278  if (LibList.empty())279    fail("no input files\n");280  StringRef LibDir = Args.getLastArgValue(OPT_libdir_EQ);281  bool SeparateMode = Args.hasArg(OPT_separate);282 283  ReportKind ReportLevel =284      SeparateMode ? ReportKind::Summary : ReportKind::Discrepancy;285  if (const opt::Arg *A = Args.getLastArg(OPT_report_EQ)) {286    ReportLevel = StringSwitch<ReportKind>(A->getValue())287                      .Case("summary", ReportKind::Summary)288                      .Case("discrepancy", ReportKind::Discrepancy)289                      .Case("full", ReportKind::Full)290                      .Default(ReportKind::Error);291    if (ReportLevel == ReportKind::Error)292      fail(Twine("invalid option for --report: ", StringRef(A->getValue())));293  }294 295  for (size_t I = 0; I < LibList.size(); ++I) {296    // In SeparateMode we report on input libraries individually; otherwise297    // we do one big combined search.  Reading to the end of LibList here298    // will cause the outer while loop to terminate cleanly.299    if (SeparateMode) {300      SDKNames.clear();301      SDKNames.populateFromFile(LibDir, LibList[I]);302      if (SDKNames.empty())303        continue;304    } else {305      do306        SDKNames.populateFromFile(LibDir, LibList[I]);307      while (++I < LibList.size());308      if (SDKNames.empty()) {309        WithColor::error() << "NO symbols found!\n";310        break;311      }312      outs() << "Found a grand total of " << SDKNames.size()313             << " library symbols\n";314    }315    unsigned TLIdoesSDKdoesnt = 0;316    unsigned TLIdoesntSDKdoes = 0;317    unsigned TLIandSDKboth = 0;318    unsigned TLIandSDKneither = 0;319 320    for (unsigned FI = LibFunc::Begin_LibFunc; FI != LibFunc::End_LibFunc;321         ++FI) {322      LibFunc LF = static_cast<LibFunc>(FI);323 324      StringRef TLIName = TLI.getStandardName(LF);325      bool TLIHas = TLI.has(LF);326      bool SDKHas = SDKNames.count(TLIName) == 1;327      int Which = int(TLIHas) * 2 + int(SDKHas);328      switch (Which) {329      case 0: ++TLIandSDKneither; break;330      case 1: ++TLIdoesntSDKdoes; break;331      case 2: ++TLIdoesSDKdoesnt; break;332      case 3: ++TLIandSDKboth;    break;333      }334      // If the results match, report only if user requested a full report.335      ReportKind Threshold =336          TLIHas == SDKHas ? ReportKind::Full : ReportKind::Discrepancy;337      if (Threshold <= ReportLevel) {338        constexpr char YesNo[2][4] = {"no ", "yes"};339        constexpr char Indicator[4][3] = {"!!", ">>", "<<", "=="};340        outs() << Indicator[Which] << " TLI " << YesNo[TLIHas] << " SDK "341               << YesNo[SDKHas] << ": ";342        printPrintableName(outs(), TLIName);343        outs() << '\n';344      }345    }346 347    assert(TLIandSDKboth + TLIandSDKneither + TLIdoesSDKdoesnt +348               TLIdoesntSDKdoes ==349           LibFunc::End_LibFunc - LibFunc::Begin_LibFunc);350    (void) TLIandSDKneither;351    outs() << "<< Total TLI yes SDK no:  " << TLIdoesSDKdoesnt352           << "\n>> Total TLI no  SDK yes: " << TLIdoesntSDKdoes353           << "\n== Total TLI yes SDK yes: " << TLIandSDKboth;354    if (TLIandSDKboth == 0) {355      outs() << " *** NO TLI SYMBOLS FOUND";356      if (SeparateMode)357        outs() << " in '" << LibList[I] << "'";358    }359    outs() << '\n';360 361    if (!SeparateMode) {362      if (TLIdoesSDKdoesnt == 0 && TLIdoesntSDKdoes == 0)363        outs() << "PASS: LLVM TLI matched SDK libraries successfully.\n";364      else365        outs() << "FAIL: LLVM TLI doesn't match SDK libraries.\n";366    }367  }368}369