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