xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-tli-checker/llvm-tli-checker.cpp (revision 349cc55c9796c4596a5b9904cd3281af295f878f)
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