10b57cec5SDimitry Andric //===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // Thread safe wrappers and utilities for Module and LLVMContext. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 13fe6060f1SDimitry Andric #ifndef LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H 14fe6060f1SDimitry Andric #define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H 150b57cec5SDimitry Andric 160b57cec5SDimitry Andric #include "llvm/IR/LLVMContext.h" 170b57cec5SDimitry Andric #include "llvm/IR/Module.h" 180b57cec5SDimitry Andric #include "llvm/Support/Compiler.h" 190b57cec5SDimitry Andric 200b57cec5SDimitry Andric #include <functional> 210b57cec5SDimitry Andric #include <memory> 220b57cec5SDimitry Andric #include <mutex> 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric namespace llvm { 250b57cec5SDimitry Andric namespace orc { 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric /// An LLVMContext together with an associated mutex that can be used to lock 280b57cec5SDimitry Andric /// the context to prevent concurrent access by other threads. 290b57cec5SDimitry Andric class ThreadSafeContext { 300b57cec5SDimitry Andric private: 310b57cec5SDimitry Andric struct State { StateState320b57cec5SDimitry Andric State(std::unique_ptr<LLVMContext> Ctx) : Ctx(std::move(Ctx)) {} 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric std::unique_ptr<LLVMContext> Ctx; 350b57cec5SDimitry Andric std::recursive_mutex Mutex; 360b57cec5SDimitry Andric }; 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric public: 390b57cec5SDimitry Andric // RAII based lock for ThreadSafeContext. 40*bdd1243dSDimitry Andric class [[nodiscard]] Lock { 410b57cec5SDimitry Andric public: Lock(std::shared_ptr<State> S)428bcb0991SDimitry Andric Lock(std::shared_ptr<State> S) : S(std::move(S)), L(this->S->Mutex) {} 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric private: 450b57cec5SDimitry Andric std::shared_ptr<State> S; 468bcb0991SDimitry Andric std::unique_lock<std::recursive_mutex> L; 470b57cec5SDimitry Andric }; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric /// Construct a null context. 500b57cec5SDimitry Andric ThreadSafeContext() = default; 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric /// Construct a ThreadSafeContext from the given LLVMContext. ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx)530b57cec5SDimitry Andric ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx) 540b57cec5SDimitry Andric : S(std::make_shared<State>(std::move(NewCtx))) { 550b57cec5SDimitry Andric assert(S->Ctx != nullptr && 560b57cec5SDimitry Andric "Can not construct a ThreadSafeContext from a nullptr"); 570b57cec5SDimitry Andric } 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric /// Returns a pointer to the LLVMContext that was used to construct this 600b57cec5SDimitry Andric /// instance, or null if the instance was default constructed. getContext()610b57cec5SDimitry Andric LLVMContext *getContext() { return S ? S->Ctx.get() : nullptr; } 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric /// Returns a pointer to the LLVMContext that was used to construct this 640b57cec5SDimitry Andric /// instance, or null if the instance was default constructed. getContext()650b57cec5SDimitry Andric const LLVMContext *getContext() const { return S ? S->Ctx.get() : nullptr; } 660b57cec5SDimitry Andric getLock()678bcb0991SDimitry Andric Lock getLock() const { 680b57cec5SDimitry Andric assert(S && "Can not lock an empty ThreadSafeContext"); 690b57cec5SDimitry Andric return Lock(S); 700b57cec5SDimitry Andric } 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric private: 730b57cec5SDimitry Andric std::shared_ptr<State> S; 740b57cec5SDimitry Andric }; 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric /// An LLVM Module together with a shared ThreadSafeContext. 770b57cec5SDimitry Andric class ThreadSafeModule { 780b57cec5SDimitry Andric public: 790b57cec5SDimitry Andric /// Default construct a ThreadSafeModule. This results in a null module and 800b57cec5SDimitry Andric /// null context. 810b57cec5SDimitry Andric ThreadSafeModule() = default; 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric ThreadSafeModule(ThreadSafeModule &&Other) = default; 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric ThreadSafeModule &operator=(ThreadSafeModule &&Other) { 860b57cec5SDimitry Andric // We have to explicitly define this move operator to copy the fields in 870b57cec5SDimitry Andric // reverse order (i.e. module first) to ensure the dependencies are 880b57cec5SDimitry Andric // protected: The old module that is being overwritten must be destroyed 890b57cec5SDimitry Andric // *before* the context that it depends on. 900b57cec5SDimitry Andric // We also need to lock the context to make sure the module tear-down 910b57cec5SDimitry Andric // does not overlap any other work on the context. 920b57cec5SDimitry Andric if (M) { 938bcb0991SDimitry Andric auto L = TSCtx.getLock(); 940b57cec5SDimitry Andric M = nullptr; 950b57cec5SDimitry Andric } 960b57cec5SDimitry Andric M = std::move(Other.M); 970b57cec5SDimitry Andric TSCtx = std::move(Other.TSCtx); 980b57cec5SDimitry Andric return *this; 990b57cec5SDimitry Andric } 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric /// Construct a ThreadSafeModule from a unique_ptr<Module> and a 1020b57cec5SDimitry Andric /// unique_ptr<LLVMContext>. This creates a new ThreadSafeContext from the 1030b57cec5SDimitry Andric /// given context. ThreadSafeModule(std::unique_ptr<Module> M,std::unique_ptr<LLVMContext> Ctx)1040b57cec5SDimitry Andric ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx) 1050b57cec5SDimitry Andric : M(std::move(M)), TSCtx(std::move(Ctx)) {} 1060b57cec5SDimitry Andric 1070b57cec5SDimitry Andric /// Construct a ThreadSafeModule from a unique_ptr<Module> and an 1080b57cec5SDimitry Andric /// existing ThreadSafeContext. ThreadSafeModule(std::unique_ptr<Module> M,ThreadSafeContext TSCtx)1090b57cec5SDimitry Andric ThreadSafeModule(std::unique_ptr<Module> M, ThreadSafeContext TSCtx) 1100b57cec5SDimitry Andric : M(std::move(M)), TSCtx(std::move(TSCtx)) {} 1110b57cec5SDimitry Andric ~ThreadSafeModule()1120b57cec5SDimitry Andric ~ThreadSafeModule() { 1130b57cec5SDimitry Andric // We need to lock the context while we destruct the module. 1140b57cec5SDimitry Andric if (M) { 1158bcb0991SDimitry Andric auto L = TSCtx.getLock(); 1160b57cec5SDimitry Andric M = nullptr; 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric } 1190b57cec5SDimitry Andric 1200b57cec5SDimitry Andric /// Boolean conversion: This ThreadSafeModule will evaluate to true if it 1210b57cec5SDimitry Andric /// wraps a non-null module. 1228bcb0991SDimitry Andric explicit operator bool() const { 1230b57cec5SDimitry Andric if (M) { 1240b57cec5SDimitry Andric assert(TSCtx.getContext() && 1250b57cec5SDimitry Andric "Non-null module must have non-null context"); 1260b57cec5SDimitry Andric return true; 1270b57cec5SDimitry Andric } 1280b57cec5SDimitry Andric return false; 1290b57cec5SDimitry Andric } 1300b57cec5SDimitry Andric 1318bcb0991SDimitry Andric /// Locks the associated ThreadSafeContext and calls the given function 1328bcb0991SDimitry Andric /// on the contained Module. decltype(auto)1335ffd83dbSDimitry Andric template <typename Func> decltype(auto) withModuleDo(Func &&F) { 1348bcb0991SDimitry Andric assert(M && "Can not call on null module"); 1358bcb0991SDimitry Andric auto Lock = TSCtx.getLock(); 1368bcb0991SDimitry Andric return F(*M); 1378bcb0991SDimitry Andric } 1388bcb0991SDimitry Andric 1398bcb0991SDimitry Andric /// Locks the associated ThreadSafeContext and calls the given function 1408bcb0991SDimitry Andric /// on the contained Module. decltype(auto)1415ffd83dbSDimitry Andric template <typename Func> decltype(auto) withModuleDo(Func &&F) const { 142*bdd1243dSDimitry Andric assert(M && "Can not call on null module"); 1438bcb0991SDimitry Andric auto Lock = TSCtx.getLock(); 1448bcb0991SDimitry Andric return F(*M); 1458bcb0991SDimitry Andric } 1468bcb0991SDimitry Andric 147*bdd1243dSDimitry Andric /// Locks the associated ThreadSafeContext and calls the given function, 148*bdd1243dSDimitry Andric /// passing the contained std::unique_ptr<Module>. The given function should 149*bdd1243dSDimitry Andric /// consume the Module. decltype(auto)150*bdd1243dSDimitry Andric template <typename Func> decltype(auto) consumingModuleDo(Func &&F) { 151*bdd1243dSDimitry Andric auto Lock = TSCtx.getLock(); 152*bdd1243dSDimitry Andric return F(std::move(M)); 153*bdd1243dSDimitry Andric } 154*bdd1243dSDimitry Andric 1558bcb0991SDimitry Andric /// Get a raw pointer to the contained module without locking the context. getModuleUnlocked()1568bcb0991SDimitry Andric Module *getModuleUnlocked() { return M.get(); } 1578bcb0991SDimitry Andric 1588bcb0991SDimitry Andric /// Get a raw pointer to the contained module without locking the context. getModuleUnlocked()1598bcb0991SDimitry Andric const Module *getModuleUnlocked() const { return M.get(); } 1608bcb0991SDimitry Andric 1618bcb0991SDimitry Andric /// Returns the context for this ThreadSafeModule. getContext()1628bcb0991SDimitry Andric ThreadSafeContext getContext() const { return TSCtx; } 1638bcb0991SDimitry Andric 1640b57cec5SDimitry Andric private: 1650b57cec5SDimitry Andric std::unique_ptr<Module> M; 1660b57cec5SDimitry Andric ThreadSafeContext TSCtx; 1670b57cec5SDimitry Andric }; 1680b57cec5SDimitry Andric 1690b57cec5SDimitry Andric using GVPredicate = std::function<bool(const GlobalValue &)>; 1700b57cec5SDimitry Andric using GVModifier = std::function<void(GlobalValue &)>; 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric /// Clones the given module on to a new context. 1730b57cec5SDimitry Andric ThreadSafeModule 174e8d8bef9SDimitry Andric cloneToNewContext(const ThreadSafeModule &TSMW, 1750b57cec5SDimitry Andric GVPredicate ShouldCloneDef = GVPredicate(), 1760b57cec5SDimitry Andric GVModifier UpdateClonedDefSource = GVModifier()); 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric } // End namespace orc 1790b57cec5SDimitry Andric } // End namespace llvm 1800b57cec5SDimitry Andric 181fe6060f1SDimitry Andric #endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H 182