1 //===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Thread safe wrappers and utilities for Module and LLVMContext. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H 14 #define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H 15 16 #include "llvm/IR/LLVMContext.h" 17 #include "llvm/IR/Module.h" 18 #include "llvm/Support/Compiler.h" 19 20 #include <functional> 21 #include <memory> 22 #include <mutex> 23 24 namespace llvm { 25 namespace orc { 26 27 /// An LLVMContext together with an associated mutex that can be used to lock 28 /// the context to prevent concurrent access by other threads. 29 class ThreadSafeContext { 30 private: 31 struct State { 32 State(std::unique_ptr<LLVMContext> Ctx) : Ctx(std::move(Ctx)) {} 33 34 std::unique_ptr<LLVMContext> Ctx; 35 std::recursive_mutex Mutex; 36 }; 37 38 public: 39 // RAII based lock for ThreadSafeContext. 40 class LLVM_NODISCARD Lock { 41 private: 42 using UnderlyingLock = std::lock_guard<std::recursive_mutex>; 43 44 public: 45 Lock(std::shared_ptr<State> S) 46 : S(std::move(S)), 47 L(llvm::make_unique<UnderlyingLock>(this->S->Mutex)) {} 48 49 private: 50 std::shared_ptr<State> S; 51 std::unique_ptr<UnderlyingLock> L; 52 }; 53 54 /// Construct a null context. 55 ThreadSafeContext() = default; 56 57 /// Construct a ThreadSafeContext from the given LLVMContext. 58 ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx) 59 : S(std::make_shared<State>(std::move(NewCtx))) { 60 assert(S->Ctx != nullptr && 61 "Can not construct a ThreadSafeContext from a nullptr"); 62 } 63 64 /// Returns a pointer to the LLVMContext that was used to construct this 65 /// instance, or null if the instance was default constructed. 66 LLVMContext *getContext() { return S ? S->Ctx.get() : nullptr; } 67 68 /// Returns a pointer to the LLVMContext that was used to construct this 69 /// instance, or null if the instance was default constructed. 70 const LLVMContext *getContext() const { return S ? S->Ctx.get() : nullptr; } 71 72 Lock getLock() { 73 assert(S && "Can not lock an empty ThreadSafeContext"); 74 return Lock(S); 75 } 76 77 private: 78 std::shared_ptr<State> S; 79 }; 80 81 /// An LLVM Module together with a shared ThreadSafeContext. 82 class ThreadSafeModule { 83 public: 84 /// Default construct a ThreadSafeModule. This results in a null module and 85 /// null context. 86 ThreadSafeModule() = default; 87 88 ThreadSafeModule(ThreadSafeModule &&Other) = default; 89 90 ThreadSafeModule &operator=(ThreadSafeModule &&Other) { 91 // We have to explicitly define this move operator to copy the fields in 92 // reverse order (i.e. module first) to ensure the dependencies are 93 // protected: The old module that is being overwritten must be destroyed 94 // *before* the context that it depends on. 95 // We also need to lock the context to make sure the module tear-down 96 // does not overlap any other work on the context. 97 if (M) { 98 auto L = getContextLock(); 99 M = nullptr; 100 } 101 M = std::move(Other.M); 102 TSCtx = std::move(Other.TSCtx); 103 return *this; 104 } 105 106 /// Construct a ThreadSafeModule from a unique_ptr<Module> and a 107 /// unique_ptr<LLVMContext>. This creates a new ThreadSafeContext from the 108 /// given context. 109 ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx) 110 : M(std::move(M)), TSCtx(std::move(Ctx)) {} 111 112 /// Construct a ThreadSafeModule from a unique_ptr<Module> and an 113 /// existing ThreadSafeContext. 114 ThreadSafeModule(std::unique_ptr<Module> M, ThreadSafeContext TSCtx) 115 : M(std::move(M)), TSCtx(std::move(TSCtx)) {} 116 117 ~ThreadSafeModule() { 118 // We need to lock the context while we destruct the module. 119 if (M) { 120 auto L = getContextLock(); 121 M = nullptr; 122 } 123 } 124 125 /// Get the module wrapped by this ThreadSafeModule. 126 Module *getModule() { return M.get(); } 127 128 /// Get the module wrapped by this ThreadSafeModule. 129 const Module *getModule() const { return M.get(); } 130 131 /// Take out a lock on the ThreadSafeContext for this module. 132 ThreadSafeContext::Lock getContextLock() { return TSCtx.getLock(); } 133 134 /// Boolean conversion: This ThreadSafeModule will evaluate to true if it 135 /// wraps a non-null module. 136 explicit operator bool() { 137 if (M) { 138 assert(TSCtx.getContext() && 139 "Non-null module must have non-null context"); 140 return true; 141 } 142 return false; 143 } 144 145 private: 146 std::unique_ptr<Module> M; 147 ThreadSafeContext TSCtx; 148 }; 149 150 using GVPredicate = std::function<bool(const GlobalValue &)>; 151 using GVModifier = std::function<void(GlobalValue &)>; 152 153 /// Clones the given module on to a new context. 154 ThreadSafeModule 155 cloneToNewContext(ThreadSafeModule &TSMW, 156 GVPredicate ShouldCloneDef = GVPredicate(), 157 GVModifier UpdateClonedDefSource = GVModifier()); 158 159 } // End namespace orc 160 } // End namespace llvm 161 162 #endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H 163