Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 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 |