Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/include/Luau/SharedCodeAllocator.h
2727 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#pragma once
3
4
#include "Luau/CodeAllocationData.h"
5
#include "Luau/CodeGen.h"
6
#include "Luau/Common.h"
7
#include "Luau/NativeProtoExecData.h"
8
9
#include <array>
10
#include <atomic>
11
#include <memory>
12
#include <mutex>
13
#include <optional>
14
#include <stdint.h>
15
#include <unordered_map>
16
#include <vector>
17
18
namespace Luau
19
{
20
namespace CodeGen
21
{
22
23
// SharedCodeAllocator is a native executable code allocator that provides
24
// shared ownership of the native code. Code is allocated on a per-module
25
// basis. Each module is uniquely identifiable via an id, which may be a hash
26
// or other unique value. Each module may contain multiple natively compiled
27
// functions (protos).
28
//
29
// The module is the unit of shared ownership (i.e., it is where the reference
30
// count is maintained).
31
32
33
struct CodeAllocator;
34
class NativeModule;
35
class NativeModuleRef;
36
class SharedCodeAllocator;
37
38
39
// A NativeModule represents a single natively-compiled module (script). It is
40
// the unit of shared ownership and is thus where the reference count is
41
// maintained. It owns a set of NativeProtos, with associated native exec data,
42
// and the allocated native data and code.
43
class NativeModule
44
{
45
public:
46
NativeModule(
47
SharedCodeAllocator* allocator,
48
const std::optional<ModuleId>& moduleId,
49
const uint8_t* moduleBaseAddress,
50
std::vector<NativeProtoExecDataPtr> nativeProtos
51
) noexcept;
52
NativeModule(
53
SharedCodeAllocator* allocator,
54
const std::optional<ModuleId>& moduleId,
55
CodeAllocationData codeAllocationData,
56
std::vector<NativeProtoExecDataPtr> nativeProtos
57
) noexcept;
58
59
NativeModule(const NativeModule&) = delete;
60
NativeModule(NativeModule&&) = delete;
61
NativeModule& operator=(const NativeModule&) = delete;
62
NativeModule& operator=(NativeModule&&) = delete;
63
64
// The NativeModule must not be destroyed if there are any outstanding
65
// references. It should thus only be destroyed by a call to release()
66
// that releases the last reference.
67
~NativeModule() noexcept;
68
69
size_t addRef() const noexcept;
70
size_t addRefs(size_t count) const noexcept;
71
size_t release() const noexcept;
72
[[nodiscard]] size_t getRefcount() const noexcept;
73
74
[[nodiscard]] const std::optional<ModuleId>& getModuleId() const noexcept;
75
76
// Gets the base address of the executable native code for the module.
77
[[nodiscard]] const uint8_t* getModuleBaseAddress() const noexcept;
78
79
// Gets the information about code allocation for this module.
80
[[nodiscard]] CodeAllocationData getCodeAllocationData() const noexcept;
81
82
// Attempts to find the NativeProto with the given bytecode id. If no
83
// NativeProto for that bytecode id exists, a null pointer is returned.
84
[[nodiscard]] const uint32_t* tryGetNativeProto(uint32_t bytecodeId) const noexcept;
85
86
[[nodiscard]] const std::vector<NativeProtoExecDataPtr>& getNativeProtos() const noexcept;
87
88
private:
89
mutable std::atomic<size_t> refcount = 0;
90
91
SharedCodeAllocator* allocator = nullptr;
92
std::optional<ModuleId> moduleId = {};
93
const uint8_t* moduleBaseAddress_DEPRECATED = nullptr;
94
CodeAllocationData codeAllocationData;
95
96
std::vector<NativeProtoExecDataPtr> nativeProtos = {};
97
};
98
99
// A NativeModuleRef is an owning reference to a NativeModule. (Note: We do
100
// not use shared_ptr, to avoid complex state management in the Luau GC Proto
101
// object.)
102
class NativeModuleRef
103
{
104
public:
105
NativeModuleRef() noexcept = default;
106
NativeModuleRef(const NativeModule* nativeModule) noexcept;
107
108
NativeModuleRef(const NativeModuleRef& other) noexcept;
109
NativeModuleRef(NativeModuleRef&& other) noexcept;
110
NativeModuleRef& operator=(NativeModuleRef other) noexcept;
111
112
~NativeModuleRef() noexcept;
113
114
void reset() noexcept;
115
void swap(NativeModuleRef& other) noexcept;
116
117
[[nodiscard]] bool empty() const noexcept;
118
explicit operator bool() const noexcept;
119
120
[[nodiscard]] const NativeModule* get() const noexcept;
121
[[nodiscard]] const NativeModule* operator->() const noexcept;
122
[[nodiscard]] const NativeModule& operator*() const noexcept;
123
124
private:
125
const NativeModule* nativeModule = nullptr;
126
};
127
128
class SharedCodeAllocator
129
{
130
public:
131
SharedCodeAllocator(CodeAllocator* codeAllocator) noexcept;
132
133
SharedCodeAllocator(const SharedCodeAllocator&) = delete;
134
SharedCodeAllocator(SharedCodeAllocator&&) = delete;
135
SharedCodeAllocator& operator=(const SharedCodeAllocator&) = delete;
136
SharedCodeAllocator& operator=(SharedCodeAllocator&&) = delete;
137
138
~SharedCodeAllocator() noexcept;
139
140
// If we have a NativeModule for the given ModuleId, an owning reference to
141
// it is returned. Otherwise, an empty NativeModuleRef is returned.
142
[[nodiscard]] NativeModuleRef tryGetNativeModule(const ModuleId& moduleId) const noexcept;
143
144
// If we have a NativeModule for the given ModuleId, an owning reference to
145
// it is returned. Otherwise, a new NativeModule is created for that ModuleId
146
// using the provided NativeProtos, data, and code (space is allocated for the
147
// data and code such that it can be executed). Like std::map::insert, the
148
// bool result is true if a new module was created; false if an existing
149
// module is being returned.
150
std::pair<NativeModuleRef, bool> getOrInsertNativeModule(
151
const ModuleId& moduleId,
152
std::vector<NativeProtoExecDataPtr> nativeProtos,
153
const uint8_t* data,
154
size_t dataSize,
155
const uint8_t* code,
156
size_t codeSize
157
);
158
159
NativeModuleRef insertAnonymousNativeModule(
160
std::vector<NativeProtoExecDataPtr> nativeProtos,
161
const uint8_t* data,
162
size_t dataSize,
163
const uint8_t* code,
164
size_t codeSize
165
);
166
167
// If a NativeModule exists for the given ModuleId and that NativeModule
168
// is no longer referenced, the NativeModule is destroyed. This should
169
// usually only be called by NativeModule::release() when the reference
170
// count becomes zero
171
void eraseNativeModuleIfUnreferenced(const NativeModule& nativeModule);
172
173
private:
174
struct ModuleIdHash
175
{
176
[[nodiscard]] size_t operator()(const ModuleId& moduleId) const noexcept;
177
};
178
179
[[nodiscard]] NativeModuleRef tryGetNativeModuleWithLockHeld(const ModuleId& moduleId) const noexcept;
180
181
mutable std::mutex mutex;
182
183
std::unordered_map<ModuleId, std::unique_ptr<NativeModule>, ModuleIdHash, std::equal_to<>> identifiedModules;
184
185
std::atomic<size_t> anonymousModuleCount = 0;
186
187
CodeAllocator* codeAllocator = nullptr;
188
};
189
190
} // namespace CodeGen
191
} // namespace Luau
192
193