LCOV - code coverage report
Current view: top level - src - llvm-julia-licm.cpp (source / functions) Hit Total Coverage
Test: [test only] commit 0f242327d2cc9bd130497f44b6350c924185606a Lines: 135 185 73.0 %
Date: 2022-07-16 23:42:53 Functions: 15 22 68.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 76 100 76.0 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : 
       3                 :            : #include "llvm-version.h"
       4                 :            : #include "passes.h"
       5                 :            : 
       6                 :            : #include <llvm/Analysis/LoopInfo.h>
       7                 :            : #include <llvm/Analysis/LoopPass.h>
       8                 :            : #include <llvm/Analysis/LoopIterator.h>
       9                 :            : #include <llvm/Analysis/MemorySSA.h>
      10                 :            : #include <llvm/Analysis/MemorySSAUpdater.h>
      11                 :            : #include <llvm/Analysis/ValueTracking.h>
      12                 :            : #include <llvm/Analysis/ScalarEvolution.h>
      13                 :            : #include <llvm/ADT/Statistic.h>
      14                 :            : #include <llvm/IR/Dominators.h>
      15                 :            : #include <llvm/IR/LegacyPassManager.h>
      16                 :            : #include <llvm/IR/Verifier.h>
      17                 :            : #include <llvm/Transforms/Utils/LoopUtils.h>
      18                 :            : 
      19                 :            : #include "llvm-pass-helpers.h"
      20                 :            : #include "julia.h"
      21                 :            : #include "llvm-alloc-helpers.h"
      22                 :            : #include "codegen_shared.h"
      23                 :            : 
      24                 :            : #define DEBUG_TYPE "julia-licm"
      25                 :            : 
      26                 :            : using namespace llvm;
      27                 :            : 
      28                 :            : STATISTIC(HoistedPreserveBegin, "Number of gc_preserve_begin instructions hoisted out of a loop");
      29                 :            : STATISTIC(SunkPreserveEnd, "Number of gc_preserve_end instructions sunk out of a loop");
      30                 :            : STATISTIC(ErasedPreserveEnd, "Number of gc_preserve_end instructions removed from nonterminating loops");
      31                 :            : STATISTIC(HoistedWriteBarrier, "Number of write barriers hoisted out of a loop");
      32                 :            : STATISTIC(HoistedAllocation, "Number of allocations hoisted out of a loop");
      33                 :            : 
      34                 :            : /*
      35                 :            :  * Julia LICM pass.
      36                 :            :  * This takes care of some julia intrinsics that is safe to move around/out of loops but
      37                 :            :  * can't be handled by LLVM's LICM. These intrinsics can be moved outside of
      38                 :            :  * loop context as well but it is inside a loop where they matter the most.
      39                 :            :  */
      40                 :            : 
      41                 :            : namespace {
      42                 :            : 
      43                 :            : //Stolen and modified from LICM.cpp
      44                 :          0 : static void eraseInstruction(Instruction &I,
      45                 :            :                              MemorySSAUpdater &MSSAU) {
      46         [ #  # ]:          0 :   if (MSSAU.getMemorySSA())
      47                 :          0 :     MSSAU.removeMemoryAccess(&I);
      48                 :          0 :   I.eraseFromParent();
      49                 :          0 : }
      50                 :            : 
      51                 :            : //Stolen and modified from LICM.cpp
      52                 :       9867 : static void moveInstructionBefore(Instruction &I, Instruction &Dest,
      53                 :            :                                   MemorySSAUpdater &MSSAU,
      54                 :            :                                   ScalarEvolution *SE) {
      55                 :       9867 :   I.moveBefore(&Dest);
      56         [ -  + ]:       9867 :   if (MSSAU.getMemorySSA())
      57         [ #  # ]:          0 :     if (MemoryUseOrDef *OldMemAcc = cast_or_null<MemoryUseOrDef>(
      58                 :            :             MSSAU.getMemorySSA()->getMemoryAccess(&I)))
      59                 :          0 :       MSSAU.moveToPlace(OldMemAcc, Dest.getParent(),
      60                 :            :                          MemorySSA::BeforeTerminator);
      61         [ -  + ]:       9867 :   if (SE)
      62                 :          0 :     SE->forgetValue(&I);
      63                 :       9867 : }
      64                 :            : 
      65                 :      22454 : static void createNewInstruction(Instruction *New, Instruction *Ref, MemorySSAUpdater &MSSAU) {
      66   [ -  +  -  -  :      22454 :   if (MSSAU.getMemorySSA() && MSSAU.getMemorySSA()->getMemoryAccess(Ref)) {
                   -  + ]
      67                 :            :     // Create a new MemoryAccess and let MemorySSA set its defining access.
      68                 :          0 :     MemoryAccess *NewMemAcc = MSSAU.createMemoryAccessInBB(
      69                 :          0 :         New, nullptr, New->getParent(), MemorySSA::Beginning);
      70         [ #  # ]:          0 :     if (NewMemAcc) {
      71         [ #  # ]:          0 :       if (auto *MemDef = dyn_cast<MemoryDef>(NewMemAcc))
      72                 :          0 :         MSSAU.insertDef(MemDef, /*RenameUses=*/true);
      73                 :            :       else {
      74                 :          0 :         auto *MemUse = cast<MemoryUse>(NewMemAcc);
      75                 :          0 :         MSSAU.insertUse(MemUse, /*RenameUses=*/true);
      76                 :            :       }
      77                 :            :     }
      78                 :            :   }
      79                 :      22454 : }
      80                 :            : 
      81                 :            : //Stolen and modified to update SE from LoopInfo.cpp
      82                 :            : static bool makeLoopInvariant(Loop *L, Value *V, bool &Changed, Instruction *InsertPt, MemorySSAUpdater &MSSAU, ScalarEvolution *SE);
      83                 :            : 
      84                 :      68103 : static bool makeLoopInvariant(Loop *L, Instruction *I, bool &Changed, Instruction *InsertPt, MemorySSAUpdater &MSSAU, ScalarEvolution *SE) {
      85                 :            :   // Test if the value is already loop-invariant.
      86         [ +  + ]:      68103 :   if (L->isLoopInvariant(I))
      87                 :       1516 :     return true;
      88         [ +  + ]:      66587 :   if (!isSafeToSpeculativelyExecute(I))
      89                 :      66439 :     return false;
      90         [ +  + ]:        148 :   if (I->mayReadFromMemory())
      91                 :         90 :     return false;
      92                 :            :   // EH block instructions are immobile.
      93         [ -  + ]:         58 :   if (I->isEHPad())
      94                 :          0 :     return false;
      95                 :            :   // Don't hoist instructions with loop-variant operands.
      96         [ +  - ]:         58 :   for (Value *Operand : I->operands())
      97         [ +  - ]:         58 :     if (!makeLoopInvariant(L, Operand, Changed, InsertPt, MSSAU, SE))
      98                 :         58 :       return false;
      99                 :            : 
     100                 :            :   // Hoist.
     101                 :          0 :   moveInstructionBefore(*I, *InsertPt, MSSAU, SE);
     102                 :            : 
     103                 :            :   // There is possibility of hoisting this instruction above some arbitrary
     104                 :            :   // condition. Any metadata defined on it can be control dependent on this
     105                 :            :   // condition. Conservatively strip it here so that we don't give any wrong
     106                 :            :   // information to the optimizer.
     107                 :          0 :   I->dropUnknownNonDebugMetadata();
     108                 :            : 
     109                 :          0 :   Changed = true;
     110                 :          0 :   return true;
     111                 :            : }
     112                 :            : 
     113                 :      70328 : static bool makeLoopInvariant(Loop *L, Value *V, bool &Changed, Instruction *InsertPt, MemorySSAUpdater &MSSAU, ScalarEvolution *SE) {
     114         [ +  + ]:      70328 :   if (Instruction *I = dyn_cast<Instruction>(V))
     115                 :      68103 :     return makeLoopInvariant(L, I, Changed, InsertPt, MSSAU, SE);
     116                 :       2225 :   return true; // All non-instructions are loop-invariant.
     117                 :            : }
     118                 :            : 
     119                 :            : struct JuliaLICMPassLegacy : public LoopPass {
     120                 :            :     static char ID;
     121                 :       1148 :     JuliaLICMPassLegacy() : LoopPass(ID) {};
     122                 :            : 
     123                 :            :     bool runOnLoop(Loop *L, LPPassManager &LPM) override;
     124                 :            : 
     125                 :            :     protected:
     126                 :       1148 :         void getAnalysisUsage(AnalysisUsage &AU) const override {
     127                 :       1148 :             getLoopAnalysisUsage(AU);
     128                 :       1148 :         }
     129                 :            : };
     130                 :            : 
     131                 :            : struct JuliaLICM : public JuliaPassContext {
     132                 :            :     function_ref<DominatorTree &()> GetDT;
     133                 :            :     function_ref<LoopInfo &()> GetLI;
     134                 :            :     function_ref<MemorySSA *()> GetMSSA;
     135                 :            :     function_ref<ScalarEvolution *()> GetSE;
     136                 :     514150 :     JuliaLICM(function_ref<DominatorTree &()> GetDT,
     137                 :            :               function_ref<LoopInfo &()> GetLI,
     138                 :            :               function_ref<MemorySSA *()> GetMSSA,
     139                 :     514150 :               function_ref<ScalarEvolution *()> GetSE) :
     140                 :            :                 GetDT(GetDT),
     141                 :            :                 GetLI(GetLI),
     142                 :            :                 GetMSSA(GetMSSA),
     143                 :     514150 :                 GetSE(GetSE) {}
     144                 :            : 
     145                 :     514150 :     bool runOnLoop(Loop *L)
     146                 :            :     {
     147                 :            :         // Get the preheader block to move instructions into,
     148                 :            :         // required to run this pass.
     149                 :     514150 :         BasicBlock *preheader = L->getLoopPreheader();
     150         [ -  + ]:     514150 :         if (!preheader)
     151                 :          0 :             return false;
     152                 :     514150 :         BasicBlock *header = L->getHeader();
     153                 :     514150 :         const llvm::DataLayout &DL = header->getModule()->getDataLayout();
     154                 :     514150 :         initFunctions(*header->getModule());
     155                 :            :         // Also require `gc_preserve_begin_func` whereas
     156                 :            :         // `gc_preserve_end_func` is optional since the input to
     157                 :            :         // `gc_preserve_end_func` must be from `gc_preserve_begin_func`.
     158                 :            :         // We also hoist write barriers here, so we don't exit if write_barrier_func exists
     159   [ +  +  +  +  :     514150 :         if (!gc_preserve_begin_func && !write_barrier_func && !write_barrier_binding_func &&
                   +  + ]
     160         [ +  + ]:     324916 :             !alloc_obj_func)
     161                 :     159488 :             return false;
     162                 :     354662 :         auto LI = &GetLI();
     163                 :     354662 :         auto DT = &GetDT();
     164                 :     354662 :         auto MSSA = GetMSSA();
     165                 :     354662 :         auto SE = GetSE();
     166                 :     709324 :         MemorySSAUpdater MSSAU(MSSA);
     167                 :            : 
     168                 :            :         // Lazy initialization of exit blocks insertion points.
     169                 :     354662 :         bool exit_pts_init = false;
     170                 :     709324 :         SmallVector<Instruction*, 8> _exit_pts;
     171                 :       4603 :         auto get_exit_pts = [&] () -> ArrayRef<Instruction*> {
     172         [ +  + ]:       4603 :             if (!exit_pts_init) {
     173                 :       4146 :                 exit_pts_init = true;
     174                 :       8292 :                 SmallVector<BasicBlock*, 8> exit_bbs;
     175                 :       4146 :                 L->getUniqueExitBlocks(exit_bbs);
     176         [ +  + ]:      23955 :                 for (BasicBlock *bb: exit_bbs) {
     177                 :      19809 :                     _exit_pts.push_back(&*bb->getFirstInsertionPt());
     178                 :            :                 }
     179                 :            :             }
     180                 :       4603 :             return _exit_pts;
     181                 :     354662 :         };
     182                 :            : 
     183                 :     354662 :         bool changed = false;
     184                 :            :         // Scan in the right order so that we'll hoist the `begin`
     185                 :            :         // before we consider sinking `end`.
     186                 :     354662 :         LoopBlocksRPO worklist(L);
     187                 :     354662 :         worklist.perform(LI);
     188         [ +  + ]:    5579460 :         for (auto *bb : worklist) {
     189         [ +  + ]:   36088700 :             for (BasicBlock::iterator II = bb->begin(), E = bb->end(); II != E;) {
     190                 :   30863900 :                 auto call = dyn_cast<CallInst>(&*II++);
     191         [ +  + ]:   30863900 :                 if (!call)
     192                 :   29387000 :                     continue;
     193                 :    1476890 :                 Value *callee = call->getCalledOperand();
     194                 :            :                 assert(callee != nullptr);
     195                 :            :                 // It is always legal to extend the preserve period
     196                 :            :                 // so we only need to make sure it is legal to move/clone
     197                 :            :                 // the calls.
     198                 :            :                 // If all the input arguments dominates the whole loop we can
     199                 :            :                 // hoist the `begin` and if a `begin` dominates the loop the
     200                 :            :                 // corresponding `end` can be moved to the loop exit.
     201         [ +  + ]:    1476890 :                 if (callee == gc_preserve_begin_func) {
     202                 :      10302 :                     bool canhoist = true;
     203         [ +  + ]:      14830 :                     for (Use &U : call->args()) {
     204                 :            :                         // Check if all arguments are generated outside the loop
     205                 :      10311 :                         auto origin = dyn_cast<Instruction>(U.get());
     206         [ +  + ]:      10311 :                         if (!origin)
     207                 :       3254 :                             continue;
     208         [ +  + ]:       7057 :                         if (!DT->properlyDominates(origin->getParent(), header)) {
     209                 :       5783 :                             canhoist = false;
     210                 :       5783 :                             break;
     211                 :            :                         }
     212                 :            :                     }
     213         [ +  + ]:      10302 :                     if (!canhoist)
     214                 :       5783 :                         continue;
     215                 :       4519 :                     ++HoistedPreserveBegin;
     216                 :       4519 :                     moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE);
     217                 :       4519 :                     changed = true;
     218                 :            :                 }
     219         [ +  + ]:    1466590 :                 else if (callee == gc_preserve_end_func) {
     220                 :      10490 :                     auto begin = cast<Instruction>(call->getArgOperand(0));
     221         [ +  + ]:      10490 :                     if (!DT->properlyDominates(begin->getParent(), header))
     222                 :       5887 :                         continue;
     223                 :       4603 :                     changed = true;
     224                 :       4603 :                     auto exit_pts = get_exit_pts();
     225         [ -  + ]:       4603 :                     if (exit_pts.empty()) {
     226                 :          0 :                         ++ErasedPreserveEnd;
     227                 :          0 :                         eraseInstruction(*call, MSSAU);
     228                 :          0 :                         continue;
     229                 :            :                     }
     230                 :       4603 :                     ++SunkPreserveEnd;
     231                 :       4603 :                     moveInstructionBefore(*call, *exit_pts[0], MSSAU, SE);
     232         [ +  + ]:      27057 :                     for (unsigned i = 1; i < exit_pts.size(); i++) {
     233                 :            :                         // Clone exit
     234                 :      22454 :                         auto CI = CallInst::Create(call, {}, exit_pts[i]);
     235                 :      22454 :                         createNewInstruction(CI, call, MSSAU);
     236                 :            :                     }
     237                 :            :                 }
     238         [ +  + ]:    1456100 :                 else if (callee == write_barrier_func ||
     239         [ +  + ]:    1389980 :                          callee == write_barrier_binding_func) {
     240                 :      66559 :                     bool valid = true;
     241         [ +  + ]:      67945 :                     for (std::size_t i = 0; i < call->arg_size(); i++) {
     242         [ +  + ]:      67915 :                         if (!makeLoopInvariant(L, call->getArgOperand(i),
     243                 :            :                             changed, preheader->getTerminator(),
     244                 :            :                             MSSAU, SE)) {
     245                 :      66529 :                             valid = false;
     246                 :      66529 :                             break;
     247                 :            :                         }
     248                 :            :                     }
     249         [ +  + ]:      66559 :                     if (valid) {
     250                 :         30 :                         ++HoistedWriteBarrier;
     251                 :         30 :                         moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE);
     252                 :         30 :                         changed = true;
     253                 :      66559 :                     }
     254                 :            :                 }
     255         [ +  + ]:    1389540 :                 else if (callee == alloc_obj_func) {
     256                 :      96898 :                     jl_alloc::AllocUseInfo use_info;
     257                 :      96898 :                     jl_alloc::CheckInst::Stack check_stack;
     258                 :      96898 :                     jl_alloc::EscapeAnalysisRequiredArgs required{use_info, check_stack, *this, DL};
     259                 :      96898 :                     jl_alloc::runEscapeAnalysis(call, required, jl_alloc::EscapeAnalysisOptionalArgs().with_valid_set(&L->getBlocksSet()));
     260   [ +  +  +  + ]:      96898 :                     if (use_info.escaped || use_info.addrescaped) {
     261                 :      96113 :                         continue;
     262                 :            :                     }
     263                 :        785 :                     bool valid = true;
     264         [ +  + ]:       3140 :                     for (std::size_t i = 0; i < call->arg_size(); i++) {
     265         [ -  + ]:       2355 :                         if (!makeLoopInvariant(L, call->getArgOperand(i), changed,
     266                 :            :                             preheader->getTerminator(), MSSAU, SE)) {
     267                 :          0 :                             valid = false;
     268                 :          0 :                             break;
     269                 :            :                         }
     270                 :            :                     }
     271         [ +  + ]:        785 :                     if (use_info.refstore) {
     272                 :            :                         // We need to add write barriers to any stores
     273                 :            :                         // that may start crossing generations
     274                 :         70 :                         continue;
     275                 :            :                     }
     276         [ +  - ]:        715 :                     if (valid) {
     277                 :        715 :                         ++HoistedAllocation;
     278                 :        715 :                         moveInstructionBefore(*call, *preheader->getTerminator(), MSSAU, SE);
     279                 :        715 :                         changed = true;
     280                 :            :                     }
     281                 :            :                 }
     282                 :            :             }
     283                 :            :         }
     284   [ +  +  -  + ]:     354662 :         if (changed && SE) {
     285                 :          0 :             SE->forgetLoopDispositions(L);
     286                 :            :         }
     287                 :            :         assert(!verifyFunction(*L->getHeader()->getParent(), &errs()));
     288                 :     354662 :         return changed;
     289                 :            :     }
     290                 :            : };
     291                 :            : 
     292                 :     514150 : bool JuliaLICMPassLegacy::runOnLoop(Loop *L, LPPassManager &LPM) {
     293                 :     354662 :     auto GetDT = [this]() -> DominatorTree & {
     294                 :     354662 :         return getAnalysis<DominatorTreeWrapperPass>().getDomTree();
     295                 :     514150 :     };
     296                 :     354662 :     auto GetLI = [this]() -> LoopInfo & {
     297                 :     354662 :         return getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
     298                 :     514150 :     };
     299                 :     354662 :     auto GetMSSA = []() {
     300                 :     354662 :         return nullptr;
     301                 :            :     };
     302                 :     354662 :     auto GetSE = []() {
     303                 :     354662 :         return nullptr;
     304                 :            :     };
     305                 :     514150 :     auto juliaLICM = JuliaLICM(GetDT, GetLI, GetMSSA, GetSE);
     306                 :     514150 :     return juliaLICM.runOnLoop(L);
     307                 :            : }
     308                 :            : 
     309                 :            : char JuliaLICMPassLegacy::ID = 0;
     310                 :            : static RegisterPass<JuliaLICMPassLegacy>
     311                 :            :         Y("JuliaLICM", "LICM for julia specific intrinsics.",
     312                 :            :           false, false);
     313                 :            : } //namespace
     314                 :            : 
     315                 :          0 : PreservedAnalyses JuliaLICMPass::run(Loop &L, LoopAnalysisManager &AM,
     316                 :            :                           LoopStandardAnalysisResults &AR, LPMUpdater &U)
     317                 :            : {
     318                 :          0 :     auto GetDT = [&AR]() -> DominatorTree & {
     319                 :          0 :         return AR.DT;
     320                 :          0 :     };
     321                 :          0 :     auto GetLI = [&AR]() -> LoopInfo & {
     322                 :          0 :         return AR.LI;
     323                 :          0 :     };
     324                 :          0 :     auto GetMSSA = [&AR]() {
     325                 :          0 :         return AR.MSSA;
     326                 :          0 :     };
     327                 :          0 :     auto GetSE = [&AR]() {
     328                 :          0 :         return &AR.SE;
     329                 :          0 :     };
     330                 :          0 :     auto juliaLICM = JuliaLICM(GetDT, GetLI, GetMSSA, GetSE);
     331         [ #  # ]:          0 :     if (juliaLICM.runOnLoop(&L)) {
     332                 :          0 :         auto preserved = getLoopPassPreservedAnalyses();
     333                 :          0 :         preserved.preserveSet<CFGAnalyses>();
     334                 :          0 :         preserved.preserve<MemorySSAAnalysis>();
     335                 :          0 :         return preserved;
     336                 :            :     }
     337                 :          0 :     return PreservedAnalyses::all();
     338                 :            : }
     339                 :            : 
     340                 :       1148 : Pass *createJuliaLICMPass()
     341                 :            : {
     342                 :       1148 :     return new JuliaLICMPassLegacy();
     343                 :            : }
     344                 :            : 
     345                 :          0 : extern "C" JL_DLLEXPORT void LLVMExtraJuliaLICMPass_impl(LLVMPassManagerRef PM)
     346                 :            : {
     347                 :          0 :     unwrap(PM)->add(createJuliaLICMPass());
     348                 :          0 : }

Generated by: LCOV version 1.14