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