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