LCOV - code coverage report
Current view: top level - src - llvm-final-gc-lowering.cpp (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 172 199 86.4 %
Date: 2022-07-17 01:01:28 Functions: 17 19 89.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 38 52 73.1 %

           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/IR/LegacyPassManager.h>
       7                 :            : #include <llvm/IR/Function.h>
       8                 :            : #include <llvm/IR/IntrinsicInst.h>
       9                 :            : #include <llvm/IR/Module.h>
      10                 :            : #include <llvm/IR/IRBuilder.h>
      11                 :            : #include <llvm/Pass.h>
      12                 :            : #include <llvm/Support/Debug.h>
      13                 :            : #include <llvm/Transforms/Utils/ModuleUtils.h>
      14                 :            : 
      15                 :            : #include "codegen_shared.h"
      16                 :            : #include "julia.h"
      17                 :            : #include "julia_internal.h"
      18                 :            : #include "llvm-pass-helpers.h"
      19                 :            : 
      20                 :            : #define DEBUG_TYPE "final_gc_lowering"
      21                 :            : 
      22                 :            : using namespace llvm;
      23                 :            : 
      24                 :            : // The final GC lowering pass. This pass lowers platform-agnostic GC
      25                 :            : // intrinsics to platform-dependent instruction sequences. The
      26                 :            : // intrinsics it targets are those produced by the late GC frame
      27                 :            : // lowering pass.
      28                 :            : //
      29                 :            : // This pass targets typical back-ends for which the standard Julia
      30                 :            : // runtime library is available. Atypical back-ends should supply
      31                 :            : // their own lowering pass.
      32                 :            : 
      33                 :            : struct FinalLowerGC: private JuliaPassContext {
      34                 :            :     bool runOnFunction(Function &F);
      35                 :            :     bool doInitialization(Module &M);
      36                 :            :     bool doFinalization(Module &M);
      37                 :            : 
      38                 :            : private:
      39                 :            :     Function *queueRootFunc;
      40                 :            :     Function *queueBindingFunc;
      41                 :            :     Function *poolAllocFunc;
      42                 :            :     Function *bigAllocFunc;
      43                 :            :     Instruction *pgcstack;
      44                 :            : 
      45                 :            :     // Lowers a `julia.new_gc_frame` intrinsic.
      46                 :            :     Value *lowerNewGCFrame(CallInst *target, Function &F);
      47                 :            : 
      48                 :            :     // Lowers a `julia.push_gc_frame` intrinsic.
      49                 :            :     void lowerPushGCFrame(CallInst *target, Function &F);
      50                 :            : 
      51                 :            :     // Lowers a `julia.pop_gc_frame` intrinsic.
      52                 :            :     void lowerPopGCFrame(CallInst *target, Function &F);
      53                 :            : 
      54                 :            :     // Lowers a `julia.get_gc_frame_slot` intrinsic.
      55                 :            :     Value *lowerGetGCFrameSlot(CallInst *target, Function &F);
      56                 :            : 
      57                 :            :     // Lowers a `julia.gc_alloc_bytes` intrinsic.
      58                 :            :     Value *lowerGCAllocBytes(CallInst *target, Function &F);
      59                 :            : 
      60                 :            :     // Lowers a `julia.queue_gc_root` intrinsic.
      61                 :            :     Value *lowerQueueGCRoot(CallInst *target, Function &F);
      62                 :            : 
      63                 :            :     // Lowers a `julia.queue_gc_binding` intrinsic.
      64                 :            :     Value *lowerQueueGCBinding(CallInst *target, Function &F);
      65                 :            : };
      66                 :            : 
      67                 :      60842 : Value *FinalLowerGC::lowerNewGCFrame(CallInst *target, Function &F)
      68                 :            : {
      69                 :            :     assert(target->arg_size() == 1);
      70                 :      60842 :     unsigned nRoots = cast<ConstantInt>(target->getArgOperand(0))->getLimitedValue(INT_MAX);
      71                 :            : 
      72                 :            :     // Create the GC frame.
      73                 :            :     AllocaInst *gcframe = new AllocaInst(
      74                 :      60842 :         T_prjlvalue,
      75                 :            :         0,
      76                 :      60842 :         ConstantInt::get(Type::getInt32Ty(F.getContext()), nRoots + 2),
      77                 :      60842 :         Align(16));
      78                 :      60842 :     gcframe->insertAfter(target);
      79                 :      60842 :     gcframe->takeName(target);
      80                 :            : 
      81                 :            :     // Zero out the GC frame.
      82                 :      60842 :     BitCastInst *tempSlot_i8 = new BitCastInst(gcframe, Type::getInt8PtrTy(F.getContext()), "");
      83                 :      60842 :     tempSlot_i8->insertAfter(gcframe);
      84                 :      60842 :     Type *argsT[2] = {tempSlot_i8->getType(), Type::getInt32Ty(F.getContext())};
      85                 :      60842 :     Function *memset = Intrinsic::getDeclaration(F.getParent(), Intrinsic::memset, makeArrayRef(argsT));
      86                 :            :     Value *args[4] = {
      87                 :            :         tempSlot_i8, // dest
      88                 :      60842 :         ConstantInt::get(Type::getInt8Ty(F.getContext()), 0), // val
      89                 :      60842 :         ConstantInt::get(Type::getInt32Ty(F.getContext()), sizeof(jl_value_t*) * (nRoots + 2)), // len
      90                 :     182526 :         ConstantInt::get(Type::getInt1Ty(F.getContext()), 0)}; // volatile
      91                 :      60842 :     CallInst *zeroing = CallInst::Create(memset, makeArrayRef(args));
      92                 :      60842 :     cast<MemSetInst>(zeroing)->setDestAlignment(16);
      93                 :      60842 :     zeroing->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
      94                 :      60842 :     zeroing->insertAfter(tempSlot_i8);
      95                 :            : 
      96                 :      60842 :     return gcframe;
      97                 :            : }
      98                 :            : 
      99                 :      60842 : void FinalLowerGC::lowerPushGCFrame(CallInst *target, Function &F)
     100                 :            : {
     101                 :            :     assert(target->arg_size() == 2);
     102                 :      60842 :     auto gcframe = target->getArgOperand(0);
     103                 :      60842 :     unsigned nRoots = cast<ConstantInt>(target->getArgOperand(1))->getLimitedValue(INT_MAX);
     104                 :            : 
     105                 :      60842 :     IRBuilder<> builder(target->getContext());
     106                 :      60842 :     builder.SetInsertPoint(&*(++BasicBlock::iterator(target)));
     107                 :     243368 :     StoreInst *inst = builder.CreateAlignedStore(
     108                 :      60842 :                 ConstantInt::get(getSizeTy(F.getContext()), JL_GC_ENCODE_PUSHARGS(nRoots)),
     109                 :            :                 builder.CreateBitCast(
     110                 :      60842 :                         builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 0),
     111                 :      60842 :                         getSizeTy(F.getContext())->getPointerTo()),
     112                 :      60842 :                 Align(sizeof(void*)));
     113                 :      60842 :     inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
     114                 :      60842 :     auto T_ppjlvalue = JuliaType::get_ppjlvalue_ty(F.getContext());
     115                 :     243368 :     inst = builder.CreateAlignedStore(
     116                 :      60842 :             builder.CreateAlignedLoad(T_ppjlvalue, pgcstack, Align(sizeof(void*))),
     117                 :            :             builder.CreatePointerCast(
     118                 :      60842 :                     builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 1),
     119                 :      60842 :                     PointerType::get(T_ppjlvalue, 0)),
     120                 :      60842 :             Align(sizeof(void*)));
     121                 :      60842 :     inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
     122                 :     121684 :     inst = builder.CreateAlignedStore(
     123                 :            :             gcframe,
     124                 :      60842 :             builder.CreateBitCast(pgcstack, PointerType::get(PointerType::get(T_prjlvalue, 0), 0)),
     125                 :     121684 :             Align(sizeof(void*)));
     126                 :      60842 : }
     127                 :            : 
     128                 :      58182 : void FinalLowerGC::lowerPopGCFrame(CallInst *target, Function &F)
     129                 :            : {
     130                 :            :     assert(target->arg_size() == 1);
     131                 :      58182 :     auto gcframe = target->getArgOperand(0);
     132                 :            : 
     133                 :     116364 :     IRBuilder<> builder(target->getContext());
     134                 :      58182 :     builder.SetInsertPoint(target);
     135                 :            :     Instruction *gcpop =
     136                 :      58182 :         cast<Instruction>(builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 1));
     137                 :      58182 :     Instruction *inst = builder.CreateAlignedLoad(T_prjlvalue, gcpop, Align(sizeof(void*)));
     138                 :      58182 :     inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
     139                 :     116364 :     inst = builder.CreateAlignedStore(
     140                 :            :         inst,
     141                 :      58182 :         builder.CreateBitCast(pgcstack,
     142                 :      58182 :             PointerType::get(T_prjlvalue, 0)),
     143                 :      58182 :         Align(sizeof(void*)));
     144                 :      58182 :     inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
     145                 :      58182 : }
     146                 :            : 
     147                 :     560842 : Value *FinalLowerGC::lowerGetGCFrameSlot(CallInst *target, Function &F)
     148                 :            : {
     149                 :            :     assert(target->arg_size() == 2);
     150                 :     560842 :     auto gcframe = target->getArgOperand(0);
     151                 :     560842 :     auto index = target->getArgOperand(1);
     152                 :            : 
     153                 :            :     // Initialize an IR builder.
     154                 :     560842 :     IRBuilder<> builder(target->getContext());
     155                 :     560842 :     builder.SetInsertPoint(target);
     156                 :            : 
     157                 :            :     // The first two slots are reserved, so we'll add two to the index.
     158                 :     560842 :     index = builder.CreateAdd(index, ConstantInt::get(Type::getInt32Ty(F.getContext()), 2));
     159                 :            : 
     160                 :            :     // Lower the intrinsic as a GEP.
     161                 :     560842 :     auto gep = builder.CreateInBoundsGEP(T_prjlvalue, gcframe, index);
     162                 :     560842 :     gep->takeName(target);
     163                 :     560842 :     return gep;
     164                 :            : }
     165                 :            : 
     166                 :      26899 : Value *FinalLowerGC::lowerQueueGCRoot(CallInst *target, Function &F)
     167                 :            : {
     168                 :            :     assert(target->arg_size() == 1);
     169                 :      26899 :     target->setCalledFunction(queueRootFunc);
     170                 :      26899 :     return target;
     171                 :            : }
     172                 :            : 
     173                 :        639 : Value *FinalLowerGC::lowerQueueGCBinding(CallInst *target, Function &F)
     174                 :            : {
     175                 :            :     assert(target->arg_size() == 1);
     176                 :        639 :     target->setCalledFunction(queueBindingFunc);
     177                 :        639 :     return target;
     178                 :            : }
     179                 :            : 
     180                 :      63997 : Value *FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F)
     181                 :            : {
     182                 :            :     assert(target->arg_size() == 2);
     183                 :      63997 :     auto sz = (size_t)cast<ConstantInt>(target->getArgOperand(1))->getZExtValue();
     184                 :            :     // This is strongly architecture and OS dependent
     185                 :            :     int osize;
     186                 :      63997 :     int offset = jl_gc_classify_pools(sz, &osize);
     187                 :      63997 :     IRBuilder<> builder(target);
     188                 :      63997 :     builder.SetCurrentDebugLocation(target->getDebugLoc());
     189                 :      63997 :     auto ptls = target->getArgOperand(0);
     190                 :            :     CallInst *newI;
     191         [ -  + ]:      63997 :     if (offset < 0) {
     192                 :          0 :         newI = builder.CreateCall(
     193                 :            :             bigAllocFunc,
     194                 :          0 :             { ptls, ConstantInt::get(getSizeTy(F.getContext()), sz + sizeof(void*)) });
     195                 :            :     }
     196                 :            :     else {
     197                 :      63997 :         auto pool_offs = ConstantInt::get(Type::getInt32Ty(F.getContext()), offset);
     198                 :      63997 :         auto pool_osize = ConstantInt::get(Type::getInt32Ty(F.getContext()), osize);
     199                 :      63997 :         newI = builder.CreateCall(poolAllocFunc, { ptls, pool_offs, pool_osize });
     200                 :            :     }
     201                 :      63997 :     newI->setAttributes(newI->getCalledFunction()->getAttributes());
     202                 :      63997 :     newI->takeName(target);
     203                 :      63997 :     return newI;
     204                 :            : }
     205                 :            : 
     206                 :      40403 : bool FinalLowerGC::doInitialization(Module &M) {
     207                 :            :     // Initialize platform-agnostic references.
     208                 :      40403 :     initAll(M);
     209                 :            : 
     210                 :            :     // Initialize platform-specific references.
     211                 :      40403 :     queueRootFunc = getOrDeclare(jl_well_known::GCQueueRoot);
     212                 :      40403 :     queueBindingFunc = getOrDeclare(jl_well_known::GCQueueBinding);
     213                 :      40403 :     poolAllocFunc = getOrDeclare(jl_well_known::GCPoolAlloc);
     214                 :      40403 :     bigAllocFunc = getOrDeclare(jl_well_known::GCBigAlloc);
     215                 :            : 
     216                 :      40403 :     GlobalValue *functionList[] = {queueRootFunc, queueBindingFunc, poolAllocFunc, bigAllocFunc};
     217                 :      40403 :     unsigned j = 0;
     218         [ +  + ]:     202015 :     for (unsigned i = 0; i < sizeof(functionList) / sizeof(void*); i++) {
     219         [ -  + ]:     161612 :         if (!functionList[i])
     220                 :          0 :             continue;
     221         [ -  + ]:     161612 :         if (i != j)
     222                 :          0 :             functionList[j] = functionList[i];
     223                 :     161612 :         j++;
     224                 :            :     }
     225         [ +  - ]:      40403 :     if (j != 0)
     226                 :      40403 :         appendToCompilerUsed(M, ArrayRef<GlobalValue*>(functionList, j));
     227                 :      40403 :     return true;
     228                 :            : }
     229                 :            : 
     230                 :      40403 : bool FinalLowerGC::doFinalization(Module &M)
     231                 :            : {
     232                 :      40403 :     GlobalValue *functionList[] = {queueRootFunc, queueBindingFunc, poolAllocFunc, bigAllocFunc};
     233                 :      40403 :     queueRootFunc = queueBindingFunc = poolAllocFunc = bigAllocFunc = nullptr;
     234                 :      40403 :     auto used = M.getGlobalVariable("llvm.compiler.used");
     235         [ -  + ]:      40403 :     if (!used)
     236                 :          0 :         return false;
     237                 :            :     SmallPtrSet<Constant*, 16> InitAsSet(
     238                 :            :         functionList,
     239                 :      80806 :         functionList + sizeof(functionList) / sizeof(void*));
     240                 :      40403 :     bool changed = false;
     241                 :      80806 :     SmallVector<Constant*, 16> init;
     242                 :      40403 :     ConstantArray *CA = cast<ConstantArray>(used->getInitializer());
     243         [ +  + ]:     202015 :     for (auto &Op : CA->operands()) {
     244                 :     161612 :         Constant *C = cast_or_null<Constant>(Op);
     245         [ +  - ]:     161612 :         if (InitAsSet.count(C->stripPointerCasts())) {
     246                 :     161612 :             changed = true;
     247                 :     161612 :             continue;
     248                 :            :         }
     249                 :          0 :         init.push_back(C);
     250                 :            :     }
     251         [ -  + ]:      40403 :     if (!changed)
     252                 :          0 :         return false;
     253                 :      40403 :     used->eraseFromParent();
     254         [ +  - ]:      40403 :     if (init.empty())
     255                 :      40403 :         return true;
     256                 :          0 :     ArrayType *ATy = ArrayType::get(Type::getInt8PtrTy(M.getContext()), init.size());
     257                 :          0 :     used = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
     258                 :          0 :                                     ConstantArray::get(ATy, init), "llvm.compiler.used");
     259                 :          0 :     used->setSection("llvm.metadata");
     260                 :          0 :     return true;
     261                 :            : }
     262                 :            : 
     263                 :            : template<typename TIterator>
     264                 :     713219 : static void replaceInstruction(
     265                 :            :     Instruction *oldInstruction,
     266                 :            :     Value *newInstruction,
     267                 :            :     TIterator &it)
     268                 :            : {
     269         [ +  + ]:     713219 :     if (newInstruction != oldInstruction) {
     270                 :     685681 :         oldInstruction->replaceAllUsesWith(newInstruction);
     271                 :     685681 :         it = oldInstruction->eraseFromParent();
     272                 :            :     }
     273                 :            :     else {
     274                 :      27538 :         ++it;
     275                 :            :     }
     276                 :     713219 : }
     277                 :            : 
     278                 :     137811 : bool FinalLowerGC::runOnFunction(Function &F)
     279                 :            : {
     280                 :            :     LLVM_DEBUG(dbgs() << "FINAL GC LOWERING: Processing function " << F.getName() << "\n");
     281                 :            :     // Check availability of functions again since they might have been deleted.
     282                 :     137811 :     initFunctions(*F.getParent());
     283         [ +  + ]:     137811 :     if (!pgcstack_getter)
     284                 :       5557 :         return false;
     285                 :            : 
     286                 :            :     // Look for a call to 'julia.get_pgcstack'.
     287                 :     132254 :     pgcstack = getPGCstack(F);
     288         [ +  + ]:     132254 :     if (!pgcstack)
     289                 :       1183 :         return false;
     290                 :            : 
     291                 :            :     // Acquire intrinsic functions.
     292                 :     131071 :     auto newGCFrameFunc = getOrNull(jl_intrinsics::newGCFrame);
     293                 :     131071 :     auto pushGCFrameFunc = getOrNull(jl_intrinsics::pushGCFrame);
     294                 :     131071 :     auto popGCFrameFunc = getOrNull(jl_intrinsics::popGCFrame);
     295                 :     131071 :     auto getGCFrameSlotFunc = getOrNull(jl_intrinsics::getGCFrameSlot);
     296                 :     131071 :     auto GCAllocBytesFunc = getOrNull(jl_intrinsics::GCAllocBytes);
     297                 :     131071 :     auto queueGCRootFunc = getOrNull(jl_intrinsics::queueGCRoot);
     298                 :     131071 :     auto queueGCBindingFunc = getOrNull(jl_intrinsics::queueGCBinding);
     299                 :            : 
     300                 :            :     // Lower all calls to supported intrinsics.
     301         [ +  + ]:    1875880 :     for (BasicBlock &BB : F) {
     302         [ +  + ]:   27203300 :         for (auto it = BB.begin(); it != BB.end();) {
     303                 :   25458500 :             auto *CI = dyn_cast<CallInst>(&*it);
     304         [ +  + ]:   25458500 :             if (!CI) {
     305                 :   23361700 :                 ++it;
     306                 :   23361700 :                 continue;
     307                 :            :             }
     308                 :            : 
     309                 :    2096840 :             Value *callee = CI->getCalledOperand();
     310                 :            : 
     311         [ +  + ]:    2096840 :             if (callee == newGCFrameFunc) {
     312                 :      60842 :                 replaceInstruction(CI, lowerNewGCFrame(CI, F), it);
     313                 :            :             }
     314         [ +  + ]:    2036000 :             else if (callee == pushGCFrameFunc) {
     315                 :      60842 :                 lowerPushGCFrame(CI, F);
     316                 :      60842 :                 it = CI->eraseFromParent();
     317                 :            :             }
     318         [ +  + ]:    1975160 :             else if (callee == popGCFrameFunc) {
     319                 :      58182 :                 lowerPopGCFrame(CI, F);
     320                 :      58182 :                 it = CI->eraseFromParent();
     321                 :            :             }
     322         [ +  + ]:    1916970 :             else if (callee == getGCFrameSlotFunc) {
     323                 :     560842 :                 replaceInstruction(CI, lowerGetGCFrameSlot(CI, F), it);
     324                 :            :             }
     325         [ +  + ]:    1356130 :             else if (callee == GCAllocBytesFunc) {
     326                 :      63997 :                 replaceInstruction(CI, lowerGCAllocBytes(CI, F), it);
     327                 :            :             }
     328         [ +  + ]:    1292130 :             else if (callee == queueGCRootFunc) {
     329                 :      26899 :                 replaceInstruction(CI, lowerQueueGCRoot(CI, F), it);
     330                 :            :             }
     331         [ +  + ]:    1265240 :             else if (callee == queueGCBindingFunc) {
     332                 :        639 :                 replaceInstruction(CI, lowerQueueGCBinding(CI, F), it);
     333                 :            :             }
     334                 :            :             else {
     335                 :    1264600 :                 ++it;
     336                 :            :             }
     337                 :            :         }
     338                 :            :     }
     339                 :            : 
     340                 :     131071 :     return true;
     341                 :            : }
     342                 :            : 
     343                 :            : struct FinalLowerGCLegacy: public FunctionPass {
     344                 :            :     static char ID;
     345                 :         17 :     FinalLowerGCLegacy() : FunctionPass(ID), finalLowerGC(FinalLowerGC()) {}
     346                 :            : 
     347                 :            : protected:
     348                 :         17 :     void getAnalysisUsage(AnalysisUsage &AU) const override {
     349                 :         17 :         FunctionPass::getAnalysisUsage(AU);
     350                 :         17 :     }
     351                 :            : 
     352                 :            : private:
     353                 :            :     bool runOnFunction(Function &F) override;
     354                 :            :     bool doInitialization(Module &M) override;
     355                 :            :     bool doFinalization(Module &M) override;
     356                 :            : 
     357                 :            :     FinalLowerGC finalLowerGC;
     358                 :            : };
     359                 :            : 
     360                 :     137811 : bool FinalLowerGCLegacy::runOnFunction(Function &F) {
     361                 :     137811 :     return finalLowerGC.runOnFunction(F);
     362                 :            : }
     363                 :            : 
     364                 :      40403 : bool FinalLowerGCLegacy::doInitialization(Module &M) {
     365                 :      40403 :     return finalLowerGC.doInitialization(M);
     366                 :            : }
     367                 :            : 
     368                 :      40403 : bool FinalLowerGCLegacy::doFinalization(Module &M) {
     369                 :      40403 :     return finalLowerGC.doFinalization(M);
     370                 :            : }
     371                 :            : 
     372                 :            : 
     373                 :          0 : PreservedAnalyses FinalLowerGCPass::run(Module &M, ModuleAnalysisManager &AM)
     374                 :            : {
     375                 :          0 :     auto finalLowerGC = FinalLowerGC();
     376                 :          0 :     bool modified = false;
     377                 :          0 :     modified |= finalLowerGC.doInitialization(M);
     378         [ #  # ]:          0 :     for (auto &F : M.functions()) {
     379         [ #  # ]:          0 :         if (F.isDeclaration())
     380                 :          0 :             continue;
     381                 :          0 :         modified |= finalLowerGC.runOnFunction(F);
     382                 :            :     }
     383                 :          0 :     modified |= finalLowerGC.doFinalization(M);
     384         [ #  # ]:          0 :     if (modified) {
     385                 :          0 :         return PreservedAnalyses::allInSet<CFGAnalyses>();
     386                 :            :     }
     387                 :          0 :     return PreservedAnalyses::all();
     388                 :            : }
     389                 :            : 
     390                 :            : char FinalLowerGCLegacy::ID = 0;
     391                 :            : static RegisterPass<FinalLowerGCLegacy> X("FinalLowerGC", "Final GC intrinsic lowering pass", false, false);
     392                 :            : 
     393                 :         17 : Pass *createFinalLowerGCPass()
     394                 :            : {
     395                 :         17 :     return new FinalLowerGCLegacy();
     396                 :            : }
     397                 :            : 
     398                 :          0 : extern "C" JL_DLLEXPORT void LLVMExtraAddFinalLowerGCPass_impl(LLVMPassManagerRef PM)
     399                 :            : {
     400                 :          0 :     unwrap(PM)->add(createFinalLowerGCPass());
     401                 :          0 : }

Generated by: LCOV version 1.14