Path: blob/main/contrib/llvm-project/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp
35231 views
//===-- llvm-tli-checker.cpp - Compare TargetLibraryInfo to SDK libraries -===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/ADT/SmallString.h"9#include "llvm/ADT/StringMap.h"10#include "llvm/Analysis/TargetLibraryInfo.h"11#include "llvm/Config/llvm-config.h"12#include "llvm/Demangle/Demangle.h"13#include "llvm/Object/Archive.h"14#include "llvm/Object/ELFObjectFile.h"15#include "llvm/Option/ArgList.h"16#include "llvm/Option/Option.h"17#include "llvm/Support/FileSystem.h"18#include "llvm/Support/InitLLVM.h"19#include "llvm/Support/Path.h"20#include "llvm/Support/WithColor.h"21#include "llvm/TargetParser/Triple.h"2223using namespace llvm;24using namespace llvm::object;2526// Command-line option boilerplate.27namespace {28enum ID {29OPT_INVALID = 0, // This is not an option ID.30#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),31#include "Opts.inc"32#undef OPTION33};3435#define PREFIX(NAME, VALUE) \36static constexpr StringLiteral NAME##_init[] = VALUE; \37static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \38std::size(NAME##_init) - 1);39#include "Opts.inc"40#undef PREFIX4142using namespace llvm::opt;43static constexpr opt::OptTable::Info InfoTable[] = {44#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),45#include "Opts.inc"46#undef OPTION47};4849class TLICheckerOptTable : public opt::GenericOptTable {50public:51TLICheckerOptTable() : GenericOptTable(InfoTable) {}52};53} // end anonymous namespace5455// We have three levels of reporting.56enum class ReportKind {57Error, // For argument parsing errors.58Summary, // Report counts but not details.59Discrepancy, // Report where TLI and the library differ.60Full // Report for every known-to-TLI function.61};6263// Most of the ObjectFile interfaces return an Expected<T>, so make it easy64// to ignore errors.65template <typename T>66static T unwrapIgnoreError(Expected<T> E, T Default = T()) {67if (E)68return std::move(*E);69// Sink the error and return a nothing value.70consumeError(E.takeError());71return Default;72}7374static void fail(const Twine &Message) {75WithColor::error() << Message << '\n';76exit(EXIT_FAILURE);77}7879// Some problem occurred with an archive member; complain and continue.80static void reportArchiveChildIssue(const object::Archive::Child &C, int Index,81StringRef ArchiveFilename) {82// First get the member name.83std::string ChildName;84Expected<StringRef> NameOrErr = C.getName();85if (NameOrErr)86ChildName = std::string(NameOrErr.get());87else {88// Ignore the name-fetch error, just report the index.89consumeError(NameOrErr.takeError());90ChildName = "<file index: " + std::to_string(Index) + ">";91}9293WithColor::warning() << ArchiveFilename << "(" << ChildName94<< "): member is not usable\n";95}9697// Return Name, and if Name is mangled, append "aka" and the demangled name.98static std::string getPrintableName(StringRef Name) {99std::string OutputName = "'";100OutputName += Name;101OutputName += "'";102std::string DemangledName(demangle(Name));103if (Name != DemangledName) {104OutputName += " aka ";105OutputName += DemangledName;106}107return OutputName;108}109110// Store all the names that TargetLibraryInfo knows about; the bool indicates111// whether TLI has it marked as "available" for the target of interest.112// This is a vector to preserve the sorted order for better reporting.113struct TLINameList : std::vector<std::pair<StringRef, bool>> {114// Record all the TLI info in the vector.115void initialize(StringRef TargetTriple);116// Print out what we found.117void dump();118};119static TLINameList TLINames;120121void TLINameList::initialize(StringRef TargetTriple) {122Triple T(TargetTriple);123TargetLibraryInfoImpl TLII(T);124TargetLibraryInfo TLI(TLII);125126reserve(LibFunc::NumLibFuncs);127size_t NumAvailable = 0;128for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) {129LibFunc LF = (LibFunc)FI;130bool Available = TLI.has(LF);131// getName returns names only for available funcs.132TLII.setAvailable(LF);133emplace_back(TLI.getName(LF), Available);134if (Available)135++NumAvailable;136}137outs() << "TLI knows " << LibFunc::NumLibFuncs << " symbols, " << NumAvailable138<< " available for '" << TargetTriple << "'\n";139}140141void TLINameList::dump() {142// Assume this gets called after initialize(), so we have the above line of143// output as a header. So, for example, no need to repeat the triple.144for (auto &TLIName : TLINames) {145outs() << (TLIName.second ? " " : "not ")146<< "available: " << getPrintableName(TLIName.first) << '\n';147}148}149150// Store all the exported symbol names we found in the input libraries.151// We use a map to get hashed lookup speed; the bool is meaningless.152class SDKNameMap : public StringMap<bool> {153void maybeInsertSymbol(const SymbolRef &S, const ObjectFile &O);154void populateFromObject(ObjectFile *O);155void populateFromArchive(Archive *A);156157public:158void populateFromFile(StringRef LibDir, StringRef LibName);159};160static SDKNameMap SDKNames;161162// Insert defined global function symbols into the map if valid.163void SDKNameMap::maybeInsertSymbol(const SymbolRef &S, const ObjectFile &O) {164SymbolRef::Type Type = unwrapIgnoreError(S.getType());165uint32_t Flags = unwrapIgnoreError(S.getFlags());166section_iterator Section = unwrapIgnoreError(S.getSection(),167/*Default=*/O.section_end());168if (Type == SymbolRef::ST_Function && (Flags & SymbolRef::SF_Global) &&169Section != O.section_end()) {170StringRef Name = unwrapIgnoreError(S.getName());171insert({ Name, true });172}173}174175// Given an ObjectFile, extract the global function symbols.176void SDKNameMap::populateFromObject(ObjectFile *O) {177// FIXME: Support other formats.178if (!O->isELF()) {179WithColor::warning() << O->getFileName()180<< ": only ELF-format files are supported\n";181return;182}183const auto *ELF = cast<ELFObjectFileBase>(O);184185if (ELF->getEType() == ELF::ET_REL) {186for (const auto &S : ELF->symbols())187maybeInsertSymbol(S, *O);188} else {189for (const auto &S : ELF->getDynamicSymbolIterators())190maybeInsertSymbol(S, *O);191}192}193194// Unpack an archive and populate from the component object files.195// This roughly imitates dumpArchive() from llvm-objdump.cpp.196void SDKNameMap::populateFromArchive(Archive *A) {197Error Err = Error::success();198int Index = -1;199for (const auto &C : A->children(Err)) {200++Index;201Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();202if (!ChildOrErr) {203if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) {204// Issue a generic warning.205consumeError(std::move(E));206reportArchiveChildIssue(C, Index, A->getFileName());207}208continue;209}210if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get()))211populateFromObject(O);212// Ignore non-object archive members.213}214if (Err)215WithColor::defaultErrorHandler(std::move(Err));216}217218// Unpack a library file and extract the global function names.219void SDKNameMap::populateFromFile(StringRef LibDir, StringRef LibName) {220// Pick an arbitrary but reasonable default size.221SmallString<255> Filepath(LibDir);222sys::path::append(Filepath, LibName);223if (!sys::fs::exists(Filepath)) {224WithColor::warning() << StringRef(Filepath) << ": not found\n";225return;226}227outs() << "\nLooking for symbols in '" << StringRef(Filepath) << "'\n";228auto ExpectedBinary = createBinary(Filepath);229if (!ExpectedBinary) {230// FIXME: Report this better.231WithColor::defaultWarningHandler(ExpectedBinary.takeError());232return;233}234OwningBinary<Binary> OBinary = std::move(*ExpectedBinary);235Binary &Binary = *OBinary.getBinary();236size_t Precount = size();237if (Archive *A = dyn_cast<Archive>(&Binary))238populateFromArchive(A);239else if (ObjectFile *O = dyn_cast<ObjectFile>(&Binary))240populateFromObject(O);241else {242WithColor::warning() << StringRef(Filepath)243<< ": not an archive or object file\n";244return;245}246if (Precount == size())247WithColor::warning() << StringRef(Filepath) << ": no symbols found\n";248else249outs() << "Found " << size() - Precount << " global function symbols in '"250<< StringRef(Filepath) << "'\n";251}252253int main(int argc, char *argv[]) {254InitLLVM X(argc, argv);255BumpPtrAllocator A;256StringSaver Saver(A);257TLICheckerOptTable Tbl;258opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver,259[&](StringRef Msg) { fail(Msg); });260261if (Args.hasArg(OPT_help)) {262std::string Usage(argv[0]);263Usage += " [options] library-file [library-file...]";264Tbl.printHelp(outs(), Usage.c_str(),265"LLVM TargetLibraryInfo versus SDK checker");266outs() << "\nPass @FILE as argument to read options or library names from "267"FILE.\n";268return 0;269}270271TLINames.initialize(Args.getLastArgValue(OPT_triple_EQ));272273// --dump-tli doesn't require any input files.274if (Args.hasArg(OPT_dump_tli)) {275TLINames.dump();276return 0;277}278279std::vector<std::string> LibList = Args.getAllArgValues(OPT_INPUT);280if (LibList.empty())281fail("no input files\n");282StringRef LibDir = Args.getLastArgValue(OPT_libdir_EQ);283bool SeparateMode = Args.hasArg(OPT_separate);284285ReportKind ReportLevel =286SeparateMode ? ReportKind::Summary : ReportKind::Discrepancy;287if (const opt::Arg *A = Args.getLastArg(OPT_report_EQ)) {288ReportLevel = StringSwitch<ReportKind>(A->getValue())289.Case("summary", ReportKind::Summary)290.Case("discrepancy", ReportKind::Discrepancy)291.Case("full", ReportKind::Full)292.Default(ReportKind::Error);293if (ReportLevel == ReportKind::Error)294fail(Twine("invalid option for --report: ", StringRef(A->getValue())));295}296297for (size_t I = 0; I < LibList.size(); ++I) {298// In SeparateMode we report on input libraries individually; otherwise299// we do one big combined search. Reading to the end of LibList here300// will cause the outer while loop to terminate cleanly.301if (SeparateMode) {302SDKNames.clear();303SDKNames.populateFromFile(LibDir, LibList[I]);304if (SDKNames.empty())305continue;306} else {307do308SDKNames.populateFromFile(LibDir, LibList[I]);309while (++I < LibList.size());310if (SDKNames.empty()) {311WithColor::error() << "NO symbols found!\n";312break;313}314outs() << "Found a grand total of " << SDKNames.size()315<< " library symbols\n";316}317unsigned TLIdoesSDKdoesnt = 0;318unsigned TLIdoesntSDKdoes = 0;319unsigned TLIandSDKboth = 0;320unsigned TLIandSDKneither = 0;321for (auto &TLIName : TLINames) {322bool TLIHas = TLIName.second;323bool SDKHas = SDKNames.count(TLIName.first) == 1;324int Which = int(TLIHas) * 2 + int(SDKHas);325switch (Which) {326case 0: ++TLIandSDKneither; break;327case 1: ++TLIdoesntSDKdoes; break;328case 2: ++TLIdoesSDKdoesnt; break;329case 3: ++TLIandSDKboth; break;330}331// If the results match, report only if user requested a full report.332ReportKind Threshold =333TLIHas == SDKHas ? ReportKind::Full : ReportKind::Discrepancy;334if (Threshold <= ReportLevel) {335constexpr char YesNo[2][4] = {"no ", "yes"};336constexpr char Indicator[4][3] = {"!!", ">>", "<<", "=="};337outs() << Indicator[Which] << " TLI " << YesNo[TLIHas] << " SDK "338<< YesNo[SDKHas] << ": " << getPrintableName(TLIName.first)339<< '\n';340}341}342343assert(TLIandSDKboth + TLIandSDKneither + TLIdoesSDKdoesnt +344TLIdoesntSDKdoes ==345LibFunc::NumLibFuncs);346(void) TLIandSDKneither;347outs() << "<< Total TLI yes SDK no: " << TLIdoesSDKdoesnt348<< "\n>> Total TLI no SDK yes: " << TLIdoesntSDKdoes349<< "\n== Total TLI yes SDK yes: " << TLIandSDKboth;350if (TLIandSDKboth == 0) {351outs() << " *** NO TLI SYMBOLS FOUND";352if (SeparateMode)353outs() << " in '" << LibList[I] << "'";354}355outs() << '\n';356357if (!SeparateMode) {358if (TLIdoesSDKdoesnt == 0 && TLIdoesntSDKdoes == 0)359outs() << "PASS: LLVM TLI matched SDK libraries successfully.\n";360else361outs() << "FAIL: LLVM TLI doesn't match SDK libraries.\n";362}363}364}365366367