[llvm] 62dd488 - Add llvm-tli-checker

Paul Robinson via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 8 15:02:30 PST 2021


Author: Paul Robinson
Date: 2021-11-08T14:59:13-08:00
New Revision: 62dd488164f5b68cce1ac3825f857b0108476c3c

URL: https://github.com/llvm/llvm-project/commit/62dd488164f5b68cce1ac3825f857b0108476c3c
DIFF: https://github.com/llvm/llvm-project/commit/62dd488164f5b68cce1ac3825f857b0108476c3c.diff

LOG: Add llvm-tli-checker

A new tool that compares TargetLibraryInfo's opinion of the availability
of library function calls against the functions actually exported by a
specified set of libraries. Can be helpful in verifying the correctness
of TLI for a given target, and avoid mishaps such as had to be addressed
in D107509 and 94b4598d.

The tool currently supports ELF object files only, although it's unlikely
to be hard to add support for other formats.

Differential Revision: https://reviews.llvm.org/D111358

Added: 
    llvm/docs/CommandGuide/llvm-tli-checker.rst
    llvm/test/tools/llvm-tli-checker/ps4-tli-check.s
    llvm/tools/llvm-tli-checker/CMakeLists.txt
    llvm/tools/llvm-tli-checker/Opts.td
    llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp

Modified: 
    llvm/docs/CommandGuide/index.rst

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/index.rst b/llvm/docs/CommandGuide/index.rst
index 4f5c912bb4111..ee69cd83074f1 100644
--- a/llvm/docs/CommandGuide/index.rst
+++ b/llvm/docs/CommandGuide/index.rst
@@ -82,3 +82,4 @@ Developer Tools
    llvm-locstats
    llvm-pdbutil
    llvm-profgen
+   llvm-tli-checker

diff  --git a/llvm/docs/CommandGuide/llvm-tli-checker.rst b/llvm/docs/CommandGuide/llvm-tli-checker.rst
new file mode 100644
index 0000000000000..bbd2bf29f46b9
--- /dev/null
+++ b/llvm/docs/CommandGuide/llvm-tli-checker.rst
@@ -0,0 +1,85 @@
+llvm-tli-checker - TargetLibraryInfo vs library checker
+=======================================================
+
+.. program:: llvm-tli-checker
+
+SYNOPSIS
+--------
+
+:program:`llvm-tli-checker` [*options*] [*library-file...*]
+
+DESCRIPTION
+-----------
+
+:program:`llvm-tli-checker` compares TargetLibraryInfo's opinion of the
+availability of library functions against the set of functions exported
+by the specified library files, reporting any disagreements between TLI's
+opinion and whether the function is actually present. This is primarily
+useful for vendors to ensure the TLI for their target is correct, and
+the compiler will not "optimize" some code sequence into a library call
+that is not actually available.
+
+EXAMPLE
+-------
+
+.. code-block:: console
+
+  $ llvm-tli-checker --triple x86_64-scei-ps4 example.so
+  TLI knows 466 symbols, 235 available for 'x86_64-scei-ps4'
+
+  Looking for symbols in 'example.so'
+  Found 235 global function symbols in 'example.so'
+  Found a grand total of 235 library symbols
+  << TLI yes SDK no:  '_ZdaPv' aka operator delete[](void*)
+  >> TLI no  SDK yes: '_ZdaPvj' aka operator delete[](void*, unsigned int)
+  << Total TLI yes SDK no:  1
+  >> Total TLI no  SDK yes: 1
+  == Total TLI yes SDK yes: 234
+  FAIL: LLVM TLI doesn't match SDK libraries.
+
+OPTIONS
+-------
+
+.. option:: --dump-tli
+
+  Print "available"/"not available" for each library function, according to
+  TargetLibraryInfo's information for the specified triple, and exit. This
+  option does not read any input files.
+
+.. option:: --help, -h
+
+  Print a summary of command line options and exit.
+
+.. option:: --libdir=<directory>
+
+  A base directory to prepend to each library file path. This is handy
+  when there are a number of library files all in the same directory, or
+  a list of input filenames are kept in a response file.
+
+.. option:: --report=<level>
+
+  The amount of information to report.  <level> can be summary, discrepancy,
+  or full. A summary report gives only the count of matching and mis-matching
+  symbols; discrepancy lists the mis-matching symbols; and full lists all
+  symbols known to TLI, matching or mis-matching. The default is discrepancy.
+
+.. option:: --separate
+
+  Read and report a summary for each library file separately.  This can be
+  useful to identify library files that don't contribute anything that TLI
+  knows about. Implies --report=summary (can be overridden).
+
+.. option:: --triple=<triple>
+
+  The triple to use for initializing TargetLibraryInfo.
+
+.. option:: @<FILE>
+
+ Read command-line options and/or library names from response file `<FILE>`.
+
+EXIT STATUS
+-----------
+
+:program:`llvm-tli-checker` returns 0 even if there are mismatches. It returns a
+non-zero exit code if there is an unrecognized option, or no input files are
+provided.

