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