LCOV - code coverage report
Current view: top level - src - llvm-multiversioning.cpp (source / functions) Hit Total Coverage
Test: [test only] commit 0f242327d2cc9bd130497f44b6350c924185606a Lines: 243 653 37.2 %
Date: 2022-07-16 23:42:53 Functions: 27 74 36.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 115 388 29.6 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : 
       3                 :            : // Function multi-versioning
       4                 :            : // LLVM pass to clone function for different archs
       5                 :            : 
       6                 :            : #include "llvm-version.h"
       7                 :            : #include "passes.h"
       8                 :            : 
       9                 :            : #include <llvm-c/Core.h>
      10                 :            : #include <llvm-c/Types.h>
      11                 :            : 
      12                 :            : #include <llvm/Pass.h>
      13                 :            : #include <llvm/ADT/Statistic.h>
      14                 :            : #include <llvm/IR/Module.h>
      15                 :            : #include <llvm/IR/LegacyPassManager.h>
      16                 :            : #include <llvm/IR/Function.h>
      17                 :            : #include <llvm/IR/Instructions.h>
      18                 :            : #include <llvm/IR/Constants.h>
      19                 :            : #include <llvm/IR/LLVMContext.h>
      20                 :            : #include <llvm/Analysis/LoopInfo.h>
      21                 :            : #include <llvm/Analysis/CallGraph.h>
      22                 :            : #include <llvm/IR/LegacyPassManager.h>
      23                 :            : #include <llvm/IR/IRBuilder.h>
      24                 :            : #include <llvm/IR/DebugInfoMetadata.h>
      25                 :            : #include <llvm/IR/Verifier.h>
      26                 :            : #include <llvm/Transforms/Utils/Cloning.h>
      27                 :            : 
      28                 :            : #include "julia.h"
      29                 :            : #include "julia_internal.h"
      30                 :            : #include "processor.h"
      31                 :            : #include "support/dtypes.h"
      32                 :            : 
      33                 :            : #include <map>
      34                 :            : #include <memory>
      35                 :            : #include <set>
      36                 :            : #include <vector>
      37                 :            : 
      38                 :            : #include "codegen_shared.h"
      39                 :            : #include "julia_assert.h"
      40                 :            : 
      41                 :            : #define DEBUG_TYPE "julia_multiversioning"
      42                 :            : #undef DEBUG
      43                 :            : 
      44                 :            : using namespace llvm;
      45                 :            : 
      46                 :            : extern Optional<bool> always_have_fma(Function&);
      47                 :            : 
      48                 :            : namespace {
      49                 :            : constexpr uint32_t clone_mask =
      50                 :            :     JL_TARGET_CLONE_LOOP | JL_TARGET_CLONE_SIMD | JL_TARGET_CLONE_MATH | JL_TARGET_CLONE_CPU;
      51                 :            : 
      52                 :            : // Treat identical mapping as missing and return `def` in that case.
      53                 :            : // We mainly need this to identify cloned function using value map after LLVM cloning
      54                 :            : // functions fills the map with identity entries.
      55                 :            : template<typename T>
      56                 :          0 : Value *map_get(T &&vmap, Value *key, Value *def=nullptr)
      57                 :            : {
      58                 :          0 :     auto val = vmap.lookup(key);
      59   [ #  #  #  #  :          0 :     if (!val || key == val)
                   #  # ]
      60                 :          0 :         return def;
      61                 :          0 :     return val;
      62                 :            : }
      63                 :            : 
      64                 :            : // Iterate through uses of a particular type.
      65                 :            : // Recursively scan through `ConstantExpr` and `ConstantAggregate` use.
      66                 :            : template<typename U>
      67                 :            : struct ConstantUses {
      68                 :            :     template<typename T>
      69                 :            :     struct Info {
      70                 :            :         Use *use;
      71                 :            :         T *val;
      72                 :            :         // If `samebits == true`, the offset the original value appears in the constant.
      73                 :            :         size_t offset;
      74                 :            :         // This specify whether the original value appears in the current value in exactly
      75                 :            :         // the same bit pattern (with possibly an offset determined by `offset`).
      76                 :            :         bool samebits;
      77                 :          0 :         Info(Use *use, T *val, size_t offset, bool samebits) :
      78                 :            :             use(use),
      79                 :            :             val(val),
      80                 :            :             offset(offset),
      81                 :          0 :             samebits(samebits)
      82                 :            :         {
      83                 :          0 :         }
      84                 :          0 :         Info(Use *use, size_t offset, bool samebits) :
      85                 :            :             use(use),
      86                 :          0 :             val(cast<T>(use->getUser())),
      87                 :            :             offset(offset),
      88                 :          0 :             samebits(samebits)
      89                 :            :         {
      90                 :          0 :         }
      91                 :            :     };
      92                 :            :     using UseInfo = Info<U>;
      93                 :            :     struct Frame : Info<Constant> {
      94                 :            :         template<typename... Args>
      95                 :          0 :         Frame(Args &&... args) :
      96                 :          0 :             Info<Constant>(std::forward<Args>(args)...),
      97         [ #  # ]:          0 :             cur(this->val->use_empty() ? nullptr : &*this->val->use_begin()),
      98         [ #  # ]:          0 :             _next(cur ? cur->getNext() : nullptr)
      99                 :            :         {
     100                 :          0 :         }
     101                 :            :     private:
     102                 :          0 :         void next()
     103                 :            :         {
     104                 :          0 :             cur = _next;
     105         [ #  # ]:          0 :             if (!cur)
     106                 :          0 :                 return;
     107                 :          0 :             _next = cur->getNext();
     108                 :            :         }
     109                 :            :         Use *cur;
     110                 :            :         Use *_next;
     111                 :            :         friend struct ConstantUses;
     112                 :            :     };
     113                 :          0 :     ConstantUses(Constant *c, Module &M)
     114                 :          0 :         : stack{Frame(nullptr, c, 0u, true)},
     115                 :          0 :           M(M)
     116                 :            :     {
     117                 :          0 :         forward();
     118                 :          0 :     }
     119                 :          0 :     UseInfo get_info() const
     120                 :            :     {
     121                 :          0 :         auto &top = stack.back();
     122                 :          0 :         return UseInfo(top.cur, top.offset, top.samebits);
     123                 :            :     }
     124                 :          0 :     const SmallVector<Frame, 4> &get_stack() const
     125                 :            :     {
     126                 :          0 :         return stack;
     127                 :            :     }
     128                 :          0 :     void next()
     129                 :            :     {
     130                 :          0 :         stack.back().next();
     131                 :          0 :         forward();
     132                 :          0 :     }
     133                 :          0 :     bool done()
     134                 :            :     {
     135                 :          0 :         return stack.empty();
     136                 :            :     }
     137                 :            : private:
     138                 :            :     void forward();
     139                 :            :     SmallVector<Frame, 4> stack;
     140                 :            :     Module &M;
     141                 :            : };
     142                 :            : 
     143                 :            : template<typename U>
     144                 :          0 : void ConstantUses<U>::forward()
     145                 :            : {
     146         [ #  # ]:          0 :     assert(!stack.empty());
     147                 :          0 :     auto frame = &stack.back();
     148                 :          0 :     const DataLayout &DL = M.getDataLayout();
     149                 :          0 :     auto pop = [&] {
     150                 :          0 :         stack.pop_back();
     151   [ #  #  #  # ]:          0 :         if (stack.empty()) {
     152                 :          0 :             return false;
     153                 :            :         }
     154                 :          0 :         frame = &stack.back();
     155                 :          0 :         return true;
     156                 :            :     };
     157                 :          0 :     auto push = [&] (Use *use, Constant *c, size_t offset, bool samebits) {
     158                 :          0 :         stack.emplace_back(use, c, offset, samebits);
     159                 :          0 :         frame = &stack.back();
     160                 :            :     };
     161                 :          0 :     auto handle_constaggr = [&] (Use *use, ConstantAggregate *aggr) {
     162   [ #  #  #  # ]:          0 :         if (!frame->samebits) {
     163                 :          0 :             push(use, aggr, 0, false);
     164                 :          0 :             return;
     165                 :            :         }
     166   [ #  #  #  # ]:          0 :         if (auto strct = dyn_cast<ConstantStruct>(aggr)) {
     167                 :          0 :             auto layout = DL.getStructLayout(strct->getType());
     168                 :          0 :             push(use, strct, frame->offset + layout->getElementOffset(use->getOperandNo()), true);
     169                 :            :         }
     170   [ #  #  #  # ]:          0 :         else if (auto ary = dyn_cast<ConstantArray>(aggr)) {
     171                 :          0 :             auto elty = ary->getType()->getElementType();
     172                 :          0 :             push(use, ary, frame->offset + DL.getTypeAllocSize(elty) * use->getOperandNo(), true);
     173                 :            :         }
     174   [ #  #  #  # ]:          0 :         else if (auto vec = dyn_cast<ConstantVector>(aggr)) {
     175                 :          0 :             auto elty = vec->getType()->getElementType();
     176                 :          0 :             push(use, vec, frame->offset + DL.getTypeAllocSize(elty) * use->getOperandNo(), true);
     177                 :            :         }
     178                 :            :         else {
     179                 :          0 :             jl_safe_printf("Unknown ConstantAggregate:\n");
     180                 :          0 :             llvm_dump(aggr);
     181                 :          0 :             abort();
     182                 :            :         }
     183                 :            :     };
     184                 :          0 :     auto handle_constexpr = [&] (Use *use, ConstantExpr *expr) {
     185   [ #  #  #  # ]:          0 :         if (!frame->samebits) {
     186                 :          0 :             push(use, expr, 0, false);
     187                 :          0 :             return;
     188                 :            :         }
     189                 :          0 :         auto opcode = expr->getOpcode();
     190   [ #  #  #  #  :          0 :         if (opcode == Instruction::PtrToInt || opcode == Instruction::IntToPtr ||
          #  #  #  #  #  
                #  #  # ]
     191   [ #  #  #  # ]:          0 :             opcode == Instruction::AddrSpaceCast || opcode == Instruction::BitCast) {
     192                 :          0 :             push(use, expr, frame->offset, true);
     193                 :            :         }
     194                 :            :         else {
     195                 :          0 :             push(use, expr, 0, false);
     196                 :            :         }
     197                 :            :     };
     198                 :          0 :     while (true) {
     199                 :          0 :         auto use = frame->cur;
     200         [ #  # ]:          0 :         if (!use) {
     201         [ #  # ]:          0 :             if (!pop())
     202                 :          0 :                 return;
     203                 :          0 :             continue;
     204                 :            :         }
     205                 :          0 :         auto user = use->getUser();
     206         [ #  # ]:          0 :         if (isa<U>(user))
     207                 :          0 :             return;
     208                 :          0 :         frame->next();
     209         [ #  # ]:          0 :         if (auto aggr = dyn_cast<ConstantAggregate>(user)) {
     210                 :          0 :             handle_constaggr(use, aggr);
     211                 :            :         }
     212         [ #  # ]:          0 :         else if (auto expr = dyn_cast<ConstantExpr>(user)) {
     213                 :          0 :             handle_constexpr(use, expr);
     214                 :            :         }
     215                 :            :     }
     216                 :            : }
     217                 :            : 
     218                 :            : struct CloneCtx {
     219                 :            :     struct Target {
     220                 :            :         int idx;
     221                 :            :         uint32_t flags;
     222                 :            :         std::unique_ptr<ValueToValueMapTy> vmap; // ValueToValueMapTy is not movable....
     223                 :            :         // function ids that needs relocation to be initialized
     224                 :            :         std::set<uint32_t> relocs{};
     225                 :          1 :         Target(int idx, const jl_target_spec_t &spec) :
     226                 :            :             idx(idx),
     227                 :          1 :             flags(spec.flags),
     228                 :          1 :             vmap(new ValueToValueMapTy)
     229                 :            :         {
     230                 :          1 :         }
     231                 :            :     };
     232                 :            :     struct Group : Target {
     233                 :            :         std::vector<Target> clones;
     234                 :            :         std::set<uint32_t> clone_fs;
     235                 :          1 :         Group(int base, const jl_target_spec_t &spec) :
     236                 :            :             Target(base, spec),
     237                 :            :             clones{},
     238                 :          1 :             clone_fs{}
     239                 :          1 :         {}
     240                 :      29064 :         Function *base_func(Function *orig_f) const
     241                 :            :         {
     242         [ +  - ]:      29064 :             if (idx == 0)
     243                 :      29064 :                 return orig_f;
     244                 :          0 :             return cast<Function>(vmap->lookup(orig_f));
     245                 :            :         }
     246                 :            :     };
     247                 :            :     CloneCtx(Module &M, function_ref<LoopInfo&(Function&)> GetLI, function_ref<CallGraph&()> GetCG, bool allow_bad_fvars);
     248                 :            :     void clone_bases();
     249                 :            :     void collect_func_infos();
     250                 :            :     void clone_all_partials();
     251                 :            :     void fix_gv_uses();
     252                 :            :     void fix_inst_uses();
     253                 :            :     void emit_metadata();
     254                 :            : private:
     255                 :            :     void prepare_vmap(ValueToValueMapTy &vmap);
     256                 :            :     bool is_vector(FunctionType *ty) const;
     257                 :            :     void clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap);
     258                 :            :     uint32_t collect_func_info(Function &F);
     259                 :            :     void check_partial(Group &grp, Target &tgt);
     260                 :            :     void clone_partial(Group &grp, Target &tgt);
     261                 :            :     void add_features(Function *F, StringRef name, StringRef features, uint32_t flags) const;
     262                 :            :     template<typename T>
     263                 :            :     T *add_comdat(T *G) const;
     264                 :            :     uint32_t get_func_id(Function *F);
     265                 :            :     template<typename Stack>
     266                 :            :     Constant *rewrite_gv_init(const Stack& stack);
     267                 :            :     template<typename Stack>
     268                 :            :     Value *rewrite_inst_use(const Stack& stack, Value *replace, Instruction *insert_before);
     269                 :            :     std::pair<uint32_t,GlobalVariable*> get_reloc_slot(Function *F);
     270                 :            :     Constant *get_ptrdiff32(Constant *ptr, Constant *base) const;
     271                 :            :     template<typename T>
     272                 :            :     Constant *emit_offset_table(const std::vector<T*> &vars, StringRef name) const;
     273                 :            :     void rewrite_alias(GlobalAlias *alias, Function* F);
     274                 :            : 
     275                 :            :     MDNode *tbaa_const;
     276                 :            :     std::vector<jl_target_spec_t> specs;
     277                 :            :     std::vector<Group> groups{};
     278                 :            :     std::vector<Function*> fvars;
     279                 :            :     std::vector<Constant*> gvars;
     280                 :            :     Module &M;
     281                 :            :     function_ref<LoopInfo&(Function&)> GetLI;
     282                 :            :     function_ref<CallGraph&()> GetCG;
     283                 :            : 
     284                 :            :     // Map from original functiton to one based index in `fvars`
     285                 :            :     std::map<const Function*,uint32_t> func_ids{};
     286                 :            :     std::vector<Function*> orig_funcs{};
     287                 :            :     std::vector<uint32_t> func_infos{};
     288                 :            :     std::set<Function*> cloned{};
     289                 :            :     // GV addresses and their corresponding function id (i.e. 0-based index in `fvars`)
     290                 :            :     std::vector<std::pair<Constant*,uint32_t>> gv_relocs{};
     291                 :            :     // Mapping from function id (i.e. 0-based index in `fvars`) to GVs to be initialized.
     292                 :            :     std::map<uint32_t,GlobalVariable*> const_relocs;
     293                 :            :     // Functions that were referred to by a global alias, and might not have other uses.
     294                 :            :     std::set<uint32_t> alias_relocs;
     295                 :            :     bool has_veccall{false};
     296                 :            :     bool has_cloneall{false};
     297                 :            :     bool allow_bad_fvars{false};
     298                 :            : };
     299                 :            : 
     300                 :            : template<typename T>
     301                 :          2 : static inline std::vector<T*> consume_gv(Module &M, const char *name, bool allow_bad_fvars)
     302                 :            : {
     303                 :            :     // Get information about sysimg export functions from the two global variables.
     304                 :            :     // Strip them from the Module so that it's easier to handle the uses.
     305                 :          2 :     GlobalVariable *gv = M.getGlobalVariable(name);
     306   [ +  -  +  - ]:          2 :     assert(gv && gv->hasInitializer());
     307                 :          2 :     auto *ary = cast<ConstantArray>(gv->getInitializer());
     308                 :          2 :     unsigned nele = ary->getNumOperands();
     309                 :          2 :     std::vector<T*> res(nele);
     310                 :          2 :     unsigned i = 0;
     311         [ +  + ]:      43963 :     while (i < nele) {
     312                 :      43961 :         llvm::Value *val = ary->getOperand(i)->stripPointerCasts();
     313   [ -  +  -  -  :      43961 :         if (allow_bad_fvars && (!isa<T>(val) || (isa<Function>(val) && cast<Function>(val)->isDeclaration()))) {
          -  -  -  -  -  
                      + ]
     314                 :            :             // Shouldn't happen in regular use, but can happen in bugpoint.
     315                 :          0 :             nele--;
     316                 :          0 :             continue;
     317                 :            :         }
     318                 :      43961 :         res[i++] = cast<T>(val);
     319                 :            :     }
     320                 :          2 :     res.resize(nele);
     321         [ -  + ]:          2 :     assert(gv->use_empty());
     322                 :          2 :     gv->eraseFromParent();
     323         [ +  - ]:          2 :     if (ary->use_empty())
     324                 :          2 :         ary->destroyConstant();
     325                 :          2 :     return res;
     326                 :            : }
     327                 :            : 
     328                 :            : // Collect basic information about targets and functions.
     329                 :          1 : CloneCtx::CloneCtx(Module &M, function_ref<LoopInfo&(Function&)> GetLI, function_ref<CallGraph&()> GetCG, bool allow_bad_fvars)
     330                 :          1 :     : tbaa_const(tbaa_make_child_with_context(M.getContext(), "jtbaa_const", nullptr, true).first),
     331                 :            :       specs(jl_get_llvm_clone_targets()),
     332                 :            :       fvars(consume_gv<Function>(M, "jl_sysimg_fvars", allow_bad_fvars)),
     333                 :            :       gvars(consume_gv<Constant>(M, "jl_sysimg_gvars", false)),
     334                 :            :       M(M),
     335                 :            :       GetLI(GetLI),
     336                 :            :       GetCG(GetCG),
     337                 :          1 :       allow_bad_fvars(allow_bad_fvars)
     338                 :            : {
     339                 :          1 :     groups.emplace_back(0, specs[0]);
     340                 :          1 :     uint32_t ntargets = specs.size();
     341         [ -  + ]:          1 :     for (uint32_t i = 1; i < ntargets; i++) {
     342                 :          0 :         auto &spec = specs[i];
     343         [ #  # ]:          0 :         if (spec.flags & JL_TARGET_CLONE_ALL) {
     344                 :          0 :             has_cloneall = true;
     345                 :          0 :             groups.emplace_back(i, spec);
     346                 :            :         }
     347                 :            :         else {
     348                 :          0 :             auto base = spec.base;
     349                 :          0 :             bool found = false;
     350         [ #  # ]:          0 :             for (auto &grp: groups) {
     351         [ #  # ]:          0 :                 if (grp.idx == base) {
     352                 :          0 :                     found = true;
     353                 :          0 :                     grp.clones.emplace_back(i, spec);
     354                 :          0 :                     break;
     355                 :            :                 }
     356                 :            :             }
     357                 :            :             (void)found;
     358                 :            :         }
     359                 :            :     }
     360                 :          1 :     uint32_t nfvars = fvars.size();
     361         [ +  + ]:      28496 :     for (uint32_t i = 0; i < nfvars; i++)
     362                 :      28495 :         func_ids[fvars[i]] = i + 1;
     363         [ +  + ]:      29206 :     for (auto &F: M) {
     364         [ +  + ]:      29205 :         if (F.empty())
     365                 :        141 :             continue;
     366                 :      29064 :         orig_funcs.push_back(&F);
     367                 :            :     }
     368                 :          1 : }
     369                 :            : 
     370                 :          0 : void CloneCtx::prepare_vmap(ValueToValueMapTy &vmap)
     371                 :            : {
     372                 :            :     // Workaround LLVM `CloneFunctionInfo` bug (?) pre-5.0
     373                 :            :     // The `DICompileUnit`s are being cloned but are not added to the `llvm.dbg.cu` metadata
     374                 :            :     // which triggers assertions when generating native code/in the verifier.
     375                 :            :     // Fix this by forcing an identical mapping for all `DICompileUnit` recorded.
     376                 :            :     // The `DISubprogram` cloning on LLVM 5.0 handles this
     377                 :            :     // but it doesn't hurt to enforce the identity either.
     378                 :          0 :     auto &MD = vmap.MD();
     379         [ #  # ]:          0 :     for (auto cu: M.debug_compile_units()) {
     380                 :          0 :         MD[cu].reset(cu);
     381                 :            :     }
     382                 :          0 : }
     383                 :            : 
     384                 :          0 : void CloneCtx::clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap)
     385                 :            : {
     386                 :          0 :     Function::arg_iterator DestI = new_f->arg_begin();
     387         [ #  # ]:          0 :     for (Function::const_arg_iterator J = F->arg_begin(); J != F->arg_end(); ++J) {
     388                 :          0 :         DestI->setName(J->getName());
     389                 :          0 :         vmap[&*J] = &*DestI++;
     390                 :            :     }
     391                 :          0 :     SmallVector<ReturnInst*,8> Returns;
     392                 :            : #if JL_LLVM_VERSION >= 130000
     393                 :            :     // We are cloning into the same module
     394                 :          0 :     CloneFunctionInto(new_f, F, vmap, CloneFunctionChangeType::GlobalChanges, Returns);
     395                 :            : #else
     396                 :            :     CloneFunctionInto(new_f, F, vmap, true, Returns);
     397                 :            : #endif
     398                 :          0 : }
     399                 :            : 
     400                 :            : // Clone all clone_all targets. Makes sure that the base targets are all available.
     401                 :          1 : void CloneCtx::clone_bases()
     402                 :            : {
     403         [ +  - ]:          1 :     if (!has_cloneall)
     404                 :          1 :         return;
     405                 :          0 :     uint32_t ngrps = groups.size();
     406         [ #  # ]:          0 :     for (uint32_t gid = 1; gid < ngrps; gid++) {
     407                 :          0 :         auto &grp = groups[gid];
     408                 :          0 :         auto suffix = ".clone_" + std::to_string(grp.idx);
     409                 :          0 :         auto &vmap = *grp.vmap;
     410                 :            :         // Fill in old->new mapping. We need to do this before cloning the function so that
     411                 :            :         // the intra target calls are automatically fixed up on cloning.
     412         [ #  # ]:          0 :         for (auto F: orig_funcs) {
     413                 :          0 :             Function *new_f = Function::Create(F->getFunctionType(), F->getLinkage(),
     414                 :          0 :                                                F->getName() + suffix, &M);
     415                 :          0 :             new_f->copyAttributesFrom(F);
     416                 :          0 :             vmap[F] = new_f;
     417                 :            :         }
     418                 :          0 :         prepare_vmap(vmap);
     419         [ #  # ]:          0 :         for (auto F: orig_funcs) {
     420                 :          0 :             clone_function(F, cast<Function>(vmap.lookup(F)), vmap);
     421                 :            :         }
     422                 :            :     }
     423                 :            : }
     424                 :            : 
     425                 :     287603 : bool CloneCtx::is_vector(FunctionType *ty) const
     426                 :            : {
     427         [ +  + ]:     287603 :     if (ty->getReturnType()->isVectorTy())
     428                 :          4 :         return true;
     429         [ +  + ]:     929011 :     for (auto arg: ty->params()) {
     430         [ -  + ]:     641412 :         if (arg->isVectorTy()) {
     431                 :          0 :             return true;
     432                 :            :         }
     433                 :            :     }
     434                 :     287599 :     return false;
     435                 :            : }
     436                 :            : 
     437                 :      29064 : uint32_t CloneCtx::collect_func_info(Function &F)
     438                 :            : {
     439                 :      29064 :     uint32_t flag = 0;
     440         [ +  + ]:      29064 :     if (!GetLI(F).empty())
     441                 :       3917 :         flag |= JL_TARGET_CLONE_LOOP;
     442         [ +  + ]:      29064 :     if (is_vector(F.getFunctionType())) {
     443                 :          2 :         flag |= JL_TARGET_CLONE_SIMD;
     444                 :          2 :         has_veccall = true;
     445                 :            :     }
     446         [ +  + ]:     370672 :     for (auto &bb: F) {
     447         [ +  + ]:    5530030 :         for (auto &I: bb) {
     448         [ +  + ]:    5188420 :             if (auto call = dyn_cast<CallInst>(&I)) {
     449         [ +  + ]:     258539 :                 if (is_vector(call->getFunctionType())) {
     450                 :          2 :                     has_veccall = true;
     451                 :          2 :                     flag |= JL_TARGET_CLONE_SIMD;
     452                 :            :                 }
     453         [ +  + ]:     258539 :                 if (auto callee = call->getCalledFunction()) {
     454                 :     208253 :                     auto name = callee->getName();
     455   [ +  -  +  +  :     208253 :                     if (name.startswith("llvm.muladd.") || name.startswith("llvm.fma.")) {
                   +  + ]
     456                 :         18 :                         flag |= JL_TARGET_CLONE_MATH;
     457                 :            :                     }
     458         [ +  + ]:     208235 :                     else if (name.startswith("julia.cpu.")) {
     459         [ +  - ]:         22 :                         if (name.startswith("julia.cpu.have_fma.")) {
     460                 :            :                             // for some platforms we know they always do (or don't) support
     461                 :            :                             // FMA. in those cases we don't need to clone the function.
     462         [ +  - ]:         22 :                             if (!always_have_fma(*callee).hasValue())
     463                 :         22 :                                 flag |= JL_TARGET_CLONE_CPU;
     464                 :            :                         } else {
     465                 :          0 :                             flag |= JL_TARGET_CLONE_CPU;
     466                 :            :                         }
     467                 :            :                     }
     468                 :            :                 }
     469                 :            :             }
     470         [ +  + ]:    4929880 :             else if (auto store = dyn_cast<StoreInst>(&I)) {
     471         [ +  + ]:     291833 :                 if (store->getValueOperand()->getType()->isVectorTy()) {
     472                 :          2 :                     flag |= JL_TARGET_CLONE_SIMD;
     473                 :            :                 }
     474                 :            :             }
     475         [ +  + ]:    4638050 :             else if (I.getType()->isVectorTy()) {
     476                 :          2 :                 flag |= JL_TARGET_CLONE_SIMD;
     477                 :            :             }
     478         [ +  + ]:    5188420 :             if (auto mathOp = dyn_cast<FPMathOperator>(&I)) {
     479         [ +  + ]:       1344 :                 if (mathOp->getFastMathFlags().any()) {
     480                 :         56 :                     flag |= JL_TARGET_CLONE_MATH;
     481                 :            :                 }
     482                 :            :             }
     483   [ +  +  +  +  :    5188420 :             if (has_veccall && (flag & JL_TARGET_CLONE_SIMD) && (flag & JL_TARGET_CLONE_MATH)) {
                   -  + ]
     484                 :          0 :                 return flag;
     485                 :            :             }
     486                 :            :         }
     487                 :            :     }
     488                 :      29064 :     return flag;
     489                 :            : }
     490                 :            : 
     491                 :          1 : void CloneCtx::collect_func_infos()
     492                 :            : {
     493                 :          1 :     uint32_t nfuncs = orig_funcs.size();
     494                 :          1 :     func_infos.resize(nfuncs);
     495         [ +  + ]:      29065 :     for (uint32_t i = 0; i < nfuncs; i++) {
     496                 :      29064 :         func_infos[i] = collect_func_info(*orig_funcs[i]);
     497                 :            :     }
     498                 :          1 : }
     499                 :            : 
     500                 :          1 : void CloneCtx::clone_all_partials()
     501                 :            : {
     502                 :            :     // First decide what to clone
     503                 :            :     // Do this before actually cloning the functions
     504                 :            :     // so that the call graph is easier to understand
     505         [ +  + ]:          2 :     for (auto &grp: groups) {
     506         [ -  + ]:          1 :         for (auto &tgt: grp.clones) {
     507                 :          0 :             check_partial(grp, tgt);
     508                 :            :         }
     509                 :            :     }
     510         [ +  + ]:          2 :     for (auto &grp: groups) {
     511         [ -  + ]:          1 :         for (auto &tgt: grp.clones)
     512                 :          0 :             clone_partial(grp, tgt);
     513                 :            :         // Also set feature strings for base target functions
     514                 :            :         // now that all the actual cloning is done.
     515                 :          1 :         auto &base_spec = specs[grp.idx];
     516         [ +  + ]:      29065 :         for (auto orig_f: orig_funcs) {
     517                 :      29064 :             add_features(grp.base_func(orig_f), base_spec.cpu_name,
     518                 :      29064 :                          base_spec.cpu_features, base_spec.flags);
     519                 :            :         }
     520                 :            :     }
     521                 :          1 :     func_infos.clear(); // We don't need this anymore
     522                 :          1 : }
     523                 :            : 
     524                 :          0 : void CloneCtx::check_partial(Group &grp, Target &tgt)
     525                 :            : {
     526                 :          0 :     auto flag = specs[tgt.idx].flags & clone_mask;
     527                 :          0 :     auto suffix = ".clone_" + std::to_string(tgt.idx);
     528                 :          0 :     auto &vmap = *tgt.vmap;
     529                 :          0 :     uint32_t nfuncs = func_infos.size();
     530                 :            : 
     531                 :          0 :     std::set<Function*> all_origs;
     532                 :            :     // Use a simple heuristic to decide which function we need to clone.
     533         [ #  # ]:          0 :     for (uint32_t i = 0; i < nfuncs; i++) {
     534         [ #  # ]:          0 :         if (!(func_infos[i] & flag))
     535                 :          0 :             continue;
     536                 :          0 :         auto orig_f = orig_funcs[i];
     537                 :            :         // Fill in old->new mapping. We need to do this before cloning the function so that
     538                 :            :         // the intra target calls are automatically fixed up on cloning.
     539                 :          0 :         auto F = grp.base_func(orig_f);
     540                 :          0 :         Function *new_f = Function::Create(F->getFunctionType(), F->getLinkage(),
     541                 :          0 :                                            F->getName() + suffix, &M);
     542                 :          0 :         new_f->copyAttributesFrom(F);
     543                 :          0 :         vmap[F] = new_f;
     544         [ #  # ]:          0 :         if (!has_cloneall)
     545                 :          0 :             cloned.insert(orig_f);
     546                 :          0 :         grp.clone_fs.insert(i);
     547                 :          0 :         all_origs.insert(orig_f);
     548                 :            :     }
     549         [ #  # ]:          0 :     std::set<Function*> sets[2]{all_origs, std::set<Function*>{}};
     550                 :          0 :     auto *cur_set = &sets[0];
     551                 :          0 :     auto *next_set = &sets[1];
     552                 :            :     // Reduce dispatch by expand the cloning set to functions that are directly called by
     553                 :            :     // and calling cloned functions.
     554                 :          0 :     auto &graph = GetCG();
     555         [ #  # ]:          0 :     while (!cur_set->empty()) {
     556         [ #  # ]:          0 :         for (auto orig_f: *cur_set) {
     557                 :            :             // Use the uncloned function since it's already in the call graph
     558                 :          0 :             auto node = graph[orig_f];
     559         [ #  # ]:          0 :             for (const auto &I: *node) {
     560                 :          0 :                 auto child_node = I.second;
     561                 :          0 :                 auto orig_child_f = child_node->getFunction();
     562         [ #  # ]:          0 :                 if (!orig_child_f)
     563                 :          0 :                     continue;
     564                 :            :                 // Already cloned
     565         [ #  # ]:          0 :                 if (all_origs.count(orig_child_f))
     566                 :          0 :                     continue;
     567                 :          0 :                 bool calling_clone = false;
     568         [ #  # ]:          0 :                 for (const auto &I2: *child_node) {
     569                 :          0 :                     auto orig_child_f2 = I2.second->getFunction();
     570         [ #  # ]:          0 :                     if (!orig_child_f2)
     571                 :          0 :                         continue;
     572         [ #  # ]:          0 :                     if (all_origs.count(orig_child_f2)) {
     573                 :          0 :                         calling_clone = true;
     574                 :          0 :                         break;
     575                 :            :                     }
     576                 :            :                 }
     577         [ #  # ]:          0 :                 if (!calling_clone)
     578                 :          0 :                     continue;
     579                 :          0 :                 next_set->insert(orig_child_f);
     580                 :          0 :                 all_origs.insert(orig_child_f);
     581                 :          0 :                 auto child_f = grp.base_func(orig_child_f);
     582                 :          0 :                 Function *new_f = Function::Create(child_f->getFunctionType(),
     583                 :            :                                                    child_f->getLinkage(),
     584                 :          0 :                                                    child_f->getName() + suffix, &M);
     585                 :          0 :                 new_f->copyAttributesFrom(child_f);
     586                 :          0 :                 vmap[child_f] = new_f;
     587                 :            :             }
     588                 :            :         }
     589                 :          0 :         std::swap(cur_set, next_set);
     590                 :          0 :         next_set->clear();
     591                 :            :     }
     592         [ #  # ]:          0 :     for (uint32_t i = 0; i < nfuncs; i++) {
     593                 :            :         // Only need to handle expanded functions
     594         [ #  # ]:          0 :         if (func_infos[i] & flag)
     595                 :          0 :             continue;
     596                 :          0 :         auto orig_f = orig_funcs[i];
     597         [ #  # ]:          0 :         if (all_origs.count(orig_f)) {
     598         [ #  # ]:          0 :             if (!has_cloneall)
     599                 :          0 :                 cloned.insert(orig_f);
     600                 :          0 :             grp.clone_fs.insert(i);
     601                 :            :         }
     602                 :            :     }
     603                 :          0 : }
     604                 :            : 
     605                 :          0 : void CloneCtx::clone_partial(Group &grp, Target &tgt)
     606                 :            : {
     607                 :          0 :     auto &spec = specs[tgt.idx];
     608                 :          0 :     auto &vmap = *tgt.vmap;
     609                 :          0 :     uint32_t nfuncs = orig_funcs.size();
     610                 :          0 :     prepare_vmap(vmap);
     611         [ #  # ]:          0 :     for (uint32_t i = 0; i < nfuncs; i++) {
     612                 :          0 :         auto orig_f = orig_funcs[i];
     613                 :          0 :         auto F = grp.base_func(orig_f);
     614         [ #  # ]:          0 :         if (auto new_v = map_get(vmap, F)) {
     615                 :          0 :             auto new_f = cast<Function>(new_v);
     616         [ #  # ]:          0 :             assert(new_f != F);
     617                 :          0 :             clone_function(F, new_f, vmap);
     618                 :            :             // We can set the feature strings now since no one is going to
     619                 :            :             // clone these functions again.
     620                 :          0 :             add_features(new_f, spec.cpu_name, spec.cpu_features, spec.flags);
     621                 :            :         }
     622                 :            :     }
     623                 :          0 : }
     624                 :            : 
     625                 :      29064 : void CloneCtx::add_features(Function *F, StringRef name, StringRef features, uint32_t flags) const
     626                 :            : {
     627                 :      29064 :     auto attr = F->getFnAttribute("target-features");
     628         [ -  + ]:      29064 :     if (attr.isStringAttribute()) {
     629                 :          0 :         std::string new_features(attr.getValueAsString());
     630                 :          0 :         new_features += ",";
     631                 :          0 :         new_features += features;
     632                 :          0 :         F->addFnAttr("target-features", new_features);
     633                 :            :     }
     634                 :            :     else {
     635                 :      29064 :         F->addFnAttr("target-features", features);
     636                 :            :     }
     637                 :      29064 :     F->addFnAttr("target-cpu", name);
     638         [ +  - ]:      29064 :     if (!F->hasFnAttribute(Attribute::OptimizeNone)) {
     639         [ -  + ]:      29064 :         if (flags & JL_TARGET_OPTSIZE) {
     640                 :          0 :             F->addFnAttr(Attribute::OptimizeForSize);
     641                 :            :         }
     642         [ -  + ]:      29064 :         else if (flags & JL_TARGET_MINSIZE) {
     643                 :          0 :             F->addFnAttr(Attribute::MinSize);
     644                 :            :         }
     645                 :            :     }
     646                 :      29064 : }
     647                 :            : 
     648                 :          0 : uint32_t CloneCtx::get_func_id(Function *F)
     649                 :            : {
     650                 :          0 :     auto &ref = func_ids[F];
     651         [ #  # ]:          0 :     if (!ref) {
     652   [ #  #  #  #  :          0 :         if (allow_bad_fvars && F->isDeclaration()) {
                   #  # ]
     653                 :            :             // This should never happen in regular use, but can happen if
     654                 :            :             // bugpoint deletes the function. Just do something here to
     655                 :            :             // allow bugpoint to proceed.
     656                 :          0 :             return (uint32_t)-1;
     657                 :            :         }
     658                 :          0 :         fvars.push_back(F);
     659                 :          0 :         ref = fvars.size();
     660                 :            :     }
     661                 :          0 :     return ref - 1;
     662                 :            : }
     663                 :            : 
     664                 :            : template<typename Stack>
     665                 :          0 : Constant *CloneCtx::rewrite_gv_init(const Stack& stack)
     666                 :            : {
     667                 :            :     // Null initialize so that LLVM put it in the correct section.
     668                 :          0 :     SmallVector<Constant*, 8> args;
     669                 :          0 :     Constant *res = ConstantPointerNull::get(cast<PointerType>(stack[0].val->getType()));
     670                 :          0 :     uint32_t nlevel = stack.size();
     671         [ #  # ]:          0 :     for (uint32_t i = 1; i < nlevel; i++) {
     672                 :          0 :         auto &frame = stack[i];
     673                 :          0 :         auto val = frame.val;
     674                 :          0 :         Use *use = frame.use;
     675                 :          0 :         unsigned idx = use->getOperandNo();
     676                 :          0 :         unsigned nargs = val->getNumOperands();
     677                 :          0 :         args.resize(nargs);
     678         [ #  # ]:          0 :         for (unsigned j = 0; j < nargs; j++) {
     679         [ #  # ]:          0 :             if (idx == j) {
     680                 :          0 :                 args[j] = res;
     681                 :            :             }
     682                 :            :             else {
     683                 :          0 :                 args[j] = cast<Constant>(val->getOperand(j));
     684                 :            :             }
     685                 :            :         }
     686         [ #  # ]:          0 :         if (auto expr = dyn_cast<ConstantExpr>(val)) {
     687                 :          0 :             res = expr->getWithOperands(args);
     688                 :            :         }
     689         [ #  # ]:          0 :         else if (auto ary = dyn_cast<ConstantArray>(val)) {
     690                 :          0 :             res = ConstantArray::get(ary->getType(), args);
     691                 :            :         }
     692         [ #  # ]:          0 :         else if (auto strct = dyn_cast<ConstantStruct>(val)) {
     693                 :          0 :             res = ConstantStruct::get(strct->getType(), args);
     694                 :            :         }
     695         [ #  # ]:          0 :         else if (isa<ConstantVector>(val)) {
     696                 :          0 :             res = ConstantVector::get(args);
     697                 :            :         }
     698                 :            :         else {
     699                 :          0 :             jl_safe_printf("Unknown const use.");
     700                 :          0 :             llvm_dump(val);
     701                 :          0 :             abort();
     702                 :            :         }
     703                 :            :     }
     704                 :          0 :     return res;
     705                 :            : }
     706                 :            : 
     707                 :            : // replace an alias to a function with a trampoline and (uninitialized) global variable slot
     708                 :          0 : void CloneCtx::rewrite_alias(GlobalAlias *alias, Function *F)
     709                 :            : {
     710         [ #  # ]:          0 :     assert(!is_vector(F->getFunctionType()));
     711                 :            : 
     712                 :            :     Function *trampoline =
     713                 :          0 :         Function::Create(F->getFunctionType(), alias->getLinkage(), "", &M);
     714                 :          0 :     trampoline->copyAttributesFrom(F);
     715                 :          0 :     trampoline->takeName(alias);
     716                 :          0 :     alias->eraseFromParent();
     717                 :            : 
     718                 :            :     uint32_t id;
     719                 :            :     GlobalVariable *slot;
     720                 :          0 :     std::tie(id, slot) = get_reloc_slot(F);
     721         [ #  # ]:          0 :     for (auto &grp: groups) {
     722                 :          0 :         grp.relocs.insert(id);
     723         [ #  # ]:          0 :         for (auto &tgt: grp.clones) {
     724                 :          0 :             tgt.relocs.insert(id);
     725                 :            :         }
     726                 :            :     }
     727                 :          0 :     alias_relocs.insert(id);
     728                 :            : 
     729                 :          0 :     auto BB = BasicBlock::Create(F->getContext(), "top", trampoline);
     730                 :          0 :     IRBuilder<> irbuilder(BB);
     731                 :            : 
     732                 :          0 :     auto ptr = irbuilder.CreateLoad(F->getType(), slot);
     733                 :          0 :     ptr->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
     734                 :          0 :     ptr->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(F->getContext(), None));
     735                 :            : 
     736                 :          0 :     std::vector<Value *> Args;
     737         [ #  # ]:          0 :     for (auto &arg : trampoline->args())
     738                 :          0 :         Args.push_back(&arg);
     739                 :          0 :     auto call = irbuilder.CreateCall(F->getFunctionType(), ptr, makeArrayRef(Args));
     740         [ #  # ]:          0 :     if (F->isVarArg())
     741                 :            : #if (defined(_CPU_ARM_) || defined(_CPU_PPC_) || defined(_CPU_PPC64_))
     742                 :            :         abort();    // musttail support is very bad on ARM, PPC, PPC64 (as of LLVM 3.9)
     743                 :            : #else
     744                 :          0 :         call->setTailCallKind(CallInst::TCK_MustTail);
     745                 :            : #endif
     746                 :            :     else
     747                 :          0 :         call->setTailCallKind(CallInst::TCK_Tail);
     748                 :            : 
     749         [ #  # ]:          0 :     if (F->getReturnType() == Type::getVoidTy(F->getContext()))
     750                 :          0 :         irbuilder.CreateRetVoid();
     751                 :            :     else
     752                 :          0 :         irbuilder.CreateRet(call);
     753                 :          0 : }
     754                 :            : 
     755                 :          1 : void CloneCtx::fix_gv_uses()
     756                 :            : {
     757                 :          0 :     auto single_pass = [&] (Function *orig_f) {
     758                 :          0 :         bool changed = false;
     759         [ #  # ]:          0 :         for (auto uses = ConstantUses<GlobalValue>(orig_f, M); !uses.done(); uses.next()) {
     760                 :          0 :             changed = true;
     761                 :          0 :             auto &stack = uses.get_stack();
     762                 :          0 :             auto info = uses.get_info();
     763                 :            :             // We only support absolute pointer relocation.
     764         [ #  # ]:          0 :             assert(info.samebits);
     765                 :            :             GlobalVariable *val;
     766         [ #  # ]:          0 :             if (auto alias = dyn_cast<GlobalAlias>(info.val)) {
     767                 :          0 :                 rewrite_alias(alias, orig_f);
     768                 :          0 :                 continue;
     769                 :            :             }
     770                 :            :             else {
     771                 :          0 :                 val = cast<GlobalVariable>(info.val);
     772                 :            :             }
     773         [ #  # ]:          0 :             assert(info.use->getOperandNo() == 0);
     774         [ #  # ]:          0 :             assert(!val->isConstant());
     775                 :          0 :             auto fid = get_func_id(orig_f);
     776                 :          0 :             auto addr = ConstantExpr::getPtrToInt(val, getSizeTy(val->getContext()));
     777         [ #  # ]:          0 :             if (info.offset)
     778                 :          0 :                 addr = ConstantExpr::getAdd(addr, ConstantInt::get(getSizeTy(val->getContext()), info.offset));
     779                 :          0 :             gv_relocs.emplace_back(addr, fid);
     780                 :          0 :             val->setInitializer(rewrite_gv_init(stack));
     781                 :            :         }
     782                 :          0 :         return changed;
     783                 :          1 :     };
     784         [ +  + ]:      29065 :     for (auto orig_f: orig_funcs) {
     785   [ +  -  +  -  :      29064 :         if (!has_cloneall && !cloned.count(orig_f))
                   +  - ]
     786                 :      29064 :             continue;
     787         [ #  # ]:          0 :         while (single_pass(orig_f)) {
     788                 :            :         }
     789                 :            :     }
     790                 :          1 : }
     791                 :            : 
     792                 :          0 : std::pair<uint32_t,GlobalVariable*> CloneCtx::get_reloc_slot(Function *F)
     793                 :            : {
     794                 :            :     // Null initialize so that LLVM put it in the correct section.
     795                 :          0 :     auto id = get_func_id(F);
     796                 :          0 :     auto &slot = const_relocs[id];
     797         [ #  # ]:          0 :     if (!slot)
     798                 :          0 :         slot = new GlobalVariable(M, F->getType(), false, GlobalVariable::InternalLinkage,
     799                 :          0 :                                   ConstantPointerNull::get(F->getType()),
     800                 :          0 :                                   F->getName() + ".reloc_slot");
     801                 :          0 :     return std::make_pair(id, slot);
     802                 :            : }
     803                 :            : 
     804                 :            : template<typename Stack>
     805                 :          0 : Value *CloneCtx::rewrite_inst_use(const Stack& stack, Value *replace, Instruction *insert_before)
     806                 :            : {
     807                 :          0 :     SmallVector<Constant*, 8> args;
     808                 :          0 :     uint32_t nlevel = stack.size();
     809         [ #  # ]:          0 :     for (uint32_t i = 1; i < nlevel; i++) {
     810                 :          0 :         auto &frame = stack[i];
     811                 :          0 :         auto val = frame.val;
     812                 :          0 :         Use *use = frame.use;
     813                 :          0 :         unsigned idx = use->getOperandNo();
     814         [ #  # ]:          0 :         if (auto expr = dyn_cast<ConstantExpr>(val)) {
     815                 :          0 :             auto inst = expr->getAsInstruction();
     816                 :          0 :             inst->replaceUsesOfWith(val->getOperand(idx), replace);
     817                 :          0 :             inst->insertBefore(insert_before);
     818                 :          0 :             replace = inst;
     819                 :          0 :             continue;
     820                 :            :         }
     821                 :          0 :         unsigned nargs = val->getNumOperands();
     822                 :          0 :         args.resize(nargs);
     823         [ #  # ]:          0 :         for (unsigned j = 0; j < nargs; j++) {
     824                 :          0 :             auto op = val->getOperand(j);
     825         [ #  # ]:          0 :             if (idx == j) {
     826                 :          0 :                 args[j] = UndefValue::get(op->getType());
     827                 :            :             }
     828                 :            :             else {
     829                 :          0 :                 args[j] = cast<Constant>(op);
     830                 :            :             }
     831                 :            :         }
     832         [ #  # ]:          0 :         if (auto ary = dyn_cast<ConstantArray>(val)) {
     833                 :          0 :             replace = InsertValueInst::Create(ConstantArray::get(ary->getType(), args),
     834                 :            :                                               replace, {idx}, "", insert_before);
     835                 :            :         }
     836         [ #  # ]:          0 :         else if (auto strct = dyn_cast<ConstantStruct>(val)) {
     837                 :          0 :             replace = InsertValueInst::Create(ConstantStruct::get(strct->getType(), args),
     838                 :            :                                               replace, {idx}, "", insert_before);
     839                 :            :         }
     840         [ #  # ]:          0 :         else if (isa<ConstantVector>(val)) {
     841                 :          0 :             replace = InsertElementInst::Create(ConstantVector::get(args), replace,
     842                 :          0 :                                                 ConstantInt::get(getSizeTy(insert_before->getContext()), idx), "",
     843                 :            :                                                 insert_before);
     844                 :            :         }
     845                 :            :         else {
     846                 :          0 :             jl_safe_printf("Unknown const use.");
     847                 :          0 :             llvm_dump(val);
     848                 :          0 :             abort();
     849                 :            :         }
     850                 :            :     }
     851                 :          0 :     return replace;
     852                 :            : }
     853                 :            : 
     854                 :          1 : void CloneCtx::fix_inst_uses()
     855                 :            : {
     856                 :          1 :     uint32_t nfuncs = orig_funcs.size();
     857         [ +  + ]:          2 :     for (auto &grp: groups) {
     858                 :          2 :         auto suffix = ".clone_" + std::to_string(grp.idx);
     859         [ +  + ]:      29065 :         for (uint32_t i = 0; i < nfuncs; i++) {
     860         [ +  - ]:      29064 :             if (!grp.clone_fs.count(i))
     861                 :      29064 :                 continue;
     862                 :          0 :             auto orig_f = orig_funcs[i];
     863                 :          0 :             auto F = grp.base_func(orig_f);
     864                 :            :             bool changed;
     865         [ #  # ]:          0 :             do {
     866                 :          0 :                 changed = false;
     867         [ #  # ]:          0 :                 for (auto uses = ConstantUses<Instruction>(F, M); !uses.done(); uses.next()) {
     868                 :          0 :                     auto info = uses.get_info();
     869                 :          0 :                     auto use_i = info.val;
     870                 :          0 :                     auto use_f = use_i->getFunction();
     871         [ #  # ]:          0 :                     if (!use_f->getName().endswith(suffix))
     872                 :          0 :                         continue;
     873                 :          0 :                     Instruction *insert_before = use_i;
     874         [ #  # ]:          0 :                     if (auto phi = dyn_cast<PHINode>(use_i))
     875                 :          0 :                         insert_before = phi->getIncomingBlock(*info.use)->getTerminator();
     876                 :            :                     uint32_t id;
     877                 :            :                     GlobalVariable *slot;
     878                 :          0 :                     std::tie(id, slot) = get_reloc_slot(orig_f);
     879                 :          0 :                     Instruction *ptr = new LoadInst(orig_f->getType(), slot, "", false, insert_before);
     880                 :          0 :                     ptr->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
     881                 :          0 :                     ptr->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(ptr->getContext(), None));
     882                 :          0 :                     use_i->setOperand(info.use->getOperandNo(),
     883                 :            :                                       rewrite_inst_use(uses.get_stack(), ptr,
     884                 :            :                                                        insert_before));
     885                 :            : 
     886                 :          0 :                     grp.relocs.insert(id);
     887         [ #  # ]:          0 :                     for (auto &tgt: grp.clones) {
     888                 :            :                         // The enclosing function of the use is cloned,
     889                 :            :                         // no need to deal with this use on this target.
     890         [ #  # ]:          0 :                         if (map_get(*tgt.vmap, use_f))
     891                 :          0 :                             continue;
     892                 :          0 :                         tgt.relocs.insert(id);
     893                 :            :                     }
     894                 :            : 
     895                 :          0 :                     changed = true;
     896                 :            :                 }
     897                 :            :             } while (changed);
     898                 :            :         }
     899                 :            :     }
     900                 :          1 : }
     901                 :            : 
     902                 :            : template<typename T>
     903                 :          8 : inline T *CloneCtx::add_comdat(T *G) const
     904                 :            : {
     905                 :            : #if defined(_OS_WINDOWS_)
     906                 :            :     // add __declspec(dllexport) to everything marked for export
     907                 :            :     if (G->getLinkage() == GlobalValue::ExternalLinkage)
     908                 :            :         G->setDLLStorageClass(GlobalValue::DLLExportStorageClass);
     909                 :            :     else
     910                 :            :         G->setDLLStorageClass(GlobalValue::DefaultStorageClass);
     911                 :            : #endif
     912                 :          8 :     return G;
     913                 :            : }
     914                 :            : 
     915                 :      43959 : Constant *CloneCtx::get_ptrdiff32(Constant *ptr, Constant *base) const
     916                 :            : {
     917         [ +  - ]:      43959 :     if (ptr->getType()->isPointerTy())
     918                 :      43959 :         ptr = ConstantExpr::getPtrToInt(ptr, getSizeTy(ptr->getContext()));
     919                 :      43959 :     auto ptrdiff = ConstantExpr::getSub(ptr, base);
     920                 :      43959 :     return sizeof(void*) == 8 ? ConstantExpr::getTrunc(ptrdiff, Type::getInt32Ty(ptr->getContext())) : ptrdiff;
     921                 :            : }
     922                 :            : 
     923                 :            : template<typename T>
     924                 :          2 : Constant *CloneCtx::emit_offset_table(const std::vector<T*> &vars, StringRef name) const
     925                 :            : {
     926                 :          2 :     auto T_int32 = Type::getInt32Ty(M.getContext());
     927                 :          2 :     auto T_size = getSizeTy(M.getContext());
     928         [ -  + ]:          2 :     assert(!vars.empty());
     929                 :          4 :     add_comdat(GlobalAlias::create(T_size, 0, GlobalVariable::ExternalLinkage,
     930                 :          2 :                                    name + "_base",
     931                 :          2 :                                    ConstantExpr::getBitCast(vars[0], T_size->getPointerTo()), &M));
     932                 :          2 :     auto vbase = ConstantExpr::getPtrToInt(vars[0], T_size);
     933                 :          2 :     uint32_t nvars = vars.size();
     934                 :          2 :     std::vector<Constant*> offsets(nvars + 1);
     935                 :          2 :     offsets[0] = ConstantInt::get(T_int32, nvars);
     936                 :          2 :     offsets[1] = ConstantInt::get(T_int32, 0);
     937         [ +  + ]:      43961 :     for (uint32_t i = 1; i < nvars; i++)
     938                 :      43959 :         offsets[i + 1] = get_ptrdiff32(vars[i], vbase);
     939                 :          2 :     ArrayType *vars_type = ArrayType::get(T_int32, nvars + 1);
     940                 :          2 :     add_comdat(new GlobalVariable(M, vars_type, true,
     941                 :            :                                   GlobalVariable::ExternalLinkage,
     942                 :          2 :                                   ConstantArray::get(vars_type, offsets),
     943                 :          2 :                                   name + "_offsets"));
     944                 :          2 :     return vbase;
     945                 :            : }
     946                 :            : 
     947                 :          1 : void CloneCtx::emit_metadata()
     948                 :            : {
     949                 :          1 :     uint32_t nfvars = fvars.size();
     950   [ -  +  -  - ]:          1 :     if (allow_bad_fvars && nfvars == 0) {
     951                 :            :         // Will result in a non-loadable sysimg, but `allow_bad_fvars` is for bugpoint only
     952                 :          0 :         return;
     953                 :            :     }
     954                 :            : 
     955                 :            :     // Store back the information about exported functions.
     956                 :          1 :     auto fbase = emit_offset_table(fvars, "jl_sysimg_fvars");
     957                 :          1 :     auto gbase = emit_offset_table(gvars, "jl_sysimg_gvars");
     958                 :            : 
     959                 :          1 :     uint32_t ntargets = specs.size();
     960                 :          2 :     SmallVector<Target*, 8> targets(ntargets);
     961         [ +  + ]:          2 :     for (auto &grp: groups) {
     962                 :          1 :         targets[grp.idx] = &grp;
     963         [ -  + ]:          1 :         for (auto &tgt: grp.clones) {
     964                 :          0 :             targets[tgt.idx] = &tgt;
     965                 :            :         }
     966                 :            :     }
     967                 :            : 
     968                 :            :     // Generate `jl_dispatch_target_ids`
     969                 :            :     {
     970         [ +  - ]:          1 :         const uint32_t base_flags = has_veccall ? JL_TARGET_VEC_CALL : 0;
     971                 :          1 :         std::vector<uint8_t> data;
     972                 :          2 :         auto push_i32 = [&] (uint32_t v) {
     973                 :            :             uint8_t buff[4];
     974                 :          2 :             memcpy(buff, &v, 4);
     975                 :          2 :             data.insert(data.end(), buff, buff + 4);
     976                 :          3 :         };
     977                 :          1 :         push_i32(ntargets);
     978         [ +  + ]:          2 :         for (uint32_t i = 0; i < ntargets; i++) {
     979                 :          1 :             push_i32(base_flags | (specs[i].flags & JL_TARGET_UNKNOWN_NAME));
     980                 :          1 :             auto &specdata = specs[i].data;
     981                 :          1 :             data.insert(data.end(), specdata.begin(), specdata.end());
     982                 :            :         }
     983                 :          1 :         auto value = ConstantDataArray::get(M.getContext(), data);
     984                 :          2 :         add_comdat(new GlobalVariable(M, value->getType(), true,
     985                 :            :                                       GlobalVariable::ExternalLinkage,
     986                 :          1 :                                       value, "jl_dispatch_target_ids"));
     987                 :            :     }
     988                 :            : 
     989                 :            :     // Generate `jl_dispatch_reloc_slots`
     990                 :          2 :     std::set<uint32_t> shared_relocs;
     991                 :            :     {
     992                 :          1 :         auto T_int32 = Type::getInt32Ty(M.getContext());
     993                 :          1 :         std::stable_sort(gv_relocs.begin(), gv_relocs.end(),
     994                 :          0 :                          [] (const std::pair<Constant*,uint32_t> &lhs,
     995                 :            :                              const std::pair<Constant*,uint32_t> &rhs) {
     996                 :          0 :                              return lhs.second < rhs.second;
     997                 :            :                          });
     998                 :          1 :         std::vector<Constant*> values{nullptr};
     999                 :          1 :         uint32_t gv_reloc_idx = 0;
    1000                 :          1 :         uint32_t ngv_relocs = gv_relocs.size();
    1001         [ +  + ]:      28496 :         for (uint32_t id = 0; id < nfvars; id++) {
    1002                 :            :             // TODO:
    1003                 :            :             // explicitly set section? so that we are sure the relocation slots
    1004                 :            :             // are in the same section as `gbase`.
    1005                 :      28495 :             auto id_v = ConstantInt::get(T_int32, id);
    1006   [ -  +  -  -  :      28495 :             for (; gv_reloc_idx < ngv_relocs && gv_relocs[gv_reloc_idx].second == id;
                   -  + ]
    1007                 :            :                  gv_reloc_idx++) {
    1008                 :          0 :                 shared_relocs.insert(id);
    1009                 :          0 :                 values.push_back(id_v);
    1010                 :          0 :                 values.push_back(get_ptrdiff32(gv_relocs[gv_reloc_idx].first, gbase));
    1011                 :            :             }
    1012                 :      28495 :             auto it = const_relocs.find(id);
    1013         [ -  + ]:      28495 :             if (it != const_relocs.end()) {
    1014                 :          0 :                 values.push_back(id_v);
    1015                 :          0 :                 values.push_back(get_ptrdiff32(it->second, gbase));
    1016                 :            :             }
    1017         [ -  + ]:      28495 :             if (alias_relocs.find(id) != alias_relocs.end()) {
    1018                 :          0 :                 shared_relocs.insert(id);
    1019                 :            :             }
    1020                 :            :         }
    1021                 :          1 :         values[0] = ConstantInt::get(T_int32, values.size() / 2);
    1022                 :          1 :         ArrayType *vars_type = ArrayType::get(T_int32, values.size());
    1023                 :          2 :         add_comdat(new GlobalVariable(M, vars_type, true, GlobalVariable::ExternalLinkage,
    1024                 :          2 :                                       ConstantArray::get(vars_type, values),
    1025                 :          1 :                                       "jl_dispatch_reloc_slots"));
    1026                 :            :     }
    1027                 :            : 
    1028                 :            :     // Generate `jl_dispatch_fvars_idxs` and `jl_dispatch_fvars_offsets`
    1029                 :            :     {
    1030                 :          2 :         std::vector<uint32_t> idxs;
    1031                 :          1 :         std::vector<Constant*> offsets;
    1032         [ +  + ]:          2 :         for (uint32_t i = 0; i < ntargets; i++) {
    1033                 :          1 :             auto tgt = targets[i];
    1034                 :          1 :             auto &spec = specs[i];
    1035                 :          1 :             uint32_t len_idx = idxs.size();
    1036                 :          1 :             idxs.push_back(0); // We will fill in the real value later.
    1037                 :          1 :             uint32_t count = 0;
    1038   [ -  +  -  - ]:          1 :             if (i == 0 || spec.flags & JL_TARGET_CLONE_ALL) {
    1039                 :          1 :                 auto grp = static_cast<Group*>(tgt);
    1040                 :          1 :                 count = jl_sysimg_tag_mask;
    1041         [ +  + ]:      28496 :                 for (uint32_t j = 0; j < nfvars; j++) {
    1042   [ +  -  -  +  :      28495 :                     if (shared_relocs.count(j) || tgt->relocs.count(j)) {
                   -  + ]
    1043                 :          0 :                         count++;
    1044                 :          0 :                         idxs.push_back(j);
    1045                 :            :                     }
    1046         [ -  + ]:      28495 :                     if (i != 0) {
    1047                 :          0 :                         offsets.push_back(get_ptrdiff32(grp->base_func(fvars[j]), fbase));
    1048                 :            :                     }
    1049                 :          1 :                 }
    1050                 :            :             }
    1051                 :            :             else {
    1052                 :          0 :                 auto baseidx = spec.base;
    1053                 :          0 :                 auto grp = static_cast<Group*>(targets[baseidx]);
    1054                 :          0 :                 idxs.push_back(baseidx);
    1055         [ #  # ]:          0 :                 for (uint32_t j = 0; j < nfvars; j++) {
    1056                 :          0 :                     auto base_f = grp->base_func(fvars[j]);
    1057   [ #  #  #  #  :          0 :                     if (shared_relocs.count(j) || tgt->relocs.count(j)) {
                   #  # ]
    1058                 :          0 :                         count++;
    1059                 :          0 :                         idxs.push_back(jl_sysimg_tag_mask | j);
    1060                 :          0 :                         auto f = map_get(*tgt->vmap, base_f, base_f);
    1061                 :          0 :                         offsets.push_back(get_ptrdiff32(cast<Function>(f), fbase));
    1062                 :            :                     }
    1063         [ #  # ]:          0 :                     else if (auto f = map_get(*tgt->vmap, base_f)) {
    1064                 :          0 :                         count++;
    1065                 :          0 :                         idxs.push_back(j);
    1066                 :          0 :                         offsets.push_back(get_ptrdiff32(cast<Function>(f), fbase));
    1067                 :            :                     }
    1068                 :            :                 }
    1069                 :            :             }
    1070                 :          1 :             idxs[len_idx] = count;
    1071                 :            :         }
    1072                 :          1 :         auto idxval = ConstantDataArray::get(M.getContext(), idxs);
    1073                 :          2 :         add_comdat(new GlobalVariable(M, idxval->getType(), true,
    1074                 :            :                                       GlobalVariable::ExternalLinkage,
    1075                 :          1 :                                       idxval, "jl_dispatch_fvars_idxs"));
    1076                 :          1 :         ArrayType *offsets_type = ArrayType::get(Type::getInt32Ty(M.getContext()), offsets.size());
    1077                 :          2 :         add_comdat(new GlobalVariable(M, offsets_type, true,
    1078                 :            :                                       GlobalVariable::ExternalLinkage,
    1079                 :          2 :                                       ConstantArray::get(offsets_type, offsets),
    1080                 :          1 :                                       "jl_dispatch_fvars_offsets"));
    1081                 :            :     }
    1082                 :            : }
    1083                 :            : 
    1084                 :          2 : static bool runMultiVersioning(Module &M, function_ref<LoopInfo&(Function&)> GetLI, function_ref<CallGraph&()> GetCG, bool allow_bad_fvars)
    1085                 :            : {
    1086                 :            :     // Group targets and identify cloning bases.
    1087                 :            :     // Also initialize function info maps (we'll update these maps as we go)
    1088                 :            :     // Maps that we need includes,
    1089                 :            :     //
    1090                 :            :     //     * Original function -> ID (initialize from `fvars` and allocate ID lazily)
    1091                 :            :     //     * Cloned function -> Original function (add as we clone functions)
    1092                 :            :     //     * Original function -> Base function (target specific and updated by LLVM)
    1093                 :            :     //     * ID -> relocation slots (const).
    1094         [ +  + ]:          2 :     if (M.getName() == "sysimage")
    1095                 :          1 :         return false;
    1096                 :            : 
    1097                 :          1 :     GlobalVariable *fvars = M.getGlobalVariable("jl_sysimg_fvars");
    1098                 :          1 :     GlobalVariable *gvars = M.getGlobalVariable("jl_sysimg_gvars");
    1099   [ -  +  -  -  :          1 :     if (allow_bad_fvars && (!fvars || !fvars->hasInitializer() || !isa<ConstantArray>(fvars->getInitializer()) ||
          -  -  -  -  -  
                      - ]
    1100   [ -  -  -  -  :          1 :                             !gvars || !gvars->hasInitializer() || !isa<ConstantArray>(gvars->getInitializer())))
                   -  + ]
    1101                 :          0 :         return false;
    1102                 :            : 
    1103                 :          1 :     CloneCtx clone(M, GetLI, GetCG, allow_bad_fvars);
    1104                 :            : 
    1105                 :            :     // Collect a list of original functions and clone base functions
    1106                 :          1 :     clone.clone_bases();
    1107                 :            : 
    1108                 :            :     // Collect function info (type of instruction used)
    1109                 :          1 :     clone.collect_func_infos();
    1110                 :            : 
    1111                 :            :     // If any partially cloned target exist decide which functions to clone for these targets.
    1112                 :            :     // Clone functions for each group and collect a list of them.
    1113                 :            :     // We can also add feature strings for cloned functions
    1114                 :            :     // now that no additional cloning needs to be done.
    1115                 :          1 :     clone.clone_all_partials();
    1116                 :            : 
    1117                 :            :     // Scan **ALL** cloned functions (including full cloning for base target)
    1118                 :            :     // for global variables initialization use.
    1119                 :            :     // Replace them with `null` slot to be initialized at runtime and record relocation slot.
    1120                 :            :     // These relocations must be initialized for **ALL** targets.
    1121                 :          1 :     clone.fix_gv_uses();
    1122                 :            : 
    1123                 :            :     // For each group, scan all functions cloned by **PARTIALLY** cloned targets for
    1124                 :            :     // instruction use.
    1125                 :            :     // A function needs a const relocation slot if it is cloned and is called by a
    1126                 :            :     // uncloned function for at least one partially cloned target in the group.
    1127                 :            :     // This is also the condition that a use in an uncloned function needs to be replaced with
    1128                 :            :     // a slot load (i.e. if both the caller and the callee are always cloned or not cloned
    1129                 :            :     // on all targets, the caller site does not need a relocation slot).
    1130                 :            :     // A target needs a slot to be initialized iff at least one caller is not initialized.
    1131                 :          1 :     clone.fix_inst_uses();
    1132                 :            : 
    1133                 :            :     // Store back sysimg information with the correct format.
    1134                 :            :     // At this point, we should have fixed up all the uses of the cloned functions
    1135                 :            :     // and collected all the shared/target-specific relocations.
    1136                 :          1 :     clone.emit_metadata();
    1137                 :            : 
    1138         [ -  + ]:          1 :     assert(!verifyModule(M));
    1139                 :            : 
    1140                 :          1 :     return true;
    1141                 :            : }
    1142                 :            : 
    1143                 :            : struct MultiVersioningLegacy: public ModulePass {
    1144                 :            :     static char ID;
    1145                 :          1 :     MultiVersioningLegacy(bool allow_bad_fvars=false)
    1146                 :          1 :         : ModulePass(ID), allow_bad_fvars(allow_bad_fvars)
    1147                 :          1 :     {}
    1148                 :            : 
    1149                 :            : private:
    1150                 :            :     bool runOnModule(Module &M) override;
    1151                 :          1 :     void getAnalysisUsage(AnalysisUsage &AU) const override
    1152                 :            :     {
    1153                 :          1 :         AU.addRequired<LoopInfoWrapperPass>();
    1154                 :          1 :         AU.addRequired<CallGraphWrapperPass>();
    1155                 :          1 :         AU.addPreserved<LoopInfoWrapperPass>();
    1156                 :          1 :     }
    1157                 :            :     bool allow_bad_fvars;
    1158                 :            : };
    1159                 :            : 
    1160                 :          2 : bool MultiVersioningLegacy::runOnModule(Module &M)
    1161                 :            : {
    1162                 :      29064 :     auto GetLI = [this](Function &F) -> LoopInfo & {
    1163                 :      29064 :         return getAnalysis<LoopInfoWrapperPass>(F).getLoopInfo();
    1164                 :          2 :     };
    1165                 :          0 :     auto GetCG = [this]() -> CallGraph & {
    1166                 :          0 :         return getAnalysis<CallGraphWrapperPass>().getCallGraph();
    1167                 :          2 :     };
    1168                 :          2 :     return runMultiVersioning(M, GetLI, GetCG, allow_bad_fvars);
    1169                 :            : }
    1170                 :            : 
    1171                 :            : 
    1172                 :            : char MultiVersioningLegacy::ID = 0;
    1173                 :            : static RegisterPass<MultiVersioningLegacy> X("JuliaMultiVersioning", "JuliaMultiVersioning Pass",
    1174                 :            :                                        false /* Only looks at CFG */,
    1175                 :            :                                        false /* Analysis Pass */);
    1176                 :            : 
    1177                 :            : } // anonymous namespace
    1178                 :            : 
    1179                 :          0 : PreservedAnalyses MultiVersioning::run(Module &M, ModuleAnalysisManager &AM)
    1180                 :            : {
    1181                 :          0 :     auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
    1182                 :          0 :     auto GetLI = [&](Function &F) -> LoopInfo & {
    1183                 :          0 :         return FAM.getResult<LoopAnalysis>(F);
    1184                 :          0 :     };
    1185                 :          0 :     auto GetCG = [&]() -> CallGraph & {
    1186                 :          0 :         return AM.getResult<CallGraphAnalysis>(M);
    1187                 :          0 :     };
    1188         [ #  # ]:          0 :     if (runMultiVersioning(M, GetLI, GetCG, external_use)) {
    1189                 :          0 :         auto preserved = PreservedAnalyses::allInSet<CFGAnalyses>();
    1190                 :          0 :         preserved.preserve<LoopAnalysis>();
    1191                 :          0 :         return preserved;
    1192                 :            :     }
    1193                 :          0 :     return PreservedAnalyses::all();
    1194                 :            : }
    1195                 :            : 
    1196                 :          1 : Pass *createMultiVersioningPass(bool allow_bad_fvars)
    1197                 :            : {
    1198                 :          1 :     return new MultiVersioningLegacy(allow_bad_fvars);
    1199                 :            : }
    1200                 :            : 
    1201                 :          0 : extern "C" JL_DLLEXPORT void LLVMExtraAddMultiVersioningPass_impl(LLVMPassManagerRef PM)
    1202                 :            : {
    1203                 :          0 :     unwrap(PM)->add(createMultiVersioningPass(false));
    1204                 :          0 : }

Generated by: LCOV version 1.14