1 //===- DivergenceAnalysis.cpp --------- Divergence Analysis Implementation -==// 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 // This file implements a general divergence analysis for loop vectorization 10 // and GPU programs. It determines which branches and values in a loop or GPU 11 // program are divergent. It can help branch optimizations such as jump 12 // threading and loop unswitching to make better decisions. 13 // 14 // GPU programs typically use the SIMD execution model, where multiple threads 15 // in the same execution group have to execute in lock-step. Therefore, if the 16 // code contains divergent branches (i.e., threads in a group do not agree on 17 // which path of the branch to take), the group of threads has to execute all 18 // the paths from that branch with different subsets of threads enabled until 19 // they re-converge. 20 // 21 // Due to this execution model, some optimizations such as jump 22 // threading and loop unswitching can interfere with thread re-convergence. 23 // Therefore, an analysis that computes which branches in a GPU program are 24 // divergent can help the compiler to selectively run these optimizations. 25 // 26 // This implementation is derived from the Vectorization Analysis of the 27 // Region Vectorizer (RV). That implementation in turn is based on the approach 28 // described in 29 // 30 // Improving Performance of OpenCL on CPUs 31 // Ralf Karrenberg and Sebastian Hack 32 // CC '12 33 // 34 // This DivergenceAnalysis implementation is generic in the sense that it does 35 // not itself identify original sources of divergence. 36 // Instead specialized adapter classes, (LoopDivergenceAnalysis) for loops and 37 // (GPUDivergenceAnalysis) for GPU programs, identify the sources of divergence 38 // (e.g., special variables that hold the thread ID or the iteration variable). 39 // 40 // The generic implementation propagates divergence to variables that are data 41 // or sync dependent on a source of divergence. 42 // 43 // While data dependency is a well-known concept, the notion of sync dependency 44 // is worth more explanation. Sync dependence characterizes the control flow 45 // aspect of the propagation of branch divergence. For example, 46 // 47 // %cond = icmp slt i32 %tid, 10 48 // br i1 %cond, label %then, label %else 49 // then: 50 // br label %merge 51 // else: 52 // br label %merge 53 // merge: 54 // %a = phi i32 [ 0, %then ], [ 1, %else ] 55 // 56 // Suppose %tid holds the thread ID. Although %a is not data dependent on %tid 57 // because %tid is not on its use-def chains, %a is sync dependent on %tid 58 // because the branch "br i1 %cond" depends on %tid and affects which value %a 59 // is assigned to. 60 // 61 // The sync dependence detection (which branch induces divergence in which join 62 // points) is implemented in the SyncDependenceAnalysis. 63 // 64 // The current DivergenceAnalysis implementation has the following limitations: 65 // 1. intra-procedural. It conservatively considers the arguments of a 66 // non-kernel-entry function and the return value of a function call as 67 // divergent. 68 // 2. memory as black box. It conservatively considers values loaded from 69 // generic or local address as divergent. This can be improved by leveraging 70 // pointer analysis and/or by modelling non-escaping memory objects in SSA 71 // as done in RV. 72 // 73 //===----------------------------------------------------------------------===// 74 75 #include "llvm/Analysis/DivergenceAnalysis.h" 76 #include "llvm/Analysis/LoopInfo.h" 77 #include "llvm/Analysis/Passes.h" 78 #include "llvm/Analysis/PostDominators.h" 79 #include "llvm/Analysis/TargetTransformInfo.h" 80 #include "llvm/IR/Dominators.h" 81 #include "llvm/IR/InstIterator.h" 82 #include "llvm/IR/Instructions.h" 83 #include "llvm/IR/IntrinsicInst.h" 84 #include "llvm/IR/Value.h" 85 #include "llvm/Support/Debug.h" 86 #include "llvm/Support/raw_ostream.h" 87 #include <vector> 88 89 using namespace llvm; 90 91 #define DEBUG_TYPE "divergence-analysis" 92 93 // class DivergenceAnalysis 94 DivergenceAnalysis::DivergenceAnalysis( 95 const Function &F, const Loop *RegionLoop, const DominatorTree &DT, 96 const LoopInfo &LI, SyncDependenceAnalysis &SDA, bool IsLCSSAForm) 97 : F(F), RegionLoop(RegionLoop), DT(DT), LI(LI), SDA(SDA), 98 IsLCSSAForm(IsLCSSAForm) {} 99 100 void DivergenceAnalysis::markDivergent(const Value &DivVal) { 101 assert(isa<Instruction>(DivVal) || isa<Argument>(DivVal)); 102 assert(!isAlwaysUniform(DivVal) && "cannot be a divergent"); 103 DivergentValues.insert(&DivVal); 104 } 105 106 void DivergenceAnalysis::addUniformOverride(const Value &UniVal) { 107 UniformOverrides.insert(&UniVal); 108 } 109 110 bool DivergenceAnalysis::updateTerminator(const Instruction &Term) const { 111 if (Term.getNumSuccessors() <= 1) 112 return false; 113 if (auto *BranchTerm = dyn_cast<BranchInst>(&Term)) { 114 assert(BranchTerm->isConditional()); 115 return isDivergent(*BranchTerm->getCondition()); 116 } 117 if (auto *SwitchTerm = dyn_cast<SwitchInst>(&Term)) { 118 return isDivergent(*SwitchTerm->getCondition()); 119 } 120 if (isa<InvokeInst>(Term)) { 121 return false; // ignore abnormal executions through landingpad 122 } 123 124 llvm_unreachable("unexpected terminator"); 125 } 126 127 bool DivergenceAnalysis::updateNormalInstruction(const Instruction &I) const { 128 // TODO function calls with side effects, etc 129 for (const auto &Op : I.operands()) { 130 if (isDivergent(*Op)) 131 return true; 132 } 133 return false; 134 } 135 136 bool DivergenceAnalysis::isTemporalDivergent(const BasicBlock &ObservingBlock, 137 const Value &Val) const { 138 const auto *Inst = dyn_cast<const Instruction>(&Val); 139 if (!Inst) 140 return false; 141 // check whether any divergent loop carrying Val terminates before control 142 // proceeds to ObservingBlock 143 for (const auto *Loop = LI.getLoopFor(Inst->getParent()); 144 Loop != RegionLoop && !Loop->contains(&ObservingBlock); 145 Loop = Loop->getParentLoop()) { 146 if (DivergentLoops.find(Loop) != DivergentLoops.end()) 147 return true; 148 } 149 150 return false; 151 } 152 153 bool DivergenceAnalysis::updatePHINode(const PHINode &Phi) const { 154 // joining divergent disjoint path in Phi parent block 155 if (!Phi.hasConstantOrUndefValue() && isJoinDivergent(*Phi.getParent())) { 156 return true; 157 } 158 159 // An incoming value could be divergent by itself. 160 // Otherwise, an incoming value could be uniform within the loop 161 // that carries its definition but it may appear divergent 162 // from outside the loop. This happens when divergent loop exits 163 // drop definitions of that uniform value in different iterations. 164 // 165 // for (int i = 0; i < n; ++i) { // 'i' is uniform inside the loop 166 // if (i % thread_id == 0) break; // divergent loop exit 167 // } 168 // int divI = i; // divI is divergent 169 for (size_t i = 0; i < Phi.getNumIncomingValues(); ++i) { 170 const auto *InVal = Phi.getIncomingValue(i); 171 if (isDivergent(*Phi.getIncomingValue(i)) || 172 isTemporalDivergent(*Phi.getParent(), *InVal)) { 173 return true; 174 } 175 } 176 return false; 177 } 178 179 bool DivergenceAnalysis::inRegion(const Instruction &I) const { 180 return I.getParent() && inRegion(*I.getParent()); 181 } 182 183 bool DivergenceAnalysis::inRegion(const BasicBlock &BB) const { 184 return (!RegionLoop && BB.getParent() == &F) || RegionLoop->contains(&BB); 185 } 186 187 static bool usesLiveOut(const Instruction &I, const Loop *DivLoop) { 188 for (auto &Op : I.operands()) { 189 auto *OpInst = dyn_cast<Instruction>(&Op); 190 if (!OpInst) 191 continue; 192 if (DivLoop->contains(OpInst->getParent())) 193 return true; 194 } 195 return false; 196 } 197 198 // marks all users of loop-carried values of the loop headed by LoopHeader as 199 // divergent 200 void DivergenceAnalysis::taintLoopLiveOuts(const BasicBlock &LoopHeader) { 201 auto *DivLoop = LI.getLoopFor(&LoopHeader); 202 assert(DivLoop && "loopHeader is not actually part of a loop"); 203 204 SmallVector<BasicBlock *, 8> TaintStack; 205 DivLoop->getExitBlocks(TaintStack); 206 207 // Otherwise potential users of loop-carried values could be anywhere in the 208 // dominance region of DivLoop (including its fringes for phi nodes) 209 DenseSet<const BasicBlock *> Visited; 210 for (auto *Block : TaintStack) { 211 Visited.insert(Block); 212 } 213 Visited.insert(&LoopHeader); 214 215 while (!TaintStack.empty()) { 216 auto *UserBlock = TaintStack.back(); 217 TaintStack.pop_back(); 218 219 // don't spread divergence beyond the region 220 if (!inRegion(*UserBlock)) 221 continue; 222 223 assert(!DivLoop->contains(UserBlock) && 224 "irreducible control flow detected"); 225 226 // phi nodes at the fringes of the dominance region 227 if (!DT.dominates(&LoopHeader, UserBlock)) { 228 // all PHI nodes of UserBlock become divergent 229 for (auto &Phi : UserBlock->phis()) { 230 Worklist.push_back(&Phi); 231 } 232 continue; 233 } 234 235 // taint outside users of values carried by DivLoop 236 for (auto &I : *UserBlock) { 237 if (isAlwaysUniform(I)) 238 continue; 239 if (isDivergent(I)) 240 continue; 241 if (!usesLiveOut(I, DivLoop)) 242 continue; 243 244 markDivergent(I); 245 if (I.isTerminator()) { 246 propagateBranchDivergence(I); 247 } else { 248 pushUsers(I); 249 } 250 } 251 252 // visit all blocks in the dominance region 253 for (auto *SuccBlock : successors(UserBlock)) { 254 if (!Visited.insert(SuccBlock).second) { 255 continue; 256 } 257 TaintStack.push_back(SuccBlock); 258 } 259 } 260 } 261 262 void DivergenceAnalysis::pushPHINodes(const BasicBlock &Block) { 263 for (const auto &Phi : Block.phis()) { 264 if (isDivergent(Phi)) 265 continue; 266 Worklist.push_back(&Phi); 267 } 268 } 269 270 void DivergenceAnalysis::pushUsers(const Value &V) { 271 for (const auto *User : V.users()) { 272 const auto *UserInst = dyn_cast<const Instruction>(User); 273 if (!UserInst) 274 continue; 275 276 if (isDivergent(*UserInst)) 277 continue; 278 279 // only compute divergent inside loop 280 if (!inRegion(*UserInst)) 281 continue; 282 Worklist.push_back(UserInst); 283 } 284 } 285 286 bool DivergenceAnalysis::propagateJoinDivergence(const BasicBlock &JoinBlock, 287 const Loop *BranchLoop) { 288 LLVM_DEBUG(dbgs() << "\tpropJoinDiv " << JoinBlock.getName() << "\n"); 289 290 // ignore divergence outside the region 291 if (!inRegion(JoinBlock)) { 292 return false; 293 } 294 295 // push non-divergent phi nodes in JoinBlock to the worklist 296 pushPHINodes(JoinBlock); 297 298 // JoinBlock is a divergent loop exit 299 if (BranchLoop && !BranchLoop->contains(&JoinBlock)) { 300 return true; 301 } 302 303 // disjoint-paths divergent at JoinBlock 304 markBlockJoinDivergent(JoinBlock); 305 return false; 306 } 307 308 void DivergenceAnalysis::propagateBranchDivergence(const Instruction &Term) { 309 LLVM_DEBUG(dbgs() << "propBranchDiv " << Term.getParent()->getName() << "\n"); 310 311 markDivergent(Term); 312 313 // Don't propagate divergence from unreachable blocks. 314 if (!DT.isReachableFromEntry(Term.getParent())) 315 return; 316 317 const auto *BranchLoop = LI.getLoopFor(Term.getParent()); 318 319 // whether there is a divergent loop exit from BranchLoop (if any) 320 bool IsBranchLoopDivergent = false; 321 322 // iterate over all blocks reachable by disjoint from Term within the loop 323 // also iterates over loop exits that become divergent due to Term. 324 for (const auto *JoinBlock : SDA.join_blocks(Term)) { 325 IsBranchLoopDivergent |= propagateJoinDivergence(*JoinBlock, BranchLoop); 326 } 327 328 // Branch loop is a divergent loop due to the divergent branch in Term 329 if (IsBranchLoopDivergent) { 330 assert(BranchLoop); 331 if (!DivergentLoops.insert(BranchLoop).second) { 332 return; 333 } 334 propagateLoopDivergence(*BranchLoop); 335 } 336 } 337 338 void DivergenceAnalysis::propagateLoopDivergence(const Loop &ExitingLoop) { 339 LLVM_DEBUG(dbgs() << "propLoopDiv " << ExitingLoop.getName() << "\n"); 340 341 // don't propagate beyond region 342 if (!inRegion(*ExitingLoop.getHeader())) 343 return; 344 345 const auto *BranchLoop = ExitingLoop.getParentLoop(); 346 347 // Uses of loop-carried values could occur anywhere 348 // within the dominance region of the definition. All loop-carried 349 // definitions are dominated by the loop header (reducible control). 350 // Thus all users have to be in the dominance region of the loop header, 351 // except PHI nodes that can also live at the fringes of the dom region 352 // (incoming defining value). 353 if (!IsLCSSAForm) 354 taintLoopLiveOuts(*ExitingLoop.getHeader()); 355 356 // whether there is a divergent loop exit from BranchLoop (if any) 357 bool IsBranchLoopDivergent = false; 358 359 // iterate over all blocks reachable by disjoint paths from exits of 360 // ExitingLoop also iterates over loop exits (of BranchLoop) that in turn 361 // become divergent. 362 for (const auto *JoinBlock : SDA.join_blocks(ExitingLoop)) { 363 IsBranchLoopDivergent |= propagateJoinDivergence(*JoinBlock, BranchLoop); 364 } 365 366 // Branch loop is a divergent due to divergent loop exit in ExitingLoop 367 if (IsBranchLoopDivergent) { 368 assert(BranchLoop); 369 if (!DivergentLoops.insert(BranchLoop).second) { 370 return; 371 } 372 propagateLoopDivergence(*BranchLoop); 373 } 374 } 375 376 void DivergenceAnalysis::compute() { 377 for (auto *DivVal : DivergentValues) { 378 pushUsers(*DivVal); 379 } 380 381 // propagate divergence 382 while (!Worklist.empty()) { 383 const Instruction &I = *Worklist.back(); 384 Worklist.pop_back(); 385 386 // maintain uniformity of overrides 387 if (isAlwaysUniform(I)) 388 continue; 389 390 bool WasDivergent = isDivergent(I); 391 if (WasDivergent) 392 continue; 393 394 // propagate divergence caused by terminator 395 if (I.isTerminator()) { 396 if (updateTerminator(I)) { 397 // propagate control divergence to affected instructions 398 propagateBranchDivergence(I); 399 continue; 400 } 401 } 402 403 // update divergence of I due to divergent operands 404 bool DivergentUpd = false; 405 const auto *Phi = dyn_cast<const PHINode>(&I); 406 if (Phi) { 407 DivergentUpd = updatePHINode(*Phi); 408 } else { 409 DivergentUpd = updateNormalInstruction(I); 410 } 411 412 // propagate value divergence to users 413 if (DivergentUpd) { 414 markDivergent(I); 415 pushUsers(I); 416 } 417 } 418 } 419 420 bool DivergenceAnalysis::isAlwaysUniform(const Value &V) const { 421 return UniformOverrides.find(&V) != UniformOverrides.end(); 422 } 423 424 bool DivergenceAnalysis::isDivergent(const Value &V) const { 425 return DivergentValues.find(&V) != DivergentValues.end(); 426 } 427 428 bool DivergenceAnalysis::isDivergentUse(const Use &U) const { 429 Value &V = *U.get(); 430 Instruction &I = *cast<Instruction>(U.getUser()); 431 return isDivergent(V) || isTemporalDivergent(*I.getParent(), V); 432 } 433 434 void DivergenceAnalysis::print(raw_ostream &OS, const Module *) const { 435 if (DivergentValues.empty()) 436 return; 437 // iterate instructions using instructions() to ensure a deterministic order. 438 for (auto &I : instructions(F)) { 439 if (isDivergent(I)) 440 OS << "DIVERGENT:" << I << '\n'; 441 } 442 } 443 444 // class GPUDivergenceAnalysis 445 GPUDivergenceAnalysis::GPUDivergenceAnalysis(Function &F, 446 const DominatorTree &DT, 447 const PostDominatorTree &PDT, 448 const LoopInfo &LI, 449 const TargetTransformInfo &TTI) 450 : SDA(DT, PDT, LI), DA(F, nullptr, DT, LI, SDA, false) { 451 for (auto &I : instructions(F)) { 452 if (TTI.isSourceOfDivergence(&I)) { 453 DA.markDivergent(I); 454 } else if (TTI.isAlwaysUniform(&I)) { 455 DA.addUniformOverride(I); 456 } 457 } 458 for (auto &Arg : F.args()) { 459 if (TTI.isSourceOfDivergence(&Arg)) { 460 DA.markDivergent(Arg); 461 } 462 } 463 464 DA.compute(); 465 } 466 467 bool GPUDivergenceAnalysis::isDivergent(const Value &val) const { 468 return DA.isDivergent(val); 469 } 470 471 bool GPUDivergenceAnalysis::isDivergentUse(const Use &use) const { 472 return DA.isDivergentUse(use); 473 } 474 475 void GPUDivergenceAnalysis::print(raw_ostream &OS, const Module *mod) const { 476 OS << "Divergence of kernel " << DA.getFunction().getName() << " {\n"; 477 DA.print(OS, mod); 478 OS << "}\n"; 479 } 480