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