|
| 1 | +// This file is a part of Julia. License is MIT: https://julialang.org/license |
| 2 | + |
| 3 | +#include "llvm-version.h" |
| 4 | + |
| 5 | +#include <llvm/Analysis/LoopInfo.h> |
| 6 | +#include <llvm/Analysis/LoopPass.h> |
| 7 | +#include "llvm/Analysis/LoopIterator.h" |
| 8 | +#include <llvm/IR/Dominators.h> |
| 9 | +#include <llvm/IR/LegacyPassManager.h> |
| 10 | +#include <llvm/Transforms/Utils/LoopUtils.h> |
| 11 | + |
| 12 | +#include "llvm-pass-helpers.h" |
| 13 | + |
| 14 | +#define DEBUG_TYPE "julia-licm" |
| 15 | + |
| 16 | +using namespace llvm; |
| 17 | + |
| 18 | +/* |
| 19 | + * Julia LICM pass. |
| 20 | + * This takes care of some julia intrinsics that is safe to move around/out of loops but |
| 21 | + * can't be handled by LLVM's LICM. These intrinsics can be moved outside of |
| 22 | + * loop context as well but it is inside a loop where they matter the most. |
| 23 | + */ |
| 24 | + |
| 25 | +namespace { |
| 26 | + |
| 27 | +struct JuliaLICMPass : public LoopPass, public JuliaPassContext { |
| 28 | + static char ID; |
| 29 | + JuliaLICMPass() : LoopPass(ID) {}; |
| 30 | + |
| 31 | + bool runOnLoop(Loop *L, LPPassManager &LPM) override |
| 32 | + { |
| 33 | + // Get the preheader block to move instructions into, |
| 34 | + // required to run this pass. |
| 35 | + BasicBlock *preheader = L->getLoopPreheader(); |
| 36 | + if (!preheader) |
| 37 | + return false; |
| 38 | + BasicBlock *header = L->getHeader(); |
| 39 | + initFunctions(*header->getModule()); |
| 40 | + // Also require `gc_preserve_begin_func` whereas |
| 41 | + // `gc_preserve_end_func` is optional since the input to |
| 42 | + // `gc_preserve_end_func` must be from `gc_preserve_begin_func`. |
| 43 | + if (!gc_preserve_begin_func) |
| 44 | + return false; |
| 45 | + auto LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo(); |
| 46 | + auto DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree(); |
| 47 | + |
| 48 | + // Lazy initialization of exit blocks insertion points. |
| 49 | + bool exit_pts_init = false; |
| 50 | + SmallVector<Instruction*, 8> _exit_pts; |
| 51 | + auto get_exit_pts = [&] () -> ArrayRef<Instruction*> { |
| 52 | + if (!exit_pts_init) { |
| 53 | + exit_pts_init = true; |
| 54 | + SmallVector<BasicBlock*, 8> exit_bbs; |
| 55 | + L->getUniqueExitBlocks(exit_bbs); |
| 56 | + for (BasicBlock *bb: exit_bbs) { |
| 57 | + _exit_pts.push_back(&*bb->getFirstInsertionPt()); |
| 58 | + } |
| 59 | + } |
| 60 | + return _exit_pts; |
| 61 | + }; |
| 62 | + |
| 63 | + bool changed = false; |
| 64 | + // Scan in the right order so that we'll hoist the `begin` |
| 65 | + // before we consider sinking `end`. |
| 66 | + LoopBlocksRPO worklist(L); |
| 67 | + worklist.perform(LI); |
| 68 | + for (auto *bb : worklist) { |
| 69 | + for (BasicBlock::iterator II = bb->begin(), E = bb->end(); II != E;) { |
| 70 | + auto call = dyn_cast<CallInst>(&*II++); |
| 71 | + if (!call) |
| 72 | + continue; |
| 73 | + auto callee = call->getCalledValue(); |
| 74 | + assert(callee); |
| 75 | + // It is always legal to extend the preserve period |
| 76 | + // so we only need to make sure it is legal to move/clone |
| 77 | + // the calls. |
| 78 | + // If all the input arguments dominates the whole loop we can |
| 79 | + // hoist the `begin` and if a `begin` dominates the loop the |
| 80 | + // corresponding `end` can be moved to the loop exit. |
| 81 | + if (callee == gc_preserve_begin_func) { |
| 82 | + bool canhoist = true; |
| 83 | + for (Use &U : call->arg_operands()) { |
| 84 | + // Check if all arguments are generated outside the loop |
| 85 | + auto origin = dyn_cast<Instruction>(U.get()); |
| 86 | + if (!origin) |
| 87 | + continue; |
| 88 | + if (!DT->properlyDominates(origin->getParent(), header)) { |
| 89 | + canhoist = false; |
| 90 | + break; |
| 91 | + } |
| 92 | + } |
| 93 | + if (!canhoist) |
| 94 | + continue; |
| 95 | + call->moveBefore(preheader->getTerminator()); |
| 96 | + changed = true; |
| 97 | + } |
| 98 | + else if (callee == gc_preserve_end_func) { |
| 99 | + auto begin = cast<Instruction>(call->getArgOperand(0)); |
| 100 | + if (!DT->properlyDominates(begin->getParent(), header)) |
| 101 | + continue; |
| 102 | + changed = true; |
| 103 | + auto exit_pts = get_exit_pts(); |
| 104 | + if (exit_pts.empty()) { |
| 105 | + call->eraseFromParent(); |
| 106 | + continue; |
| 107 | + } |
| 108 | + call->moveBefore(exit_pts[0]); |
| 109 | + for (unsigned i = 1; i < exit_pts.size(); i++) { |
| 110 | + // Clone exit |
| 111 | + CallInst::Create(call, {}, exit_pts[i]); |
| 112 | + } |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + return changed; |
| 117 | + } |
| 118 | + |
| 119 | + void getAnalysisUsage(AnalysisUsage &AU) const override |
| 120 | + { |
| 121 | + getLoopAnalysisUsage(AU); |
| 122 | + } |
| 123 | +}; |
| 124 | + |
| 125 | +char JuliaLICMPass::ID = 0; |
| 126 | +static RegisterPass<JuliaLICMPass> |
| 127 | + Y("JuliaLICM", "LICM for julia specific intrinsics.", |
| 128 | + false, false); |
| 129 | +} |
| 130 | + |
| 131 | +Pass *createJuliaLICMPass() |
| 132 | +{ |
| 133 | + return new JuliaLICMPass(); |
| 134 | +} |
0 commit comments