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: |
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 | |
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, Other: 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> |
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> |
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, Other: &DynoStatsBefore); |
179 | OS << '\n'; |
180 | } |
181 | } |
182 | |
183 | } // namespace bolt |
184 | } // namespace llvm |
185 | |
186 | #endif |
187 | |