1349cc55cSDimitry Andric //===-- llvm-tli-checker.cpp - Compare TargetLibraryInfo to SDK libraries -===// 2349cc55cSDimitry Andric // 3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6349cc55cSDimitry Andric // 7349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 8349cc55cSDimitry Andric 9349cc55cSDimitry Andric #include "llvm/ADT/SmallString.h" 10349cc55cSDimitry Andric #include "llvm/ADT/StringMap.h" 11349cc55cSDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h" 12349cc55cSDimitry Andric #include "llvm/Config/llvm-config.h" 13349cc55cSDimitry Andric #include "llvm/Demangle/Demangle.h" 14349cc55cSDimitry Andric #include "llvm/Object/Archive.h" 15349cc55cSDimitry Andric #include "llvm/Object/ELFObjectFile.h" 16349cc55cSDimitry Andric #include "llvm/Option/ArgList.h" 17349cc55cSDimitry Andric #include "llvm/Option/Option.h" 18349cc55cSDimitry Andric #include "llvm/Support/FileSystem.h" 19349cc55cSDimitry Andric #include "llvm/Support/InitLLVM.h" 20349cc55cSDimitry Andric #include "llvm/Support/Path.h" 21349cc55cSDimitry Andric #include "llvm/Support/WithColor.h" 22*06c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h" 23349cc55cSDimitry Andric 24349cc55cSDimitry Andric using namespace llvm; 25349cc55cSDimitry Andric using namespace llvm::object; 26349cc55cSDimitry Andric 27349cc55cSDimitry Andric // Command-line option boilerplate. 28349cc55cSDimitry Andric namespace { 29349cc55cSDimitry Andric enum ID { 30349cc55cSDimitry Andric OPT_INVALID = 0, // This is not an option ID. 31349cc55cSDimitry Andric #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 32349cc55cSDimitry Andric HELPTEXT, METAVAR, VALUES) \ 33349cc55cSDimitry Andric OPT_##ID, 34349cc55cSDimitry Andric #include "Opts.inc" 35349cc55cSDimitry Andric #undef OPTION 36349cc55cSDimitry Andric }; 37349cc55cSDimitry Andric 38bdd1243dSDimitry Andric #define PREFIX(NAME, VALUE) \ 39bdd1243dSDimitry Andric static constexpr StringLiteral NAME##_init[] = VALUE; \ 40bdd1243dSDimitry Andric static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ 41bdd1243dSDimitry Andric std::size(NAME##_init) - 1); 42349cc55cSDimitry Andric #include "Opts.inc" 43349cc55cSDimitry Andric #undef PREFIX 44349cc55cSDimitry Andric 45bdd1243dSDimitry Andric static constexpr opt::OptTable::Info InfoTable[] = { 46349cc55cSDimitry Andric #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 47349cc55cSDimitry Andric HELPTEXT, METAVAR, VALUES) \ 48349cc55cSDimitry Andric { \ 49349cc55cSDimitry Andric PREFIX, NAME, HELPTEXT, \ 50349cc55cSDimitry Andric METAVAR, OPT_##ID, opt::Option::KIND##Class, \ 51349cc55cSDimitry Andric PARAM, FLAGS, OPT_##GROUP, \ 52349cc55cSDimitry Andric OPT_##ALIAS, ALIASARGS, VALUES}, 53349cc55cSDimitry Andric #include "Opts.inc" 54349cc55cSDimitry Andric #undef OPTION 55349cc55cSDimitry Andric }; 56349cc55cSDimitry Andric 57bdd1243dSDimitry Andric class TLICheckerOptTable : public opt::GenericOptTable { 58349cc55cSDimitry Andric public: 59bdd1243dSDimitry Andric TLICheckerOptTable() : GenericOptTable(InfoTable) {} 60349cc55cSDimitry Andric }; 614824e7fdSDimitry Andric } // end anonymous namespace 62349cc55cSDimitry Andric 63349cc55cSDimitry Andric // We have three levels of reporting. 64349cc55cSDimitry Andric enum class ReportKind { 65349cc55cSDimitry Andric Error, // For argument parsing errors. 66349cc55cSDimitry Andric Summary, // Report counts but not details. 67349cc55cSDimitry Andric Discrepancy, // Report where TLI and the library differ. 68349cc55cSDimitry Andric Full // Report for every known-to-TLI function. 69349cc55cSDimitry Andric }; 70349cc55cSDimitry Andric 71349cc55cSDimitry Andric // Most of the ObjectFile interfaces return an Expected<T>, so make it easy 724824e7fdSDimitry Andric // to ignore errors. 734824e7fdSDimitry Andric template <typename T> 744824e7fdSDimitry Andric static T unwrapIgnoreError(Expected<T> E, T Default = T()) { 75349cc55cSDimitry Andric if (E) 76349cc55cSDimitry Andric return std::move(*E); 77349cc55cSDimitry Andric // Sink the error and return a nothing value. 78349cc55cSDimitry Andric consumeError(E.takeError()); 794824e7fdSDimitry Andric return Default; 80349cc55cSDimitry Andric } 81349cc55cSDimitry Andric 82349cc55cSDimitry Andric static void fail(const Twine &Message) { 83349cc55cSDimitry Andric WithColor::error() << Message << '\n'; 84349cc55cSDimitry Andric exit(EXIT_FAILURE); 85349cc55cSDimitry Andric } 86349cc55cSDimitry Andric 87349cc55cSDimitry Andric // Some problem occurred with an archive member; complain and continue. 88349cc55cSDimitry Andric static void reportArchiveChildIssue(const object::Archive::Child &C, int Index, 89349cc55cSDimitry Andric StringRef ArchiveFilename) { 90349cc55cSDimitry Andric // First get the member name. 91349cc55cSDimitry Andric std::string ChildName; 92349cc55cSDimitry Andric Expected<StringRef> NameOrErr = C.getName(); 93349cc55cSDimitry Andric if (NameOrErr) 94349cc55cSDimitry Andric ChildName = std::string(NameOrErr.get()); 95349cc55cSDimitry Andric else { 96349cc55cSDimitry Andric // Ignore the name-fetch error, just report the index. 97349cc55cSDimitry Andric consumeError(NameOrErr.takeError()); 98349cc55cSDimitry Andric ChildName = "<file index: " + std::to_string(Index) + ">"; 99349cc55cSDimitry Andric } 100349cc55cSDimitry Andric 101349cc55cSDimitry Andric WithColor::warning() << ArchiveFilename << "(" << ChildName 102349cc55cSDimitry Andric << "): member is not usable\n"; 103349cc55cSDimitry Andric } 104349cc55cSDimitry Andric 105349cc55cSDimitry Andric // Return Name, and if Name is mangled, append "aka" and the demangled name. 1064824e7fdSDimitry Andric static std::string getPrintableName(StringRef Name) { 107349cc55cSDimitry Andric std::string OutputName = "'"; 108349cc55cSDimitry Andric OutputName += Name; 109349cc55cSDimitry Andric OutputName += "'"; 110*06c3fb27SDimitry Andric std::string DemangledName(demangle(Name)); 1114824e7fdSDimitry Andric if (Name != DemangledName) { 112349cc55cSDimitry Andric OutputName += " aka "; 1134824e7fdSDimitry Andric OutputName += DemangledName; 114349cc55cSDimitry Andric } 115349cc55cSDimitry Andric return OutputName; 116349cc55cSDimitry Andric } 117349cc55cSDimitry Andric 118349cc55cSDimitry Andric // Store all the names that TargetLibraryInfo knows about; the bool indicates 119349cc55cSDimitry Andric // whether TLI has it marked as "available" for the target of interest. 120349cc55cSDimitry Andric // This is a vector to preserve the sorted order for better reporting. 121349cc55cSDimitry Andric struct TLINameList : std::vector<std::pair<StringRef, bool>> { 122349cc55cSDimitry Andric // Record all the TLI info in the vector. 123349cc55cSDimitry Andric void initialize(StringRef TargetTriple); 124349cc55cSDimitry Andric // Print out what we found. 125349cc55cSDimitry Andric void dump(); 126349cc55cSDimitry Andric }; 1274824e7fdSDimitry Andric static TLINameList TLINames; 128349cc55cSDimitry Andric 129349cc55cSDimitry Andric void TLINameList::initialize(StringRef TargetTriple) { 130349cc55cSDimitry Andric Triple T(TargetTriple); 131349cc55cSDimitry Andric TargetLibraryInfoImpl TLII(T); 132349cc55cSDimitry Andric TargetLibraryInfo TLI(TLII); 133349cc55cSDimitry Andric 134349cc55cSDimitry Andric reserve(LibFunc::NumLibFuncs); 135349cc55cSDimitry Andric size_t NumAvailable = 0; 136349cc55cSDimitry Andric for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) { 137349cc55cSDimitry Andric LibFunc LF = (LibFunc)FI; 138349cc55cSDimitry Andric bool Available = TLI.has(LF); 139349cc55cSDimitry Andric // getName returns names only for available funcs. 140349cc55cSDimitry Andric TLII.setAvailable(LF); 141349cc55cSDimitry Andric emplace_back(TLI.getName(LF), Available); 142349cc55cSDimitry Andric if (Available) 143349cc55cSDimitry Andric ++NumAvailable; 144349cc55cSDimitry Andric } 145349cc55cSDimitry Andric outs() << "TLI knows " << LibFunc::NumLibFuncs << " symbols, " << NumAvailable 146349cc55cSDimitry Andric << " available for '" << TargetTriple << "'\n"; 147349cc55cSDimitry Andric } 148349cc55cSDimitry Andric 149349cc55cSDimitry Andric void TLINameList::dump() { 150349cc55cSDimitry Andric // Assume this gets called after initialize(), so we have the above line of 151349cc55cSDimitry Andric // output as a header. So, for example, no need to repeat the triple. 152349cc55cSDimitry Andric for (auto &TLIName : TLINames) { 153349cc55cSDimitry Andric outs() << (TLIName.second ? " " : "not ") 1544824e7fdSDimitry Andric << "available: " << getPrintableName(TLIName.first) << '\n'; 155349cc55cSDimitry Andric } 156349cc55cSDimitry Andric } 157349cc55cSDimitry Andric 158349cc55cSDimitry Andric // Store all the exported symbol names we found in the input libraries. 159349cc55cSDimitry Andric // We use a map to get hashed lookup speed; the bool is meaningless. 160349cc55cSDimitry Andric class SDKNameMap : public StringMap<bool> { 161bdd1243dSDimitry Andric void maybeInsertSymbol(const SymbolRef &S, const ObjectFile &O); 162349cc55cSDimitry Andric void populateFromObject(ObjectFile *O); 163349cc55cSDimitry Andric void populateFromArchive(Archive *A); 164349cc55cSDimitry Andric 165349cc55cSDimitry Andric public: 166349cc55cSDimitry Andric void populateFromFile(StringRef LibDir, StringRef LibName); 167349cc55cSDimitry Andric }; 1684824e7fdSDimitry Andric static SDKNameMap SDKNames; 169349cc55cSDimitry Andric 170bdd1243dSDimitry Andric // Insert defined global function symbols into the map if valid. 171bdd1243dSDimitry Andric void SDKNameMap::maybeInsertSymbol(const SymbolRef &S, const ObjectFile &O) { 172bdd1243dSDimitry Andric SymbolRef::Type Type = unwrapIgnoreError(S.getType()); 173bdd1243dSDimitry Andric uint32_t Flags = unwrapIgnoreError(S.getFlags()); 174bdd1243dSDimitry Andric section_iterator Section = unwrapIgnoreError(S.getSection(), 175bdd1243dSDimitry Andric /*Default=*/O.section_end()); 176bdd1243dSDimitry Andric if (Type == SymbolRef::ST_Function && (Flags & SymbolRef::SF_Global) && 177bdd1243dSDimitry Andric Section != O.section_end()) { 178bdd1243dSDimitry Andric StringRef Name = unwrapIgnoreError(S.getName()); 179bdd1243dSDimitry Andric insert({ Name, true }); 180bdd1243dSDimitry Andric } 181bdd1243dSDimitry Andric } 182bdd1243dSDimitry Andric 183349cc55cSDimitry Andric // Given an ObjectFile, extract the global function symbols. 184349cc55cSDimitry Andric void SDKNameMap::populateFromObject(ObjectFile *O) { 1854824e7fdSDimitry Andric // FIXME: Support other formats. 186349cc55cSDimitry Andric if (!O->isELF()) { 1874824e7fdSDimitry Andric WithColor::warning() << O->getFileName() 1884824e7fdSDimitry Andric << ": only ELF-format files are supported\n"; 189349cc55cSDimitry Andric return; 190349cc55cSDimitry Andric } 1914824e7fdSDimitry Andric const auto *ELF = cast<ELFObjectFileBase>(O); 192349cc55cSDimitry Andric 193bdd1243dSDimitry Andric if (ELF->getEType() == ELF::ET_REL) { 194bdd1243dSDimitry Andric for (const auto &S : ELF->symbols()) 195bdd1243dSDimitry Andric maybeInsertSymbol(S, *O); 196bdd1243dSDimitry Andric } else { 197bdd1243dSDimitry Andric for (const auto &S : ELF->getDynamicSymbolIterators()) 198bdd1243dSDimitry Andric maybeInsertSymbol(S, *O); 199349cc55cSDimitry Andric } 200349cc55cSDimitry Andric } 201349cc55cSDimitry Andric 202349cc55cSDimitry Andric // Unpack an archive and populate from the component object files. 203349cc55cSDimitry Andric // This roughly imitates dumpArchive() from llvm-objdump.cpp. 204349cc55cSDimitry Andric void SDKNameMap::populateFromArchive(Archive *A) { 205349cc55cSDimitry Andric Error Err = Error::success(); 206349cc55cSDimitry Andric int Index = -1; 207bdd1243dSDimitry Andric for (const auto &C : A->children(Err)) { 208349cc55cSDimitry Andric ++Index; 209349cc55cSDimitry Andric Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary(); 210349cc55cSDimitry Andric if (!ChildOrErr) { 211349cc55cSDimitry Andric if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) { 212349cc55cSDimitry Andric // Issue a generic warning. 213349cc55cSDimitry Andric consumeError(std::move(E)); 214349cc55cSDimitry Andric reportArchiveChildIssue(C, Index, A->getFileName()); 215349cc55cSDimitry Andric } 216349cc55cSDimitry Andric continue; 217349cc55cSDimitry Andric } 218349cc55cSDimitry Andric if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get())) 219349cc55cSDimitry Andric populateFromObject(O); 220349cc55cSDimitry Andric // Ignore non-object archive members. 221349cc55cSDimitry Andric } 222349cc55cSDimitry Andric if (Err) 223349cc55cSDimitry Andric WithColor::defaultErrorHandler(std::move(Err)); 224349cc55cSDimitry Andric } 225349cc55cSDimitry Andric 226349cc55cSDimitry Andric // Unpack a library file and extract the global function names. 227349cc55cSDimitry Andric void SDKNameMap::populateFromFile(StringRef LibDir, StringRef LibName) { 228349cc55cSDimitry Andric // Pick an arbitrary but reasonable default size. 229349cc55cSDimitry Andric SmallString<255> Filepath(LibDir); 230349cc55cSDimitry Andric sys::path::append(Filepath, LibName); 231349cc55cSDimitry Andric if (!sys::fs::exists(Filepath)) { 2324824e7fdSDimitry Andric WithColor::warning() << StringRef(Filepath) << ": not found\n"; 233349cc55cSDimitry Andric return; 234349cc55cSDimitry Andric } 235349cc55cSDimitry Andric outs() << "\nLooking for symbols in '" << StringRef(Filepath) << "'\n"; 236349cc55cSDimitry Andric auto ExpectedBinary = createBinary(Filepath); 237349cc55cSDimitry Andric if (!ExpectedBinary) { 238349cc55cSDimitry Andric // FIXME: Report this better. 239349cc55cSDimitry Andric WithColor::defaultWarningHandler(ExpectedBinary.takeError()); 240349cc55cSDimitry Andric return; 241349cc55cSDimitry Andric } 242349cc55cSDimitry Andric OwningBinary<Binary> OBinary = std::move(*ExpectedBinary); 243349cc55cSDimitry Andric Binary &Binary = *OBinary.getBinary(); 244349cc55cSDimitry Andric size_t Precount = size(); 245349cc55cSDimitry Andric if (Archive *A = dyn_cast<Archive>(&Binary)) 246349cc55cSDimitry Andric populateFromArchive(A); 247349cc55cSDimitry Andric else if (ObjectFile *O = dyn_cast<ObjectFile>(&Binary)) 248349cc55cSDimitry Andric populateFromObject(O); 249349cc55cSDimitry Andric else { 2504824e7fdSDimitry Andric WithColor::warning() << StringRef(Filepath) 2514824e7fdSDimitry Andric << ": not an archive or object file\n"; 252349cc55cSDimitry Andric return; 253349cc55cSDimitry Andric } 254349cc55cSDimitry Andric if (Precount == size()) 2554824e7fdSDimitry Andric WithColor::warning() << StringRef(Filepath) << ": no symbols found\n"; 256349cc55cSDimitry Andric else 257349cc55cSDimitry Andric outs() << "Found " << size() - Precount << " global function symbols in '" 258349cc55cSDimitry Andric << StringRef(Filepath) << "'\n"; 259349cc55cSDimitry Andric } 260349cc55cSDimitry Andric 261349cc55cSDimitry Andric int main(int argc, char *argv[]) { 262349cc55cSDimitry Andric InitLLVM X(argc, argv); 263349cc55cSDimitry Andric BumpPtrAllocator A; 264349cc55cSDimitry Andric StringSaver Saver(A); 265349cc55cSDimitry Andric TLICheckerOptTable Tbl; 266349cc55cSDimitry Andric opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, 267349cc55cSDimitry Andric [&](StringRef Msg) { fail(Msg); }); 268349cc55cSDimitry Andric 269349cc55cSDimitry Andric if (Args.hasArg(OPT_help)) { 270349cc55cSDimitry Andric std::string Usage(argv[0]); 271349cc55cSDimitry Andric Usage += " [options] library-file [library-file...]"; 272349cc55cSDimitry Andric Tbl.printHelp(outs(), Usage.c_str(), 273349cc55cSDimitry Andric "LLVM TargetLibraryInfo versus SDK checker"); 274349cc55cSDimitry Andric outs() << "\nPass @FILE as argument to read options or library names from " 275349cc55cSDimitry Andric "FILE.\n"; 276349cc55cSDimitry Andric return 0; 277349cc55cSDimitry Andric } 278349cc55cSDimitry Andric 279349cc55cSDimitry Andric TLINames.initialize(Args.getLastArgValue(OPT_triple_EQ)); 280349cc55cSDimitry Andric 281349cc55cSDimitry Andric // --dump-tli doesn't require any input files. 282349cc55cSDimitry Andric if (Args.hasArg(OPT_dump_tli)) { 283349cc55cSDimitry Andric TLINames.dump(); 284349cc55cSDimitry Andric return 0; 285349cc55cSDimitry Andric } 286349cc55cSDimitry Andric 287349cc55cSDimitry Andric std::vector<std::string> LibList = Args.getAllArgValues(OPT_INPUT); 2884824e7fdSDimitry Andric if (LibList.empty()) 2894824e7fdSDimitry Andric fail("no input files\n"); 290349cc55cSDimitry Andric StringRef LibDir = Args.getLastArgValue(OPT_libdir_EQ); 291349cc55cSDimitry Andric bool SeparateMode = Args.hasArg(OPT_separate); 292349cc55cSDimitry Andric 293349cc55cSDimitry Andric ReportKind ReportLevel = 294349cc55cSDimitry Andric SeparateMode ? ReportKind::Summary : ReportKind::Discrepancy; 295349cc55cSDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_report_EQ)) { 296349cc55cSDimitry Andric ReportLevel = StringSwitch<ReportKind>(A->getValue()) 297349cc55cSDimitry Andric .Case("summary", ReportKind::Summary) 298349cc55cSDimitry Andric .Case("discrepancy", ReportKind::Discrepancy) 299349cc55cSDimitry Andric .Case("full", ReportKind::Full) 300349cc55cSDimitry Andric .Default(ReportKind::Error); 3014824e7fdSDimitry Andric if (ReportLevel == ReportKind::Error) 3024824e7fdSDimitry Andric fail(Twine("invalid option for --report: ", StringRef(A->getValue()))); 303349cc55cSDimitry Andric } 304349cc55cSDimitry Andric 305349cc55cSDimitry Andric for (size_t I = 0; I < LibList.size(); ++I) { 306349cc55cSDimitry Andric // In SeparateMode we report on input libraries individually; otherwise 307349cc55cSDimitry Andric // we do one big combined search. Reading to the end of LibList here 308349cc55cSDimitry Andric // will cause the outer while loop to terminate cleanly. 309349cc55cSDimitry Andric if (SeparateMode) { 310349cc55cSDimitry Andric SDKNames.clear(); 311349cc55cSDimitry Andric SDKNames.populateFromFile(LibDir, LibList[I]); 312349cc55cSDimitry Andric if (SDKNames.empty()) 313349cc55cSDimitry Andric continue; 314349cc55cSDimitry Andric } else { 315349cc55cSDimitry Andric do 316349cc55cSDimitry Andric SDKNames.populateFromFile(LibDir, LibList[I]); 317349cc55cSDimitry Andric while (++I < LibList.size()); 318349cc55cSDimitry Andric if (SDKNames.empty()) { 319349cc55cSDimitry Andric WithColor::error() << "NO symbols found!\n"; 320349cc55cSDimitry Andric break; 321349cc55cSDimitry Andric } 322349cc55cSDimitry Andric outs() << "Found a grand total of " << SDKNames.size() 323349cc55cSDimitry Andric << " library symbols\n"; 324349cc55cSDimitry Andric } 325349cc55cSDimitry Andric unsigned TLIdoesSDKdoesnt = 0; 326349cc55cSDimitry Andric unsigned TLIdoesntSDKdoes = 0; 327349cc55cSDimitry Andric unsigned TLIandSDKboth = 0; 328349cc55cSDimitry Andric unsigned TLIandSDKneither = 0; 329349cc55cSDimitry Andric for (auto &TLIName : TLINames) { 330349cc55cSDimitry Andric bool TLIHas = TLIName.second; 331349cc55cSDimitry Andric bool SDKHas = SDKNames.count(TLIName.first) == 1; 332349cc55cSDimitry Andric int Which = int(TLIHas) * 2 + int(SDKHas); 333349cc55cSDimitry Andric switch (Which) { 334349cc55cSDimitry Andric case 0: ++TLIandSDKneither; break; 335349cc55cSDimitry Andric case 1: ++TLIdoesntSDKdoes; break; 336349cc55cSDimitry Andric case 2: ++TLIdoesSDKdoesnt; break; 337349cc55cSDimitry Andric case 3: ++TLIandSDKboth; break; 338349cc55cSDimitry Andric } 339349cc55cSDimitry Andric // If the results match, report only if user requested a full report. 340349cc55cSDimitry Andric ReportKind Threshold = 341349cc55cSDimitry Andric TLIHas == SDKHas ? ReportKind::Full : ReportKind::Discrepancy; 342349cc55cSDimitry Andric if (Threshold <= ReportLevel) { 343349cc55cSDimitry Andric constexpr char YesNo[2][4] = {"no ", "yes"}; 344349cc55cSDimitry Andric constexpr char Indicator[4][3] = {"!!", ">>", "<<", "=="}; 345349cc55cSDimitry Andric outs() << Indicator[Which] << " TLI " << YesNo[TLIHas] << " SDK " 3464824e7fdSDimitry Andric << YesNo[SDKHas] << ": " << getPrintableName(TLIName.first) 3474824e7fdSDimitry Andric << '\n'; 348349cc55cSDimitry Andric } 349349cc55cSDimitry Andric } 350349cc55cSDimitry Andric 351349cc55cSDimitry Andric assert(TLIandSDKboth + TLIandSDKneither + TLIdoesSDKdoesnt + 352349cc55cSDimitry Andric TLIdoesntSDKdoes == 353349cc55cSDimitry Andric LibFunc::NumLibFuncs); 35481ad6265SDimitry Andric (void) TLIandSDKneither; 355349cc55cSDimitry Andric outs() << "<< Total TLI yes SDK no: " << TLIdoesSDKdoesnt 356349cc55cSDimitry Andric << "\n>> Total TLI no SDK yes: " << TLIdoesntSDKdoes 357349cc55cSDimitry Andric << "\n== Total TLI yes SDK yes: " << TLIandSDKboth; 358349cc55cSDimitry Andric if (TLIandSDKboth == 0) { 359349cc55cSDimitry Andric outs() << " *** NO TLI SYMBOLS FOUND"; 360349cc55cSDimitry Andric if (SeparateMode) 361349cc55cSDimitry Andric outs() << " in '" << LibList[I] << "'"; 362349cc55cSDimitry Andric } 363349cc55cSDimitry Andric outs() << '\n'; 364349cc55cSDimitry Andric 365349cc55cSDimitry Andric if (!SeparateMode) { 366349cc55cSDimitry Andric if (TLIdoesSDKdoesnt == 0 && TLIdoesntSDKdoes == 0) 367349cc55cSDimitry Andric outs() << "PASS: LLVM TLI matched SDK libraries successfully.\n"; 368349cc55cSDimitry Andric else 369349cc55cSDimitry Andric outs() << "FAIL: LLVM TLI doesn't match SDK libraries.\n"; 370349cc55cSDimitry Andric } 371349cc55cSDimitry Andric } 372349cc55cSDimitry Andric } 373