1 //===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Thread safe wrappers and utilities for Module and LLVMContext.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H
15 #define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H
16 
17 #include "llvm/IR/LLVMContext.h"
18 #include "llvm/IR/Module.h"
19 #include "llvm/Support/Compiler.h"
20 
21 #include <functional>
22 #include <memory>
23 #include <mutex>
24 
25 namespace llvm {
26 namespace orc {
27 
28 /// An LLVMContext together with an associated mutex that can be used to lock
29 /// the context to prevent concurrent access by other threads.
30 class ThreadSafeContext {
31 private:
32   struct State {
StateState33     State(std::unique_ptr<LLVMContext> Ctx) : Ctx(std::move(Ctx)) {}
34 
35     std::unique_ptr<LLVMContext> Ctx;
36     std::recursive_mutex Mutex;
37   };
38 
39 public:
40   // RAII based lock for ThreadSafeContext.
41   class LLVM_NODISCARD Lock {
42   private:
43     using UnderlyingLock = std::lock_guard<std::recursive_mutex>;
44 
45   public:
Lock(std::shared_ptr<State> S)46     Lock(std::shared_ptr<State> S)
47         : S(std::move(S)),
48           L(llvm::make_unique<UnderlyingLock>(this->S->Mutex)) {}
49 
50   private:
51     std::shared_ptr<State> S;
52     std::unique_ptr<UnderlyingLock> L;
53   };
54 
55   /// Construct a null context.
56   ThreadSafeContext() = default;
57 
58   /// Construct a ThreadSafeContext from the given LLVMContext.
ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx)59   ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx)
60       : S(std::make_shared<State>(std::move(NewCtx))) {
61     assert(S->Ctx != nullptr &&
62            "Can not construct a ThreadSafeContext from a nullptr");
63   }
64 
65   /// Returns a pointer to the LLVMContext that was used to construct this
66   /// instance, or null if the instance was default constructed.
getContext()67   LLVMContext *getContext() { return S ? S->Ctx.get() : nullptr; }
68 
69   /// Returns a pointer to the LLVMContext that was used to construct this
70   /// instance, or null if the instance was default constructed.
getContext()71   const LLVMContext *getContext() const { return S ? S->Ctx.get() : nullptr; }
72 
getLock()73   Lock getLock() {
74     assert(S && "Can not lock an empty ThreadSafeContext");
75     return Lock(S);
76   }
77 
78 private:
79   std::shared_ptr<State> S;
80 };
81 
82 /// An LLVM Module together with a shared ThreadSafeContext.
83 class ThreadSafeModule {
84 public:
85   /// Default construct a ThreadSafeModule. This results in a null module and
86   /// null context.
87   ThreadSafeModule() = default;
88 
89   ThreadSafeModule(ThreadSafeModule &&Other) = default;
90 
91   ThreadSafeModule &operator=(ThreadSafeModule &&Other) {
92     // We have to explicitly define this move operator to copy the fields in
93     // reverse order (i.e. module first) to ensure the dependencies are
94     // protected: The old module that is being overwritten must be destroyed
95     // *before* the context that it depends on.
96     // We also need to lock the context to make sure the module tear-down
97     // does not overlap any other work on the context.
98     if (M) {
99       auto L = getContextLock();
100       M = nullptr;
101     }
102     M = std::move(Other.M);
103     TSCtx = std::move(Other.TSCtx);
104     return *this;
105   }
106 
107   /// Construct a ThreadSafeModule from a unique_ptr<Module> and a
108   /// unique_ptr<LLVMContext>. This creates a new ThreadSafeContext from the
109   /// given context.
ThreadSafeModule(std::unique_ptr<Module> M,std::unique_ptr<LLVMContext> Ctx)110   ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx)
111       : M(std::move(M)), TSCtx(std::move(Ctx)) {}
112 
113   /// Construct a ThreadSafeModule from a unique_ptr<Module> and an
114   /// existing ThreadSafeContext.
ThreadSafeModule(std::unique_ptr<Module> M,ThreadSafeContext TSCtx)115   ThreadSafeModule(std::unique_ptr<Module> M, ThreadSafeContext TSCtx)
116       : M(std::move(M)), TSCtx(std::move(TSCtx)) {}
117 
~ThreadSafeModule()118   ~ThreadSafeModule() {
119     // We need to lock the context while we destruct the module.
120     if (M) {
121       auto L = getContextLock();
122       M = nullptr;
123     }
124   }
125 
126   /// Get the module wrapped by this ThreadSafeModule.
getModule()127   Module *getModule() { return M.get(); }
128 
129   /// Get the module wrapped by this ThreadSafeModule.
getModule()130   const Module *getModule() const { return M.get(); }
131 
132   /// Take out a lock on the ThreadSafeContext for this module.
getContextLock()133   ThreadSafeContext::Lock getContextLock() { return TSCtx.getLock(); }
134 
135   /// Boolean conversion: This ThreadSafeModule will evaluate to true if it
136   /// wraps a non-null module.
137   explicit operator bool() {
138     if (M) {
139       assert(TSCtx.getContext() &&
140              "Non-null module must have non-null context");
141       return true;
142     }
143     return false;
144   }
145 
146 private:
147   std::unique_ptr<Module> M;
148   ThreadSafeContext TSCtx;
149 };
150 
151 using GVPredicate = std::function<bool(const GlobalValue &)>;
152 using GVModifier = std::function<void(GlobalValue &)>;
153 
154 /// Clones the given module on to a new context.
155 ThreadSafeModule
156 cloneToNewContext(ThreadSafeModule &TSMW,
157                   GVPredicate ShouldCloneDef = GVPredicate(),
158                   GVModifier UpdateClonedDefSource = GVModifier());
159 
160 } // End namespace orc
161 } // End namespace llvm
162 
163 #endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H
164