diff  --git a/llvm/test/tools/llvm-tli-checker/ps4-tli-check.s b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.s
new file mode 100644
index 0000000000000..e17ceb104a28f
--- /dev/null
+++ b/llvm/test/tools/llvm-tli-checker/ps4-tli-check.s
@@ -0,0 +1,288 @@
+# REQUIRES: x86-registered-target
+#
+# RUN: llvm-mc --triple=x86_64-scei-ps4 --filetype=obj %s -o %t.o
+# RUN: ld.lld --shared %t.o -o %t.so
+# RUN: llvm-tli-checker --triple=x86_64-scei-ps4 %t.so | FileCheck %s
+#
+# RUN: llvm-mc --triple=x86_64-scei-ps4 --defsym WRONG=1 --filetype=obj %s -o %t2.o
+# RUN: ld.lld --shared %t2.o -o %t2.so
+# RUN: echo %t2.so > %t2.txt
+# RUN: llvm-tli-checker --triple x86_64-scei-ps4 @%t2.txt | \
+# RUN:     FileCheck %s --check-prefix=WRONG_SUMMARY --check-prefix=WRONG_DETAIL \
+# RUN:     --implicit-check-not="==" --implicit-check-not="<<" --implicit-check-not=">>"
+# RUN: llvm-tli-checker --triple x86_64-scei-ps4 @%t2.txt --report=summary | \
+# RUN:     FileCheck %s --check-prefix=WRONG_SUMMARY \
+# RUN:     --implicit-check-not="==" --implicit-check-not="<<" --implicit-check-not=">>"
+## --separate implies --report=summary.
+# RUN: llvm-tli-checker --triple x86_64-scei-ps4 @%t2.txt --separate | \
+# RUN:     FileCheck %s --check-prefix=WRONG_SUMMARY \
+# RUN:     --implicit-check-not="==" --implicit-check-not="<<" --implicit-check-not=">>"
+#
+# RUN: llvm-tli-checker --triple x86_64-scei-ps4 --dump-tli > %t3.txt
+# RUN: FileCheck %s --check-prefix=AVAIL --input-file %t3.txt
+# RUN: FileCheck %s --check-prefix=UNAVAIL --input-file %t3.txt
+#
+# CHECK: << Total TLI yes SDK no:  0
+# CHECK: >> Total TLI no  SDK yes: 0
+# CHECK: == Total TLI yes SDK yes: 235
+#
+# WRONG_DETAIL: << TLI yes SDK no : '_ZdaPv'
+# WRONG_DETAIL: >> TLI no  SDK yes: '_ZdaPvj'
+# WRONG_SUMMARY: << Total TLI yes SDK no:  1{{$}}
+# WRONG_SUMMARY: >> Total TLI no  SDK yes: 1{{$}}
+# WRONG_SUMMARY: == Total TLI yes SDK yes: 234
+#
+## The -COUNT suffix doesn't care if there are too many matches, so check
+## the exact count first; the two directives should add up to that.
+# AVAIL: TLI knows 466 symbols, 235 available
+# AVAIL-COUNT-235: {{^}} available
+# UNAVAIL-COUNT-231: not available
+
+.macro defname name
+.globl \name
+.type  \name , at function
+\name : nop
+.endm
+
+.text
+# For the WRONG case, omit _ZdaPv and include _ZdaPvj.
+.ifdef WRONG
+defname _ZdaPvj
+.else
+defname _ZdaPv
+.endif
+defname _ZdaPvRKSt9nothrow_t
+defname _ZdaPvSt11align_val_t
+defname _ZdaPvSt11align_val_tRKSt9nothrow_t
+defname _ZdaPvm
+defname _ZdaPvmSt11align_val_t
+defname _ZdlPv
+defname _ZdlPvRKSt9nothrow_t
+defname _ZdlPvSt11align_val_t
+defname _ZdlPvSt11align_val_tRKSt9nothrow_t
+defname _ZdlPvm
+defname _ZdlPvmSt11align_val_t
+defname _Znam
+defname _ZnamRKSt9nothrow_t
+defname _ZnamSt11align_val_t
+defname _ZnamSt11align_val_tRKSt9nothrow_t
+defname _Znwm
+defname _ZnwmRKSt9nothrow_t
+defname _ZnwmSt11align_val_t
+defname _ZnwmSt11align_val_tRKSt9nothrow_t
+defname __cxa_atexit
+defname __cxa_guard_abort
+defname __cxa_guard_acquire
+defname __cxa_guard_release
+defname abs
+defname acos
+defname acosf
+defname acosh
+defname acoshf
+defname acoshl
+defname acosl
+defname aligned_alloc
+defname asin
+defname asinf
+defname asinh
+defname asinhf
+defname asinhl
+defname asinl
+defname atan
+defname atan2
+defname atan2f
+defname atan2l
+defname atanf
+defname atanh
+defname atanhf
+defname atanhl
+defname atanl
+defname atof
+defname atoi
+defname atol
+defname atoll
+defname calloc
+defname cbrt
+defname cbrtf
+defname cbrtl
+defname ceil
+defname ceilf
+defname ceill
+defname clearerr
+defname copysign
+defname copysignf
+defname copysignl
+defname cos
+defname cosf
+defname cosh
+defname coshf
+defname coshl
+defname cosl
+defname exp
+defname exp2
+defname exp2f
+defname exp2l
+defname expf
+defname expl
+defname expm1
+defname expm1f
+defname expm1l
+defname fabs
+defname fabsf
+defname fabsl
+defname fclose
+defname fdopen
+defname feof
+defname ferror
+defname fflush
+defname fgetc
+defname fgetpos
+defname fgets
+defname fileno
+defname floor
+defname floorf
+defname floorl
+defname fmax
+defname fmaxf
+defname fmaxl
+defname fmin
+defname fminf
+defname fminl
+defname fmod
+defname fmodf
+defname fmodl
+defname fopen
+defname fprintf
+defname fputc
+defname fputs
+defname fread
+defname free
+defname frexp
+defname frexpf
+defname frexpl
+defname fscanf
+defname fseek
+defname fsetpos
+defname ftell
+defname fwrite
+defname getc
+defname getchar
+defname gets
+defname isdigit
+defname labs
+defname ldexp
+defname ldexpf
+defname ldexpl
+defname llabs
+defname log
+defname log10
+defname log10f
+defname log10l
+defname log1p
+defname log1pf
+defname log1pl
+defname log2
+defname log2f
+defname log2l
+defname logb
+defname logbf
+defname logbl
+defname logf
+defname logl
+defname malloc
+defname memalign
+defname memchr
+defname memcmp
+defname memcpy
+defname memmove
+defname memset
+defname mktime
+defname modf
+defname modff
+defname modfl
+defname nearbyint
+defname nearbyintf
+defname nearbyintl
+defname perror
+defname posix_memalign
+defname pow
+defname powf
+defname powl
+defname printf
+defname putc
+defname putchar
+defname puts
+defname qsort
+defname realloc
+defname remainder
+defname remainderf
+defname remainderl
+defname remove
+defname rewind
+defname rint
+defname rintf
+defname rintl
+defname round
+defname roundf
+defname roundl
+defname scanf
+defname setbuf
+defname setvbuf
+defname sin
+defname sinf
+defname sinh
+defname sinhf
+defname sinhl
+defname sinl
+defname snprintf
+defname sprintf
+defname sqrt
+defname sqrtf
+defname sqrtl
+defname sscanf
+defname strcasecmp
+defname strcat
+defname strchr
+defname strcmp
+defname strcoll
+defname strcpy
+defname strcspn
+defname strdup
+defname strlen
+defname strncasecmp
+defname strncat
+defname strncmp
+defname strncpy
+defname strpbrk
+defname strrchr
+defname strspn
+defname strstr
+defname strtod
+defname strtof
+defname strtok
+defname strtok_r
+defname strtol
+defname strtold
+defname strtoll
+defname strtoul
+defname strtoull
+defname strxfrm
+defname tan
+defname tanf
+defname tanh
+defname tanhf
+defname tanhl
+defname tanl
+defname trunc
+defname truncf
+defname truncl
+defname ungetc
+defname vfprintf
+defname vfscanf
+defname vprintf
+defname vscanf
+defname vsnprintf
+defname vsprintf
+defname vsscanf
+defname wcslen
+

