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