LCOV - code coverage report
Current view: top level - src - llvm-ptls.cpp (source / functions) Hit Total Coverage
Test: [test only] commit 0f242327d2cc9bd130497f44b6350c924185606a Lines: 97 119 81.5 %
Date: 2022-07-16 23:42:53 Functions: 11 13 84.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 24 34 70.6 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : // LLVM pass to lower TLS access and remove references to julia intrinsics
       3                 :            : 
       4                 :            : #include "llvm-version.h"
       5                 :            : #include "support/dtypes.h"
       6                 :            : #include "passes.h"
       7                 :            : 
       8                 :            : #include <llvm-c/Core.h>
       9                 :            : #include <llvm-c/Types.h>
      10                 :            : 
      11                 :            : #include <llvm/Pass.h>
      12                 :            : #include <llvm/IR/Module.h>
      13                 :            : #include <llvm/IR/LegacyPassManager.h>
      14                 :            : #include <llvm/IR/Function.h>
      15                 :            : #include <llvm/IR/Instructions.h>
      16                 :            : #include <llvm/IR/Constants.h>
      17                 :            : #include <llvm/IR/LLVMContext.h>
      18                 :            : #include <llvm/IR/MDBuilder.h>
      19                 :            : 
      20                 :            : #include <llvm/IR/InlineAsm.h>
      21                 :            : #include <llvm/Transforms/Utils/BasicBlockUtils.h>
      22                 :            : 
      23                 :            : #include "julia.h"
      24                 :            : #include "julia_internal.h"
      25                 :            : #include "codegen_shared.h"
      26                 :            : #include "julia_assert.h"
      27                 :            : 
      28                 :            : #define DEBUG_TYPE "lower_ptls"
      29                 :            : #undef DEBUG
      30                 :            : 
      31                 :            : using namespace llvm;
      32                 :            : 
      33                 :            : typedef Instruction TerminatorInst;
      34                 :            : 
      35                 :            : namespace {
      36                 :            : 
      37                 :            : struct LowerPTLS {
      38                 :     334499 :     LowerPTLS(bool imaging_mode=false)
      39                 :     334499 :         : imaging_mode(imaging_mode)
      40                 :     334499 :     {}
      41                 :            : 
      42                 :            :     bool runOnModule(Module &M, bool *CFGModified);
      43                 :            : private:
      44                 :            :     const bool imaging_mode;
      45                 :            :     Module *M;
      46                 :            :     Function *pgcstack_getter;
      47                 :            :     MDNode *tbaa_const;
      48                 :            :     FunctionType *FT_pgcstack_getter;
      49                 :            :     PointerType *T_pgcstack_getter;
      50                 :            :     PointerType *T_pppjlvalue;
      51                 :            :     GlobalVariable *pgcstack_func_slot{nullptr};
      52                 :            :     GlobalVariable *pgcstack_key_slot{nullptr};
      53                 :            :     GlobalVariable *pgcstack_offset{nullptr};
      54                 :            :     void set_pgcstack_attrs(CallInst *pgcstack) const;
      55                 :            :     Instruction *emit_pgcstack_tp(Value *offset, Instruction *insertBefore) const;
      56                 :            :     template<typename T> T *add_comdat(T *G) const;
      57                 :            :     GlobalVariable *create_aliased_global(Type *T, StringRef name) const;
      58                 :            :     void fix_pgcstack_use(CallInst *pgcstack, bool *CFGModified);
      59                 :            : };
      60                 :            : 
      61                 :      18294 : void LowerPTLS::set_pgcstack_attrs(CallInst *pgcstack) const
      62                 :            : {
      63                 :      18294 :     addFnAttr(pgcstack, Attribute::ReadNone);
      64                 :      18294 :     addFnAttr(pgcstack, Attribute::NoUnwind);
      65                 :      18294 : }
      66                 :            : 
      67                 :     329874 : Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefore) const
      68                 :            : {
      69                 :            :     Value *tls;
      70                 :            : #if defined(_CPU_X86_64_) || defined(_CPU_X86_)
      71         [ +  + ]:     329874 :     if (insertBefore->getFunction()->callsFunctionThatReturnsTwice()) {
      72                 :            :         // Workaround LLVM bug by hiding the offset computation
      73                 :            :         // (and therefore the optimization opportunity) from LLVM.
      74                 :            :         // Ref https://github.com/JuliaLang/julia/issues/17288
      75                 :        289 :         static const std::string const_asm_str = [&] () {
      76                 :        289 :             std::string stm;
      77                 :            : #  if defined(_CPU_X86_64_)
      78                 :        289 :             raw_string_ostream(stm) << "movq %fs:0, $0;\naddq $$" << jl_tls_offset << ", $0";
      79                 :            : #  else
      80                 :            :             raw_string_ostream(stm) << "movl %gs:0, $0;\naddl $$" << jl_tls_offset << ", $0";
      81                 :            : #  endif
      82                 :        289 :             return stm;
      83   [ +  +  +  - ]:      13612 :         }();
      84                 :            : #  if defined(_CPU_X86_64_)
      85                 :      13612 :         const char *dyn_asm_str = "movq %fs:0, $0;\naddq $1, $0";
      86                 :            : #  else
      87                 :            :         const char *dyn_asm_str = "movl %gs:0, $0;\naddl $1, $0";
      88                 :            : #  endif
      89                 :            : 
      90                 :            :         // The add instruction clobbers flags
      91         [ +  + ]:      13612 :         if (offset) {
      92                 :        738 :             std::vector<Type*> args(0);
      93                 :        738 :             args.push_back(offset->getType());
      94                 :        738 :             auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), args, false),
      95                 :            :                                      dyn_asm_str, "=&r,r,~{dirflag},~{fpsr},~{flags}", false);
      96                 :        738 :             tls = CallInst::Create(tp, offset, "pgcstack_i8", insertBefore);
      97                 :            :         }
      98                 :            :         else {
      99                 :      12874 :             auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), false),
     100                 :            :                                      const_asm_str.c_str(), "=r,~{dirflag},~{fpsr},~{flags}",
     101                 :            :                                      false);
     102                 :      12874 :             tls = CallInst::Create(tp, "pgcstack_i8", insertBefore);
     103                 :            :         }
     104                 :            :     }
     105                 :            :     else
     106                 :            : #endif
     107                 :            :     {
     108                 :            :         // AArch64/ARM doesn't seem to have this issue.
     109                 :            :         // (Possibly because there are many more registers and the offset is
     110                 :            :         // positive and small)
     111                 :            :         // It's also harder to emit the offset in a generic way on ARM/AArch64
     112                 :            :         // (need to generate one or two `add` with shift) so let llvm emit
     113                 :            :         // the add for now.
     114                 :            : #if defined(_CPU_AARCH64_)
     115                 :            :         const char *asm_str = "mrs $0, tpidr_el0";
     116                 :            : #elif defined(__ARM_ARCH) && __ARM_ARCH >= 7
     117                 :            :         const char *asm_str = "mrc p15, 0, $0, c13, c0, 3";
     118                 :            : #elif defined(_CPU_X86_64_)
     119                 :     316262 :         const char *asm_str = "movq %fs:0, $0";
     120                 :            : #elif defined(_CPU_X86_)
     121                 :            :         const char *asm_str = "movl %gs:0, $0";
     122                 :            : #else
     123                 :            :         const char *asm_str = nullptr;
     124                 :            :         assert(0 && "Cannot emit thread pointer for this architecture.");
     125                 :            : #endif
     126         [ +  + ]:     316262 :         if (!offset)
     127                 :     298706 :             offset = ConstantInt::getSigned(getSizeTy(insertBefore->getContext()), jl_tls_offset);
     128                 :     316262 :         auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), false), asm_str, "=r", false);
     129                 :     316262 :         tls = CallInst::Create(tp, "thread_ptr", insertBefore);
     130                 :     316262 :         tls = GetElementPtrInst::Create(Type::getInt8Ty(insertBefore->getContext()), tls, {offset}, "ppgcstack_i8", insertBefore);
     131                 :            :     }
     132                 :     329874 :     tls = new BitCastInst(tls, T_pppjlvalue->getPointerTo(), "ppgcstack", insertBefore);
     133                 :     329874 :     return new LoadInst(T_pppjlvalue, tls, "pgcstack", false, insertBefore);
     134                 :            : }
     135                 :            : 
     136                 :          3 : GlobalVariable *LowerPTLS::create_aliased_global(Type *T, StringRef name) const
     137                 :            : {
     138                 :            :     // Create a static global variable and points a global alias to it so that
     139                 :            :     // the address is visible externally but LLVM can still assume that the
     140                 :            :     // address of this variable doesn't need dynamic relocation
     141                 :            :     // (can be accessed with a single PC-rel load).
     142                 :          3 :     auto GV = new GlobalVariable(*M, T, false, GlobalVariable::InternalLinkage,
     143                 :          3 :                                  Constant::getNullValue(T), name + ".real");
     144                 :          3 :     add_comdat(GlobalAlias::create(T, 0, GlobalVariable::ExternalLinkage,
     145                 :          3 :                                    name, GV, M));
     146                 :          3 :     return GV;
     147                 :            : }
     148                 :            : 
     149                 :            : template<typename T>
     150                 :          3 : inline T *LowerPTLS::add_comdat(T *G) const
     151                 :            : {
     152                 :            : #if defined(_OS_WINDOWS_)
     153                 :            :     // add __declspec(dllexport) to everything marked for export
     154                 :            :     if (G->getLinkage() == GlobalValue::ExternalLinkage)
     155                 :            :         G->setDLLStorageClass(GlobalValue::DLLExportStorageClass);
     156                 :            :     else
     157                 :            :         G->setDLLStorageClass(GlobalValue::DefaultStorageClass);
     158                 :            : #endif
     159                 :          3 :     return G;
     160                 :            : }
     161                 :            : 
     162                 :     685617 : void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, bool *CFGModified)
     163                 :            : {
     164         [ +  + ]:     685617 :     if (pgcstack->use_empty()) {
     165                 :     355743 :         pgcstack->eraseFromParent();
     166                 :     355743 :         return;
     167                 :            :     }
     168                 :            : 
     169         [ +  + ]:     329874 :     if (imaging_mode) {
     170         [ +  - ]:      18294 :         if (jl_tls_elf_support) {
     171                 :            :             // if (offset != 0)
     172                 :            :             //     pgcstack = tp + offset;
     173                 :            :             // else
     174                 :            :             //     pgcstack = getter();
     175                 :      18294 :             auto offset = new LoadInst(getSizeTy(pgcstack->getContext()), pgcstack_offset, "", false, pgcstack);
     176                 :      18294 :             offset->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
     177                 :      18294 :             offset->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
     178                 :            :             auto cmp = new ICmpInst(pgcstack, CmpInst::ICMP_NE, offset,
     179                 :      18294 :                                     Constant::getNullValue(offset->getType()));
     180                 :      18294 :             MDBuilder MDB(pgcstack->getContext());
     181                 :      18294 :             SmallVector<uint32_t, 2> Weights{9, 1};
     182                 :            :             TerminatorInst *fastTerm;
     183                 :            :             TerminatorInst *slowTerm;
     184                 :      18294 :             SplitBlockAndInsertIfThenElse(cmp, pgcstack, &fastTerm, &slowTerm,
     185                 :            :                                           MDB.createBranchWeights(Weights));
     186         [ -  + ]:      18294 :             if (CFGModified)
     187                 :          0 :             *CFGModified = true;
     188                 :            : 
     189                 :      18294 :             auto fastTLS = emit_pgcstack_tp(offset, fastTerm);
     190                 :      18294 :             auto phi = PHINode::Create(T_pppjlvalue, 2, "", pgcstack);
     191                 :      18294 :             pgcstack->replaceAllUsesWith(phi);
     192                 :      18294 :             pgcstack->moveBefore(slowTerm);
     193                 :      18294 :             auto getter = new LoadInst(T_pgcstack_getter, pgcstack_func_slot, "", false, pgcstack);
     194                 :      18294 :             getter->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
     195                 :      18294 :             getter->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
     196                 :      18294 :             pgcstack->setCalledFunction(pgcstack->getFunctionType(), getter);
     197                 :      18294 :             set_pgcstack_attrs(pgcstack);
     198                 :            : 
     199                 :      18294 :             phi->addIncoming(fastTLS, fastTLS->getParent());
     200                 :      18294 :             phi->addIncoming(pgcstack, pgcstack->getParent());
     201                 :            : 
     202                 :      18294 :             return;
     203                 :            :         }
     204                 :            :         // In imaging mode, we emit the function address as a load of a static
     205                 :            :         // variable to be filled (in `staticdata.c`) at initialization time of the sysimg.
     206                 :            :         // This way we can bypass the extra indirection in `jl_get_pgcstack`
     207                 :            :         // since we may not know which getter function to use ahead of time.
     208                 :          0 :         auto getter = new LoadInst(T_pgcstack_getter, pgcstack_func_slot, "", false, pgcstack);
     209                 :          0 :         getter->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
     210                 :          0 :         getter->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
     211                 :            : #if defined(_OS_DARWIN_)
     212                 :            :         auto key = new LoadInst(getSizeTy(pgcstack->getContext()), pgcstack_key_slot, "", false, pgcstack);
     213                 :            :         key->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
     214                 :            :         key->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
     215                 :            :         auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, getter, {key}, "", pgcstack);
     216                 :            :         new_pgcstack->takeName(pgcstack);
     217                 :            :         pgcstack->replaceAllUsesWith(new_pgcstack);
     218                 :            :         pgcstack->eraseFromParent();
     219                 :            :         pgcstack = new_pgcstack;
     220                 :            : #else
     221                 :          0 :         pgcstack->setCalledFunction(pgcstack->getFunctionType(), getter);
     222                 :            : #endif
     223                 :          0 :         set_pgcstack_attrs(pgcstack);
     224                 :            :     }
     225         [ +  - ]:     311580 :     else if (jl_tls_offset != -1) {
     226                 :     311580 :         pgcstack->replaceAllUsesWith(emit_pgcstack_tp(nullptr, pgcstack));
     227                 :     311580 :         pgcstack->eraseFromParent();
     228                 :            :     }
     229                 :            :     else {
     230                 :            :         // use the address of the actual getter function directly
     231                 :            :         jl_get_pgcstack_func *f;
     232                 :            :         jl_pgcstack_key_t k;
     233                 :          0 :         jl_pgcstack_getkey(&f, &k);
     234                 :          0 :         Constant *val = ConstantInt::get(getSizeTy(pgcstack->getContext()), (uintptr_t)f);
     235                 :          0 :         val = ConstantExpr::getIntToPtr(val, T_pgcstack_getter);
     236                 :            : #if defined(_OS_DARWIN_)
     237                 :            :         assert(sizeof(k) == sizeof(uintptr_t));
     238                 :            :         Constant *key = ConstantInt::get(getSizeTy(pgcstack->getContext()), (uintptr_t)k);
     239                 :            :         auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, val, {key}, "", pgcstack);
     240                 :            :         new_pgcstack->takeName(pgcstack);
     241                 :            :         pgcstack->replaceAllUsesWith(new_pgcstack);
     242                 :            :         pgcstack->eraseFromParent();
     243                 :            :         pgcstack = new_pgcstack;
     244                 :            : #else
     245                 :          0 :         pgcstack->setCalledFunction(pgcstack->getFunctionType(), val);
     246                 :            : #endif
     247                 :          0 :         set_pgcstack_attrs(pgcstack);
     248                 :            :     }
     249                 :            : }
     250                 :            : 
     251                 :     334499 : bool LowerPTLS::runOnModule(Module &_M, bool *CFGModified)
     252                 :            : {
     253                 :     334499 :     M = &_M;
     254                 :     334499 :     pgcstack_getter = M->getFunction("julia.get_pgcstack");
     255         [ +  + ]:     334499 :     if (!pgcstack_getter)
     256                 :       1293 :         return false;
     257                 :            : 
     258                 :     333206 :     tbaa_const = tbaa_make_child_with_context(_M.getContext(), "jtbaa_const", nullptr, true).first;
     259                 :            : 
     260                 :     333206 :     FT_pgcstack_getter = pgcstack_getter->getFunctionType();
     261                 :            : #if defined(_OS_DARWIN_)
     262                 :            :     assert(sizeof(jl_pgcstack_key_t) == sizeof(uintptr_t));
     263                 :            :     FT_pgcstack_getter = FunctionType::get(FT_pgcstack_getter->getReturnType(), {getSizeTy(_M.getContext())}, false);
     264                 :            : #endif
     265                 :     333206 :     T_pgcstack_getter = FT_pgcstack_getter->getPointerTo();
     266                 :     333206 :     T_pppjlvalue = cast<PointerType>(FT_pgcstack_getter->getReturnType());
     267         [ +  + ]:     333206 :     if (imaging_mode) {
     268                 :          1 :         pgcstack_func_slot = create_aliased_global(T_pgcstack_getter, "jl_pgcstack_func_slot");
     269                 :          1 :         pgcstack_key_slot = create_aliased_global(getSizeTy(_M.getContext()), "jl_pgcstack_key_slot"); // >= sizeof(jl_pgcstack_key_t)
     270                 :          1 :         pgcstack_offset = create_aliased_global(getSizeTy(_M.getContext()), "jl_tls_offset");
     271                 :            :     }
     272                 :            : 
     273         [ +  + ]:    1018820 :     for (auto it = pgcstack_getter->user_begin(); it != pgcstack_getter->user_end();) {
     274                 :     685617 :         auto call = cast<CallInst>(*it);
     275                 :     685617 :         ++it;
     276         [ -  + ]:     685617 :         assert(call->getCalledOperand() == pgcstack_getter);
     277                 :     685617 :         fix_pgcstack_use(call, CFGModified);
     278                 :            :     }
     279         [ -  + ]:     333206 :     assert(pgcstack_getter->use_empty());
     280                 :     333206 :     pgcstack_getter->eraseFromParent();
     281                 :     333206 :     return true;
     282                 :            : }
     283                 :            : 
     284                 :            : struct LowerPTLSLegacy: public ModulePass {
     285                 :            :     static char ID;
     286                 :       1052 :     LowerPTLSLegacy(bool imaging_mode=false)
     287                 :       1052 :         : ModulePass(ID),
     288                 :       1052 :           imaging_mode(imaging_mode)
     289                 :       1052 :     {}
     290                 :            : 
     291                 :            :     bool imaging_mode;
     292                 :     334499 :     bool runOnModule(Module &M) override {
     293                 :     334499 :         LowerPTLS lower(imaging_mode);
     294                 :     334499 :         return lower.runOnModule(M, nullptr);
     295                 :            :     }
     296                 :            : };
     297                 :            : 
     298                 :            : char LowerPTLSLegacy::ID = 0;
     299                 :            : 
     300                 :            : static RegisterPass<LowerPTLSLegacy> X("LowerPTLS", "LowerPTLS Pass",
     301                 :            :                                  false /* Only looks at CFG */,
     302                 :            :                                  false /* Analysis Pass */);
     303                 :            : 
     304                 :            : } // anonymous namespace
     305                 :            : 
     306                 :          0 : PreservedAnalyses LowerPTLSPass::run(Module &M, ModuleAnalysisManager &AM) {
     307                 :          0 :     LowerPTLS lower(imaging_mode);
     308                 :          0 :     bool CFGModified = false;
     309         [ #  # ]:          0 :     if (lower.runOnModule(M, &CFGModified)) {
     310         [ #  # ]:          0 :         if (CFGModified) {
     311                 :          0 :             return PreservedAnalyses::none();
     312                 :            :         } else {
     313                 :          0 :             return PreservedAnalyses::allInSet<CFGAnalyses>();
     314                 :            :         }
     315                 :            :     }
     316                 :          0 :     return PreservedAnalyses::all();
     317                 :            : }
     318                 :            : 
     319                 :       1052 : Pass *createLowerPTLSPass(bool imaging_mode)
     320                 :            : {
     321                 :       1052 :     return new LowerPTLSLegacy(imaging_mode);
     322                 :            : }
     323                 :            : 
     324                 :          0 : extern "C" JL_DLLEXPORT void LLVMExtraAddLowerPTLSPass_impl(LLVMPassManagerRef PM, LLVMBool imaging_mode)
     325                 :            : {
     326                 :          0 :     unwrap(PM)->add(createLowerPTLSPass(imaging_mode));
     327                 :          0 : }

Generated by: LCOV version 1.14