diff  --git a/llvm/tools/llvm-tli-checker/CMakeLists.txt b/llvm/tools/llvm-tli-checker/CMakeLists.txt
new file mode 100644
index 0000000000000..9be44e4d895e6
--- /dev/null
+++ b/llvm/tools/llvm-tli-checker/CMakeLists.txt
@@ -0,0 +1,26 @@
+set(LLVM_LINK_COMPONENTS
+  Analysis
+  BinaryFormat
+  BitReader
+  BitstreamReader
+  Core
+  Demangle
+  MC
+  MCParser
+  Object
+  Option
+  Remarks
+  Support
+  TextAPI
+  )
+
+set(LLVM_TARGET_DEFINITIONS Opts.td)
+tablegen(LLVM Opts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(TLICheckerOptsTableGen)
+
+add_llvm_tool(llvm-tli-checker
+  llvm-tli-checker.cpp
+
+  DEPENDS
+  TLICheckerOptsTableGen
+  )

diff  --git a/llvm/tools/llvm-tli-checker/Opts.td b/llvm/tools/llvm-tli-checker/Opts.td
new file mode 100644
index 0000000000000..b1acef4093c45
--- /dev/null
+++ b/llvm/tools/llvm-tli-checker/Opts.td
@@ -0,0 +1,16 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name, string help> : Flag<["--"], name>, HelpText<help>;
+multiclass Eq<string name, string metavar, string help> {
+  def NAME #_EQ : Joined<["--"], name #"=">,
+                  HelpText<help>, MetaVarName<metavar>;
+  def : Separate<["--"], name>, Alias<!cast<Joined>(NAME #_EQ)>;
+}
+
+def help : F<"help", "Display available options">;
+def : Flag<["-"], "h">, HelpText<"Alias for --help">, Alias<help>;
+def dump_tli : F<"dump-tli", "Dump TLI's list of functions and whether they are available">;
+defm triple : Eq<"triple", "<triple>", "Target triple">;
+defm libdir : Eq<"libdir", "<directory>", "Root directory for finding library files">;
+def separate : F<"separate", "Report on each library file separately">;
+def report_EQ : Joined<["--"], "report=">, HelpText<"Level of detail to report">, Values<"summary,discrepancy,full">;

diff  --git a/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp b/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp
new file mode 100644
index 0000000000000..bf25efc0b0bdf
--- /dev/null
+++ b/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp
@@ -0,0 +1,357 @@
+//===-- llvm-tli-checker.cpp - Compare TargetLibraryInfo to SDK libraries -===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+// Command-line option boilerplate.
+namespace {
+enum ID {
+  OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  OPT_##ID,
+#include "Opts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Opts.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  {                                                                            \
+      PREFIX,      NAME,      HELPTEXT,                                        \
+      METAVAR,     OPT_##ID,  opt::Option::KIND##Class,                        \
+      PARAM,       FLAGS,     OPT_##GROUP,                                     \
+      OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Opts.inc"
+#undef OPTION
+};
+
+class TLICheckerOptTable : public opt::OptTable {
+public:
+  TLICheckerOptTable() : OptTable(InfoTable) {}
+};
+} // namespace
+
+// We have three levels of reporting.
+enum class ReportKind {
+  Error,       // For argument parsing errors.
+  Summary,     // Report counts but not details.
+  Discrepancy, // Report where TLI and the library 
diff er.
+  Full         // Report for every known-to-TLI function.
+};
+
+// Most of the ObjectFile interfaces return an Expected<T>, so make it easy
+// to ignore those.
+template <typename T> T unwrapIgnoreError(Expected<T> E) {
+  if (E)
+    return std::move(*E);
+  // Sink the error and return a nothing value.
+  consumeError(E.takeError());
+  return T();
+}
+
+static void fail(const Twine &Message) {
+  WithColor::error() << Message << '\n';
+  exit(EXIT_FAILURE);
+}
+
+// Some problem occurred with an archive member; complain and continue.
+static void reportArchiveChildIssue(const object::Archive::Child &C, int Index,
+                                    StringRef ArchiveFilename) {
+  // First get the member name.
+  std::string ChildName;
+  Expected<StringRef> NameOrErr = C.getName();
+  if (NameOrErr)
+    ChildName = std::string(NameOrErr.get());
+  else {
+    // Ignore the name-fetch error, just report the index.
+    consumeError(NameOrErr.takeError());
+    ChildName = "<file index: " + std::to_string(Index) + ">";
+  }
+
+  WithColor::warning() << ArchiveFilename << "(" << ChildName
+                       << "): member is not usable\n";
+}
+
+// Return Name, and if Name is mangled, append "aka" and the demangled name.
+static std::string PrintableName(StringRef Name) {
+  std::string OutputName = "'";
+  OutputName += Name;
+  OutputName += "'";
+  if (Name.startswith("_Z") || Name.startswith("??")) {
+    OutputName += " aka ";
+    OutputName += demangle(Name.str());
+  }
+  return OutputName;
+}
+
+// Store all the names that TargetLibraryInfo knows about; the bool indicates
+// whether TLI has it marked as "available" for the target of interest.
+// This is a vector to preserve the sorted order for better reporting.
+struct TLINameList : std::vector<std::pair<StringRef, bool>> {
+  // Record all the TLI info in the vector.
+  void initialize(StringRef TargetTriple);
+  // Print out what we found.
+  void dump();
+};
+TLINameList TLINames;
+
+void TLINameList::initialize(StringRef TargetTriple) {
+  Triple T(TargetTriple);
+  TargetLibraryInfoImpl TLII(T);
+  TargetLibraryInfo TLI(TLII);
+
+  reserve(LibFunc::NumLibFuncs);
+  size_t NumAvailable = 0;
+  for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) {
+    LibFunc LF = (LibFunc)FI;
+    bool Available = TLI.has(LF);
+    // getName returns names only for available funcs.
+    TLII.setAvailable(LF);
+    emplace_back(TLI.getName(LF), Available);
+    if (Available)
+      ++NumAvailable;
+  }
+  outs() << "TLI knows " << LibFunc::NumLibFuncs << " symbols, " << NumAvailable
+         << " available for '" << TargetTriple << "'\n";
+}
+
+void TLINameList::dump() {
+  // Assume this gets called after initialize(), so we have the above line of
+  // output as a header.  So, for example, no need to repeat the triple.
+  for (auto &TLIName : TLINames) {
+    outs() << (TLIName.second ? "    " : "not ")
+           << "available: " << PrintableName(TLIName.first) << '\n';
+  }
+}
+
+// Store all the exported symbol names we found in the input libraries.
+// We use a map to get hashed lookup speed; the bool is meaningless.
+class SDKNameMap : public StringMap<bool> {
+  void populateFromObject(ObjectFile *O);
+  void populateFromArchive(Archive *A);
+
+public:
+  void populateFromFile(StringRef LibDir, StringRef LibName);
+};
+SDKNameMap SDKNames;
+
+// Given an ObjectFile, extract the global function symbols.
+void SDKNameMap::populateFromObject(ObjectFile *O) {
+  // FIXME: Support COFF.
+  if (!O->isELF()) {
+    WithColor::warning() << "Only ELF-format files are supported\n";
+    return;
+  }
+  auto *ELF = cast<const ELFObjectFileBase>(O);
+
+  for (auto I = ELF->getDynamicSymbolIterators().begin();
+       I != ELF->getDynamicSymbolIterators().end(); ++I) {
+    // We want only global function symbols.
+    SymbolRef::Type Type = unwrapIgnoreError(I->getType());
+    uint32_t Flags = unwrapIgnoreError(I->getFlags());
+    StringRef Name = unwrapIgnoreError(I->getName());
+    if (Type == SymbolRef::ST_Function && (Flags & SymbolRef::SF_Global))
+      insert({Name, true});
+  }
+}
+
+// Unpack an archive and populate from the component object files.
+// This roughly imitates dumpArchive() from llvm-objdump.cpp.
+void SDKNameMap::populateFromArchive(Archive *A) {
+  Error Err = Error::success();
+  int Index = -1;
+  for (auto &C : A->children(Err)) {
+    ++Index;
+    Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();
+    if (!ChildOrErr) {
+      if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) {
+        // Issue a generic warning.
+        consumeError(std::move(E));
+        reportArchiveChildIssue(C, Index, A->getFileName());
+      }
+      continue;
+    }
+    if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
+      populateFromObject(O);
+    // Ignore non-object archive members.
+  }
+  if (Err)
+    WithColor::defaultErrorHandler(std::move(Err));
+}
+
+// Unpack a library file and extract the global function names.
+void SDKNameMap::populateFromFile(StringRef LibDir, StringRef LibName) {
+  // Pick an arbitrary but reasonable default size.
+  SmallString<255> Filepath(LibDir);
+  sys::path::append(Filepath, LibName);
+  if (!sys::fs::exists(Filepath)) {
+    WithColor::warning() << "Could not find '" << StringRef(Filepath) << "'\n";
+    return;
+  }
+  outs() << "\nLooking for symbols in '" << StringRef(Filepath) << "'\n";
+  auto ExpectedBinary = createBinary(Filepath);
+  if (!ExpectedBinary) {
+    // FIXME: Report this better.
+    WithColor::defaultWarningHandler(ExpectedBinary.takeError());
+    return;
+  }
+  OwningBinary<Binary> OBinary = std::move(*ExpectedBinary);
+  Binary &Binary = *OBinary.getBinary();
+  size_t Precount = size();
+  if (Archive *A = dyn_cast<Archive>(&Binary))
+    populateFromArchive(A);
+  else if (ObjectFile *O = dyn_cast<ObjectFile>(&Binary))
+    populateFromObject(O);
+  else {
+    WithColor::warning() << "Not an Archive or ObjectFile: '"
+                         << StringRef(Filepath) << "'\n";
+    return;
+  }
+  if (Precount == size())
+    WithColor::warning() << "No symbols found in '" << StringRef(Filepath)
+                         << "'\n";
+  else
+    outs() << "Found " << size() - Precount << " global function symbols in '"
+           << StringRef(Filepath) << "'\n";
+}
+
+int main(int argc, char *argv[]) {
+  InitLLVM X(argc, argv);
+  BumpPtrAllocator A;
+  StringSaver Saver(A);
+  TLICheckerOptTable Tbl;
+  opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,
+                                         [&](StringRef Msg) { fail(Msg); });
+
+  if (Args.hasArg(OPT_help)) {
+    std::string Usage(argv[0]);
+    Usage += " [options] library-file [library-file...]";
+    Tbl.printHelp(outs(), Usage.c_str(),
+                  "LLVM TargetLibraryInfo versus SDK checker");
+    outs() << "\nPass @FILE as argument to read options or library names from "
+              "FILE.\n";
+    return 0;
+  }
+
+  TLINames.initialize(Args.getLastArgValue(OPT_triple_EQ));
+
+  // --dump-tli doesn't require any input files.
+  if (Args.hasArg(OPT_dump_tli)) {
+    TLINames.dump();
+    return 0;
+  }
+
+  std::vector<std::string> LibList = Args.getAllArgValues(OPT_INPUT);
+  if (LibList.empty()) {
+    WithColor::error() << "No input files\n";
+    exit(EXIT_FAILURE);
+  }
+  StringRef LibDir = Args.getLastArgValue(OPT_libdir_EQ);
+  bool SeparateMode = Args.hasArg(OPT_separate);
+
+  ReportKind ReportLevel =
+      SeparateMode ? ReportKind::Summary : ReportKind::Discrepancy;
+  if (const opt::Arg *A = Args.getLastArg(OPT_report_EQ)) {
+    ReportLevel = StringSwitch<ReportKind>(A->getValue())
+                      .Case("summary", ReportKind::Summary)
+                      .Case("discrepancy", ReportKind::Discrepancy)
+                      .Case("full", ReportKind::Full)
+                      .Default(ReportKind::Error);
+    if (ReportLevel == ReportKind::Error) {
+      WithColor::error() << "invalid option for --report: " << A->getValue();
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  for (size_t I = 0; I < LibList.size(); ++I) {
+    // In SeparateMode we report on input libraries individually; otherwise
+    // we do one big combined search.  Reading to the end of LibList here
+    // will cause the outer while loop to terminate cleanly.
+    if (SeparateMode) {
+      SDKNames.clear();
+      SDKNames.populateFromFile(LibDir, LibList[I]);
+      if (SDKNames.empty())
+        continue;
+    } else {
+      do
+        SDKNames.populateFromFile(LibDir, LibList[I]);
+      while (++I < LibList.size());
+      if (SDKNames.empty()) {
+        WithColor::error() << "NO symbols found!\n";
+        break;
+      }
+      outs() << "Found a grand total of " << SDKNames.size()
+             << " library symbols\n";
+    }
+    unsigned TLIdoesSDKdoesnt = 0;
+    unsigned TLIdoesntSDKdoes = 0;
+    unsigned TLIandSDKboth = 0;
+    unsigned TLIandSDKneither = 0;
+    for (auto &TLIName : TLINames) {
+      bool TLIHas = TLIName.second;
+      bool SDKHas = SDKNames.count(TLIName.first) == 1;
+      int Which = int(TLIHas) * 2 + int(SDKHas);
+      switch (Which) {
+      case 0: ++TLIandSDKneither; break;
+      case 1: ++TLIdoesntSDKdoes; break;
+      case 2: ++TLIdoesSDKdoesnt; break;
+      case 3: ++TLIandSDKboth;    break;
+      }
+      // If the results match, report only if user requested a full report.
+      ReportKind Threshold =
+          TLIHas == SDKHas ? ReportKind::Full : ReportKind::Discrepancy;
+      if (Threshold <= ReportLevel) {
+        constexpr char YesNo[2][4] = {"no ", "yes"};
+        constexpr char Indicator[4][3] = {"!!", ">>", "<<", "=="};
+        outs() << Indicator[Which] << " TLI " << YesNo[TLIHas] << " SDK "
+               << YesNo[SDKHas] << ": " << PrintableName(TLIName.first) << '\n';
+      }
+    }
+
+    assert(TLIandSDKboth + TLIandSDKneither + TLIdoesSDKdoesnt +
+               TLIdoesntSDKdoes ==
+           LibFunc::NumLibFuncs);
+    outs() << "<< Total TLI yes SDK no:  " << TLIdoesSDKdoesnt
+           << "\n>> Total TLI no  SDK yes: " << TLIdoesntSDKdoes
+           << "\n== Total TLI yes SDK yes: " << TLIandSDKboth;
+    if (TLIandSDKboth == 0) {
+      outs() << " *** NO TLI SYMBOLS FOUND";
+      if (SeparateMode)
+        outs() << " in '" << LibList[I] << "'";
+    }
+    outs() << '\n';
+
+    if (!SeparateMode) {
+      if (TLIdoesSDKdoesnt == 0 && TLIdoesntSDKdoes == 0)
+        outs() << "PASS: LLVM TLI matched SDK libraries successfully.\n";
+      else
+        outs() << "FAIL: LLVM TLI doesn't match SDK libraries.\n";
+    }
+  }
+}


        


More information about the llvm-commits mailing list