xref: /llvm-project/bolt/include/bolt/Core/DynoStats.h (revision 52cf07116bf0a8cab87b0f55176d198bcaa02575)
1 //===- bolt/Core/DynoStats.h - Dynamic execution stats ----------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Keep track of statistics about the trace of execution captured in BOLT
10 // profile.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef BOLT_CORE_DYNO_STATS_H
15 #define BOLT_CORE_DYNO_STATS_H
16 
17 #include "llvm/ADT/ArrayRef.h"
18 #include "llvm/MC/MCInstPrinter.h"
19 #include "llvm/Support/raw_ostream.h"
20 #include <map>
21 #include <unordered_map>
22 
23 namespace llvm {
24 
25 namespace bolt {
26 class BinaryFunction;
27 
28 /// Class encapsulating runtime statistics about an execution unit.
29 class DynoStats {
30 
31 #define REAL_DYNO_STATS                                                        \
32   D(FORWARD_COND_BRANCHES, "executed forward branches", Fn)                    \
33   D(FORWARD_COND_BRANCHES_TAKEN, "taken forward branches", Fn)                 \
34   D(BACKWARD_COND_BRANCHES, "executed backward branches", Fn)                  \
35   D(BACKWARD_COND_BRANCHES_TAKEN, "taken backward branches", Fn)               \
36   D(UNCOND_BRANCHES, "executed unconditional branches", Fn)                    \
37   D(FUNCTION_CALLS, "all function calls", Fn)                                  \
38   D(INDIRECT_CALLS, "indirect calls", Fn)                                      \
39   D(PLT_CALLS, "PLT calls", Fn)                                                \
40   D(INSTRUCTIONS, "executed instructions", Fn)                                 \
41   D(LOADS, "executed load instructions", Fn)                                   \
42   D(STORES, "executed store instructions", Fn)                                 \
43   D(JUMP_TABLE_BRANCHES, "taken jump table branches", Fn)                      \
44   D(UNKNOWN_INDIRECT_BRANCHES, "taken unknown indirect branches", Fn)          \
45   D(ALL_BRANCHES, "total branches", Fadd(ALL_CONDITIONAL, UNCOND_BRANCHES))    \
46   D(ALL_TAKEN, "taken branches", Fadd(TAKEN_CONDITIONAL, UNCOND_BRANCHES))     \
47   D(NONTAKEN_CONDITIONAL, "non-taken conditional branches",                    \
48     Fsub(ALL_CONDITIONAL, TAKEN_CONDITIONAL))                                  \
49   D(TAKEN_CONDITIONAL, "taken conditional branches",                           \
50     Fadd(FORWARD_COND_BRANCHES_TAKEN, BACKWARD_COND_BRANCHES_TAKEN))           \
51   D(ALL_CONDITIONAL, "all conditional branches",                               \
52     Fadd(FORWARD_COND_BRANCHES, BACKWARD_COND_BRANCHES))                       \
53   D(VENEER_CALLS_AARCH64, "linker-inserted veneer calls", Fn)
54 
55 #define DYNO_STATS                                                             \
56   D(FIRST_DYNO_STAT, "<reserved>", 0)                                          \
57   REAL_DYNO_STATS                                                              \
58   D(LAST_DYNO_STAT, "<reserved>", 0)
59 
60 public:
61 #define D(name, ...) name,
62   enum Category : uint8_t { DYNO_STATS };
63 #undef D
64 
65 private:
66   uint64_t Stats[LAST_DYNO_STAT + 1];
67   bool PrintAArch64Stats;
68 
69 #define D(name, desc, ...) desc,
70   static constexpr const char *Desc[] = { DYNO_STATS };
71 #undef D
72 
73 public:
DynoStats(bool PrintAArch64Stats)74   DynoStats(bool PrintAArch64Stats) {
75     this->PrintAArch64Stats = PrintAArch64Stats;
76     for (auto Stat = FIRST_DYNO_STAT + 0; Stat < LAST_DYNO_STAT; ++Stat)
77       Stats[Stat] = 0;
78   }
79 
80   uint64_t &operator[](size_t I) {
81     assert(I > FIRST_DYNO_STAT && I < LAST_DYNO_STAT && "index out of bounds");
82     return Stats[I];
83   }
84 
85   uint64_t operator[](size_t I) const {
86     switch (I) {
87 #define D(name, desc, func) \
88     case name: \
89       return func;
90 #define Fn Stats[I]
91 #define Fadd(a, b) operator[](a) + operator[](b)
92 #define Fsub(a, b) operator[](a) - operator[](b)
93 #define F(a) operator[](a)
94 #define Radd(a, b) (a + b)
95 #define Rsub(a, b) (a - b)
96     DYNO_STATS
97 #undef Rsub
98 #undef Radd
99 #undef F
100 #undef Fsub
101 #undef Fadd
102 #undef Fn
103 #undef D
104     default:
105       llvm_unreachable("index out of bounds");
106     }
107     return 0;
108   }
109 
110   void print(raw_ostream &OS, const DynoStats *Other = nullptr,
111              MCInstPrinter *Printer = nullptr) const;
112 
113   void operator+=(const DynoStats &Other);
114   bool operator<(const DynoStats &Other) const;
115   bool operator==(const DynoStats &Other) const;
116   bool operator!=(const DynoStats &Other) const { return !operator==(Other); }
117   bool lessThan(const DynoStats &Other, ArrayRef<Category> Keys) const;
118 
Description(const Category C)119   static const char *Description(const Category C) { return Desc[C]; }
120 
121   /// Maps instruction opcodes to:
122   /// 1. Accumulated executed instruction counts.
123   /// 2. a multimap that records highest execution counts, function names,
124   /// and BB offsets where instructions of these opcodes occur.
125   using MaxOpcodeHistogramTy =
126       std::multimap<uint64_t, std::pair<StringRef, uint32_t>>;
127   using OpcodeHistogramTy =
128       std::unordered_map<unsigned, std::pair<uint64_t, MaxOpcodeHistogramTy>>;
129   using OpcodeStatTy = OpcodeHistogramTy::value_type;
130 
131   OpcodeHistogramTy OpcodeHistogram;
132 };
133 
134 inline raw_ostream &operator<<(raw_ostream &OS, const DynoStats &Stats) {
135   Stats.print(OS, nullptr);
136   return OS;
137 }
138 
139 DynoStats operator+(const DynoStats &A, const DynoStats &B);
140 
141 /// Return dynostats for the function.
142 ///
143 /// The function relies on branch instructions being in-sync with CFG for
144 /// branch instructions stats. Thus it is better to call it after
145 /// fixBranches().
146 DynoStats getDynoStats(BinaryFunction &BF);
147 
148 /// Return program-wide dynostats.
149 template <typename FuncsType>
getDynoStats(FuncsType & Funcs,bool IsAArch64)150 inline DynoStats getDynoStats(FuncsType &Funcs, bool IsAArch64) {
151   DynoStats dynoStats(IsAArch64);
152   for (auto &BFI : Funcs) {
153     auto &BF = BFI.second;
154     if (BF.isSimple())
155       dynoStats += getDynoStats(BF);
156   }
157   return dynoStats;
158 }
159 
160 /// Call a function with optional before and after dynostats printing.
161 template <typename FnType, typename FuncsType>
callWithDynoStats(raw_ostream & OS,FnType && Func,FuncsType & Funcs,StringRef Phase,const bool Flag,bool IsAArch64)162 inline void callWithDynoStats(raw_ostream &OS, FnType &&Func, FuncsType &Funcs,
163                               StringRef Phase, const bool Flag,
164                               bool IsAArch64) {
165   DynoStats DynoStatsBefore(IsAArch64);
166   if (Flag)
167     DynoStatsBefore = getDynoStats(Funcs, IsAArch64);
168 
169   Func();
170 
171   if (Flag) {
172     const DynoStats DynoStatsAfter = getDynoStats(Funcs, IsAArch64);
173     const bool Changed = (DynoStatsAfter != DynoStatsBefore);
174     OS << "BOLT-INFO: program-wide dynostats after running " << Phase
175        << (Changed ? "" : " (no change)") << ":\n\n"
176        << DynoStatsBefore << '\n';
177     if (Changed)
178       DynoStatsAfter.print(OS, &DynoStatsBefore);
179     OS << '\n';
180   }
181 }
182 
183 } // namespace bolt
184 } // namespace llvm
185 
186 #endif
187