blob: 5787500387c4bb5dc4b34369fc11f29488423559 [file] [log] [blame]
Andrew Scull0372a572018-11-16 15:47:06 +00001//===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===//
2//
Andrew Walbran16937d02019-10-22 13:54:20 +01003// 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
Andrew Scull0372a572018-11-16 15:47:06 +00006//
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
24namespace llvm {
25namespace 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.
29class ThreadSafeContext {
30private:
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
38public:
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
77private:
78 std::shared_ptr<State> S;
79};
80
81/// An LLVM Module together with a shared ThreadSafeContext.
82class ThreadSafeModule {
83public:
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
145private:
146 std::unique_ptr<Module> M;
147 ThreadSafeContext TSCtx;
148};
149
150using GVPredicate = std::function<bool(const GlobalValue &)>;
151using GVModifier = std::function<void(GlobalValue &)>;
152
153/// Clones the given module on to a new context.
154ThreadSafeModule
155cloneToNewContext(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