xref: /freebsd-src/contrib/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/ThreadSafeModule.h (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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