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/ADT/Triple.h" 12349cc55cSDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h" 13349cc55cSDimitry Andric #include "llvm/Config/llvm-config.h" 14349cc55cSDimitry Andric #include "llvm/Demangle/Demangle.h" 15349cc55cSDimitry Andric #include "llvm/Object/Archive.h" 16349cc55cSDimitry Andric #include "llvm/Object/ELFObjectFile.h" 17349cc55cSDimitry Andric #include "llvm/Option/ArgList.h" 18349cc55cSDimitry Andric #include "llvm/Option/Option.h" 19349cc55cSDimitry Andric #include "llvm/Support/FileSystem.h" 20349cc55cSDimitry Andric #include "llvm/Support/InitLLVM.h" 21349cc55cSDimitry Andric #include "llvm/Support/Path.h" 22349cc55cSDimitry Andric #include "llvm/Support/WithColor.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 38349cc55cSDimitry Andric #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; 39349cc55cSDimitry Andric #include "Opts.inc" 40349cc55cSDimitry Andric #undef PREFIX 41349cc55cSDimitry Andric 424824e7fdSDimitry Andric static const opt::OptTable::Info InfoTable[] = { 43349cc55cSDimitry Andric #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 44349cc55cSDimitry Andric HELPTEXT, METAVAR, VALUES) \ 45349cc55cSDimitry Andric { \ 46349cc55cSDimitry Andric PREFIX, NAME, HELPTEXT, \ 47349cc55cSDimitry Andric METAVAR, OPT_##ID, opt::Option::KIND##Class, \ 48349cc55cSDimitry Andric PARAM, FLAGS, OPT_##GROUP, \ 49349cc55cSDimitry Andric OPT_##ALIAS, ALIASARGS, VALUES}, 50349cc55cSDimitry Andric #include "Opts.inc" 51349cc55cSDimitry Andric #undef OPTION 52349cc55cSDimitry Andric }; 53349cc55cSDimitry Andric 54349cc55cSDimitry Andric class TLICheckerOptTable : public opt::OptTable { 55349cc55cSDimitry Andric public: 56349cc55cSDimitry Andric TLICheckerOptTable() : OptTable(InfoTable) {} 57349cc55cSDimitry Andric }; 584824e7fdSDimitry Andric } // end anonymous namespace 59349cc55cSDimitry Andric 60349cc55cSDimitry Andric // We have three levels of reporting. 61349cc55cSDimitry Andric enum class ReportKind { 62349cc55cSDimitry Andric Error, // For argument parsing errors. 63349cc55cSDimitry Andric Summary, // Report counts but not details. 64349cc55cSDimitry Andric Discrepancy, // Report where TLI and the library differ. 65349cc55cSDimitry Andric Full // Report for every known-to-TLI function. 66349cc55cSDimitry Andric }; 67349cc55cSDimitry Andric 68349cc55cSDimitry Andric // Most of the ObjectFile interfaces return an Expected<T>, so make it easy 694824e7fdSDimitry Andric // to ignore errors. 704824e7fdSDimitry Andric template <typename T> 714824e7fdSDimitry Andric static T unwrapIgnoreError(Expected<T> E, T Default = T()) { 72349cc55cSDimitry Andric if (E) 73349cc55cSDimitry Andric return std::move(*E); 74349cc55cSDimitry Andric // Sink the error and return a nothing value. 75349cc55cSDimitry Andric consumeError(E.takeError()); 764824e7fdSDimitry Andric return Default; 77349cc55cSDimitry Andric } 78349cc55cSDimitry Andric 79349cc55cSDimitry Andric static void fail(const Twine &Message) { 80349cc55cSDimitry Andric WithColor::error() << Message << '\n'; 81349cc55cSDimitry Andric exit(EXIT_FAILURE); 82349cc55cSDimitry Andric } 83349cc55cSDimitry Andric 84349cc55cSDimitry Andric // Some problem occurred with an archive member; complain and continue. 85349cc55cSDimitry Andric static void reportArchiveChildIssue(const object::Archive::Child &C, int Index, 86349cc55cSDimitry Andric StringRef ArchiveFilename) { 87349cc55cSDimitry Andric // First get the member name. 88349cc55cSDimitry Andric std::string ChildName; 89349cc55cSDimitry Andric Expected<StringRef> NameOrErr = C.getName(); 90349cc55cSDimitry Andric if (NameOrErr) 91349cc55cSDimitry Andric ChildName = std::string(NameOrErr.get()); 92349cc55cSDimitry Andric else { 93349cc55cSDimitry Andric // Ignore the name-fetch error, just report the index. 94349cc55cSDimitry Andric consumeError(NameOrErr.takeError()); 95349cc55cSDimitry Andric ChildName = "<file index: " + std::to_string(Index) + ">"; 96349cc55cSDimitry Andric } 97349cc55cSDimitry Andric 98349cc55cSDimitry Andric WithColor::warning() << ArchiveFilename << "(" << ChildName 99349cc55cSDimitry Andric << "): member is not usable\n"; 100349cc55cSDimitry Andric } 101349cc55cSDimitry Andric 102349cc55cSDimitry Andric // Return Name, and if Name is mangled, append "aka" and the demangled name. 1034824e7fdSDimitry Andric static std::string getPrintableName(StringRef Name) { 104349cc55cSDimitry Andric std::string OutputName = "'"; 105349cc55cSDimitry Andric OutputName += Name; 106349cc55cSDimitry Andric OutputName += "'"; 1074824e7fdSDimitry Andric std::string DemangledName(demangle(Name.str())); 1084824e7fdSDimitry Andric if (Name != DemangledName) { 109349cc55cSDimitry Andric OutputName += " aka "; 1104824e7fdSDimitry Andric OutputName += DemangledName; 111349cc55cSDimitry Andric } 112349cc55cSDimitry Andric return OutputName; 113349cc55cSDimitry Andric } 114349cc55cSDimitry Andric 115349cc55cSDimitry Andric // Store all the names that TargetLibraryInfo knows about; the bool indicates 116349cc55cSDimitry Andric // whether TLI has it marked as "available" for the target of interest. 117349cc55cSDimitry Andric // This is a vector to preserve the sorted order for better reporting. 118349cc55cSDimitry Andric struct TLINameList : std::vector<std::pair<StringRef, bool>> { 119349cc55cSDimitry Andric // Record all the TLI info in the vector. 120349cc55cSDimitry Andric void initialize(StringRef TargetTriple); 121349cc55cSDimitry Andric // Print out what we found. 122349cc55cSDimitry Andric void dump(); 123349cc55cSDimitry Andric }; 1244824e7fdSDimitry Andric static TLINameList TLINames; 125349cc55cSDimitry Andric 126349cc55cSDimitry Andric void TLINameList::initialize(StringRef TargetTriple) { 127349cc55cSDimitry Andric Triple T(TargetTriple); 128349cc55cSDimitry Andric TargetLibraryInfoImpl TLII(T); 129349cc55cSDimitry Andric TargetLibraryInfo TLI(TLII); 130349cc55cSDimitry Andric 131349cc55cSDimitry Andric reserve(LibFunc::NumLibFuncs); 132349cc55cSDimitry Andric size_t NumAvailable = 0; 133349cc55cSDimitry Andric for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) { 134349cc55cSDimitry Andric LibFunc LF = (LibFunc)FI; 135349cc55cSDimitry Andric bool Available = TLI.has(LF); 136349cc55cSDimitry Andric // getName returns names only for available funcs. 137349cc55cSDimitry Andric TLII.setAvailable(LF); 138349cc55cSDimitry Andric emplace_back(TLI.getName(LF), Available); 139349cc55cSDimitry Andric if (Available) 140349cc55cSDimitry Andric ++NumAvailable; 141349cc55cSDimitry Andric } 142349cc55cSDimitry Andric outs() << "TLI knows " << LibFunc::NumLibFuncs << " symbols, " << NumAvailable 143349cc55cSDimitry Andric << " available for '" << TargetTriple << "'\n"; 144349cc55cSDimitry Andric } 145349cc55cSDimitry Andric 146349cc55cSDimitry Andric void TLINameList::dump() { 147349cc55cSDimitry Andric // Assume this gets called after initialize(), so we have the above line of 148349cc55cSDimitry Andric // output as a header. So, for example, no need to repeat the triple. 149349cc55cSDimitry Andric for (auto &TLIName : TLINames) { 150349cc55cSDimitry Andric outs() << (TLIName.second ? " " : "not ") 1514824e7fdSDimitry Andric << "available: " << getPrintableName(TLIName.first) << '\n'; 152349cc55cSDimitry Andric } 153349cc55cSDimitry Andric } 154349cc55cSDimitry Andric 155349cc55cSDimitry Andric // Store all the exported symbol names we found in the input libraries. 156349cc55cSDimitry Andric // We use a map to get hashed lookup speed; the bool is meaningless. 157349cc55cSDimitry Andric class SDKNameMap : public StringMap<bool> { 158349cc55cSDimitry Andric void populateFromObject(ObjectFile *O); 159349cc55cSDimitry Andric void populateFromArchive(Archive *A); 160349cc55cSDimitry Andric 161349cc55cSDimitry Andric public: 162349cc55cSDimitry Andric void populateFromFile(StringRef LibDir, StringRef LibName); 163349cc55cSDimitry Andric }; 1644824e7fdSDimitry Andric static SDKNameMap SDKNames; 165349cc55cSDimitry Andric 166349cc55cSDimitry Andric // Given an ObjectFile, extract the global function symbols. 167349cc55cSDimitry Andric void SDKNameMap::populateFromObject(ObjectFile *O) { 1684824e7fdSDimitry Andric // FIXME: Support other formats. 169349cc55cSDimitry Andric if (!O->isELF()) { 1704824e7fdSDimitry Andric WithColor::warning() << O->getFileName() 1714824e7fdSDimitry Andric << ": only ELF-format files are supported\n"; 172349cc55cSDimitry Andric return; 173349cc55cSDimitry Andric } 1744824e7fdSDimitry Andric const auto *ELF = cast<ELFObjectFileBase>(O); 175349cc55cSDimitry Andric 1764824e7fdSDimitry Andric for (auto &S : ELF->getDynamicSymbolIterators()) { 1774824e7fdSDimitry Andric // We want only defined global function symbols. 1784824e7fdSDimitry Andric SymbolRef::Type Type = unwrapIgnoreError(S.getType()); 1794824e7fdSDimitry Andric uint32_t Flags = unwrapIgnoreError(S.getFlags()); 1804824e7fdSDimitry Andric section_iterator Section = unwrapIgnoreError(S.getSection(), 1814824e7fdSDimitry Andric /*Default=*/O->section_end()); 1824824e7fdSDimitry Andric StringRef Name = unwrapIgnoreError(S.getName()); 1834824e7fdSDimitry Andric if (Type == SymbolRef::ST_Function && (Flags & SymbolRef::SF_Global) && 1844824e7fdSDimitry Andric Section != O->section_end()) 185349cc55cSDimitry Andric insert({Name, true}); 186349cc55cSDimitry Andric } 187349cc55cSDimitry Andric } 188349cc55cSDimitry Andric 189349cc55cSDimitry Andric // Unpack an archive and populate from the component object files. 190349cc55cSDimitry Andric // This roughly imitates dumpArchive() from llvm-objdump.cpp. 191349cc55cSDimitry Andric void SDKNameMap::populateFromArchive(Archive *A) { 192349cc55cSDimitry Andric Error Err = Error::success(); 193349cc55cSDimitry Andric int Index = -1; 194349cc55cSDimitry Andric for (auto &C : A->children(Err)) { 195349cc55cSDimitry Andric ++Index; 196349cc55cSDimitry Andric Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary(); 197349cc55cSDimitry Andric if (!ChildOrErr) { 198349cc55cSDimitry Andric if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) { 199349cc55cSDimitry Andric // Issue a generic warning. 200349cc55cSDimitry Andric consumeError(std::move(E)); 201349cc55cSDimitry Andric reportArchiveChildIssue(C, Index, A->getFileName()); 202349cc55cSDimitry Andric } 203349cc55cSDimitry Andric continue; 204349cc55cSDimitry Andric } 205349cc55cSDimitry Andric if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get())) 206349cc55cSDimitry Andric populateFromObject(O); 207349cc55cSDimitry Andric // Ignore non-object archive members. 208349cc55cSDimitry Andric } 209349cc55cSDimitry Andric if (Err) 210349cc55cSDimitry Andric WithColor::defaultErrorHandler(std::move(Err)); 211349cc55cSDimitry Andric } 212349cc55cSDimitry Andric 213349cc55cSDimitry Andric // Unpack a library file and extract the global function names. 214349cc55cSDimitry Andric void SDKNameMap::populateFromFile(StringRef LibDir, StringRef LibName) { 215349cc55cSDimitry Andric // Pick an arbitrary but reasonable default size. 216349cc55cSDimitry Andric SmallString<255> Filepath(LibDir); 217349cc55cSDimitry Andric sys::path::append(Filepath, LibName); 218349cc55cSDimitry Andric if (!sys::fs::exists(Filepath)) { 2194824e7fdSDimitry Andric WithColor::warning() << StringRef(Filepath) << ": not found\n"; 220349cc55cSDimitry Andric return; 221349cc55cSDimitry Andric } 222349cc55cSDimitry Andric outs() << "\nLooking for symbols in '" << StringRef(Filepath) << "'\n"; 223349cc55cSDimitry Andric auto ExpectedBinary = createBinary(Filepath); 224349cc55cSDimitry Andric if (!ExpectedBinary) { 225349cc55cSDimitry Andric // FIXME: Report this better. 226349cc55cSDimitry Andric WithColor::defaultWarningHandler(ExpectedBinary.takeError()); 227349cc55cSDimitry Andric return; 228349cc55cSDimitry Andric } 229349cc55cSDimitry Andric OwningBinary<Binary> OBinary = std::move(*ExpectedBinary); 230349cc55cSDimitry Andric Binary &Binary = *OBinary.getBinary(); 231349cc55cSDimitry Andric size_t Precount = size(); 232349cc55cSDimitry Andric if (Archive *A = dyn_cast<Archive>(&Binary)) 233349cc55cSDimitry Andric populateFromArchive(A); 234349cc55cSDimitry Andric else if (ObjectFile *O = dyn_cast<ObjectFile>(&Binary)) 235349cc55cSDimitry Andric populateFromObject(O); 236349cc55cSDimitry Andric else { 2374824e7fdSDimitry Andric WithColor::warning() << StringRef(Filepath) 2384824e7fdSDimitry Andric << ": not an archive or object file\n"; 239349cc55cSDimitry Andric return; 240349cc55cSDimitry Andric } 241349cc55cSDimitry Andric if (Precount == size()) 2424824e7fdSDimitry Andric WithColor::warning() << StringRef(Filepath) << ": no symbols found\n"; 243349cc55cSDimitry Andric else 244349cc55cSDimitry Andric outs() << "Found " << size() - Precount << " global function symbols in '" 245349cc55cSDimitry Andric << StringRef(Filepath) << "'\n"; 246349cc55cSDimitry Andric } 247349cc55cSDimitry Andric 248349cc55cSDimitry Andric int main(int argc, char *argv[]) { 249349cc55cSDimitry Andric InitLLVM X(argc, argv); 250349cc55cSDimitry Andric BumpPtrAllocator A; 251349cc55cSDimitry Andric StringSaver Saver(A); 252349cc55cSDimitry Andric TLICheckerOptTable Tbl; 253349cc55cSDimitry Andric opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, 254349cc55cSDimitry Andric [&](StringRef Msg) { fail(Msg); }); 255349cc55cSDimitry Andric 256349cc55cSDimitry Andric if (Args.hasArg(OPT_help)) { 257349cc55cSDimitry Andric std::string Usage(argv[0]); 258349cc55cSDimitry Andric Usage += " [options] library-file [library-file...]"; 259349cc55cSDimitry Andric Tbl.printHelp(outs(), Usage.c_str(), 260349cc55cSDimitry Andric "LLVM TargetLibraryInfo versus SDK checker"); 261349cc55cSDimitry Andric outs() << "\nPass @FILE as argument to read options or library names from " 262349cc55cSDimitry Andric "FILE.\n"; 263349cc55cSDimitry Andric return 0; 264349cc55cSDimitry Andric } 265349cc55cSDimitry Andric 266349cc55cSDimitry Andric TLINames.initialize(Args.getLastArgValue(OPT_triple_EQ)); 267349cc55cSDimitry Andric 268349cc55cSDimitry Andric // --dump-tli doesn't require any input files. 269349cc55cSDimitry Andric if (Args.hasArg(OPT_dump_tli)) { 270349cc55cSDimitry Andric TLINames.dump(); 271349cc55cSDimitry Andric return 0; 272349cc55cSDimitry Andric } 273349cc55cSDimitry Andric 274349cc55cSDimitry Andric std::vector<std::string> LibList = Args.getAllArgValues(OPT_INPUT); 2754824e7fdSDimitry Andric if (LibList.empty()) 2764824e7fdSDimitry Andric fail("no input files\n"); 277349cc55cSDimitry Andric StringRef LibDir = Args.getLastArgValue(OPT_libdir_EQ); 278349cc55cSDimitry Andric bool SeparateMode = Args.hasArg(OPT_separate); 279349cc55cSDimitry Andric 280349cc55cSDimitry Andric ReportKind ReportLevel = 281349cc55cSDimitry Andric SeparateMode ? ReportKind::Summary : ReportKind::Discrepancy; 282349cc55cSDimitry Andric if (const opt::Arg *A = Args.getLastArg(OPT_report_EQ)) { 283349cc55cSDimitry Andric ReportLevel = StringSwitch<ReportKind>(A->getValue()) 284349cc55cSDimitry Andric .Case("summary", ReportKind::Summary) 285349cc55cSDimitry Andric .Case("discrepancy", ReportKind::Discrepancy) 286349cc55cSDimitry Andric .Case("full", ReportKind::Full) 287349cc55cSDimitry Andric .Default(ReportKind::Error); 2884824e7fdSDimitry Andric if (ReportLevel == ReportKind::Error) 2894824e7fdSDimitry Andric fail(Twine("invalid option for --report: ", StringRef(A->getValue()))); 290349cc55cSDimitry Andric } 291349cc55cSDimitry Andric 292349cc55cSDimitry Andric for (size_t I = 0; I < LibList.size(); ++I) { 293349cc55cSDimitry Andric // In SeparateMode we report on input libraries individually; otherwise 294349cc55cSDimitry Andric // we do one big combined search. Reading to the end of LibList here 295349cc55cSDimitry Andric // will cause the outer while loop to terminate cleanly. 296349cc55cSDimitry Andric if (SeparateMode) { 297349cc55cSDimitry Andric SDKNames.clear(); 298349cc55cSDimitry Andric SDKNames.populateFromFile(LibDir, LibList[I]); 299349cc55cSDimitry Andric if (SDKNames.empty()) 300349cc55cSDimitry Andric continue; 301349cc55cSDimitry Andric } else { 302349cc55cSDimitry Andric do 303349cc55cSDimitry Andric SDKNames.populateFromFile(LibDir, LibList[I]); 304349cc55cSDimitry Andric while (++I < LibList.size()); 305349cc55cSDimitry Andric if (SDKNames.empty()) { 306349cc55cSDimitry Andric WithColor::error() << "NO symbols found!\n"; 307349cc55cSDimitry Andric break; 308349cc55cSDimitry Andric } 309349cc55cSDimitry Andric outs() << "Found a grand total of " << SDKNames.size() 310349cc55cSDimitry Andric << " library symbols\n"; 311349cc55cSDimitry Andric } 312349cc55cSDimitry Andric unsigned TLIdoesSDKdoesnt = 0; 313349cc55cSDimitry Andric unsigned TLIdoesntSDKdoes = 0; 314349cc55cSDimitry Andric unsigned TLIandSDKboth = 0; 315349cc55cSDimitry Andric unsigned TLIandSDKneither = 0; 316349cc55cSDimitry Andric for (auto &TLIName : TLINames) { 317349cc55cSDimitry Andric bool TLIHas = TLIName.second; 318349cc55cSDimitry Andric bool SDKHas = SDKNames.count(TLIName.first) == 1; 319349cc55cSDimitry Andric int Which = int(TLIHas) * 2 + int(SDKHas); 320349cc55cSDimitry Andric switch (Which) { 321349cc55cSDimitry Andric case 0: ++TLIandSDKneither; break; 322349cc55cSDimitry Andric case 1: ++TLIdoesntSDKdoes; break; 323349cc55cSDimitry Andric case 2: ++TLIdoesSDKdoesnt; break; 324349cc55cSDimitry Andric case 3: ++TLIandSDKboth; break; 325349cc55cSDimitry Andric } 326349cc55cSDimitry Andric // If the results match, report only if user requested a full report. 327349cc55cSDimitry Andric ReportKind Threshold = 328349cc55cSDimitry Andric TLIHas == SDKHas ? ReportKind::Full : ReportKind::Discrepancy; 329349cc55cSDimitry Andric if (Threshold <= ReportLevel) { 330349cc55cSDimitry Andric constexpr char YesNo[2][4] = {"no ", "yes"}; 331349cc55cSDimitry Andric constexpr char Indicator[4][3] = {"!!", ">>", "<<", "=="}; 332349cc55cSDimitry Andric outs() << Indicator[Which] << " TLI " << YesNo[TLIHas] << " SDK " 3334824e7fdSDimitry Andric << YesNo[SDKHas] << ": " << getPrintableName(TLIName.first) 3344824e7fdSDimitry Andric << '\n'; 335349cc55cSDimitry Andric } 336349cc55cSDimitry Andric } 337349cc55cSDimitry Andric 338349cc55cSDimitry Andric assert(TLIandSDKboth + TLIandSDKneither + TLIdoesSDKdoesnt + 339349cc55cSDimitry Andric TLIdoesntSDKdoes == 340349cc55cSDimitry Andric LibFunc::NumLibFuncs); 341*81ad6265SDimitry Andric (void) TLIandSDKneither; 342349cc55cSDimitry Andric outs() << "<< Total TLI yes SDK no: " << TLIdoesSDKdoesnt 343349cc55cSDimitry Andric << "\n>> Total TLI no SDK yes: " << TLIdoesntSDKdoes 344349cc55cSDimitry Andric << "\n== Total TLI yes SDK yes: " << TLIandSDKboth; 345349cc55cSDimitry Andric if (TLIandSDKboth == 0) { 346349cc55cSDimitry Andric outs() << " *** NO TLI SYMBOLS FOUND"; 347349cc55cSDimitry Andric if (SeparateMode) 348349cc55cSDimitry Andric outs() << " in '" << LibList[I] << "'"; 349349cc55cSDimitry Andric } 350349cc55cSDimitry Andric outs() << '\n'; 351349cc55cSDimitry Andric 352349cc55cSDimitry Andric if (!SeparateMode) { 353349cc55cSDimitry Andric if (TLIdoesSDKdoesnt == 0 && TLIdoesntSDKdoes == 0) 354349cc55cSDimitry Andric outs() << "PASS: LLVM TLI matched SDK libraries successfully.\n"; 355349cc55cSDimitry Andric else 356349cc55cSDimitry Andric outs() << "FAIL: LLVM TLI doesn't match SDK libraries.\n"; 357349cc55cSDimitry Andric } 358349cc55cSDimitry Andric } 359349cc55cSDimitry Andric } 360