138be8f40SPaul Robinson //===-- llvm-tli-checker.cpp - Compare TargetLibraryInfo to SDK libraries -===// 238be8f40SPaul Robinson // 338be8f40SPaul Robinson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 438be8f40SPaul Robinson // See https://llvm.org/LICENSE.txt for license information. 538be8f40SPaul Robinson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 638be8f40SPaul Robinson // 738be8f40SPaul Robinson //===----------------------------------------------------------------------===// 838be8f40SPaul Robinson 938be8f40SPaul Robinson #include "llvm/ADT/SmallString.h" 1038be8f40SPaul Robinson #include "llvm/ADT/StringMap.h" 1138be8f40SPaul Robinson #include "llvm/Analysis/TargetLibraryInfo.h" 1238be8f40SPaul Robinson #include "llvm/Config/llvm-config.h" 1338be8f40SPaul Robinson #include "llvm/Demangle/Demangle.h" 1438be8f40SPaul Robinson #include "llvm/Object/Archive.h" 1538be8f40SPaul Robinson #include "llvm/Object/ELFObjectFile.h" 1638be8f40SPaul Robinson #include "llvm/Option/ArgList.h" 1738be8f40SPaul Robinson #include "llvm/Option/Option.h" 1838be8f40SPaul Robinson #include "llvm/Support/FileSystem.h" 1938be8f40SPaul Robinson #include "llvm/Support/InitLLVM.h" 2038be8f40SPaul Robinson #include "llvm/Support/Path.h" 2138be8f40SPaul Robinson #include "llvm/Support/WithColor.h" 2262c7f035SArchibald Elliott #include "llvm/TargetParser/Triple.h" 2338be8f40SPaul Robinson 2438be8f40SPaul Robinson using namespace llvm; 2538be8f40SPaul Robinson using namespace llvm::object; 2638be8f40SPaul Robinson 2738be8f40SPaul Robinson // Command-line option boilerplate. 2838be8f40SPaul Robinson namespace { 2938be8f40SPaul Robinson enum ID { 3038be8f40SPaul Robinson OPT_INVALID = 0, // This is not an option ID. 313f092f37SJan Svoboda #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), 3238be8f40SPaul Robinson #include "Opts.inc" 3338be8f40SPaul Robinson #undef OPTION 3438be8f40SPaul Robinson }; 3538be8f40SPaul Robinson 36*dd647e3eSChandler Carruth #define OPTTABLE_STR_TABLE_CODE 3738be8f40SPaul Robinson #include "Opts.inc" 38*dd647e3eSChandler Carruth #undef OPTTABLE_STR_TABLE_CODE 39*dd647e3eSChandler Carruth 40*dd647e3eSChandler Carruth #define OPTTABLE_PREFIXES_TABLE_CODE 41*dd647e3eSChandler Carruth #include "Opts.inc" 42*dd647e3eSChandler Carruth #undef OPTTABLE_PREFIXES_TABLE_CODE 4338be8f40SPaul Robinson 44dcb6d212SJustin Bogner using namespace llvm::opt; 4507d9ab9aSserge-sans-paille static constexpr opt::OptTable::Info InfoTable[] = { 463f092f37SJan Svoboda #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 4738be8f40SPaul Robinson #include "Opts.inc" 4838be8f40SPaul Robinson #undef OPTION 4938be8f40SPaul Robinson }; 5038be8f40SPaul Robinson 5107bb29d8Sserge-sans-paille class TLICheckerOptTable : public opt::GenericOptTable { 5238be8f40SPaul Robinson public: 53*dd647e3eSChandler Carruth TLICheckerOptTable() 54*dd647e3eSChandler Carruth : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {} 5538be8f40SPaul Robinson }; 5666071f44SPaul Robinson } // end anonymous namespace 5738be8f40SPaul Robinson 5838be8f40SPaul Robinson // We have three levels of reporting. 5938be8f40SPaul Robinson enum class ReportKind { 6038be8f40SPaul Robinson Error, // For argument parsing errors. 6138be8f40SPaul Robinson Summary, // Report counts but not details. 6238be8f40SPaul Robinson Discrepancy, // Report where TLI and the library differ. 6338be8f40SPaul Robinson Full // Report for every known-to-TLI function. 6438be8f40SPaul Robinson }; 6538be8f40SPaul Robinson 6638be8f40SPaul Robinson // Most of the ObjectFile interfaces return an Expected<T>, so make it easy 6766071f44SPaul Robinson // to ignore errors. 6866071f44SPaul Robinson template <typename T> 6966071f44SPaul Robinson static T unwrapIgnoreError(Expected<T> E, T Default = T()) { 7038be8f40SPaul Robinson if (E) 7138be8f40SPaul Robinson return std::move(*E); 7238be8f40SPaul Robinson // Sink the error and return a nothing value. 7338be8f40SPaul Robinson consumeError(E.takeError()); 7466071f44SPaul Robinson return Default; 7538be8f40SPaul Robinson } 7638be8f40SPaul Robinson 7738be8f40SPaul Robinson static void fail(const Twine &Message) { 7838be8f40SPaul Robinson WithColor::error() << Message << '\n'; 7938be8f40SPaul Robinson exit(EXIT_FAILURE); 8038be8f40SPaul Robinson } 8138be8f40SPaul Robinson 8238be8f40SPaul Robinson // Some problem occurred with an archive member; complain and continue. 8338be8f40SPaul Robinson static void reportArchiveChildIssue(const object::Archive::Child &C, int Index, 8438be8f40SPaul Robinson StringRef ArchiveFilename) { 8538be8f40SPaul Robinson // First get the member name. 8638be8f40SPaul Robinson std::string ChildName; 8738be8f40SPaul Robinson Expected<StringRef> NameOrErr = C.getName(); 8838be8f40SPaul Robinson if (NameOrErr) 8938be8f40SPaul Robinson ChildName = std::string(NameOrErr.get()); 9038be8f40SPaul Robinson else { 9138be8f40SPaul Robinson // Ignore the name-fetch error, just report the index. 9238be8f40SPaul Robinson consumeError(NameOrErr.takeError()); 9338be8f40SPaul Robinson ChildName = "<file index: " + std::to_string(Index) + ">"; 9438be8f40SPaul Robinson } 9538be8f40SPaul Robinson 9638be8f40SPaul Robinson WithColor::warning() << ArchiveFilename << "(" << ChildName 9738be8f40SPaul Robinson << "): member is not usable\n"; 9838be8f40SPaul Robinson } 9938be8f40SPaul Robinson 10038be8f40SPaul Robinson // Return Name, and if Name is mangled, append "aka" and the demangled name. 10166071f44SPaul Robinson static std::string getPrintableName(StringRef Name) { 10238be8f40SPaul Robinson std::string OutputName = "'"; 10338be8f40SPaul Robinson OutputName += Name; 10438be8f40SPaul Robinson OutputName += "'"; 1058abbc17fSNick Desaulniers std::string DemangledName(demangle(Name)); 10666071f44SPaul Robinson if (Name != DemangledName) { 10738be8f40SPaul Robinson OutputName += " aka "; 10866071f44SPaul Robinson OutputName += DemangledName; 10938be8f40SPaul Robinson } 11038be8f40SPaul Robinson return OutputName; 11138be8f40SPaul Robinson } 11238be8f40SPaul Robinson 11338be8f40SPaul Robinson // Store all the names that TargetLibraryInfo knows about; the bool indicates 11438be8f40SPaul Robinson // whether TLI has it marked as "available" for the target of interest. 11538be8f40SPaul Robinson // This is a vector to preserve the sorted order for better reporting. 11638be8f40SPaul Robinson struct TLINameList : std::vector<std::pair<StringRef, bool>> { 11738be8f40SPaul Robinson // Record all the TLI info in the vector. 11838be8f40SPaul Robinson void initialize(StringRef TargetTriple); 11938be8f40SPaul Robinson // Print out what we found. 12038be8f40SPaul Robinson void dump(); 12138be8f40SPaul Robinson }; 12266071f44SPaul Robinson static TLINameList TLINames; 12338be8f40SPaul Robinson 12438be8f40SPaul Robinson void TLINameList::initialize(StringRef TargetTriple) { 12538be8f40SPaul Robinson Triple T(TargetTriple); 12638be8f40SPaul Robinson TargetLibraryInfoImpl TLII(T); 12738be8f40SPaul Robinson TargetLibraryInfo TLI(TLII); 12838be8f40SPaul Robinson 12938be8f40SPaul Robinson reserve(LibFunc::NumLibFuncs); 13038be8f40SPaul Robinson size_t NumAvailable = 0; 13138be8f40SPaul Robinson for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) { 13238be8f40SPaul Robinson LibFunc LF = (LibFunc)FI; 13338be8f40SPaul Robinson bool Available = TLI.has(LF); 13438be8f40SPaul Robinson // getName returns names only for available funcs. 13538be8f40SPaul Robinson TLII.setAvailable(LF); 13638be8f40SPaul Robinson emplace_back(TLI.getName(LF), Available); 13738be8f40SPaul Robinson if (Available) 13838be8f40SPaul Robinson ++NumAvailable; 13938be8f40SPaul Robinson } 14038be8f40SPaul Robinson outs() << "TLI knows " << LibFunc::NumLibFuncs << " symbols, " << NumAvailable 14138be8f40SPaul Robinson << " available for '" << TargetTriple << "'\n"; 14238be8f40SPaul Robinson } 14338be8f40SPaul Robinson 14438be8f40SPaul Robinson void TLINameList::dump() { 14538be8f40SPaul Robinson // Assume this gets called after initialize(), so we have the above line of 14638be8f40SPaul Robinson // output as a header. So, for example, no need to repeat the triple. 14738be8f40SPaul Robinson for (auto &TLIName : TLINames) { 14838be8f40SPaul Robinson outs() << (TLIName.second ? " " : "not ") 14966071f44SPaul Robinson << "available: " << getPrintableName(TLIName.first) << '\n'; 15038be8f40SPaul Robinson } 15138be8f40SPaul Robinson } 15238be8f40SPaul Robinson 15338be8f40SPaul Robinson // Store all the exported symbol names we found in the input libraries. 15438be8f40SPaul Robinson // We use a map to get hashed lookup speed; the bool is meaningless. 15538be8f40SPaul Robinson class SDKNameMap : public StringMap<bool> { 156f92c1726SPaul Robinson void maybeInsertSymbol(const SymbolRef &S, const ObjectFile &O); 15738be8f40SPaul Robinson void populateFromObject(ObjectFile *O); 15838be8f40SPaul Robinson void populateFromArchive(Archive *A); 15938be8f40SPaul Robinson 16038be8f40SPaul Robinson public: 16138be8f40SPaul Robinson void populateFromFile(StringRef LibDir, StringRef LibName); 16238be8f40SPaul Robinson }; 16366071f44SPaul Robinson static SDKNameMap SDKNames; 16438be8f40SPaul Robinson 165f92c1726SPaul Robinson // Insert defined global function symbols into the map if valid. 166f92c1726SPaul Robinson void SDKNameMap::maybeInsertSymbol(const SymbolRef &S, const ObjectFile &O) { 167f92c1726SPaul Robinson SymbolRef::Type Type = unwrapIgnoreError(S.getType()); 168f92c1726SPaul Robinson uint32_t Flags = unwrapIgnoreError(S.getFlags()); 169f92c1726SPaul Robinson section_iterator Section = unwrapIgnoreError(S.getSection(), 170f92c1726SPaul Robinson /*Default=*/O.section_end()); 171f92c1726SPaul Robinson if (Type == SymbolRef::ST_Function && (Flags & SymbolRef::SF_Global) && 172f92c1726SPaul Robinson Section != O.section_end()) { 173f92c1726SPaul Robinson StringRef Name = unwrapIgnoreError(S.getName()); 174f92c1726SPaul Robinson insert({ Name, true }); 175f92c1726SPaul Robinson } 176f92c1726SPaul Robinson } 177f92c1726SPaul Robinson 17838be8f40SPaul Robinson // Given an ObjectFile, extract the global function symbols. 17938be8f40SPaul Robinson void SDKNameMap::populateFromObject(ObjectFile *O) { 18066071f44SPaul Robinson // FIXME: Support other formats. 18138be8f40SPaul Robinson if (!O->isELF()) { 18266071f44SPaul Robinson WithColor::warning() << O->getFileName() 18366071f44SPaul Robinson << ": only ELF-format files are supported\n"; 18438be8f40SPaul Robinson return; 18538be8f40SPaul Robinson } 18666071f44SPaul Robinson const auto *ELF = cast<ELFObjectFileBase>(O); 18738be8f40SPaul Robinson 188f92c1726SPaul Robinson if (ELF->getEType() == ELF::ET_REL) { 189f92c1726SPaul Robinson for (const auto &S : ELF->symbols()) 190f92c1726SPaul Robinson maybeInsertSymbol(S, *O); 191f92c1726SPaul Robinson } else { 192f92c1726SPaul Robinson for (const auto &S : ELF->getDynamicSymbolIterators()) 193f92c1726SPaul Robinson maybeInsertSymbol(S, *O); 19438be8f40SPaul Robinson } 19538be8f40SPaul Robinson } 19638be8f40SPaul Robinson 19738be8f40SPaul Robinson // Unpack an archive and populate from the component object files. 19838be8f40SPaul Robinson // This roughly imitates dumpArchive() from llvm-objdump.cpp. 19938be8f40SPaul Robinson void SDKNameMap::populateFromArchive(Archive *A) { 20038be8f40SPaul Robinson Error Err = Error::success(); 20138be8f40SPaul Robinson int Index = -1; 202e20d210eSKazu Hirata for (const auto &C : A->children(Err)) { 20338be8f40SPaul Robinson ++Index; 20438be8f40SPaul Robinson Expected<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary(); 20538be8f40SPaul Robinson if (!ChildOrErr) { 20638be8f40SPaul Robinson if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) { 20738be8f40SPaul Robinson // Issue a generic warning. 20838be8f40SPaul Robinson consumeError(std::move(E)); 20938be8f40SPaul Robinson reportArchiveChildIssue(C, Index, A->getFileName()); 21038be8f40SPaul Robinson } 21138be8f40SPaul Robinson continue; 21238be8f40SPaul Robinson } 21338be8f40SPaul Robinson if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get())) 21438be8f40SPaul Robinson populateFromObject(O); 21538be8f40SPaul Robinson // Ignore non-object archive members. 21638be8f40SPaul Robinson } 21738be8f40SPaul Robinson if (Err) 21838be8f40SPaul Robinson WithColor::defaultErrorHandler(std::move(Err)); 21938be8f40SPaul Robinson } 22038be8f40SPaul Robinson 22138be8f40SPaul Robinson // Unpack a library file and extract the global function names. 22238be8f40SPaul Robinson void SDKNameMap::populateFromFile(StringRef LibDir, StringRef LibName) { 22338be8f40SPaul Robinson // Pick an arbitrary but reasonable default size. 22438be8f40SPaul Robinson SmallString<255> Filepath(LibDir); 22538be8f40SPaul Robinson sys::path::append(Filepath, LibName); 22638be8f40SPaul Robinson if (!sys::fs::exists(Filepath)) { 22766071f44SPaul Robinson WithColor::warning() << StringRef(Filepath) << ": not found\n"; 22838be8f40SPaul Robinson return; 22938be8f40SPaul Robinson } 23038be8f40SPaul Robinson outs() << "\nLooking for symbols in '" << StringRef(Filepath) << "'\n"; 23138be8f40SPaul Robinson auto ExpectedBinary = createBinary(Filepath); 23238be8f40SPaul Robinson if (!ExpectedBinary) { 23338be8f40SPaul Robinson // FIXME: Report this better. 23438be8f40SPaul Robinson WithColor::defaultWarningHandler(ExpectedBinary.takeError()); 23538be8f40SPaul Robinson return; 23638be8f40SPaul Robinson } 23738be8f40SPaul Robinson OwningBinary<Binary> OBinary = std::move(*ExpectedBinary); 23838be8f40SPaul Robinson Binary &Binary = *OBinary.getBinary(); 23938be8f40SPaul Robinson size_t Precount = size(); 24038be8f40SPaul Robinson if (Archive *A = dyn_cast<Archive>(&Binary)) 24138be8f40SPaul Robinson populateFromArchive(A); 24238be8f40SPaul Robinson else if (ObjectFile *O = dyn_cast<ObjectFile>(&Binary)) 24338be8f40SPaul Robinson populateFromObject(O); 24438be8f40SPaul Robinson else { 24566071f44SPaul Robinson WithColor::warning() << StringRef(Filepath) 24666071f44SPaul Robinson << ": not an archive or object file\n"; 24738be8f40SPaul Robinson return; 24838be8f40SPaul Robinson } 24938be8f40SPaul Robinson if (Precount == size()) 25066071f44SPaul Robinson WithColor::warning() << StringRef(Filepath) << ": no symbols found\n"; 25138be8f40SPaul Robinson else 25238be8f40SPaul Robinson outs() << "Found " << size() - Precount << " global function symbols in '" 25338be8f40SPaul Robinson << StringRef(Filepath) << "'\n"; 25438be8f40SPaul Robinson } 25538be8f40SPaul Robinson 25638be8f40SPaul Robinson int main(int argc, char *argv[]) { 25738be8f40SPaul Robinson InitLLVM X(argc, argv); 25838be8f40SPaul Robinson BumpPtrAllocator A; 25938be8f40SPaul Robinson StringSaver Saver(A); 26038be8f40SPaul Robinson TLICheckerOptTable Tbl; 26138be8f40SPaul Robinson opt::InputArgList Args = Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, 26238be8f40SPaul Robinson [&](StringRef Msg) { fail(Msg); }); 26338be8f40SPaul Robinson 26438be8f40SPaul Robinson if (Args.hasArg(OPT_help)) { 26538be8f40SPaul Robinson std::string Usage(argv[0]); 26638be8f40SPaul Robinson Usage += " [options] library-file [library-file...]"; 26738be8f40SPaul Robinson Tbl.printHelp(outs(), Usage.c_str(), 26838be8f40SPaul Robinson "LLVM TargetLibraryInfo versus SDK checker"); 26938be8f40SPaul Robinson outs() << "\nPass @FILE as argument to read options or library names from " 27038be8f40SPaul Robinson "FILE.\n"; 27138be8f40SPaul Robinson return 0; 27238be8f40SPaul Robinson } 27338be8f40SPaul Robinson 27438be8f40SPaul Robinson TLINames.initialize(Args.getLastArgValue(OPT_triple_EQ)); 27538be8f40SPaul Robinson 27638be8f40SPaul Robinson // --dump-tli doesn't require any input files. 27738be8f40SPaul Robinson if (Args.hasArg(OPT_dump_tli)) { 27838be8f40SPaul Robinson TLINames.dump(); 27938be8f40SPaul Robinson return 0; 28038be8f40SPaul Robinson } 28138be8f40SPaul Robinson 28238be8f40SPaul Robinson std::vector<std::string> LibList = Args.getAllArgValues(OPT_INPUT); 28366071f44SPaul Robinson if (LibList.empty()) 28466071f44SPaul Robinson fail("no input files\n"); 28538be8f40SPaul Robinson StringRef LibDir = Args.getLastArgValue(OPT_libdir_EQ); 28638be8f40SPaul Robinson bool SeparateMode = Args.hasArg(OPT_separate); 28738be8f40SPaul Robinson 28838be8f40SPaul Robinson ReportKind ReportLevel = 28938be8f40SPaul Robinson SeparateMode ? ReportKind::Summary : ReportKind::Discrepancy; 29038be8f40SPaul Robinson if (const opt::Arg *A = Args.getLastArg(OPT_report_EQ)) { 29138be8f40SPaul Robinson ReportLevel = StringSwitch<ReportKind>(A->getValue()) 29238be8f40SPaul Robinson .Case("summary", ReportKind::Summary) 29338be8f40SPaul Robinson .Case("discrepancy", ReportKind::Discrepancy) 29438be8f40SPaul Robinson .Case("full", ReportKind::Full) 29538be8f40SPaul Robinson .Default(ReportKind::Error); 29666071f44SPaul Robinson if (ReportLevel == ReportKind::Error) 29766071f44SPaul Robinson fail(Twine("invalid option for --report: ", StringRef(A->getValue()))); 29838be8f40SPaul Robinson } 29938be8f40SPaul Robinson 30038be8f40SPaul Robinson for (size_t I = 0; I < LibList.size(); ++I) { 30138be8f40SPaul Robinson // In SeparateMode we report on input libraries individually; otherwise 30238be8f40SPaul Robinson // we do one big combined search. Reading to the end of LibList here 30338be8f40SPaul Robinson // will cause the outer while loop to terminate cleanly. 30438be8f40SPaul Robinson if (SeparateMode) { 30538be8f40SPaul Robinson SDKNames.clear(); 30638be8f40SPaul Robinson SDKNames.populateFromFile(LibDir, LibList[I]); 30738be8f40SPaul Robinson if (SDKNames.empty()) 30838be8f40SPaul Robinson continue; 30938be8f40SPaul Robinson } else { 31038be8f40SPaul Robinson do 31138be8f40SPaul Robinson SDKNames.populateFromFile(LibDir, LibList[I]); 31238be8f40SPaul Robinson while (++I < LibList.size()); 31338be8f40SPaul Robinson if (SDKNames.empty()) { 31438be8f40SPaul Robinson WithColor::error() << "NO symbols found!\n"; 31538be8f40SPaul Robinson break; 31638be8f40SPaul Robinson } 31738be8f40SPaul Robinson outs() << "Found a grand total of " << SDKNames.size() 31838be8f40SPaul Robinson << " library symbols\n"; 31938be8f40SPaul Robinson } 32038be8f40SPaul Robinson unsigned TLIdoesSDKdoesnt = 0; 32138be8f40SPaul Robinson unsigned TLIdoesntSDKdoes = 0; 32238be8f40SPaul Robinson unsigned TLIandSDKboth = 0; 32338be8f40SPaul Robinson unsigned TLIandSDKneither = 0; 32438be8f40SPaul Robinson for (auto &TLIName : TLINames) { 32538be8f40SPaul Robinson bool TLIHas = TLIName.second; 32638be8f40SPaul Robinson bool SDKHas = SDKNames.count(TLIName.first) == 1; 32738be8f40SPaul Robinson int Which = int(TLIHas) * 2 + int(SDKHas); 32838be8f40SPaul Robinson switch (Which) { 32938be8f40SPaul Robinson case 0: ++TLIandSDKneither; break; 33038be8f40SPaul Robinson case 1: ++TLIdoesntSDKdoes; break; 33138be8f40SPaul Robinson case 2: ++TLIdoesSDKdoesnt; break; 33238be8f40SPaul Robinson case 3: ++TLIandSDKboth; break; 33338be8f40SPaul Robinson } 33438be8f40SPaul Robinson // If the results match, report only if user requested a full report. 33538be8f40SPaul Robinson ReportKind Threshold = 33638be8f40SPaul Robinson TLIHas == SDKHas ? ReportKind::Full : ReportKind::Discrepancy; 33738be8f40SPaul Robinson if (Threshold <= ReportLevel) { 33838be8f40SPaul Robinson constexpr char YesNo[2][4] = {"no ", "yes"}; 33938be8f40SPaul Robinson constexpr char Indicator[4][3] = {"!!", ">>", "<<", "=="}; 34038be8f40SPaul Robinson outs() << Indicator[Which] << " TLI " << YesNo[TLIHas] << " SDK " 34166071f44SPaul Robinson << YesNo[SDKHas] << ": " << getPrintableName(TLIName.first) 34266071f44SPaul Robinson << '\n'; 34338be8f40SPaul Robinson } 34438be8f40SPaul Robinson } 34538be8f40SPaul Robinson 34638be8f40SPaul Robinson assert(TLIandSDKboth + TLIandSDKneither + TLIdoesSDKdoesnt + 34738be8f40SPaul Robinson TLIdoesntSDKdoes == 34838be8f40SPaul Robinson LibFunc::NumLibFuncs); 34946776f75SMartin Storsjö (void) TLIandSDKneither; 35038be8f40SPaul Robinson outs() << "<< Total TLI yes SDK no: " << TLIdoesSDKdoesnt 35138be8f40SPaul Robinson << "\n>> Total TLI no SDK yes: " << TLIdoesntSDKdoes 35238be8f40SPaul Robinson << "\n== Total TLI yes SDK yes: " << TLIandSDKboth; 35338be8f40SPaul Robinson if (TLIandSDKboth == 0) { 35438be8f40SPaul Robinson outs() << " *** NO TLI SYMBOLS FOUND"; 35538be8f40SPaul Robinson if (SeparateMode) 35638be8f40SPaul Robinson outs() << " in '" << LibList[I] << "'"; 35738be8f40SPaul Robinson } 35838be8f40SPaul Robinson outs() << '\n'; 35938be8f40SPaul Robinson 36038be8f40SPaul Robinson if (!SeparateMode) { 36138be8f40SPaul Robinson if (TLIdoesSDKdoesnt == 0 && TLIdoesntSDKdoes == 0) 36238be8f40SPaul Robinson outs() << "PASS: LLVM TLI matched SDK libraries successfully.\n"; 36338be8f40SPaul Robinson else 36438be8f40SPaul Robinson outs() << "FAIL: LLVM TLI doesn't match SDK libraries.\n"; 36538be8f40SPaul Robinson } 36638be8f40SPaul Robinson } 36738be8f40SPaul Robinson } 368