LCOV - code coverage report
Current view: top level - src - llvm-alloc-opt.cpp (source / functions) Hit Total Coverage
Test: [test only] commit 0f242327d2cc9bd130497f44b6350c924185606a Lines: 559 751 74.4 %
Date: 2022-07-16 23:42:53 Functions: 43 46 93.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 318 502 63.3 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : 
       3                 :            : #undef DEBUG
       4                 :            : #include "llvm-version.h"
       5                 :            : 
       6                 :            : #include <llvm-c/Core.h>
       7                 :            : #include <llvm-c/Types.h>
       8                 :            : 
       9                 :            : #include <llvm/ADT/SmallSet.h>
      10                 :            : #include <llvm/ADT/SmallVector.h>
      11                 :            : #include <llvm/ADT/SetVector.h>
      12                 :            : #include <llvm/ADT/Statistic.h>
      13                 :            : #include <llvm/IR/Value.h>
      14                 :            : #include <llvm/IR/CFG.h>
      15                 :            : #include <llvm/IR/LegacyPassManager.h>
      16                 :            : #include <llvm/IR/Dominators.h>
      17                 :            : #include <llvm/IR/Function.h>
      18                 :            : #include <llvm/IR/Instructions.h>
      19                 :            : #include <llvm/IR/IntrinsicInst.h>
      20                 :            : #include <llvm/IR/Module.h>
      21                 :            : #include <llvm/IR/Operator.h>
      22                 :            : #include <llvm/IR/IRBuilder.h>
      23                 :            : #include <llvm/IR/Verifier.h>
      24                 :            : #include <llvm/Pass.h>
      25                 :            : #include <llvm/Support/Debug.h>
      26                 :            : #include <llvm/Transforms/Utils/PromoteMemToReg.h>
      27                 :            : 
      28                 :            : #include <llvm/InitializePasses.h>
      29                 :            : 
      30                 :            : #include "codegen_shared.h"
      31                 :            : #include "julia.h"
      32                 :            : #include "julia_internal.h"
      33                 :            : #include "llvm-pass-helpers.h"
      34                 :            : #include "llvm-alloc-helpers.h"
      35                 :            : #include "passes.h"
      36                 :            : 
      37                 :            : #include <map>
      38                 :            : #include <set>
      39                 :            : 
      40                 :            : #define DEBUG_TYPE "alloc_opt"
      41                 :            : #include "julia_assert.h"
      42                 :            : 
      43                 :            : using namespace llvm;
      44                 :            : using namespace jl_alloc;
      45                 :            : 
      46                 :            : STATISTIC(RemovedAllocs, "Total number of heap allocations elided");
      47                 :            : STATISTIC(DeletedAllocs, "Total number of heap allocations fully deleted");
      48                 :            : STATISTIC(SplitAllocs, "Total number of allocations split into registers");
      49                 :            : STATISTIC(StackAllocs, "Total number of allocations moved to the stack");
      50                 :            : STATISTIC(RemovedTypeofs, "Total number of typeofs removed");
      51                 :            : STATISTIC(RemovedWriteBarriers, "Total number of write barriers removed");
      52                 :            : STATISTIC(RemovedGCPreserve, "Total number of GC preserve instructions removed");
      53                 :            : 
      54                 :            : namespace {
      55                 :            : 
      56                 :        487 : static void removeGCPreserve(CallInst *call, Instruction *val)
      57                 :            : {
      58                 :        487 :     ++RemovedGCPreserve;
      59                 :        487 :     auto replace = Constant::getNullValue(val->getType());
      60                 :        487 :     call->replaceUsesOfWith(val, replace);
      61         [ +  + ]:        974 :     for (auto &arg: call->args()) {
      62         [ +  + ]:        497 :         if (!isa<Constant>(arg.get())) {
      63                 :         10 :             return;
      64                 :            :         }
      65                 :            :     }
      66         [ +  + ]:        612 :     while (!call->use_empty()) {
      67                 :        135 :         auto end = cast<Instruction>(*call->user_begin());
      68                 :            :         // gc_preserve_end returns void.
      69         [ -  + ]:        135 :         assert(end->use_empty());
      70                 :        135 :         end->eraseFromParent();
      71                 :            :     }
      72                 :        477 :     call->eraseFromParent();
      73                 :            : }
      74                 :            : 
      75                 :            : /**
      76                 :            :  * Promote `julia.gc_alloc_obj` which do not have escaping root to a alloca.
      77                 :            :  * Uses that are not considered to escape the object (i.e. heap address) includes,
      78                 :            :  *
      79                 :            :  * * load
      80                 :            :  * * `pointer_from_objref`
      81                 :            :  * * Any real llvm intrinsics
      82                 :            :  * * gc preserve intrinsics
      83                 :            :  * * `ccall` gcroot array (`jl_roots` operand bundle)
      84                 :            :  * * store (as address)
      85                 :            :  * * addrspacecast, bitcast, getelementptr
      86                 :            :  *
      87                 :            :  *     The results of these cast instructions will be scanned recursively.
      88                 :            :  *
      89                 :            :  * All other uses are considered to escape conservatively.
      90                 :            :  */
      91                 :            : 
      92                 :            : /**
      93                 :            :  * TODO:
      94                 :            :  * * Return twice
      95                 :            :  * * Handle phi node.
      96                 :            :  * * Look through `pointer_from_objref`.
      97                 :            :  * * Handle jl_box*
      98                 :            :  */
      99                 :            : 
     100                 :            : struct AllocOpt : public JuliaPassContext {
     101                 :            : 
     102                 :            :     const DataLayout *DL;
     103                 :            : 
     104                 :            :     Function *lifetime_start;
     105                 :            :     Function *lifetime_end;
     106                 :            : 
     107                 :            :     bool doInitialization(Module &m);
     108                 :            :     bool runOnFunction(Function &F, function_ref<DominatorTree&()> GetDT);
     109                 :            : };
     110                 :            : 
     111                 :            : struct Optimizer {
     112                 :    1294250 :     Optimizer(Function &F, AllocOpt &pass, function_ref<DominatorTree&()> GetDT)
     113                 :    1294250 :         : F(F),
     114                 :            :           pass(pass),
     115                 :    1294250 :           GetDT(std::move(GetDT))
     116                 :    1294250 :     {}
     117                 :            : 
     118                 :            :     void initialize();
     119                 :            :     void optimizeAll();
     120                 :            :     bool finalize();
     121                 :            : private:
     122                 :            :     bool isSafepoint(Instruction *inst);
     123                 :            :     Instruction *getFirstSafepoint(BasicBlock *bb);
     124                 :            :     ssize_t getGCAllocSize(Instruction *I);
     125                 :            :     void pushInstruction(Instruction *I);
     126                 :            : 
     127                 :            :     void insertLifetimeEnd(Value *ptr, Constant *sz, Instruction *insert);
     128                 :            :     // insert llvm.lifetime.* calls for `ptr` with size `sz` based on the use of `orig`.
     129                 :            :     void insertLifetime(Value *ptr, Constant *sz, Instruction *orig);
     130                 :            : 
     131                 :            :     void checkInst(Instruction *I);
     132                 :            : 
     133                 :            :     void replaceIntrinsicUseWith(IntrinsicInst *call, Intrinsic::ID ID,
     134                 :            :                                  Instruction *orig_i, Instruction *new_i);
     135                 :            :     void removeAlloc(CallInst *orig_inst);
     136                 :            :     void moveToStack(CallInst *orig_inst, size_t sz, bool has_ref);
     137                 :            :     void splitOnStack(CallInst *orig_inst);
     138                 :            :     void optimizeTag(CallInst *orig_inst);
     139                 :            : 
     140                 :            :     Function &F;
     141                 :            :     AllocOpt &pass;
     142                 :            :     DominatorTree *_DT = nullptr;
     143                 :            :     function_ref<DominatorTree &()> GetDT;
     144                 :            : 
     145                 :      18499 :     DominatorTree &getDomTree()
     146                 :            :     {
     147         [ +  + ]:      18499 :         if (!_DT)
     148                 :       4475 :             _DT = &GetDT();
     149                 :      18499 :         return *_DT;
     150                 :            :     }
     151                 :            :     struct Lifetime {
     152                 :            :         struct Frame {
     153                 :            :             BasicBlock *bb;
     154                 :            :             pred_iterator p_cur;
     155                 :            :             pred_iterator p_end;
     156                 :      31966 :             Frame(BasicBlock *bb)
     157                 :      31966 :                 : bb(bb),
     158                 :      31966 :                   p_cur(pred_begin(bb)),
     159                 :      31966 :                   p_end(pred_end(bb))
     160                 :      31966 :             {}
     161                 :            :         };
     162                 :            :         typedef SmallVector<Frame,4> Stack;
     163                 :            :     };
     164                 :            :     struct ReplaceUses {
     165                 :            :         struct Frame {
     166                 :            :             Instruction *orig_i;
     167                 :            :             union {
     168                 :            :                 Instruction *new_i;
     169                 :            :                 uint32_t offset;
     170                 :            :             };
     171                 :      83028 :             Frame(Instruction *orig_i, Instruction *new_i)
     172                 :      83028 :                 : orig_i(orig_i),
     173                 :      83028 :                   new_i(new_i)
     174                 :      83028 :             {}
     175                 :        123 :             Frame(Instruction *orig_i, uint32_t offset)
     176                 :        123 :                 : orig_i(orig_i),
     177                 :        123 :                   offset(offset)
     178                 :        123 :             {}
     179                 :            :         };
     180                 :            :         typedef SmallVector<Frame,4> Stack;
     181                 :            :     };
     182                 :            : 
     183                 :            :     SetVector<std::pair<CallInst*,size_t>> worklist;
     184                 :            :     SmallVector<CallInst*,6> removed;
     185                 :            :     AllocUseInfo use_info;
     186                 :            :     CheckInst::Stack check_stack;
     187                 :            :     Lifetime::Stack lifetime_stack;
     188                 :            :     ReplaceUses::Stack replace_stack;
     189                 :            :     std::map<BasicBlock*, llvm::WeakVH> first_safepoint;
     190                 :            : };
     191                 :            : 
     192                 :  178023000 : void Optimizer::pushInstruction(Instruction *I)
     193                 :            : {
     194                 :  178023000 :     ssize_t sz = getGCAllocSize(I);
     195         [ +  + ]:  178023000 :     if (sz != -1) {
     196                 :    1544190 :         worklist.insert(std::make_pair(cast<CallInst>(I), sz));
     197                 :            :     }
     198                 :  178023000 : }
     199                 :            : 
     200                 :    1294250 : void Optimizer::initialize()
     201                 :            : {
     202         [ +  + ]:   22071800 :     for (auto &bb: F) {
     203         [ +  + ]:  198800000 :         for (auto &I: bb) {
     204                 :  178022000 :             pushInstruction(&I);
     205                 :            :         }
     206                 :            :     }
     207                 :    1294250 : }
     208                 :            : 
     209                 :    2838440 : void Optimizer::optimizeAll()
     210                 :            : {
     211         [ +  + ]:    2838440 :     while (!worklist.empty()) {
     212                 :    1544190 :         auto item = worklist.pop_back_val();
     213                 :    1544190 :         auto orig = item.first;
     214                 :    1544190 :         size_t sz = item.second;
     215                 :    1544190 :         checkInst(orig);
     216         [ +  + ]:    1544190 :         if (use_info.escaped) {
     217         [ +  + ]:     779872 :             if (use_info.hastypeof)
     218                 :        199 :                 optimizeTag(orig);
     219                 :    1525750 :             continue;
     220                 :            :         }
     221   [ +  +  +  + ]:     764322 :         if (use_info.haserror || use_info.returned) {
     222         [ +  + ]:     744495 :             if (use_info.hastypeof)
     223                 :         35 :                 optimizeTag(orig);
     224                 :     744495 :             continue;
     225                 :            :         }
     226   [ +  +  +  +  :      19827 :         if (!use_info.addrescaped && !use_info.hasload && (!use_info.haspreserve ||
                   -  + ]
     227         [ #  # ]:          0 :                                                            !use_info.refstore)) {
     228                 :            :             // No one took the address, no one reads anything and there's no meaningful
     229                 :            :             // preserve of fields (either no preserve/ccall or no object reference fields)
     230                 :            :             // We can just delete all the uses.
     231                 :        359 :             removeAlloc(orig);
     232                 :        359 :             continue;
     233                 :            :         }
     234                 :      19468 :         bool has_ref = false;
     235                 :      19468 :         bool has_refaggr = false;
     236         [ +  + ]:      40177 :         for (auto memop: use_info.memops) {
     237                 :      21678 :             auto &field = memop.second;
     238         [ +  + ]:      21678 :             if (field.hasobjref) {
     239                 :        969 :                 has_ref = true;
     240                 :            :                 // This can be relaxed a little based on hasload
     241                 :            :                 // TODO: add support for hasaggr load/store
     242   [ +  +  -  +  :        969 :                 if (field.hasaggr || field.multiloc || field.size != sizeof(void*)) {
                   -  - ]
     243                 :        969 :                     has_refaggr = true;
     244                 :        969 :                     break;
     245                 :            :                 }
     246                 :            :             }
     247                 :            :         }
     248   [ +  -  +  +  :      19468 :         if (!use_info.hasunknownmem && !use_info.addrescaped && !has_refaggr) {
                   +  + ]
     249                 :            :             // No one actually care about the memory layout of this object, split it.
     250                 :         54 :             splitOnStack(orig);
     251                 :         54 :             continue;
     252                 :            :         }
     253         [ +  + ]:      19414 :         if (has_refaggr) {
     254         [ +  + ]:        969 :             if (use_info.hastypeof)
     255                 :         46 :                 optimizeTag(orig);
     256                 :        969 :             continue;
     257                 :            :         }
     258                 :            :         // The object has no fields with mix reference access
     259                 :      18445 :         moveToStack(orig, sz, has_ref);
     260                 :            :     }
     261                 :    1294250 : }
     262                 :            : 
     263                 :    1294250 : bool Optimizer::finalize()
     264                 :            : {
     265         [ +  + ]:    1294250 :     if (removed.empty())
     266                 :    1289320 :         return false;
     267         [ +  + ]:      24082 :     for (auto inst: removed)
     268                 :      19160 :         inst->eraseFromParent();
     269                 :       4922 :     return true;
     270                 :            : }
     271                 :            : 
     272                 :     791408 : bool Optimizer::isSafepoint(Instruction *inst)
     273                 :            : {
     274                 :     791408 :     auto call = dyn_cast<CallInst>(inst);
     275         [ +  + ]:     791408 :     if (!call)
     276                 :     760546 :         return false;
     277         [ +  + ]:      30862 :     if (isa<IntrinsicInst>(call))
     278                 :      15087 :         return false;
     279         [ +  + ]:      15775 :     if (auto callee = call->getCalledFunction()) {
     280                 :            :         // Known functions emitted in codegen that are not safepoints
     281   [ +  +  -  +  :      13695 :         if (callee == pass.pointer_from_objref_func || callee->getName() == "memcmp") {
                   +  + ]
     282                 :        291 :             return false;
     283                 :            :         }
     284                 :            :     }
     285                 :      15484 :     return true;
     286                 :            : }
     287                 :            : 
     288                 :     136773 : Instruction *Optimizer::getFirstSafepoint(BasicBlock *bb)
     289                 :            : {
     290                 :     136773 :     auto it = first_safepoint.find(bb);
     291         [ +  + ]:     136773 :     if (it != first_safepoint.end()) {
     292                 :     108525 :         Value *Val = it->second;
     293         [ +  + ]:     108525 :         if (Val)
     294                 :      52632 :             return cast<Instruction>(Val);
     295                 :            :     }
     296                 :      84141 :     Instruction *first = nullptr;
     297         [ +  + ]:     748154 :     for (auto &I: *bb) {
     298         [ +  + ]:     678844 :         if (isSafepoint(&I)) {
     299                 :      14831 :             first = &I;
     300                 :      14831 :             break;
     301                 :            :         }
     302                 :            :     }
     303                 :      84141 :     first_safepoint[bb] = first;
     304                 :      84141 :     return first;
     305                 :            : }
     306                 :            : 
     307                 :  178023000 : ssize_t Optimizer::getGCAllocSize(Instruction *I)
     308                 :            : {
     309                 :  178023000 :     auto call = dyn_cast<CallInst>(I);
     310         [ +  + ]:  178023000 :     if (!call)
     311                 :  159394000 :         return -1;
     312         [ +  + ]:   18628600 :     if (call->getCalledOperand() != pass.alloc_obj_func)
     313                 :   17084400 :         return -1;
     314         [ -  + ]:    1544190 :     assert(call->arg_size() == 3);
     315                 :    1544190 :     size_t sz = (size_t)cast<ConstantInt>(call->getArgOperand(1))->getZExtValue();
     316   [ +  -  +  - ]:    1544190 :     if (sz < IntegerType::MAX_INT_BITS / 8 && sz < INT32_MAX)
     317                 :    1544190 :         return sz;
     318                 :          0 :     return -1;
     319                 :            : }
     320                 :            : 
     321                 :    1544190 : void Optimizer::checkInst(Instruction *I)
     322                 :            : {
     323                 :    1544190 :     jl_alloc::EscapeAnalysisRequiredArgs required{use_info, check_stack, pass, *pass.DL};
     324                 :    1544190 :     jl_alloc::runEscapeAnalysis(I, required);
     325                 :    1544190 : }
     326                 :            : 
     327                 :      73296 : void Optimizer::insertLifetimeEnd(Value *ptr, Constant *sz, Instruction *insert)
     328                 :            : {
     329                 :      73296 :     BasicBlock::iterator it(insert);
     330                 :      73296 :     BasicBlock::iterator begin(insert->getParent()->begin());
     331                 :            :     // Makes sure that the end is inserted before nearby start.
     332                 :            :     // We insert start before the allocation call, if it is the first safepoint we find for
     333                 :            :     // another instruction, it's better if we insert the end before the start instead of the
     334                 :            :     // allocation so that the two allocations do not have overlapping lifetime.
     335         [ +  + ]:     317231 :     while (it != begin) {
     336                 :     272688 :         --it;
     337         [ +  + ]:     272688 :         if (auto II = dyn_cast<IntrinsicInst>(&*it)) {
     338   [ +  +  +  +  :     487721 :             if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
                   +  + ]
     339                 :     243767 :                 II->getIntrinsicID() == Intrinsic::lifetime_end) {
     340                 :     243935 :                 insert = II;
     341                 :     243935 :                 continue;
     342                 :            :             }
     343                 :            :         }
     344                 :      28753 :         break;
     345                 :            :     }
     346                 :      73296 :     CallInst::Create(pass.lifetime_end, {sz, ptr}, "", insert);
     347                 :      73296 : }
     348                 :            : 
     349                 :      18499 : void Optimizer::insertLifetime(Value *ptr, Constant *sz, Instruction *orig)
     350                 :            : {
     351                 :      18499 :     CallInst::Create(pass.lifetime_start, {sz, ptr}, "", orig);
     352                 :      18499 :     BasicBlock *def_bb = orig->getParent();
     353                 :      36998 :     std::set<BasicBlock*> bbs{def_bb};
     354                 :      18499 :     auto &DT = getDomTree();
     355                 :            :     // Collect all BB where the allocation is live
     356         [ +  + ]:     139987 :     for (auto use: use_info.uses) {
     357                 :     121488 :         auto bb = use->getParent();
     358         [ +  + ]:     121488 :         if (!bbs.insert(bb).second)
     359                 :     116263 :             continue;
     360         [ -  + ]:       5225 :         assert(lifetime_stack.empty());
     361                 :       5225 :         Lifetime::Frame cur{bb};
     362                 :            :         while (true) {
     363         [ -  + ]:      41437 :             assert(cur.p_cur != cur.p_end);
     364                 :      41437 :             auto pred = *cur.p_cur;
     365                 :      41437 :             ++cur.p_cur;
     366         [ +  + ]:      41437 :             if (bbs.insert(pred).second) {
     367         [ +  + ]:      26741 :                 if (cur.p_cur != cur.p_end)
     368                 :       7898 :                     lifetime_stack.push_back(cur);
     369                 :      26741 :                 cur = Lifetime::Frame(pred);
     370                 :            :             }
     371         [ +  + ]:      41437 :             if (cur.p_cur == cur.p_end) {
     372         [ +  + ]:      13123 :                 if (lifetime_stack.empty())
     373                 :       5225 :                     break;
     374                 :       7898 :                 cur = lifetime_stack.back();
     375                 :       7898 :                 lifetime_stack.pop_back();
     376                 :            :             }
     377                 :      36212 :         }
     378                 :            :     }
     379                 :            : #ifndef JL_NDEBUG
     380         [ +  + ]:      68964 :     for (auto bb: bbs) {
     381         [ +  + ]:      50465 :         if (bb == def_bb)
     382                 :      18499 :             continue;
     383         [ +  - ]:      31966 :         if (DT.dominates(orig, bb))
     384                 :      31966 :             continue;
     385                 :          0 :         auto F = bb->getParent();
     386                 :          0 :         llvm_dump(F);
     387                 :          0 :         llvm_dump(orig);
     388                 :          0 :         jl_safe_printf("Does not dominate BB:\n");
     389                 :          0 :         llvm_dump(bb);
     390                 :          0 :         abort();
     391                 :            :     }
     392                 :            : #endif
     393                 :            :     // Record extra BBs that contain invisible uses.
     394                 :      36998 :     SmallSet<BasicBlock*, 8> extra_use;
     395                 :      36998 :     SmallVector<DomTreeNodeBase<BasicBlock>*, 8> dominated;
     396         [ +  + ]:      18986 :     for (auto preserve: use_info.preserves) {
     397         [ +  + ]:       7557 :         for (auto RN = DT.getNode(preserve->getParent()); RN;
     398         [ +  + ]:       7070 :              RN = dominated.empty() ? nullptr : dominated.pop_back_val()) {
     399         [ +  + ]:      13790 :             for (auto N: *RN) {
     400                 :       6720 :                 auto bb = N->getBlock();
     401         [ -  + ]:       6720 :                 if (extra_use.count(bb))
     402                 :        137 :                     continue;
     403                 :       6720 :                 bool ended = false;
     404         [ +  + ]:       7734 :                 for (auto end: preserve->users()) {
     405                 :       1151 :                     auto end_bb = cast<Instruction>(end)->getParent();
     406                 :       1151 :                     auto end_node = DT.getNode(end_bb);
     407   [ +  +  +  -  :       1151 :                     if (end_bb == bb || (end_node && DT.dominates(end_node, N))) {
             +  +  +  + ]
     408                 :        137 :                         ended = true;
     409                 :        137 :                         break;
     410                 :            :                     }
     411                 :            :                 }
     412         [ +  + ]:       6720 :                 if (ended)
     413                 :        137 :                     continue;
     414                 :       6583 :                 bbs.insert(bb);
     415                 :       6583 :                 extra_use.insert(bb);
     416                 :       6583 :                 dominated.push_back(N);
     417                 :            :             }
     418                 :            :         }
     419         [ -  + ]:        487 :         assert(dominated.empty());
     420                 :            :     }
     421                 :            :     // For each BB, find the first instruction(s) where the allocation is possibly dead.
     422                 :            :     // If all successors are live, then there isn't one.
     423                 :            :     // If all successors are dead, then it's the first instruction after the last use
     424                 :            :     // within the BB.
     425                 :            :     // If some successors are live and others are dead, it's the first instruction in
     426                 :            :     // the successors that are dead.
     427                 :      36998 :     std::vector<Instruction*> first_dead;
     428         [ +  + ]:      74609 :     for (auto bb: bbs) {
     429                 :      56110 :         bool has_use = false;
     430         [ +  + ]:      84232 :         for (auto succ: successors(bb)) {
     431                 :            :             // def_bb is the only bb in bbs that's not dominated by orig
     432   [ +  +  +  +  :      64014 :             if (succ != def_bb && bbs.count(succ)) {
                   +  + ]
     433                 :      35892 :                 has_use = true;
     434                 :      35892 :                 break;
     435                 :            :             }
     436                 :            :         }
     437         [ +  + ]:      56110 :         if (has_use) {
     438         [ +  + ]:      98371 :             for (auto succ: successors(bb)) {
     439         [ +  + ]:      62479 :                 if (!bbs.count(succ)) {
     440                 :      14252 :                     first_dead.push_back(&*succ->begin());
     441                 :            :                 }
     442                 :            :             }
     443                 :            :         }
     444         [ +  + ]:      20218 :         else if (extra_use.count(bb)) {
     445                 :       2537 :             first_dead.push_back(bb->getTerminator());
     446                 :            :         }
     447                 :            :         else {
     448         [ +  - ]:     132883 :             for (auto it = bb->rbegin(), end = bb->rend(); it != end; ++it) {
     449         [ +  + ]:     132883 :                 if (use_info.uses.count(&*it)) {
     450                 :      17681 :                     --it;
     451                 :      17681 :                     first_dead.push_back(&*it);
     452                 :      17681 :                     break;
     453                 :            :                 }
     454                 :            :             }
     455                 :            :         }
     456                 :            :     }
     457                 :      18499 :     bbs.clear();
     458                 :            :     // There can/need only be one lifetime.end for each allocation in each bb, use bbs
     459                 :            :     // to record that.
     460                 :            :     // Iterate through the first dead and find the first safepoint following each of them.
     461         [ +  + ]:     193630 :     while (!first_dead.empty()) {
     462                 :     175131 :         auto I = first_dead.back();
     463                 :     175131 :         first_dead.pop_back();
     464                 :     175131 :         auto bb = I->getParent();
     465         [ +  + ]:     175131 :         if (!bbs.insert(bb).second)
     466                 :      86256 :             continue;
     467         [ +  + ]:     162171 :         if (I == &*bb->begin()) {
     468                 :            :             // There's no use in or after this bb. If this bb is not dominated by
     469                 :            :             // the def then it has to be dead on entering this bb.
     470                 :            :             // Otherwise, there could be use that we don't track
     471                 :            :             // before hitting the next safepoint.
     472         [ +  + ]:     141953 :             if (!DT.dominates(orig, bb)) {
     473                 :       5180 :                 insertLifetimeEnd(ptr, sz, &*bb->getFirstInsertionPt());
     474                 :       5180 :                 continue;
     475                 :            :             }
     476         [ +  + ]:     136773 :             else if (auto insert = getFirstSafepoint(bb)) {
     477                 :      67463 :                 insertLifetimeEnd(ptr, sz, insert);
     478                 :      67463 :                 continue;
     479                 :            :             }
     480                 :            :         }
     481                 :            :         else {
     482   [ +  +  -  + ]:      20218 :             assert(bb == def_bb || DT.dominates(orig, I));
     483                 :      20218 :             BasicBlock::iterator it(I);
     484                 :      20218 :             BasicBlock::iterator end = bb->end();
     485                 :      20218 :             bool safepoint_found = false;
     486         [ +  + ]:     132129 :             for (; it != end; ++it) {
     487                 :     112564 :                 auto insert = &*it;
     488         [ +  + ]:     112564 :                 if (isSafepoint(insert)) {
     489                 :        653 :                     insertLifetimeEnd(ptr, sz, insert);
     490                 :        653 :                     safepoint_found = true;
     491                 :        653 :                     break;
     492                 :            :                 }
     493                 :            :             }
     494         [ +  + ]:      20218 :             if (safepoint_found) {
     495                 :        653 :                 continue;
     496                 :            :             }
     497                 :            :         }
     498         [ +  + ]:     229536 :         for (auto succ: successors(bb)) {
     499                 :     140661 :             first_dead.push_back(&*succ->begin());
     500                 :            :         }
     501                 :            :     }
     502                 :      18499 : }
     503                 :            : 
     504                 :       1438 : void Optimizer::replaceIntrinsicUseWith(IntrinsicInst *call, Intrinsic::ID ID,
     505                 :            :                                         Instruction *orig_i, Instruction *new_i)
     506                 :            : {
     507                 :       1438 :     auto nargs = call->arg_size();
     508                 :       2876 :     SmallVector<Value*, 8> args(nargs);
     509                 :       2876 :     SmallVector<Type*, 8> argTys(nargs);
     510         [ +  + ]:       7190 :     for (unsigned i = 0; i < nargs; i++) {
     511                 :       5752 :         auto arg = call->getArgOperand(i);
     512         [ +  + ]:       5752 :         args[i] = arg == orig_i ? new_i : arg;
     513                 :       5752 :         argTys[i] = args[i]->getType();
     514                 :            :     }
     515                 :       1438 :     auto oldfType = call->getFunctionType();
     516                 :       1438 :     auto newfType = FunctionType::get(
     517                 :            :             oldfType->getReturnType(),
     518                 :       1438 :             makeArrayRef(argTys).slice(0, oldfType->getNumParams()),
     519                 :       1438 :             oldfType->isVarArg());
     520                 :            : 
     521                 :            :     // Accumulate an array of overloaded types for the given intrinsic
     522                 :            :     // and compute the new name mangling schema
     523                 :       2876 :     SmallVector<Type*, 4> overloadTys;
     524                 :            :     {
     525                 :       2876 :         SmallVector<Intrinsic::IITDescriptor, 8> Table;
     526                 :       1438 :         getIntrinsicInfoTableEntries(ID, Table);
     527                 :       1438 :         ArrayRef<Intrinsic::IITDescriptor> TableRef = Table;
     528                 :       1438 :         auto res = Intrinsic::matchIntrinsicSignature(newfType, TableRef, overloadTys);
     529         [ -  + ]:       1438 :         assert(res == Intrinsic::MatchIntrinsicTypes_Match);
     530                 :            :         (void)res;
     531                 :       1438 :         bool matchvararg = !Intrinsic::matchIntrinsicVarArg(newfType->isVarArg(), TableRef);
     532         [ -  + ]:       1438 :         assert(matchvararg);
     533                 :            :         (void)matchvararg;
     534                 :            :     }
     535                 :       1438 :     auto newF = Intrinsic::getDeclaration(call->getModule(), ID, overloadTys);
     536         [ -  + ]:       1438 :     assert(newF->getFunctionType() == newfType);
     537                 :       1438 :     newF->setCallingConv(call->getCallingConv());
     538                 :       1438 :     auto newCall = CallInst::Create(newF, args, "", call);
     539                 :       1438 :     newCall->setTailCallKind(call->getTailCallKind());
     540                 :       1438 :     auto old_attrs = call->getAttributes();
     541                 :       1438 :     newCall->setAttributes(AttributeList::get(pass.getLLVMContext(), getFnAttrs(old_attrs),
     542                 :            :                                               getRetAttrs(old_attrs), {}));
     543                 :       1438 :     newCall->setDebugLoc(call->getDebugLoc());
     544                 :       1438 :     call->replaceAllUsesWith(newCall);
     545                 :       1438 :     call->eraseFromParent();
     546                 :       1438 : }
     547                 :            : 
     548                 :            : // This function should not erase any safepoint so that the lifetime marker can find and cache
     549                 :            : // all the original safepoints.
     550                 :      18445 : void Optimizer::moveToStack(CallInst *orig_inst, size_t sz, bool has_ref)
     551                 :            : {
     552                 :      18445 :     ++RemovedAllocs;
     553                 :      18445 :     ++StackAllocs;
     554                 :      18445 :     auto tag = orig_inst->getArgOperand(2);
     555                 :      18445 :     removed.push_back(orig_inst);
     556                 :            :     // The allocation does not escape or get used in a phi node so none of the derived
     557                 :            :     // SSA from it are live when we run the allocation again.
     558                 :            :     // It is now safe to promote the allocation to an entry block alloca.
     559                 :      18445 :     size_t align = 1;
     560                 :            :     // TODO: This is overly conservative. May want to instead pass this as a
     561                 :            :     //       parameter to the allocation function directly.
     562         [ +  + ]:      18445 :     if (sz > 1)
     563                 :      15529 :         align = MinAlign(JL_SMALL_BYTE_ALIGNMENT, NextPowerOf2(sz));
     564                 :            :     // No debug info for prolog instructions
     565                 :      36890 :     IRBuilder<> prolog_builder(&F.getEntryBlock().front());
     566                 :            :     AllocaInst *buff;
     567                 :            :     Instruction *ptr;
     568         [ +  + ]:      18445 :     if (sz == 0) {
     569                 :          6 :         ptr = buff = prolog_builder.CreateAlloca(Type::getInt8Ty(prolog_builder.getContext()), ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), 0));
     570                 :            :     }
     571         [ -  + ]:      18439 :     else if (has_ref) {
     572                 :            :         // Allocate with the correct type so that the GC frame lowering pass will
     573                 :            :         // treat this as a non-mem2reg'd alloca
     574                 :            :         // The ccall root and GC preserve handling below makes sure that
     575                 :            :         // the alloca isn't optimized out.
     576                 :          0 :         buff = prolog_builder.CreateAlloca(pass.T_prjlvalue);
     577                 :          0 :         buff->setAlignment(Align(align));
     578                 :          0 :         ptr = cast<Instruction>(prolog_builder.CreateBitCast(buff, Type::getInt8PtrTy(prolog_builder.getContext())));
     579                 :            :     }
     580                 :            :     else {
     581                 :            :         Type *buffty;
     582         [ +  + ]:      18439 :         if (pass.DL->isLegalInteger(sz * 8))
     583                 :      17198 :             buffty = Type::getIntNTy(pass.getLLVMContext(), sz * 8);
     584                 :            :         else
     585                 :       1241 :             buffty = ArrayType::get(Type::getInt8Ty(pass.getLLVMContext()), sz);
     586                 :      18439 :         buff = prolog_builder.CreateAlloca(buffty);
     587                 :      18439 :         buff->setAlignment(Align(align));
     588                 :      18439 :         ptr = cast<Instruction>(prolog_builder.CreateBitCast(buff, Type::getInt8PtrTy(prolog_builder.getContext())));
     589                 :            :     }
     590                 :      18445 :     insertLifetime(ptr, ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), sz), orig_inst);
     591                 :      18445 :     auto new_inst = cast<Instruction>(prolog_builder.CreateBitCast(ptr, JuliaType::get_pjlvalue_ty(prolog_builder.getContext())));
     592                 :      18445 :     new_inst->takeName(orig_inst);
     593                 :            : 
     594                 :      81615 :     auto simple_replace = [&] (Instruction *orig_i, Instruction *new_i) {
     595         [ -  + ]:      81615 :         if (orig_i->user_empty()) {
     596         [ #  # ]:          0 :             if (orig_i != orig_inst)
     597                 :          0 :                 orig_i->eraseFromParent();
     598                 :          0 :             return true;
     599                 :            :         }
     600                 :      81615 :         Type *orig_t = orig_i->getType();
     601                 :      81615 :         Type *new_t = new_i->getType();
     602         [ -  + ]:      81615 :         if (orig_t == new_t) {
     603                 :          0 :             orig_i->replaceAllUsesWith(new_i);
     604         [ #  # ]:          0 :             if (orig_i != orig_inst)
     605                 :          0 :                 orig_i->eraseFromParent();
     606                 :          0 :             return true;
     607                 :            :         }
     608                 :      81615 :         return false;
     609                 :      18445 :     };
     610         [ -  + ]:      18445 :     if (simple_replace(orig_inst, new_inst))
     611                 :          0 :         return;
     612         [ -  + ]:      18445 :     assert(replace_stack.empty());
     613                 :      18445 :     ReplaceUses::Frame cur{orig_inst, new_inst};
     614                 :      81615 :     auto finish_cur = [&] () {
     615         [ -  + ]:      81615 :         assert(cur.orig_i->user_empty());
     616         [ +  + ]:      81615 :         if (cur.orig_i != orig_inst) {
     617                 :      63170 :             cur.orig_i->eraseFromParent();
     618                 :            :         }
     619                 :     100060 :     };
     620                 :      63170 :     auto push_frame = [&] (Instruction *orig_i, Instruction *new_i) {
     621         [ -  + ]:      63170 :         if (simple_replace(orig_i, new_i))
     622                 :          0 :             return;
     623                 :      63170 :         replace_stack.push_back(cur);
     624                 :      63170 :         cur = {orig_i, new_i};
     625                 :      18445 :     };
     626                 :            :     // Both `orig_i` and `new_i` should be pointer of the same type
     627                 :            :     // but possibly different address spaces. `new_i` is always in addrspace 0.
     628                 :     121166 :     auto replace_inst = [&] (Instruction *user) {
     629                 :     121166 :         Instruction *orig_i = cur.orig_i;
     630                 :     121166 :         Instruction *new_i = cur.new_i;
     631   [ +  +  +  +  :     121166 :         if (isa<LoadInst>(user) || isa<StoreInst>(user)) {
                   +  + ]
     632                 :      20093 :             user->replaceUsesOfWith(orig_i, new_i);
     633                 :            :         }
     634         [ +  + ]:     101073 :         else if (auto call = dyn_cast<CallInst>(user)) {
     635                 :      37903 :             auto callee = call->getCalledOperand();
     636         [ +  + ]:      37903 :             if (pass.pointer_from_objref_func == callee) {
     637                 :      18265 :                 call->replaceAllUsesWith(new_i);
     638                 :      18265 :                 call->eraseFromParent();
     639                 :      18265 :                 return;
     640                 :            :             }
     641         [ -  + ]:      19638 :             if (pass.typeof_func == callee) {
     642                 :          0 :                 ++RemovedTypeofs;
     643                 :          0 :                 call->replaceAllUsesWith(tag);
     644                 :          0 :                 call->eraseFromParent();
     645                 :          0 :                 return;
     646                 :            :             }
     647                 :            :             // Also remove the preserve intrinsics so that it can be better optimized.
     648         [ +  + ]:      19638 :             if (pass.gc_preserve_begin_func == callee) {
     649         [ -  + ]:        487 :                 if (has_ref) {
     650                 :          0 :                     call->replaceUsesOfWith(orig_i, buff);
     651                 :            :                 }
     652                 :            :                 else {
     653                 :        487 :                     removeGCPreserve(call, orig_i);
     654                 :            :                 }
     655                 :        487 :                 return;
     656                 :            :             }
     657         [ +  - ]:      19151 :             if (pass.write_barrier_func == callee ||
     658         [ -  + ]:      19151 :                 pass.write_barrier_binding_func == callee) {
     659                 :          0 :                 ++RemovedWriteBarriers;
     660                 :          0 :                 call->eraseFromParent();
     661                 :          0 :                 return;
     662                 :            :             }
     663         [ +  + ]:      19151 :             if (auto intrinsic = dyn_cast<IntrinsicInst>(call)) {
     664         [ +  - ]:       1438 :                 if (Intrinsic::ID ID = intrinsic->getIntrinsicID()) {
     665                 :       1438 :                     replaceIntrinsicUseWith(intrinsic, ID, orig_i, new_i);
     666                 :       1438 :                     return;
     667                 :            :                 }
     668                 :            :             }
     669                 :            :             // remove from operand bundle
     670         [ -  + ]:      17713 :             Value *replace = has_ref ? (Value*)buff : Constant::getNullValue(orig_i->getType());
     671                 :      17713 :             user->replaceUsesOfWith(orig_i, replace);
     672                 :            :         }
     673   [ +  +  +  +  :      63170 :         else if (isa<AddrSpaceCastInst>(user) || isa<BitCastInst>(user)) {
                   +  + ]
     674                 :      62250 :             auto cast_t = PointerType::getWithSamePointeeType(cast<PointerType>(user->getType()), AddressSpace::Generic);
     675                 :      62250 :             auto replace_i = new_i;
     676                 :      62250 :             Type *new_t = new_i->getType();
     677         [ +  + ]:      62250 :             if (cast_t != new_t) {
     678                 :      23019 :                 replace_i = new BitCastInst(replace_i, cast_t, "", user);
     679                 :      23019 :                 replace_i->setDebugLoc(user->getDebugLoc());
     680                 :      23019 :                 replace_i->takeName(user);
     681                 :            :             }
     682                 :      62250 :             push_frame(user, replace_i);
     683                 :            :         }
     684         [ +  - ]:        920 :         else if (auto gep = dyn_cast<GetElementPtrInst>(user)) {
     685                 :       1840 :             SmallVector<Value *, 4> IdxOperands(gep->idx_begin(), gep->idx_end());
     686                 :        920 :             auto new_gep = GetElementPtrInst::Create(gep->getSourceElementType(),
     687                 :            :                                                      new_i, IdxOperands,
     688                 :        920 :                                                      gep->getName(), gep);
     689                 :        920 :             new_gep->setIsInBounds(gep->isInBounds());
     690                 :        920 :             new_gep->takeName(gep);
     691                 :        920 :             new_gep->copyMetadata(*gep);
     692                 :        920 :             push_frame(gep, new_gep);
     693                 :            :         }
     694                 :            :         else {
     695                 :          0 :             abort();
     696                 :            :         }
     697                 :      18445 :     };
     698                 :            : 
     699                 :            :     while (true) {
     700                 :     121166 :         replace_inst(cast<Instruction>(*cur.orig_i->user_begin()));
     701         [ +  + ]:     184336 :         while (cur.orig_i->use_empty()) {
     702                 :      81615 :             finish_cur();
     703         [ +  + ]:      81615 :             if (replace_stack.empty())
     704                 :      18445 :                 return;
     705                 :      63170 :             cur = replace_stack.back();
     706                 :      63170 :             replace_stack.pop_back();
     707                 :            :         }
     708                 :            :     }
     709                 :            : }
     710                 :            : 
     711                 :            : // This function should not erase any safepoint so that the lifetime marker can find and cache
     712                 :            : // all the original safepoints.
     713                 :        359 : void Optimizer::removeAlloc(CallInst *orig_inst)
     714                 :            : {
     715                 :        359 :     ++RemovedAllocs;
     716                 :        359 :     ++DeletedAllocs;
     717                 :        359 :     auto tag = orig_inst->getArgOperand(2);
     718                 :        359 :     removed.push_back(orig_inst);
     719                 :       1502 :     auto simple_remove = [&] (Instruction *orig_i) {
     720         [ +  + ]:       1502 :         if (orig_i->user_empty()) {
     721         [ -  + ]:         89 :             if (orig_i != orig_inst)
     722                 :          0 :                 orig_i->eraseFromParent();
     723                 :         89 :             return true;
     724                 :            :         }
     725                 :       1413 :         return false;
     726                 :        359 :     };
     727         [ +  + ]:        359 :     if (simple_remove(orig_inst))
     728                 :         89 :         return;
     729         [ -  + ]:        270 :     assert(replace_stack.empty());
     730                 :        270 :     ReplaceUses::Frame cur{orig_inst, nullptr};
     731                 :       1413 :     auto finish_cur = [&] () {
     732         [ -  + ]:       1413 :         assert(cur.orig_i->user_empty());
     733         [ +  + ]:       1413 :         if (cur.orig_i != orig_inst) {
     734                 :       1143 :             cur.orig_i->eraseFromParent();
     735                 :            :         }
     736                 :       1683 :     };
     737                 :       1143 :     auto push_frame = [&] (Instruction *orig_i) {
     738         [ -  + ]:       1143 :         if (simple_remove(orig_i))
     739                 :          0 :             return;
     740                 :       1143 :         replace_stack.push_back(cur);
     741                 :       1143 :         cur = {orig_i, nullptr};
     742                 :        270 :     };
     743                 :       1851 :     auto remove_inst = [&] (Instruction *user) {
     744                 :       1851 :         Instruction *orig_i = cur.orig_i;
     745         [ +  + ]:       1851 :         if (auto store = dyn_cast<StoreInst>(user)) {
     746                 :            :             // All stores are known to be dead.
     747                 :            :             // The stored value might be an gc pointer in which case deleting the object
     748                 :            :             // might open more optimization opportunities.
     749         [ +  + ]:        703 :             if (auto stored_inst = dyn_cast<Instruction>(store->getValueOperand()))
     750                 :        556 :                 pushInstruction(stored_inst);
     751                 :        703 :             user->eraseFromParent();
     752                 :        703 :             return;
     753                 :            :         }
     754         [ +  + ]:       1148 :         else if (auto call = dyn_cast<CallInst>(user)) {
     755                 :          5 :             auto callee = call->getCalledOperand();
     756         [ -  + ]:          5 :             if (pass.gc_preserve_begin_func == callee) {
     757                 :          0 :                 removeGCPreserve(call, orig_i);
     758                 :          0 :                 return;
     759                 :            :             }
     760         [ +  - ]:          5 :             if (pass.typeof_func == callee) {
     761                 :          5 :                 ++RemovedTypeofs;
     762                 :          5 :                 call->replaceAllUsesWith(tag);
     763                 :          5 :                 call->eraseFromParent();
     764                 :          5 :                 return;
     765                 :            :             }
     766         [ #  # ]:          0 :             if (pass.write_barrier_func == callee ||
     767         [ #  # ]:          0 :                 pass.write_barrier_binding_func == callee) {
     768                 :          0 :                 ++RemovedWriteBarriers;
     769                 :          0 :                 call->eraseFromParent();
     770                 :          0 :                 return;
     771                 :            :             }
     772         [ #  # ]:          0 :             if (auto II = dyn_cast<IntrinsicInst>(call)) {
     773                 :          0 :                 auto id = II->getIntrinsicID();
     774   [ #  #  #  # ]:          0 :                 if (id == Intrinsic::memset || id == Intrinsic::lifetime_start ||
     775   [ #  #  #  #  :          0 :                     id == Intrinsic::lifetime_end || isa<DbgInfoIntrinsic>(II)) {
                   #  # ]
     776                 :          0 :                     call->eraseFromParent();
     777                 :          0 :                     return;
     778                 :            :                 }
     779                 :            :             }
     780                 :            :             // remove from operand bundle
     781                 :          0 :             user->replaceUsesOfWith(orig_i, Constant::getNullValue(orig_i->getType()));
     782                 :            :         }
     783   [ +  +  +  +  :       1522 :         else if (isa<AddrSpaceCastInst>(user) || isa<BitCastInst>(user) ||
             +  -  +  - ]
     784                 :        379 :                  isa<GetElementPtrInst>(user)) {
     785                 :       1143 :             push_frame(user);
     786                 :            :         }
     787                 :            :         else {
     788                 :          0 :             abort();
     789                 :            :         }
     790                 :        270 :     };
     791                 :            : 
     792                 :            :     while (true) {
     793                 :       1851 :         remove_inst(cast<Instruction>(*cur.orig_i->user_begin()));
     794         [ +  + ]:       2994 :         while (cur.orig_i->use_empty()) {
     795                 :       1413 :             finish_cur();
     796         [ +  + ]:       1413 :             if (replace_stack.empty())
     797                 :        270 :                 return;
     798                 :       1143 :             cur = replace_stack.back();
     799                 :       1143 :             replace_stack.pop_back();
     800                 :            :         }
     801                 :            :     }
     802                 :            : }
     803                 :            : 
     804                 :            : // Unable to optimize out the allocation, do store to load forwarding on the tag instead.
     805                 :        280 : void Optimizer::optimizeTag(CallInst *orig_inst)
     806                 :            : {
     807                 :        280 :     auto tag = orig_inst->getArgOperand(2);
     808                 :            :     // `julia.typeof` is only legal on the original pointer, no need to scan recursively
     809                 :        280 :     size_t last_deleted = removed.size();
     810         [ +  + ]:       1575 :     for (auto user: orig_inst->users()) {
     811         [ +  + ]:       1295 :         if (auto call = dyn_cast<CallInst>(user)) {
     812                 :        456 :             auto callee = call->getCalledOperand();
     813         [ +  + ]:        456 :             if (pass.typeof_func == callee) {
     814                 :        302 :                 ++RemovedTypeofs;
     815                 :        302 :                 call->replaceAllUsesWith(tag);
     816                 :            :                 // Push to the removed instructions to trigger `finalize` to
     817                 :            :                 // return the correct result.
     818                 :            :                 // Also so that we don't have to worry about iterator invalidation...
     819                 :        302 :                 removed.push_back(call);
     820                 :            :             }
     821                 :            :         }
     822                 :            :     }
     823         [ +  + ]:        582 :     while (last_deleted < removed.size())
     824                 :        302 :         removed[last_deleted++]->replaceUsesOfWith(orig_inst, UndefValue::get(orig_inst->getType()));
     825                 :        280 : }
     826                 :            : 
     827                 :         54 : void Optimizer::splitOnStack(CallInst *orig_inst)
     828                 :            : {
     829                 :         54 :     auto tag = orig_inst->getArgOperand(2);
     830                 :         54 :     ++RemovedAllocs;
     831                 :         54 :     ++SplitAllocs;
     832                 :         54 :     removed.push_back(orig_inst);
     833                 :         54 :     IRBuilder<> prolog_builder(&F.getEntryBlock().front());
     834                 :            :     struct SplitSlot {
     835                 :            :         AllocaInst *slot;
     836                 :            :         bool isref;
     837                 :            :         uint32_t offset;
     838                 :            :         uint32_t size;
     839                 :            :     };
     840                 :         54 :     SmallVector<SplitSlot,8> slots;
     841         [ +  + ]:        108 :     for (auto memop: use_info.memops) {
     842                 :         54 :         auto offset = memop.first;
     843                 :         54 :         auto &field = memop.second;
     844                 :            :         // If the field has no reader and is not a object reference field that we
     845                 :            :         // need to preserve at some point, there's no need to allocate the field.
     846   [ -  +  -  -  :         54 :         if (!field.hasload && (!field.hasobjref || !use_info.haspreserve))
                   -  - ]
     847                 :          0 :             continue;
     848                 :         54 :         SplitSlot slot{nullptr, field.hasobjref, offset, field.size};
     849                 :            :         Type *allocty;
     850         [ -  + ]:         54 :         if (field.hasobjref) {
     851                 :          0 :             allocty = pass.T_prjlvalue;
     852                 :            :         }
     853   [ +  +  +  - ]:         54 :         else if (field.elty && !field.multiloc) {
     854                 :         53 :             allocty = field.elty;
     855                 :            :         }
     856         [ -  + ]:          1 :         else if (pass.DL->isLegalInteger(field.size * 8)) {
     857                 :          0 :             allocty = Type::getIntNTy(pass.getLLVMContext(), field.size * 8);
     858                 :            :         } else {
     859                 :          1 :             allocty = ArrayType::get(Type::getInt8Ty(pass.getLLVMContext()), field.size);
     860                 :            :         }
     861                 :         54 :         slot.slot = prolog_builder.CreateAlloca(allocty);
     862                 :         54 :         insertLifetime(prolog_builder.CreateBitCast(slot.slot, Type::getInt8PtrTy(prolog_builder.getContext())),
     863                 :         54 :                        ConstantInt::get(Type::getInt64Ty(prolog_builder.getContext()), field.size), orig_inst);
     864                 :         54 :         slots.push_back(std::move(slot));
     865                 :            :     }
     866                 :         54 :     const auto nslots = slots.size();
     867                 :        108 :     auto find_slot = [&] (uint32_t offset) {
     868         [ +  + ]:        108 :         if (offset == 0)
     869                 :        107 :             return 0u;
     870                 :          1 :         unsigned lb = 0;
     871                 :          1 :         unsigned ub = slots.size();
     872         [ -  + ]:          1 :         while (lb + 1 < ub) {
     873                 :          0 :             unsigned mid = (lb + ub) / 2;
     874         [ #  # ]:          0 :             if (slots[mid].offset <= offset) {
     875                 :          0 :                 lb = mid;
     876                 :            :             }
     877                 :            :             else {
     878                 :          0 :                 ub = mid;
     879                 :            :             }
     880                 :            :         }
     881                 :          1 :         return lb;
     882                 :         54 :     };
     883                 :        123 :     auto simple_replace = [&] (Instruction *orig_i) {
     884         [ -  + ]:        123 :         if (orig_i->user_empty()) {
     885         [ #  # ]:          0 :             if (orig_i != orig_inst)
     886                 :          0 :                 orig_i->eraseFromParent();
     887                 :          0 :             return true;
     888                 :            :         }
     889                 :        123 :         return false;
     890                 :         54 :     };
     891         [ -  + ]:         54 :     if (simple_replace(orig_inst))
     892                 :          0 :         return;
     893         [ -  + ]:         54 :     assert(replace_stack.empty());
     894                 :         54 :     ReplaceUses::Frame cur{orig_inst, uint32_t(0)};
     895                 :        123 :     auto finish_cur = [&] () {
     896         [ -  + ]:        123 :         assert(cur.orig_i->user_empty());
     897         [ +  + ]:        123 :         if (cur.orig_i != orig_inst) {
     898                 :         69 :             cur.orig_i->eraseFromParent();
     899                 :            :         }
     900                 :        177 :     };
     901                 :         69 :     auto push_frame = [&] (Instruction *orig_i, uint32_t offset) {
     902         [ -  + ]:         69 :         if (simple_replace(orig_i))
     903                 :          0 :             return;
     904                 :         69 :         replace_stack.push_back(cur);
     905                 :         69 :         cur = {orig_i, offset};
     906                 :         54 :     };
     907                 :        108 :     auto slot_gep = [&] (SplitSlot &slot, uint32_t offset, Type *elty, IRBuilder<> &builder) {
     908         [ -  + ]:        108 :         assert(slot.offset <= offset);
     909                 :        108 :         offset -= slot.offset;
     910                 :        108 :         auto size = pass.DL->getTypeAllocSize(elty);
     911                 :            :         Value *addr;
     912         [ +  - ]:        108 :         if (offset % size == 0) {
     913                 :        108 :             addr = builder.CreateBitCast(slot.slot, elty->getPointerTo());
     914         [ +  + ]:        108 :             if (offset != 0) {
     915                 :          1 :                 addr = builder.CreateConstInBoundsGEP1_32(elty, addr, offset / size);
     916                 :            :             }
     917                 :            :         }
     918                 :            :         else {
     919                 :          0 :             addr = builder.CreateBitCast(slot.slot, Type::getInt8PtrTy(builder.getContext()));
     920                 :          0 :             addr = builder.CreateConstInBoundsGEP1_32(Type::getInt8Ty(builder.getContext()), addr, offset);
     921                 :          0 :             addr = builder.CreateBitCast(addr, elty->getPointerTo());
     922                 :            :         }
     923                 :        108 :         return addr;
     924                 :         54 :     };
     925                 :        177 :     auto replace_inst = [&] (Use *use) {
     926                 :        177 :         Instruction *user = cast<Instruction>(use->getUser());
     927                 :        177 :         Instruction *orig_i = cur.orig_i;
     928                 :        177 :         uint32_t offset = cur.offset;
     929         [ +  + ]:        177 :         if (auto load = dyn_cast<LoadInst>(user)) {
     930                 :         54 :             auto slot_idx = find_slot(offset);
     931                 :         54 :             auto &slot = slots[slot_idx];
     932   [ +  -  +  - ]:         54 :             assert(slot.offset <= offset && slot.offset + slot.size >= offset);
     933                 :         54 :             IRBuilder<> builder(load);
     934                 :            :             Value *val;
     935                 :         54 :             Type *load_ty = load->getType();
     936                 :            :             LoadInst *newload;
     937         [ -  + ]:         54 :             if (slot.isref) {
     938         [ #  # ]:          0 :                 assert(slot.offset == offset);
     939                 :          0 :                 newload = builder.CreateLoad(pass.T_prjlvalue, slot.slot);
     940                 :            :                 // Assume the addrspace is correct.
     941                 :          0 :                 val = builder.CreateBitCast(newload, load_ty);
     942                 :            :             }
     943                 :            :             else {
     944                 :         54 :                 newload = builder.CreateLoad(load_ty, slot_gep(slot, offset, load_ty, builder));
     945                 :         54 :                 val = newload;
     946                 :            :             }
     947                 :            :             // TODO: should we use `load->clone()`, or manually copy any other metadata?
     948                 :         54 :             newload->setAlignment(load->getAlign());
     949                 :            :             // since we're moving heap-to-stack, it is safe to downgrade the atomic level to NotAtomic
     950                 :         54 :             newload->setOrdering(AtomicOrdering::NotAtomic);
     951                 :         54 :             load->replaceAllUsesWith(val);
     952                 :         54 :             load->eraseFromParent();
     953                 :         54 :             return;
     954                 :            :         }
     955         [ +  + ]:        123 :         else if (auto store = dyn_cast<StoreInst>(user)) {
     956         [ +  + ]:         54 :             if (auto stored_inst = dyn_cast<Instruction>(store->getValueOperand()))
     957                 :          9 :                 pushInstruction(stored_inst);
     958                 :         54 :             auto slot_idx = find_slot(offset);
     959                 :         54 :             auto &slot = slots[slot_idx];
     960   [ +  -  -  + ]:         54 :             if (slot.offset > offset || slot.offset + slot.size <= offset) {
     961                 :          0 :                 store->eraseFromParent();
     962                 :          0 :                 return;
     963                 :            :             }
     964                 :         54 :             IRBuilder<> builder(store);
     965                 :         54 :             auto store_val = store->getValueOperand();
     966                 :         54 :             auto store_ty = store_val->getType();
     967                 :            :             StoreInst *newstore;
     968         [ -  + ]:         54 :             if (slot.isref) {
     969         [ #  # ]:          0 :                 assert(slot.offset == offset);
     970                 :          0 :                 auto T_pjlvalue = JuliaType::get_pjlvalue_ty(builder.getContext());
     971         [ #  # ]:          0 :                 if (!isa<PointerType>(store_ty)) {
     972                 :          0 :                     store_val = builder.CreateBitCast(store_val, getSizeTy(builder.getContext()));
     973                 :          0 :                     store_val = builder.CreateIntToPtr(store_val, T_pjlvalue);
     974                 :          0 :                     store_ty = T_pjlvalue;
     975                 :            :                 }
     976                 :            :                 else {
     977                 :          0 :                     store_ty = PointerType::getWithSamePointeeType(T_pjlvalue, cast<PointerType>(store_ty)->getAddressSpace());
     978                 :          0 :                     store_val = builder.CreateBitCast(store_val, store_ty);
     979                 :            :                 }
     980         [ #  # ]:          0 :                 if (cast<PointerType>(store_ty)->getAddressSpace() != AddressSpace::Tracked)
     981                 :          0 :                     store_val = builder.CreateAddrSpaceCast(store_val, pass.T_prjlvalue);
     982                 :          0 :                 newstore = builder.CreateStore(store_val, slot.slot);
     983                 :            :             }
     984                 :            :             else {
     985                 :         54 :                 newstore = builder.CreateStore(store_val, slot_gep(slot, offset, store_ty, builder));
     986                 :            :             }
     987                 :            :             // TODO: should we use `store->clone()`, or manually copy any other metadata?
     988                 :         54 :             newstore->setAlignment(store->getAlign());
     989                 :            :             // since we're moving heap-to-stack, it is safe to downgrade the atomic level to NotAtomic
     990                 :         54 :             newstore->setOrdering(AtomicOrdering::NotAtomic);
     991                 :         54 :             store->eraseFromParent();
     992                 :         54 :             return;
     993                 :            :         }
     994   [ +  -  -  +  :         69 :         else if (isa<AtomicCmpXchgInst>(user) || isa<AtomicRMWInst>(user)) {
                   -  + ]
     995                 :          0 :             auto slot_idx = find_slot(offset);
     996                 :          0 :             auto &slot = slots[slot_idx];
     997   [ #  #  #  # ]:          0 :             assert(slot.offset <= offset && slot.offset + slot.size >= offset);
     998                 :          0 :             IRBuilder<> builder(user);
     999                 :            :             Value *newptr;
    1000         [ #  # ]:          0 :             if (slot.isref) {
    1001         [ #  # ]:          0 :                 assert(slot.offset == offset);
    1002                 :          0 :                 newptr = slot.slot;
    1003                 :            :             }
    1004                 :            :             else {
    1005         [ #  # ]:          0 :                 Value *Val = isa<AtomicCmpXchgInst>(user) ? cast<AtomicCmpXchgInst>(user)->getNewValOperand() : cast<AtomicRMWInst>(user)->getValOperand();
    1006                 :          0 :                 newptr = slot_gep(slot, offset, Val->getType(), builder);
    1007                 :            :             }
    1008                 :          0 :             *use = newptr;
    1009                 :            :         }
    1010         [ -  + ]:         69 :         else if (auto call = dyn_cast<CallInst>(user)) {
    1011                 :          0 :             auto callee = call->getCalledOperand();
    1012         [ #  # ]:          0 :             assert(callee); // makes it clear for clang analyser that `callee` is not NULL
    1013         [ #  # ]:          0 :             if (auto intrinsic = dyn_cast<IntrinsicInst>(call)) {
    1014         [ #  # ]:          0 :                 if (Intrinsic::ID id = intrinsic->getIntrinsicID()) {
    1015         [ #  # ]:          0 :                     if (id == Intrinsic::memset) {
    1016                 :          0 :                         IRBuilder<> builder(call);
    1017                 :          0 :                         auto val_arg = cast<ConstantInt>(call->getArgOperand(1));
    1018                 :          0 :                         auto size_arg = cast<ConstantInt>(call->getArgOperand(2));
    1019                 :          0 :                         uint8_t val = val_arg->getLimitedValue();
    1020                 :          0 :                         uint32_t size = size_arg->getLimitedValue();
    1021         [ #  # ]:          0 :                         for (unsigned idx = find_slot(offset); idx < nslots; idx++) {
    1022                 :          0 :                             auto &slot = slots[idx];
    1023   [ #  #  #  # ]:          0 :                             if (slot.offset + slot.size <= offset || slot.offset >= offset + size)
    1024                 :            :                                 break;
    1025         [ #  # ]:          0 :                             if (slot.isref) {
    1026   [ #  #  #  # ]:          0 :                                 assert(slot.offset >= offset &&
    1027                 :            :                                        slot.offset + slot.size <= offset + size);
    1028                 :            :                                 Constant *ptr;
    1029         [ #  # ]:          0 :                                 if (val == 0) {
    1030                 :          0 :                                     ptr = Constant::getNullValue(pass.T_prjlvalue);
    1031                 :            :                                 }
    1032                 :            :                                 else {
    1033                 :            :                                     uint64_t intval;
    1034                 :          0 :                                     memset(&intval, val, 8);
    1035                 :          0 :                                     Constant *val = ConstantInt::get(getSizeTy(builder.getContext()), intval);
    1036                 :          0 :                                     val = ConstantExpr::getIntToPtr(val, JuliaType::get_pjlvalue_ty(builder.getContext()));
    1037                 :          0 :                                     ptr = ConstantExpr::getAddrSpaceCast(val, pass.T_prjlvalue);
    1038                 :            :                                 }
    1039                 :          0 :                                 StoreInst *store = builder.CreateAlignedStore(ptr, slot.slot, Align(sizeof(void*)));
    1040                 :          0 :                                 store->setOrdering(AtomicOrdering::NotAtomic);
    1041                 :          0 :                                 continue;
    1042                 :            :                             }
    1043                 :          0 :                             auto ptr8 = builder.CreateBitCast(slot.slot, Type::getInt8PtrTy(builder.getContext()));
    1044         [ #  # ]:          0 :                             if (offset > slot.offset)
    1045                 :          0 :                                 ptr8 = builder.CreateConstInBoundsGEP1_32(Type::getInt8Ty(builder.getContext()), ptr8,
    1046                 :          0 :                                                                           offset - slot.offset);
    1047                 :          0 :                             auto sub_size = std::min(slot.offset + slot.size, offset + size) -
    1048                 :          0 :                                 std::max(offset, slot.offset);
    1049                 :            :                             // TODO: alignment computation
    1050                 :          0 :                             builder.CreateMemSet(ptr8, val_arg, sub_size, MaybeAlign(0));
    1051                 :            :                         }
    1052                 :          0 :                         call->eraseFromParent();
    1053                 :          0 :                         return;
    1054                 :            :                     }
    1055                 :          0 :                     call->eraseFromParent();
    1056                 :          0 :                     return;
    1057                 :            :                 }
    1058                 :            :             }
    1059         [ #  # ]:          0 :             if (pass.typeof_func == callee) {
    1060                 :          0 :                 ++RemovedTypeofs;
    1061                 :          0 :                 call->replaceAllUsesWith(tag);
    1062                 :          0 :                 call->eraseFromParent();
    1063                 :          0 :                 return;
    1064                 :            :             }
    1065         [ #  # ]:          0 :             if (pass.write_barrier_func == callee ||
    1066         [ #  # ]:          0 :                 pass.write_barrier_binding_func == callee) {
    1067                 :          0 :                 ++RemovedWriteBarriers;
    1068                 :          0 :                 call->eraseFromParent();
    1069                 :          0 :                 return;
    1070                 :            :             }
    1071         [ #  # ]:          0 :             if (pass.gc_preserve_begin_func == callee) {
    1072                 :          0 :                 SmallVector<Value*,8> operands;
    1073         [ #  # ]:          0 :                 for (auto &arg: call->args()) {
    1074   [ #  #  #  #  :          0 :                     if (arg.get() == orig_i || isa<Constant>(arg.get()))
                   #  # ]
    1075                 :          0 :                         continue;
    1076                 :          0 :                     operands.push_back(arg.get());
    1077                 :            :                 }
    1078                 :          0 :                 IRBuilder<> builder(call);
    1079         [ #  # ]:          0 :                 for (auto &slot: slots) {
    1080         [ #  # ]:          0 :                     if (!slot.isref)
    1081                 :          0 :                         continue;
    1082                 :          0 :                     LoadInst *ref = builder.CreateAlignedLoad(pass.T_prjlvalue, slot.slot, Align(sizeof(void*)));
    1083                 :            :                     // since we're moving heap-to-stack, it is safe to downgrade the atomic level to NotAtomic
    1084                 :          0 :                     ref->setOrdering(AtomicOrdering::NotAtomic);
    1085                 :          0 :                     operands.push_back(ref);
    1086                 :            :                 }
    1087                 :          0 :                 auto new_call = builder.CreateCall(pass.gc_preserve_begin_func, operands);
    1088                 :          0 :                 new_call->takeName(call);
    1089                 :          0 :                 new_call->setAttributes(call->getAttributes());
    1090                 :          0 :                 call->replaceAllUsesWith(new_call);
    1091                 :          0 :                 call->eraseFromParent();
    1092                 :          0 :                 return;
    1093                 :            :             }
    1094                 :            :             // remove from operand bundle
    1095         [ #  # ]:          0 :             assert(call->isBundleOperand(use->getOperandNo()));
    1096         [ #  # ]:          0 :             assert(call->getOperandBundleForOperand(use->getOperandNo()).getTagName() ==
    1097                 :            :                    "jl_roots");
    1098                 :          0 :             SmallVector<OperandBundleDef,2> bundles;
    1099                 :          0 :             call->getOperandBundlesAsDefs(bundles);
    1100         [ #  # ]:          0 :             for (auto &bundle: bundles) {
    1101         [ #  # ]:          0 :                 if (bundle.getTag() != "jl_roots")
    1102                 :          0 :                     continue;
    1103                 :          0 :                 std::vector<Value*> operands;
    1104         [ #  # ]:          0 :                 for (auto op: bundle.inputs()) {
    1105   [ #  #  #  #  :          0 :                     if (op == orig_i || isa<Constant>(op))
                   #  # ]
    1106                 :          0 :                         continue;
    1107                 :          0 :                     operands.push_back(op);
    1108                 :            :                 }
    1109                 :          0 :                 IRBuilder<> builder(call);
    1110         [ #  # ]:          0 :                 for (auto &slot: slots) {
    1111         [ #  # ]:          0 :                     if (!slot.isref)
    1112                 :          0 :                         continue;
    1113                 :          0 :                     LoadInst *ref = builder.CreateAlignedLoad(pass.T_prjlvalue, slot.slot, Align(sizeof(void*)));
    1114                 :            :                     // since we're moving heap-to-stack, it is safe to downgrade the atomic level to NotAtomic
    1115                 :          0 :                     ref->setOrdering(AtomicOrdering::NotAtomic);
    1116                 :          0 :                     operands.push_back(ref);
    1117                 :            :                 }
    1118                 :          0 :                 bundle = OperandBundleDef("jl_roots", std::move(operands));
    1119                 :          0 :                 break;
    1120                 :            :             }
    1121                 :          0 :             auto new_call = CallInst::Create(call, bundles, call);
    1122                 :          0 :             new_call->takeName(call);
    1123                 :          0 :             call->replaceAllUsesWith(new_call);
    1124                 :          0 :             call->eraseFromParent();
    1125                 :          0 :             return;
    1126                 :            :         }
    1127   [ +  +  +  +  :         69 :         else if (isa<AddrSpaceCastInst>(user) || isa<BitCastInst>(user)) {
                   +  + ]
    1128                 :         68 :             push_frame(user, offset);
    1129                 :            :         }
    1130         [ +  - ]:          1 :         else if (auto gep = dyn_cast<GetElementPtrInst>(user)) {
    1131                 :          2 :             APInt apoffset(sizeof(void*) * 8, offset, true);
    1132                 :          1 :             gep->accumulateConstantOffset(*pass.DL, apoffset);
    1133                 :          1 :             push_frame(gep, apoffset.getLimitedValue());
    1134                 :            :         }
    1135                 :            :         else {
    1136                 :          0 :             abort();
    1137                 :            :         }
    1138                 :         54 :     };
    1139                 :            : 
    1140                 :            :     while (true) {
    1141                 :        177 :         replace_inst(&*cur.orig_i->use_begin());
    1142         [ +  + ]:        246 :         while (cur.orig_i->use_empty()) {
    1143                 :        123 :             finish_cur();
    1144         [ +  + ]:        123 :             if (replace_stack.empty())
    1145                 :         54 :                 goto cleanup;
    1146                 :         69 :             cur = replace_stack.back();
    1147                 :         69 :             replace_stack.pop_back();
    1148                 :            :         }
    1149                 :            :     }
    1150                 :         54 : cleanup:
    1151         [ +  + ]:        108 :     for (auto &slot: slots) {
    1152         [ +  - ]:         54 :         if (!slot.isref)
    1153                 :         54 :             continue;
    1154                 :          0 :         PromoteMemToReg({slot.slot}, getDomTree());
    1155                 :            :     }
    1156                 :            : }
    1157                 :            : 
    1158                 :    1239060 : bool AllocOpt::doInitialization(Module &M)
    1159                 :            : {
    1160                 :    1239060 :     initAll(M);
    1161         [ +  + ]:    1239060 :     if (!alloc_obj_func)
    1162                 :     597576 :         return false;
    1163                 :            : 
    1164                 :     641488 :     DL = &M.getDataLayout();
    1165                 :            : 
    1166                 :     641488 :     lifetime_start = Intrinsic::getDeclaration(&M, Intrinsic::lifetime_start, { Type::getInt8PtrTy(M.getContext()) });
    1167                 :     641488 :     lifetime_end = Intrinsic::getDeclaration(&M, Intrinsic::lifetime_end, { Type::getInt8PtrTy(M.getContext()) });
    1168                 :            : 
    1169                 :     641488 :     return true;
    1170                 :            : }
    1171                 :            : 
    1172                 :    2453640 : bool AllocOpt::runOnFunction(Function &F, function_ref<DominatorTree&()> GetDT)
    1173                 :            : {
    1174         [ +  + ]:    2453640 :     if (!alloc_obj_func)
    1175                 :    1159390 :         return false;
    1176                 :    1294250 :     Optimizer optimizer(F, *this, std::move(GetDT));
    1177                 :    1294250 :     optimizer.initialize();
    1178                 :    1294250 :     optimizer.optimizeAll();
    1179                 :    1294250 :     bool modified = optimizer.finalize();
    1180         [ -  + ]:    1294250 :     assert(!verifyFunction(F, &errs()));
    1181                 :    1294250 :     return modified;
    1182                 :            : }
    1183                 :            : 
    1184                 :            : struct AllocOptLegacy : public FunctionPass {
    1185                 :            :     static char ID;
    1186                 :            :     AllocOpt opt;
    1187                 :       2296 :     AllocOptLegacy() : FunctionPass(ID) {
    1188                 :       2296 :         llvm::initializeDominatorTreeWrapperPassPass(*PassRegistry::getPassRegistry());
    1189                 :       2296 :     }
    1190                 :    1239060 :     bool doInitialization(Module &m) override {
    1191                 :    1239060 :         return opt.doInitialization(m);
    1192                 :            :     }
    1193                 :    2453640 :     bool runOnFunction(Function &F) override {
    1194                 :    2458110 :         return opt.runOnFunction(F, [this]() -> DominatorTree & {return getAnalysis<DominatorTreeWrapperPass>().getDomTree();});
    1195                 :            :     }
    1196                 :       2296 :     void getAnalysisUsage(AnalysisUsage &AU) const override
    1197                 :            :     {
    1198                 :       2296 :         FunctionPass::getAnalysisUsage(AU);
    1199                 :       2296 :         AU.addRequired<DominatorTreeWrapperPass>();
    1200                 :       2296 :         AU.addPreserved<DominatorTreeWrapperPass>();
    1201                 :       2296 :         AU.setPreservesCFG();
    1202                 :       2296 :     }
    1203                 :            : };
    1204                 :            : 
    1205                 :            : char AllocOptLegacy::ID = 0;
    1206                 :            : static RegisterPass<AllocOptLegacy> X("AllocOpt", "Promote heap allocation to stack",
    1207                 :            :                                 false /* Only looks at CFG */,
    1208                 :            :                                 false /* Analysis Pass */);
    1209                 :            : 
    1210                 :            : }
    1211                 :            : 
    1212                 :       2296 : Pass *createAllocOptPass()
    1213                 :            : {
    1214                 :       2296 :     return new AllocOptLegacy();
    1215                 :            : }
    1216                 :            : 
    1217                 :          0 : PreservedAnalyses AllocOptPass::run(Function &F, FunctionAnalysisManager &AM) {
    1218                 :          0 :     AllocOpt opt;
    1219                 :          0 :     bool modified = opt.doInitialization(*F.getParent());
    1220         [ #  # ]:          0 :     if (opt.runOnFunction(F, [&]()->DominatorTree &{ return AM.getResult<DominatorTreeAnalysis>(F); })) {
    1221                 :          0 :         modified = true;
    1222                 :            :     }
    1223         [ #  # ]:          0 :     if (modified) {
    1224                 :          0 :         auto preserved = PreservedAnalyses::allInSet<CFGAnalyses>();
    1225                 :          0 :         preserved.preserve<DominatorTreeAnalysis>();
    1226                 :          0 :         return preserved;
    1227                 :            :     } else {
    1228                 :          0 :         return PreservedAnalyses::all();
    1229                 :            :     }
    1230                 :            : }
    1231                 :            : 
    1232                 :          0 : extern "C" JL_DLLEXPORT void LLVMExtraAddAllocOptPass_impl(LLVMPassManagerRef PM)
    1233                 :            : {
    1234                 :          0 :     unwrap(PM)->add(createAllocOptPass());
    1235                 :          0 : }

Generated by: LCOV version 1.14