1 //===- Transforms/Utils/SampleProfileInference.h ----------*- 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 /// \file
10 /// This file provides the interface for the profile inference algorithm, profi.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_TRANSFORMS_UTILS_SAMPLEPROFILEINFERENCE_H
15 #define LLVM_TRANSFORMS_UTILS_SAMPLEPROFILEINFERENCE_H
16 
17 #include "llvm/ADT/DenseMap.h"
18 #include "llvm/ADT/DepthFirstIterator.h"
19 #include "llvm/ADT/SmallVector.h"
20 
21 #include "llvm/IR/BasicBlock.h"
22 #include "llvm/IR/Instruction.h"
23 #include "llvm/IR/Instructions.h"
24 
25 namespace llvm {
26 
27 class Function;
28 class MachineBasicBlock;
29 class MachineFunction;
30 
31 namespace afdo_detail {
32 
33 template <class BlockT> struct TypeMap {};
34 template <> struct TypeMap<BasicBlock> {
35   using BasicBlockT = BasicBlock;
36   using FunctionT = Function;
37 };
38 template <> struct TypeMap<MachineBasicBlock> {
39   using BasicBlockT = MachineBasicBlock;
40   using FunctionT = MachineFunction;
41 };
42 
43 } // end namespace afdo_detail
44 
45 struct FlowJump;
46 
47 /// A wrapper of a binary basic block.
48 struct FlowBlock {
49   uint64_t Index;
50   uint64_t Weight{0};
51   bool HasUnknownWeight{true};
52   bool IsUnlikely{false};
53   uint64_t Flow{0};
54   std::vector<FlowJump *> SuccJumps;
55   std::vector<FlowJump *> PredJumps;
56 
57   /// Check if it is the entry block in the function.
58   bool isEntry() const { return PredJumps.empty(); }
59 
60   /// Check if it is an exit block in the function.
61   bool isExit() const { return SuccJumps.empty(); }
62 };
63 
64 /// A wrapper of a jump between two basic blocks.
65 struct FlowJump {
66   uint64_t Source;
67   uint64_t Target;
68   uint64_t Weight{0};
69   bool HasUnknownWeight{true};
70   bool IsUnlikely{false};
71   uint64_t Flow{0};
72 };
73 
74 /// A wrapper of binary function with basic blocks and jumps.
75 struct FlowFunction {
76   /// Basic blocks in the function.
77   std::vector<FlowBlock> Blocks;
78   /// Jumps between the basic blocks.
79   std::vector<FlowJump> Jumps;
80   /// The index of the entry block.
81   uint64_t Entry{0};
82 };
83 
84 /// Various thresholds and options controlling the behavior of the profile
85 /// inference algorithm. Default values are tuned for several large-scale
86 /// applications, and can be modified via corresponding command-line flags.
87 struct ProfiParams {
88   /// Evenly distribute flow when there are multiple equally likely options.
89   bool EvenFlowDistribution{false};
90 
91   /// Evenly re-distribute flow among unknown subgraphs.
92   bool RebalanceUnknown{false};
93 
94   /// Join isolated components having positive flow.
95   bool JoinIslands{false};
96 
97   /// The cost of increasing a block's count by one.
98   unsigned CostBlockInc{0};
99 
100   /// The cost of decreasing a block's count by one.
101   unsigned CostBlockDec{0};
102 
103   /// The cost of increasing a count of zero-weight block by one.
104   unsigned CostBlockZeroInc{0};
105 
106   /// The cost of increasing the entry block's count by one.
107   unsigned CostBlockEntryInc{0};
108 
109   /// The cost of decreasing the entry block's count by one.
110   unsigned CostBlockEntryDec{0};
111 
112   /// The cost of increasing an unknown block's count by one.
113   unsigned CostBlockUnknownInc{0};
114 
115   /// The cost of increasing a jump's count by one.
116   unsigned CostJumpInc{0};
117 
118   /// The cost of increasing a fall-through jump's count by one.
119   unsigned CostJumpFTInc{0};
120 
121   /// The cost of decreasing a jump's count by one.
122   unsigned CostJumpDec{0};
123 
124   /// The cost of decreasing a fall-through jump's count by one.
125   unsigned CostJumpFTDec{0};
126 
127   /// The cost of increasing an unknown jump's count by one.
128   unsigned CostJumpUnknownInc{0};
129 
130   /// The cost of increasing an unknown fall-through jump's count by one.
131   unsigned CostJumpUnknownFTInc{0};
132 
133   /// The cost of taking an unlikely block/jump.
134   const int64_t CostUnlikely = ((int64_t)1) << 30;
135 };
136 
137 void applyFlowInference(const ProfiParams &Params, FlowFunction &Func);
138 void applyFlowInference(FlowFunction &Func);
139 
140 /// Sample profile inference pass.
141 template <typename BT> class SampleProfileInference {
142 public:
143   using BasicBlockT = typename afdo_detail::TypeMap<BT>::BasicBlockT;
144   using FunctionT = typename afdo_detail::TypeMap<BT>::FunctionT;
145   using Edge = std::pair<const BasicBlockT *, const BasicBlockT *>;
146   using BlockWeightMap = DenseMap<const BasicBlockT *, uint64_t>;
147   using EdgeWeightMap = DenseMap<Edge, uint64_t>;
148   using BlockEdgeMap =
149       DenseMap<const BasicBlockT *, SmallVector<const BasicBlockT *, 8>>;
150 
151   SampleProfileInference(FunctionT &F, BlockEdgeMap &Successors,
152                          BlockWeightMap &SampleBlockWeights)
153       : F(F), Successors(Successors), SampleBlockWeights(SampleBlockWeights) {}
154 
155   /// Apply the profile inference algorithm for a given function
156   void apply(BlockWeightMap &BlockWeights, EdgeWeightMap &EdgeWeights);
157 
158 private:
159   /// Initialize flow function blocks, jumps and misc metadata.
160   void initFunction(FlowFunction &Func,
161                     const std::vector<const BasicBlockT *> &BasicBlocks,
162                     DenseMap<const BasicBlockT *, uint64_t> &BlockIndex);
163 
164   /// Try to infer branch probabilities mimicking implementation of
165   /// BranchProbabilityInfo. Unlikely taken branches are marked so that the
166   /// inference algorithm can avoid sending flow along corresponding edges.
167   void findUnlikelyJumps(const std::vector<const BasicBlockT *> &BasicBlocks,
168                          BlockEdgeMap &Successors, FlowFunction &Func);
169 
170   /// Determine whether the block is an exit in the CFG.
171   bool isExit(const BasicBlockT *BB);
172 
173   /// Function.
174   const FunctionT &F;
175 
176   /// Successors for each basic block in the CFG.
177   BlockEdgeMap &Successors;
178 
179   /// Map basic blocks to their sampled weights.
180   BlockWeightMap &SampleBlockWeights;
181 };
182 
183 template <typename BT>
184 void SampleProfileInference<BT>::apply(BlockWeightMap &BlockWeights,
185                                        EdgeWeightMap &EdgeWeights) {
186   // Find all forwards reachable blocks which the inference algorithm will be
187   // applied on.
188   df_iterator_default_set<const BasicBlockT *> Reachable;
189   for (auto *BB : depth_first_ext(&F, Reachable))
190     (void)BB /* Mark all reachable blocks */;
191 
192   // Find all backwards reachable blocks which the inference algorithm will be
193   // applied on.
194   df_iterator_default_set<const BasicBlockT *> InverseReachable;
195   for (const auto &BB : F) {
196     // An exit block is a block without any successors.
197     if (isExit(&BB)) {
198       for (auto *RBB : inverse_depth_first_ext(&BB, InverseReachable))
199         (void)RBB;
200     }
201   }
202 
203   // Keep a stable order for reachable blocks
204   DenseMap<const BasicBlockT *, uint64_t> BlockIndex;
205   std::vector<const BasicBlockT *> BasicBlocks;
206   BlockIndex.reserve(Reachable.size());
207   BasicBlocks.reserve(Reachable.size());
208   for (const auto &BB : F) {
209     if (Reachable.count(&BB) && InverseReachable.count(&BB)) {
210       BlockIndex[&BB] = BasicBlocks.size();
211       BasicBlocks.push_back(&BB);
212     }
213   }
214 
215   BlockWeights.clear();
216   EdgeWeights.clear();
217   bool HasSamples = false;
218   for (const auto *BB : BasicBlocks) {
219     auto It = SampleBlockWeights.find(BB);
220     if (It != SampleBlockWeights.end() && It->second > 0) {
221       HasSamples = true;
222       BlockWeights[BB] = It->second;
223     }
224   }
225   // Quit early for functions with a single block or ones w/o samples
226   if (BasicBlocks.size() <= 1 || !HasSamples) {
227     return;
228   }
229 
230   // Create necessary objects
231   FlowFunction Func;
232   initFunction(Func, BasicBlocks, BlockIndex);
233 
234   // Create and apply the inference network model.
235   applyFlowInference(Func);
236 
237   // Extract the resulting weights from the control flow
238   // All weights are increased by one to avoid propagation errors introduced by
239   // zero weights.
240   for (const auto *BB : BasicBlocks) {
241     BlockWeights[BB] = Func.Blocks[BlockIndex[BB]].Flow;
242   }
243   for (auto &Jump : Func.Jumps) {
244     Edge E = std::make_pair(BasicBlocks[Jump.Source], BasicBlocks[Jump.Target]);
245     EdgeWeights[E] = Jump.Flow;
246   }
247 
248 #ifndef NDEBUG
249   // Unreachable blocks and edges should not have a weight.
250   for (auto &I : BlockWeights) {
251     assert(Reachable.contains(I.first));
252     assert(InverseReachable.contains(I.first));
253   }
254   for (auto &I : EdgeWeights) {
255     assert(Reachable.contains(I.first.first) &&
256            Reachable.contains(I.first.second));
257     assert(InverseReachable.contains(I.first.first) &&
258            InverseReachable.contains(I.first.second));
259   }
260 #endif
261 }
262 
263 template <typename BT>
264 void SampleProfileInference<BT>::initFunction(
265     FlowFunction &Func, const std::vector<const BasicBlockT *> &BasicBlocks,
266     DenseMap<const BasicBlockT *, uint64_t> &BlockIndex) {
267   Func.Blocks.reserve(BasicBlocks.size());
268   // Create FlowBlocks
269   for (const auto *BB : BasicBlocks) {
270     FlowBlock Block;
271     if (SampleBlockWeights.find(BB) != SampleBlockWeights.end()) {
272       Block.HasUnknownWeight = false;
273       Block.Weight = SampleBlockWeights[BB];
274     } else {
275       Block.HasUnknownWeight = true;
276       Block.Weight = 0;
277     }
278     Block.Index = Func.Blocks.size();
279     Func.Blocks.push_back(Block);
280   }
281   // Create FlowEdges
282   for (const auto *BB : BasicBlocks) {
283     for (auto *Succ : Successors[BB]) {
284       if (!BlockIndex.count(Succ))
285         continue;
286       FlowJump Jump;
287       Jump.Source = BlockIndex[BB];
288       Jump.Target = BlockIndex[Succ];
289       Func.Jumps.push_back(Jump);
290     }
291   }
292   for (auto &Jump : Func.Jumps) {
293     uint64_t Src = Jump.Source;
294     uint64_t Dst = Jump.Target;
295     Func.Blocks[Src].SuccJumps.push_back(&Jump);
296     Func.Blocks[Dst].PredJumps.push_back(&Jump);
297   }
298 
299   // Try to infer probabilities of jumps based on the content of basic block
300   findUnlikelyJumps(BasicBlocks, Successors, Func);
301 
302   // Find the entry block
303   for (size_t I = 0; I < Func.Blocks.size(); I++) {
304     if (Func.Blocks[I].isEntry()) {
305       Func.Entry = I;
306       break;
307     }
308   }
309   assert(Func.Entry == 0 && "incorrect index of the entry block");
310 
311   // Pre-process data: make sure the entry weight is at least 1
312   auto &EntryBlock = Func.Blocks[Func.Entry];
313   if (EntryBlock.Weight == 0 && !EntryBlock.HasUnknownWeight) {
314     EntryBlock.Weight = 1;
315     EntryBlock.HasUnknownWeight = false;
316   }
317 }
318 
319 template <typename BT>
320 inline void SampleProfileInference<BT>::findUnlikelyJumps(
321     const std::vector<const BasicBlockT *> &BasicBlocks,
322     BlockEdgeMap &Successors, FlowFunction &Func) {}
323 
324 template <>
325 inline void SampleProfileInference<BasicBlock>::findUnlikelyJumps(
326     const std::vector<const BasicBlockT *> &BasicBlocks,
327     BlockEdgeMap &Successors, FlowFunction &Func) {
328   for (auto &Jump : Func.Jumps) {
329     const auto *BB = BasicBlocks[Jump.Source];
330     const auto *Succ = BasicBlocks[Jump.Target];
331     const Instruction *TI = BB->getTerminator();
332     // Check if a block ends with InvokeInst and mark non-taken branch unlikely.
333     // In that case block Succ should be a landing pad
334     if (Successors[BB].size() == 2 && Successors[BB].back() == Succ) {
335       if (isa<InvokeInst>(TI)) {
336         Jump.IsUnlikely = true;
337       }
338     }
339     const Instruction *SuccTI = Succ->getTerminator();
340     // Check if the target block contains UnreachableInst and mark it unlikely
341     if (SuccTI->getNumSuccessors() == 0) {
342       if (isa<UnreachableInst>(SuccTI)) {
343         Jump.IsUnlikely = true;
344       }
345     }
346   }
347 }
348 
349 template <typename BT>
350 inline bool SampleProfileInference<BT>::isExit(const BasicBlockT *BB) {
351   return BB->succ_empty();
352 }
353 
354 template <>
355 inline bool SampleProfileInference<BasicBlock>::isExit(const BasicBlock *BB) {
356   return succ_empty(BB);
357 }
358 
359 } // end namespace llvm
360 #endif // LLVM_TRANSFORMS_UTILS_SAMPLEPROFILEINFERENCE_H
361