18d76c711SLang Hames //===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===// 28d76c711SLang Hames // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 68d76c711SLang Hames // 78d76c711SLang Hames //===----------------------------------------------------------------------===// 88d76c711SLang Hames // 98d76c711SLang Hames // Thread safe wrappers and utilities for Module and LLVMContext. 108d76c711SLang Hames // 118d76c711SLang Hames //===----------------------------------------------------------------------===// 128d76c711SLang Hames 13aa5c09beSKazu Hirata #ifndef LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H 14aa5c09beSKazu Hirata #define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H 158d76c711SLang Hames 168d76c711SLang Hames #include "llvm/IR/LLVMContext.h" 178d76c711SLang Hames #include "llvm/IR/Module.h" 185ad09de9SLang Hames #include "llvm/Support/Compiler.h" 198d76c711SLang Hames 20867587f8SLang Hames #include <functional> 21867587f8SLang Hames #include <memory> 227e38be47SLang Hames #include <mutex> 237e38be47SLang Hames 248d76c711SLang Hames namespace llvm { 258d76c711SLang Hames namespace orc { 268d76c711SLang Hames 278d76c711SLang Hames /// An LLVMContext together with an associated mutex that can be used to lock 288d76c711SLang Hames /// the context to prevent concurrent access by other threads. 298d76c711SLang Hames class ThreadSafeContext { 308d76c711SLang Hames private: 318d76c711SLang Hames struct State { StateState324328ea34SLang Hames State(std::unique_ptr<LLVMContext> Ctx) : Ctx(std::move(Ctx)) {} 338d76c711SLang Hames 348d76c711SLang Hames std::unique_ptr<LLVMContext> Ctx; 358d76c711SLang Hames std::recursive_mutex Mutex; 368d76c711SLang Hames }; 378d76c711SLang Hames 388d76c711SLang Hames public: 398d76c711SLang Hames // RAII based lock for ThreadSafeContext. 40fa66789dSFangrui Song class [[nodiscard]] Lock { 414328ea34SLang Hames public: Lock(std::shared_ptr<State> S)426f0ac30aSBenjamin Kramer Lock(std::shared_ptr<State> S) : S(std::move(S)), L(this->S->Mutex) {} 434328ea34SLang Hames 448d76c711SLang Hames private: 458d76c711SLang Hames std::shared_ptr<State> S; 466f0ac30aSBenjamin Kramer std::unique_lock<std::recursive_mutex> L; 478d76c711SLang Hames }; 488d76c711SLang Hames 498d76c711SLang Hames /// Construct a null context. 508d76c711SLang Hames ThreadSafeContext() = default; 518d76c711SLang Hames 528d76c711SLang Hames /// Construct a ThreadSafeContext from the given LLVMContext. ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx)538d76c711SLang Hames ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx) 548d76c711SLang Hames : S(std::make_shared<State>(std::move(NewCtx))) { 558d76c711SLang Hames assert(S->Ctx != nullptr && 568d76c711SLang Hames "Can not construct a ThreadSafeContext from a nullptr"); 578d76c711SLang Hames } 588d76c711SLang Hames 598d76c711SLang Hames /// Returns a pointer to the LLVMContext that was used to construct this 608d76c711SLang Hames /// instance, or null if the instance was default constructed. getContext()614328ea34SLang Hames LLVMContext *getContext() { return S ? S->Ctx.get() : nullptr; } 628d76c711SLang Hames 633e709d5fSLang Hames /// Returns a pointer to the LLVMContext that was used to construct this 643e709d5fSLang Hames /// instance, or null if the instance was default constructed. getContext()653e709d5fSLang Hames const LLVMContext *getContext() const { return S ? S->Ctx.get() : nullptr; } 663e709d5fSLang Hames getLock()67809e9d1eSLang Hames Lock getLock() const { 688d76c711SLang Hames assert(S && "Can not lock an empty ThreadSafeContext"); 698d76c711SLang Hames return Lock(S); 708d76c711SLang Hames } 718d76c711SLang Hames 728d76c711SLang Hames private: 738d76c711SLang Hames std::shared_ptr<State> S; 748d76c711SLang Hames }; 758d76c711SLang Hames 768d76c711SLang Hames /// An LLVM Module together with a shared ThreadSafeContext. 778d76c711SLang Hames class ThreadSafeModule { 788d76c711SLang Hames public: 798d76c711SLang Hames /// Default construct a ThreadSafeModule. This results in a null module and 808d76c711SLang Hames /// null context. 818d76c711SLang Hames ThreadSafeModule() = default; 828d76c711SLang Hames 835ad09de9SLang Hames ThreadSafeModule(ThreadSafeModule &&Other) = default; 845ad09de9SLang Hames 855ad09de9SLang Hames ThreadSafeModule &operator=(ThreadSafeModule &&Other) { 865ad09de9SLang Hames // We have to explicitly define this move operator to copy the fields in 875ad09de9SLang Hames // reverse order (i.e. module first) to ensure the dependencies are 885ad09de9SLang Hames // protected: The old module that is being overwritten must be destroyed 895ad09de9SLang Hames // *before* the context that it depends on. 9028d596c3SLang Hames // We also need to lock the context to make sure the module tear-down 9128d596c3SLang Hames // does not overlap any other work on the context. 9228d596c3SLang Hames if (M) { 93809e9d1eSLang Hames auto L = TSCtx.getLock(); 9428d596c3SLang Hames M = nullptr; 9528d596c3SLang Hames } 965ad09de9SLang Hames M = std::move(Other.M); 975ad09de9SLang Hames TSCtx = std::move(Other.TSCtx); 985ad09de9SLang Hames return *this; 995ad09de9SLang Hames } 1005ad09de9SLang Hames 1018d76c711SLang Hames /// Construct a ThreadSafeModule from a unique_ptr<Module> and a 1028d76c711SLang Hames /// unique_ptr<LLVMContext>. This creates a new ThreadSafeContext from the 1038d76c711SLang Hames /// given context. ThreadSafeModule(std::unique_ptr<Module> M,std::unique_ptr<LLVMContext> Ctx)1045ad09de9SLang Hames ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx) 105cf0949aaSLang Hames : M(std::move(M)), TSCtx(std::move(Ctx)) {} 1068d76c711SLang Hames 107cf0949aaSLang Hames /// Construct a ThreadSafeModule from a unique_ptr<Module> and an 108cf0949aaSLang Hames /// existing ThreadSafeContext. ThreadSafeModule(std::unique_ptr<Module> M,ThreadSafeContext TSCtx)1095ad09de9SLang Hames ThreadSafeModule(std::unique_ptr<Module> M, ThreadSafeContext TSCtx) 110cf0949aaSLang Hames : M(std::move(M)), TSCtx(std::move(TSCtx)) {} 1118d76c711SLang Hames ~ThreadSafeModule()112cf0949aaSLang Hames ~ThreadSafeModule() { 113cf0949aaSLang Hames // We need to lock the context while we destruct the module. 114cf0949aaSLang Hames if (M) { 115809e9d1eSLang Hames auto L = TSCtx.getLock(); 116cf0949aaSLang Hames M = nullptr; 117cf0949aaSLang Hames } 118cf0949aaSLang Hames } 119cf0949aaSLang Hames 120cf0949aaSLang Hames /// Boolean conversion: This ThreadSafeModule will evaluate to true if it 121cf0949aaSLang Hames /// wraps a non-null module. 122809e9d1eSLang Hames explicit operator bool() const { 1238d76c711SLang Hames if (M) { 1244328ea34SLang Hames assert(TSCtx.getContext() && 1254328ea34SLang Hames "Non-null module must have non-null context"); 1268d76c711SLang Hames return true; 1278d76c711SLang Hames } 1288d76c711SLang Hames return false; 1298d76c711SLang Hames } 1308d76c711SLang Hames 131809e9d1eSLang Hames /// Locks the associated ThreadSafeContext and calls the given function 132809e9d1eSLang Hames /// on the contained Module. decltype(auto)133fb45968eSJustin Lebar template <typename Func> decltype(auto) withModuleDo(Func &&F) { 134809e9d1eSLang Hames assert(M && "Can not call on null module"); 135809e9d1eSLang Hames auto Lock = TSCtx.getLock(); 136809e9d1eSLang Hames return F(*M); 137809e9d1eSLang Hames } 138809e9d1eSLang Hames 139809e9d1eSLang Hames /// Locks the associated ThreadSafeContext and calls the given function 140809e9d1eSLang Hames /// on the contained Module. decltype(auto)141fb45968eSJustin Lebar template <typename Func> decltype(auto) withModuleDo(Func &&F) const { 1420bf85d7dSLang Hames assert(M && "Can not call on null module"); 143809e9d1eSLang Hames auto Lock = TSCtx.getLock(); 144809e9d1eSLang Hames return F(*M); 145809e9d1eSLang Hames } 146809e9d1eSLang Hames 1470bf85d7dSLang Hames /// Locks the associated ThreadSafeContext and calls the given function, 1480bf85d7dSLang Hames /// passing the contained std::unique_ptr<Module>. The given function should 1490bf85d7dSLang Hames /// consume the Module. decltype(auto)150*ae331245SLang Hames template <typename Func> decltype(auto) consumingModuleDo(Func &&F) { 1510bf85d7dSLang Hames auto Lock = TSCtx.getLock(); 1520bf85d7dSLang Hames return F(std::move(M)); 1530bf85d7dSLang Hames } 1540bf85d7dSLang Hames 155809e9d1eSLang Hames /// Get a raw pointer to the contained module without locking the context. getModuleUnlocked()156809e9d1eSLang Hames Module *getModuleUnlocked() { return M.get(); } 157809e9d1eSLang Hames 158809e9d1eSLang Hames /// Get a raw pointer to the contained module without locking the context. getModuleUnlocked()159809e9d1eSLang Hames const Module *getModuleUnlocked() const { return M.get(); } 160809e9d1eSLang Hames 161809e9d1eSLang Hames /// Returns the context for this ThreadSafeModule. getContext()162809e9d1eSLang Hames ThreadSafeContext getContext() const { return TSCtx; } 163809e9d1eSLang Hames 1648d76c711SLang Hames private: 1655ad09de9SLang Hames std::unique_ptr<Module> M; 166cf0949aaSLang Hames ThreadSafeContext TSCtx; 1678d76c711SLang Hames }; 1688d76c711SLang Hames 1698d76c711SLang Hames using GVPredicate = std::function<bool(const GlobalValue &)>; 1708d76c711SLang Hames using GVModifier = std::function<void(GlobalValue &)>; 1718d76c711SLang Hames 1728d76c711SLang Hames /// Clones the given module on to a new context. 1738d76c711SLang Hames ThreadSafeModule 174f12db8cfSStefan Gränitz cloneToNewContext(const ThreadSafeModule &TSMW, 1758d76c711SLang Hames GVPredicate ShouldCloneDef = GVPredicate(), 1768d76c711SLang Hames GVModifier UpdateClonedDefSource = GVModifier()); 1778d76c711SLang Hames 1788d76c711SLang Hames } // End namespace orc 1798d76c711SLang Hames } // End namespace llvm 1808d76c711SLang Hames 181aa5c09beSKazu Hirata #endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H 182