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