LCOV - code coverage report
Current view: top level - src - llvm-lower-handlers.cpp (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 92 99 92.9 %
Date: 2022-07-17 01:01:28 Functions: 5 7 71.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 34 36 94.4 %

           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-c/Core.h>
       7                 :            : #include <llvm-c/Types.h>
       8                 :            : 
       9                 :            : #include <llvm/ADT/DepthFirstIterator.h>
      10                 :            : #include <llvm/Analysis/CFG.h>
      11                 :            : #include <llvm/IR/BasicBlock.h>
      12                 :            : #include <llvm/IR/Constants.h>
      13                 :            : #include <llvm/IR/Function.h>
      14                 :            : #include <llvm/IR/Instructions.h>
      15                 :            : #include <llvm/IR/IntrinsicInst.h>
      16                 :            : #include <llvm/IR/Module.h>
      17                 :            : #include <llvm/IR/Value.h>
      18                 :            : #include <llvm/IR/LegacyPassManager.h>
      19                 :            : #include <llvm/Pass.h>
      20                 :            : #include <llvm/Support/Debug.h>
      21                 :            : #include <llvm/Transforms/Utils/BasicBlockUtils.h>
      22                 :            : 
      23                 :            : #include "julia.h"
      24                 :            : #include "julia_assert.h"
      25                 :            : #include "codegen_shared.h"
      26                 :            : #include <map>
      27                 :            : 
      28                 :            : #define DEBUG_TYPE "lower_handlers"
      29                 :            : #undef DEBUG
      30                 :            : 
      31                 :            : using namespace llvm;
      32                 :            : 
      33                 :            : /* Lowers Julia Exception Handlers and colors EH frames.
      34                 :            :  *
      35                 :            :  *  Our task is to lower:
      36                 :            :  * call void @julia.except_enter()
      37                 :            :  * <...>
      38                 :            :  * call void jl_pop_handler(1)
      39                 :            :  *
      40                 :            :  * to
      41                 :            :  *
      42                 :            :  * call void @jl_enter_handler(jl_handler *%buff)
      43                 :            :  * <...>
      44                 :            :  * call void jl_pop_handler(1)
      45                 :            :  *
      46                 :            :  * Where buff is an appropriate stack slot handler.
      47                 :            :  *
      48                 :            :  * We make the following assumptions:
      49                 :            :  *  - All EH frames are completely nested.
      50                 :            :  *  - The exception nestedness of a BB is not dynamic. I.e. we don't allow
      51                 :            :  *    the following:
      52                 :            :  *                br i1 %cond, %left, %right
      53                 :            :  *                  /                \
      54                 :            :  *           except.enter         br mid
      55                 :            :  *           br mid                  |
      56                 :            :  *                 \                 /
      57                 :            :  *              br i1 %cond, %left2, %right2
      58                 :            :  *                 /                 \
      59                 :            :  *           jl_pop_hander          ret
      60                 :            :  *           ret
      61                 :            :  *
      62                 :            :  *    The frontend doesn't emit structures like this. However, the optimizer
      63                 :            :  *    could easily introduce them, so this pass should run early after IRGen.
      64                 :            :  *
      65                 :            :  * Because of these assumptions, the algorithm is very simple. We simply label
      66                 :            :  * the handler depth at every basic block using a DFS search. For each enter
      67                 :            :  * we encounter, we record the current depth and then allocate an exception
      68                 :            :  * handler frame for every level.
      69                 :            :  *
      70                 :            :  * As an additional optimization, we also insert lifetime intrinsics for the
      71                 :            :  * handler structures to tell LLVM that it is free to re-use the stack slot
      72                 :            :  * while the handler is not being used.
      73                 :            :  */
      74                 :            : 
      75                 :            : namespace {
      76                 :            : /*
      77                 :            :  * If the module doesn't have declarations for the jl_enter_handler and setjmp
      78                 :            :  * functions, insert them.
      79                 :            :  */
      80                 :      62715 : static void ensure_enter_function(Module &M)
      81                 :            : {
      82                 :      62715 :     auto T_int8  = Type::getInt8Ty(M.getContext());
      83                 :      62715 :     auto T_pint8 = PointerType::get(T_int8, 0);
      84                 :      62715 :     auto T_void = Type::getVoidTy(M.getContext());
      85                 :      62715 :     auto T_int32 = Type::getInt32Ty(M.getContext());
      86         [ +  + ]:      62715 :     if (!M.getNamedValue(XSTR(jl_enter_handler))) {
      87                 :       2426 :         std::vector<Type*> ehargs(0);
      88                 :       2426 :         ehargs.push_back(T_pint8);
      89                 :       2426 :         Function::Create(FunctionType::get(T_void, ehargs, false),
      90                 :            :                          Function::ExternalLinkage, XSTR(jl_enter_handler), &M);
      91                 :            :     }
      92         [ +  + ]:      62715 :     if (!M.getNamedValue(jl_setjmp_name)) {
      93                 :       2426 :         std::vector<Type*> args2(0);
      94                 :       2426 :         args2.push_back(T_pint8);
      95                 :            : #ifndef _OS_WINDOWS_
      96                 :       2426 :         args2.push_back(T_int32);
      97                 :            : #endif
      98                 :            :         Function::Create(FunctionType::get(T_int32, args2, false),
      99                 :            :                          Function::ExternalLinkage, jl_setjmp_name, &M)
     100                 :       2426 :             ->addFnAttr(Attribute::ReturnsTwice);
     101                 :            :     }
     102                 :      62715 : }
     103                 :            : 
     104                 :     137811 : static bool lowerExcHandlers(Function &F) {
     105                 :     137811 :     Module &M = *F.getParent();
     106                 :     137811 :     Function *except_enter_func = M.getFunction("julia.except_enter");
     107         [ +  + ]:     137811 :     if (!except_enter_func)
     108                 :      75096 :         return false; // No EH frames in this module
     109                 :      62715 :     ensure_enter_function(M);
     110                 :      62715 :     Function *leave_func = M.getFunction(XSTR(jl_pop_handler));
     111                 :      62715 :     Function *jlenter_func = M.getFunction(XSTR(jl_enter_handler));
     112                 :      62715 :     Function *setjmp_func = M.getFunction(jl_setjmp_name);
     113                 :            : 
     114                 :      62715 :     auto T_pint8 = Type::getInt8PtrTy(M.getContext(), 0);
     115                 :      62715 :     Function *lifetime_start = Intrinsic::getDeclaration(&M, Intrinsic::lifetime_start, { T_pint8 });
     116                 :      62715 :     Function *lifetime_end = Intrinsic::getDeclaration(&M, Intrinsic::lifetime_end, { T_pint8 });
     117                 :            : 
     118                 :            :     /* Step 1: EH Depth Numbering */
     119                 :     125430 :     std::map<llvm::CallInst *, int> EnterDepth;
     120                 :     125430 :     std::map<llvm::CallInst *, int> LeaveDepth;
     121                 :     125430 :     std::map<BasicBlock *, int> ExitDepth;
     122                 :      62715 :     int MaxDepth = 0;
     123                 :            :     // Compute EH Depth at each basic block using a DFS traversal.
     124                 :     761546 :     for (df_iterator<BasicBlock *> I = df_begin(&F.getEntryBlock()),
     125         [ +  + ]:     698831 :             E = df_end(&F.getEntryBlock()); I != E; ++I) {
     126                 :     636116 :         auto *BB = *I;
     127                 :     636116 :         int Depth = 0;
     128                 :            :         /* Here we use the assumption that all incoming edges have the same
     129                 :            :          * EH depth.
     130                 :            :          */
     131         [ +  + ]:     708754 :         for (auto *Pred : predecessors(BB)) {
     132                 :     646039 :             auto it = ExitDepth.find(Pred);
     133         [ +  + ]:     646039 :             if (it != ExitDepth.end()) {
     134                 :     573401 :                 Depth = it->second;
     135                 :     573401 :                 break;
     136                 :            :             }
     137                 :            :         }
     138                 :            :         /* Compute the depth within the basic block */
     139         [ +  + ]:   10220800 :         for (auto &I : *BB) {
     140                 :    9584730 :             auto *CI = dyn_cast<CallInst>(&I);
     141         [ +  + ]:    9584730 :             if (!CI)
     142                 :    8991040 :                 continue;
     143                 :     626877 :             Function *Callee = CI->getCalledFunction();
     144         [ +  + ]:     626877 :             if (!Callee)
     145                 :      33191 :                 continue;
     146         [ +  + ]:     593686 :             if (Callee == except_enter_func)
     147                 :       4838 :                 EnterDepth[CI] = Depth++;
     148         [ +  + ]:     588848 :             else if (Callee == leave_func) {
     149                 :      10046 :                 LeaveDepth[CI] = Depth;
     150                 :      10046 :                 Depth -= cast<ConstantInt>(CI->getArgOperand(0))->getLimitedValue();
     151                 :            :             }
     152                 :            :             assert(Depth >= 0);
     153         [ +  + ]:     593686 :             if (Depth > MaxDepth)
     154                 :       4126 :                 MaxDepth = Depth;
     155                 :            :         }
     156                 :            :         /* Remember the depth at the BB boundary */
     157                 :     636116 :         ExitDepth[BB] = Depth;
     158                 :            :     }
     159                 :            : 
     160                 :            :     /* Step 2: EH Frame lowering */
     161                 :            :     // Allocate stack space for each handler. We allocate these as separate
     162                 :            :     // allocas so the optimizer can later merge and rearrange them if it wants
     163                 :            :     // to.
     164                 :      62715 :     Value *handler_sz = ConstantInt::get(Type::getInt32Ty(F.getContext()),
     165                 :            :                                          sizeof(jl_handler_t));
     166                 :      62715 :     Value *handler_sz64 = ConstantInt::get(Type::getInt64Ty(F.getContext()),
     167                 :            :                                            sizeof(jl_handler_t));
     168                 :      62715 :     Instruction *firstInst = &F.getEntryBlock().front();
     169                 :      62715 :     std::vector<AllocaInst *> buffs;
     170         [ +  + ]:      66841 :     for (int i = 0; i < MaxDepth; ++i) {
     171                 :       4126 :         auto *buff = new AllocaInst(Type::getInt8Ty(F.getContext()), 0,
     172                 :       4126 :                 handler_sz, Align(16), "", firstInst);
     173                 :       4126 :         buffs.push_back(buff);
     174                 :            :     }
     175                 :            : 
     176                 :            :     // Lower enter funcs
     177         [ +  + ]:      67553 :     for (auto it : EnterDepth) {
     178                 :            :         assert(it.second >= 0);
     179                 :       4838 :         AllocaInst *buff = buffs[it.second];
     180                 :       4838 :         CallInst *enter = it.first;
     181                 :       4838 :         auto new_enter = CallInst::Create(jlenter_func, buff, "", enter);
     182                 :            :         Value *lifetime_args[] = {
     183                 :            :             handler_sz64,
     184                 :            :             buff
     185                 :       4838 :         };
     186                 :       4838 :         CallInst::Create(lifetime_start, lifetime_args, "", new_enter);
     187                 :            : #ifndef _OS_WINDOWS_
     188                 :            :         // For LLVM 3.3 compatibility
     189                 :            :         Value *args[] = {buff,
     190                 :       4838 :                          ConstantInt::get(Type::getInt32Ty(F.getContext()), 0)};
     191                 :       4838 :         auto sj = CallInst::Create(setjmp_func, args, "", enter);
     192                 :            : #else
     193                 :            :         auto sj = CallInst::Create(setjmp_func, buff, "", enter);
     194                 :            : #endif
     195                 :            :         // We need to mark this on the call site as well. See issue #6757
     196                 :       4838 :         sj->setCanReturnTwice();
     197         [ +  + ]:       4838 :         if (auto dbg = enter->getMetadata(LLVMContext::MD_dbg)) {
     198                 :       4822 :             new_enter->setMetadata(LLVMContext::MD_dbg, dbg);
     199                 :       4822 :             sj->setMetadata(LLVMContext::MD_dbg, dbg);
     200                 :            :         }
     201                 :       4838 :         enter->replaceAllUsesWith(sj);
     202                 :       4838 :         enter->eraseFromParent();
     203                 :            :     }
     204                 :            :     // Insert lifetime end intrinsics after every leave.
     205         [ +  + ]:      72761 :     for (auto it : LeaveDepth) {
     206                 :      10046 :         int StartDepth = it.second - 1;
     207                 :      10046 :         int npops = cast<ConstantInt>(it.first->getArgOperand(0))->getLimitedValue();
     208         [ +  + ]:      20154 :         for (int i = 0; i < npops; ++i) {
     209                 :            :             assert(StartDepth-i >= 0);
     210                 :            :             Value *lifetime_args[] = {
     211                 :            :                 handler_sz64,
     212                 :      10108 :                 buffs[StartDepth-i]
     213                 :      10108 :             };
     214                 :      10108 :             auto LifetimeEnd = CallInst::Create(lifetime_end, lifetime_args);
     215                 :      10108 :             LifetimeEnd->insertAfter(it.first);
     216                 :            :         }
     217                 :            :     }
     218                 :      62715 :     return true;
     219                 :            : }
     220                 :            : 
     221                 :            : } // anonymous namespace
     222                 :            : 
     223                 :          0 : PreservedAnalyses LowerExcHandlers::run(Function &F, FunctionAnalysisManager &AM)
     224                 :            : {
     225         [ #  # ]:          0 :     if (lowerExcHandlers(F)) {
     226                 :          0 :         return PreservedAnalyses::allInSet<CFGAnalyses>();
     227                 :            :     }
     228                 :          0 :     return PreservedAnalyses::all();
     229                 :            : }
     230                 :            : 
     231                 :            : 
     232                 :            : struct LowerExcHandlersLegacy : public FunctionPass {
     233                 :            :     static char ID;
     234                 :         17 :     LowerExcHandlersLegacy() : FunctionPass(ID)
     235                 :         17 :     {}
     236                 :     137811 :     bool runOnFunction(Function &F) {
     237                 :     137811 :         return lowerExcHandlers(F);
     238                 :            :     }
     239                 :            : };
     240                 :            : 
     241                 :            : char LowerExcHandlersLegacy::ID = 0;
     242                 :            : static RegisterPass<LowerExcHandlersLegacy> X("LowerExcHandlers", "Lower Julia Exception Handlers",
     243                 :            :                                          false /* Only looks at CFG */,
     244                 :            :                                          false /* Analysis Pass */);
     245                 :            : 
     246                 :         17 : Pass *createLowerExcHandlersPass()
     247                 :            : {
     248                 :         17 :     return new LowerExcHandlersLegacy();
     249                 :            : }
     250                 :            : 
     251                 :          0 : extern "C" JL_DLLEXPORT void LLVMExtraAddLowerExcHandlersPass_impl(LLVMPassManagerRef PM)
     252                 :            : {
     253                 :          0 :     unwrap(PM)->add(createLowerExcHandlersPass());
     254                 :          0 : }

Generated by: LCOV version 1.14