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_THREADSAFEMODULE_H 14 #define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_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 { StateState32 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 /// Construct a null context. 40 ThreadSafeContext() = default; 41 42 /// Construct a ThreadSafeContext from the given LLVMContext. ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx)43 ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx) 44 : S(std::make_shared<State>(std::move(NewCtx))) { 45 assert(S->Ctx != nullptr && 46 "Can not construct a ThreadSafeContext from a nullptr"); 47 } 48 decltype(auto)49 template <typename Func> decltype(auto) withContextDo(Func &&F) { 50 if (auto TmpS = S) { 51 std::lock_guard<std::recursive_mutex> Lock(TmpS->Mutex); 52 return F(TmpS->Ctx.get()); 53 } else 54 return F((LLVMContext *)nullptr); 55 } 56 decltype(auto)57 template <typename Func> decltype(auto) withContextDo(Func &&F) const { 58 if (auto TmpS = S) { 59 std::lock_guard<std::recursive_mutex> Lock(TmpS->Mutex); 60 return F(const_cast<const LLVMContext *>(TmpS->Ctx.get())); 61 } else 62 return F((const LLVMContext *)nullptr); 63 } 64 65 private: 66 std::shared_ptr<State> S; 67 }; 68 69 /// An LLVM Module together with a shared ThreadSafeContext. 70 class ThreadSafeModule { 71 public: 72 /// Default construct a ThreadSafeModule. This results in a null module and 73 /// null context. 74 ThreadSafeModule() = default; 75 76 ThreadSafeModule(ThreadSafeModule &&Other) = default; 77 78 ThreadSafeModule &operator=(ThreadSafeModule &&Other) { 79 // We have to explicitly define this move operator to copy the fields in 80 // reverse order (i.e. module first) to ensure the dependencies are 81 // protected: The old module that is being overwritten must be destroyed 82 // *before* the context that it depends on. 83 // We also need to lock the context to make sure the module tear-down 84 // does not overlap any other work on the context. 85 TSCtx.withContextDo([this](LLVMContext *Ctx) { M = nullptr; }); 86 M = std::move(Other.M); 87 TSCtx = std::move(Other.TSCtx); 88 return *this; 89 } 90 91 /// Construct a ThreadSafeModule from a unique_ptr<Module> and a 92 /// unique_ptr<LLVMContext>. This creates a new ThreadSafeContext from the 93 /// given context. ThreadSafeModule(std::unique_ptr<Module> M,std::unique_ptr<LLVMContext> Ctx)94 ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx) 95 : M(std::move(M)), TSCtx(std::move(Ctx)) {} 96 97 /// Construct a ThreadSafeModule from a unique_ptr<Module> and an 98 /// existing ThreadSafeContext. ThreadSafeModule(std::unique_ptr<Module> M,ThreadSafeContext TSCtx)99 ThreadSafeModule(std::unique_ptr<Module> M, ThreadSafeContext TSCtx) 100 : M(std::move(M)), TSCtx(std::move(TSCtx)) {} 101 ~ThreadSafeModule()102 ~ThreadSafeModule() { 103 // We need to lock the context while we destruct the module. 104 TSCtx.withContextDo([this](LLVMContext *Ctx) { M = nullptr; }); 105 } 106 107 /// Boolean conversion: This ThreadSafeModule will evaluate to true if it 108 /// wraps a non-null module. 109 explicit operator bool() const { return !!M; } 110 111 /// Locks the associated ThreadSafeContext and calls the given function 112 /// on the contained Module. decltype(auto)113 template <typename Func> decltype(auto) withModuleDo(Func &&F) { 114 return TSCtx.withContextDo([&](LLVMContext *) { 115 assert(M && "Can not call on null module"); 116 return F(*M); 117 }); 118 } 119 120 /// Locks the associated ThreadSafeContext and calls the given function 121 /// on the contained Module. decltype(auto)122 template <typename Func> decltype(auto) withModuleDo(Func &&F) const { 123 return TSCtx.withContextDo([&](const LLVMContext *) { 124 assert(M && "Can not call on null module"); 125 return F(*M); 126 }); 127 } 128 129 /// Locks the associated ThreadSafeContext and calls the given function, 130 /// passing the contained std::unique_ptr<Module>. The given function should 131 /// consume the Module. decltype(auto)132 template <typename Func> decltype(auto) consumingModuleDo(Func &&F) { 133 return TSCtx.withContextDo([&](LLVMContext *) { 134 assert(M && "Can not call on null module"); 135 return F(std::move(M)); 136 }); 137 } 138 139 /// Get a raw pointer to the contained module without locking the context. getModuleUnlocked()140 Module *getModuleUnlocked() { return M.get(); } 141 142 /// Get a raw pointer to the contained module without locking the context. getModuleUnlocked()143 const Module *getModuleUnlocked() const { return M.get(); } 144 145 /// Returns the context for this ThreadSafeModule. getContext()146 ThreadSafeContext getContext() const { return TSCtx; } 147 148 private: 149 std::unique_ptr<Module> M; 150 ThreadSafeContext TSCtx; 151 }; 152 153 using GVPredicate = std::function<bool(const GlobalValue &)>; 154 using GVModifier = std::function<void(GlobalValue &)>; 155 156 /// Clones teh given module onto the given context. 157 LLVM_ABI ThreadSafeModule 158 cloneToContext(const ThreadSafeModule &TSMW, ThreadSafeContext TSCtx, 159 GVPredicate ShouldCloneDef = GVPredicate(), 160 GVModifier UpdateClonedDefSource = GVModifier()); 161 162 /// Clones the given module on to a new context. 163 LLVM_ABI ThreadSafeModule cloneToNewContext( 164 const ThreadSafeModule &TSMW, GVPredicate ShouldCloneDef = GVPredicate(), 165 GVModifier UpdateClonedDefSource = GVModifier()); 166 167 } // End namespace orc 168 } // End namespace llvm 169 170 #endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H 171