LLVM 23.0.0git
MemoryMapper.cpp
Go to the documentation of this file.
1//===- MemoryMapper.cpp - Cross-process memory mapper ------------*- 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
10
11#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX
15
16#if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
17#include <fcntl.h>
18#include <sys/mman.h>
19#if defined(__MVS__)
20#include "llvm/Support/BLAKE3.h"
21#include <sys/shm.h>
22#endif
23#include <unistd.h>
24#elif defined(_WIN32)
25#include <windows.h>
26#endif
27
28namespace llvm {
29namespace orc {
30
32
34 : PageSize(PageSize) {}
35
38 auto PageSize = sys::Process::getPageSize();
39 if (!PageSize)
40 return PageSize.takeError();
41 return std::make_unique<InProcessMemoryMapper>(*PageSize);
42}
43
44void InProcessMemoryMapper::reserve(size_t NumBytes,
45 OnReservedFunction OnReserved) {
46 std::error_code EC;
48 NumBytes, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
49
50 if (EC)
51 return OnReserved(errorCodeToError(EC));
52
53 {
54 std::lock_guard<std::mutex> Lock(Mutex);
55 Reservations[MB.base()].Size = MB.allocatedSize();
56 }
57
58 OnReserved(
59 ExecutorAddrRange(ExecutorAddr::fromPtr(MB.base()), MB.allocatedSize()));
60}
61
63 size_t ContentSize) {
64 return Addr.toPtr<char *>();
65}
66
68 OnInitializedFunction OnInitialized) {
69 ExecutorAddr MinAddr(~0ULL);
70 ExecutorAddr MaxAddr(0);
71
72 // FIXME: Release finalize lifetime segments.
73 for (auto &Segment : AI.Segments) {
74 auto Base = AI.MappingBase + Segment.Offset;
75 auto Size = Segment.ContentSize + Segment.ZeroFillSize;
76
77 if (Base < MinAddr)
78 MinAddr = Base;
79
80 if (Base + Size > MaxAddr)
81 MaxAddr = Base + Size;
82
83 std::memset((Base + Segment.ContentSize).toPtr<void *>(), 0,
84 Segment.ZeroFillSize);
85
87 {Base.toPtr<void *>(), Size},
88 toSysMemoryProtectionFlags(Segment.AG.getMemProt()))) {
89 return OnInitialized(errorCodeToError(EC));
90 }
91 if ((Segment.AG.getMemProt() & MemProt::Exec) == MemProt::Exec)
93 }
94
95 auto DeinitializeActions = shared::runFinalizeActions(AI.Actions);
96 if (!DeinitializeActions)
97 return OnInitialized(DeinitializeActions.takeError());
98
99 {
100 std::lock_guard<std::mutex> Lock(Mutex);
101
102 // This is the maximum range whose permission have been possibly modified
103 auto &Alloc = Allocations[MinAddr];
104 Alloc.Size = MaxAddr - MinAddr;
105 Alloc.DeinitializationActions = std::move(*DeinitializeActions);
106 Reservations[AI.MappingBase.toPtr<void *>()].Allocations.push_back(MinAddr);
107 }
108
109 OnInitialized(MinAddr);
110}
111
115 Error AllErr = Error::success();
116
117 {
118 std::lock_guard<std::mutex> Lock(Mutex);
119
120 for (auto Base : llvm::reverse(Bases)) {
121
123 Allocations[Base].DeinitializationActions)) {
124 AllErr = joinErrors(std::move(AllErr), std::move(Err));
125 }
126
127 // Reset protections to read/write so the area can be reused
129 {Base.toPtr<void *>(), Allocations[Base].Size},
132 AllErr = joinErrors(std::move(AllErr), errorCodeToError(EC));
133 }
134
135 Allocations.erase(Base);
136 }
137 }
138
139 OnDeinitialized(std::move(AllErr));
140}
141
143 OnReleasedFunction OnReleased) {
144 Error Err = Error::success();
145
146 for (auto Base : Bases) {
147 std::vector<ExecutorAddr> AllocAddrs;
148 size_t Size;
149 {
150 std::lock_guard<std::mutex> Lock(Mutex);
151 auto &R = Reservations[Base.toPtr<void *>()];
152 Size = R.Size;
153 AllocAddrs.swap(R.Allocations);
154 }
155
156 // deinitialize sub allocations
157 std::promise<MSVCPError> P;
158 auto F = P.get_future();
159 deinitialize(AllocAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
160 if (Error E = F.get()) {
161 Err = joinErrors(std::move(Err), std::move(E));
162 }
163
164 // free the memory
165 auto MB = sys::MemoryBlock(Base.toPtr<void *>(), Size);
166
168 if (EC) {
169 Err = joinErrors(std::move(Err), errorCodeToError(EC));
170 }
171
172 std::lock_guard<std::mutex> Lock(Mutex);
173 Reservations.erase(Base.toPtr<void *>());
174 }
175
176 OnReleased(std::move(Err));
177}
178
180 std::vector<ExecutorAddr> ReservationAddrs;
181 {
182 std::lock_guard<std::mutex> Lock(Mutex);
183
184 ReservationAddrs.reserve(Reservations.size());
185 for (const auto &R : Reservations) {
186 ReservationAddrs.push_back(ExecutorAddr::fromPtr(R.getFirst()));
187 }
188 }
189
190 std::promise<MSVCPError> P;
191 auto F = P.get_future();
192 release(ReservationAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
193 cantFail(F.get());
194}
195
196// SharedMemoryMapper
197
199 SymbolAddrs SAs, size_t PageSize)
200 : EPC(EPC), SAs(SAs), PageSize(PageSize) {
201#if (!defined(LLVM_ON_UNIX) || defined(__ANDROID__)) && !defined(_WIN32)
202 llvm_unreachable("SharedMemoryMapper is not supported on this platform yet");
203#endif
204}
205
208#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
209 auto PageSize = sys::Process::getPageSize();
210 if (!PageSize)
211 return PageSize.takeError();
212
213 return std::make_unique<SharedMemoryMapper>(EPC, SAs, *PageSize);
214#else
216 "SharedMemoryMapper is not supported on this platform yet",
218#endif
219}
220
221void SharedMemoryMapper::reserve(size_t NumBytes,
222 OnReservedFunction OnReserved) {
223#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
224
225 int SharedMemoryId = -1;
226 EPC.callSPSWrapperAsync<
228 SAs.Reserve,
229 [this, NumBytes, OnReserved = std::move(OnReserved), SharedMemoryId](
230 Error SerializationErr,
232 if (SerializationErr) {
233 cantFail(Result.takeError());
234 return OnReserved(std::move(SerializationErr));
235 }
236
237 if (!Result)
238 return OnReserved(Result.takeError());
239
240 ExecutorAddr RemoteAddr;
241 std::string SharedMemoryName;
242 std::tie(RemoteAddr, SharedMemoryName) = std::move(*Result);
243
244 void *LocalAddr = nullptr;
245
246#if defined(LLVM_ON_UNIX)
247
248#if defined(__MVS__)
250 reinterpret_cast<const uint8_t *>(SharedMemoryName.c_str()),
251 SharedMemoryName.size());
252 auto HashedName = BLAKE3::hash<sizeof(key_t)>(Data);
253 key_t Key = *reinterpret_cast<key_t *>(HashedName.data());
254 SharedMemoryId =
255 shmget(Key, NumBytes, IPC_CREAT | __IPC_SHAREAS | 0700);
256 if (SharedMemoryId < 0) {
257 return OnReserved(errorCodeToError(
258 std::error_code(errno, std::generic_category())));
259 }
260 LocalAddr = shmat(SharedMemoryId, nullptr, 0);
261 if (LocalAddr == reinterpret_cast<void *>(-1)) {
262 return OnReserved(errorCodeToError(
263 std::error_code(errno, std::generic_category())));
264 }
265#else
266 int SharedMemoryFile = shm_open(SharedMemoryName.c_str(), O_RDWR, 0700);
267 if (SharedMemoryFile < 0) {
268 return OnReserved(errorCodeToError(errnoAsErrorCode()));
269 }
270
271 // this prevents other processes from accessing it by name
272 shm_unlink(SharedMemoryName.c_str());
273
274 LocalAddr = mmap(nullptr, NumBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
275 SharedMemoryFile, 0);
276 if (LocalAddr == MAP_FAILED) {
277 return OnReserved(errorCodeToError(errnoAsErrorCode()));
278 }
279
280 close(SharedMemoryFile);
281#endif
282
283#elif defined(_WIN32)
284
285 std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
286 SharedMemoryName.end());
287 HANDLE SharedMemoryFile = OpenFileMappingW(
288 FILE_MAP_ALL_ACCESS, FALSE, WideSharedMemoryName.c_str());
289 if (!SharedMemoryFile)
290 return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
291
292 LocalAddr =
293 MapViewOfFile(SharedMemoryFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
294 if (!LocalAddr) {
295 CloseHandle(SharedMemoryFile);
296 return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
297 }
298
299 CloseHandle(SharedMemoryFile);
300
301#endif
302 {
303 std::lock_guard<std::mutex> Lock(Mutex);
304 Reservations.insert(
305 {RemoteAddr, {LocalAddr, NumBytes, SharedMemoryId}});
306 }
307
308 OnReserved(ExecutorAddrRange(RemoteAddr, NumBytes));
309 },
310 SAs.Instance, static_cast<uint64_t>(NumBytes));
311
312#else
313 OnReserved(make_error<StringError>(
314 "SharedMemoryMapper is not supported on this platform yet",
316#endif
317}
318
320 size_t ContentSize) {
321 auto R = Reservations.upper_bound(Addr);
322 assert(R != Reservations.begin() && "Attempt to prepare unreserved range");
323 R--;
324
325 ExecutorAddrDiff Offset = Addr - R->first;
326
327 return static_cast<char *>(R->second.LocalAddr) + Offset;
328}
329
331 OnInitializedFunction OnInitialized) {
332 auto Reservation = Reservations.upper_bound(AI.MappingBase);
333 assert(Reservation != Reservations.begin() && "Attempt to initialize unreserved range");
334 Reservation--;
335
336 auto AllocationOffset = AI.MappingBase - Reservation->first;
337
339
340 AI.Actions.swap(FR.Actions);
341
342 FR.Segments.reserve(AI.Segments.size());
343
344 for (auto Segment : AI.Segments) {
345 char *Base = static_cast<char *>(Reservation->second.LocalAddr) +
346 AllocationOffset + Segment.Offset;
347 std::memset(Base + Segment.ContentSize, 0, Segment.ZeroFillSize);
348
350 SegReq.RAG = {Segment.AG.getMemProt(),
351 Segment.AG.getMemLifetime() == MemLifetime::Finalize};
352 SegReq.Addr = AI.MappingBase + Segment.Offset;
353 SegReq.Size = Segment.ContentSize + Segment.ZeroFillSize;
354
355 FR.Segments.push_back(SegReq);
356 }
357
358 EPC.callSPSWrapperAsync<
360 SAs.Initialize,
361 [OnInitialized = std::move(OnInitialized)](
362 Error SerializationErr, Expected<ExecutorAddr> Result) mutable {
363 if (SerializationErr) {
364 cantFail(Result.takeError());
365 return OnInitialized(std::move(SerializationErr));
366 }
367
368 OnInitialized(std::move(Result));
369 },
370 SAs.Instance, Reservation->first, std::move(FR));
371}
372
374 ArrayRef<ExecutorAddr> Allocations,
376 EPC.callSPSWrapperAsync<
378 SAs.Deinitialize,
379 [OnDeinitialized = std::move(OnDeinitialized)](Error SerializationErr,
380 Error Result) mutable {
381 if (SerializationErr) {
382 cantFail(std::move(Result));
383 return OnDeinitialized(std::move(SerializationErr));
384 }
385
386 OnDeinitialized(std::move(Result));
387 },
388 SAs.Instance, Allocations);
389}
390
392 OnReleasedFunction OnReleased) {
393#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
394 Error Err = Error::success();
395
396 {
397 std::lock_guard<std::mutex> Lock(Mutex);
398
399 for (auto Base : Bases) {
400
401#if defined(LLVM_ON_UNIX)
402
403#if defined(__MVS__)
404 if (shmdt(Reservations[Base].LocalAddr) < 0 ||
405 shmctl(Reservations[Base].SharedMemoryId, IPC_RMID, NULL) < 0)
406 Err = joinErrors(std::move(Err), errorCodeToError(errnoAsErrorCode()));
407#else
408 if (munmap(Reservations[Base].LocalAddr, Reservations[Base].Size) != 0)
409 Err = joinErrors(std::move(Err), errorCodeToError(errnoAsErrorCode()));
410#endif
411
412#elif defined(_WIN32)
413
414 if (!UnmapViewOfFile(Reservations[Base].LocalAddr))
415 Err = joinErrors(std::move(Err),
416 errorCodeToError(mapWindowsError(GetLastError())));
417
418#endif
419
420 Reservations.erase(Base);
421 }
422 }
423
424 EPC.callSPSWrapperAsync<
426 SAs.Release,
427 [OnReleased = std::move(OnReleased),
428 Err = std::move(Err)](Error SerializationErr, Error Result) mutable {
429 if (SerializationErr) {
430 cantFail(std::move(Result));
431 return OnReleased(
432 joinErrors(std::move(Err), std::move(SerializationErr)));
433 }
434
435 return OnReleased(joinErrors(std::move(Err), std::move(Result)));
436 },
437 SAs.Instance, Bases);
438#else
439 OnReleased(make_error<StringError>(
440 "SharedMemoryMapper is not supported on this platform yet",
442#endif
443}
444
446 std::lock_guard<std::mutex> Lock(Mutex);
447 for (const auto &R : Reservations) {
448
449#if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
450
451#if defined(__MVS__)
452 shmdt(R.second.LocalAddr);
453#else
454 munmap(R.second.LocalAddr, R.second.Size);
455#endif
456
457#elif defined(_WIN32)
458
459 UnmapViewOfFile(R.second.LocalAddr);
460
461#else
462
463 (void)R;
464
465#endif
466 }
467}
468
469} // namespace orc
470
471} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define F(x, y, z)
Definition MD5.cpp:54
#define G(x, y, z)
Definition MD5.cpp:55
#define P(N)
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
static BLAKE3Result< NumBytes > hash(ArrayRef< uint8_t > Data)
Returns a BLAKE3 hash for the given data.
Definition BLAKE3.h:92
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Represents an address in the executor process.
static ExecutorAddr fromPtr(T *Ptr, UnwrapFn &&Unwrap=UnwrapFn())
Create an ExecutorAddr from the given pointer.
std::enable_if_t< std::is_pointer< T >::value, T > toPtr(WrapFn &&Wrap=WrapFn()) const
Cast this ExecutorAddr to a pointer of the given type.
ExecutorProcessControl supports interaction with a JIT target process.
void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override
Ensures executor memory is synchronized with working copy memory, sends functions to be called after ...
void reserve(size_t NumBytes, OnReservedFunction OnReserved) override
Reserves address space in executor process.
char * prepare(jitlink::LinkGraph &G, ExecutorAddr Addr, size_t ContentSize) override
Provides working memory The LinkGraph parameter is included to allow implementations to allocate work...
void deinitialize(ArrayRef< ExecutorAddr > Allocations, OnDeinitializedFunction OnDeInitialized) override
Runs previously specified deinitialization actions Executor addresses returned by initialize should b...
static Expected< std::unique_ptr< InProcessMemoryMapper > > Create()
void release(ArrayRef< ExecutorAddr > Reservations, OnReleasedFunction OnRelease) override
Release address space acquired through reserve()
unique_function< void(Error)> OnReleasedFunction
unique_function< void(Expected< ExecutorAddr >)> OnInitializedFunction
unique_function< void(Expected< ExecutorAddrRange >)> OnReservedFunction
unique_function< void(Error)> OnDeinitializedFunction
static Expected< std::unique_ptr< SharedMemoryMapper > > Create(ExecutorProcessControl &EPC, SymbolAddrs SAs)
char * prepare(jitlink::LinkGraph &G, ExecutorAddr Addr, size_t ContentSize) override
Provides working memory The LinkGraph parameter is included to allow implementations to allocate work...
void reserve(size_t NumBytes, OnReservedFunction OnReserved) override
Reserves address space in executor process.
void deinitialize(ArrayRef< ExecutorAddr > Allocations, OnDeinitializedFunction OnDeInitialized) override
Runs previously specified deinitialization actions Executor addresses returned by initialize should b...
void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override
Ensures executor memory is synchronized with working copy memory, sends functions to be called after ...
void release(ArrayRef< ExecutorAddr > Reservations, OnReleasedFunction OnRelease) override
Release address space acquired through reserve()
SharedMemoryMapper(ExecutorProcessControl &EPC, SymbolAddrs SAs, size_t PageSize)
This class encapsulates the notion of a memory block which has an address and a size.
Definition Memory.h:33
static LLVM_ABI std::error_code protectMappedMemory(const MemoryBlock &Block, unsigned Flags)
This method sets the protection flags for a block of memory to the state specified by /p Flags.
static LLVM_ABI std::error_code releaseMappedMemory(MemoryBlock &Block)
This method releases a block of memory that was allocated with the allocateMappedMemory method.
static LLVM_ABI void InvalidateInstructionCache(const void *Addr, size_t Len)
InvalidateInstructionCache - Before the JIT can run a block of code that has been emitted it must inv...
static LLVM_ABI MemoryBlock allocateMappedMemory(size_t NumBytes, const MemoryBlock *const NearBlock, unsigned Flags, std::error_code &EC)
This method allocates a block of memory that is suitable for loading dynamically generated code (e....
static LLVM_ABI Expected< unsigned > getPageSize()
Get the process's page size.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
shared::SPSExpected< shared::SPSTuple< shared::SPSExecutorAddr, shared::SPSString > >( shared::SPSExecutorAddr, uint64_t) SPSExecutorSharedMemoryMapperServiceReserveSignature
shared::SPSExpected< shared::SPSExecutorAddr >( shared::SPSExecutorAddr, shared::SPSExecutorAddr, shared::SPSSharedMemoryFinalizeRequest) SPSExecutorSharedMemoryMapperServiceInitializeSignature
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSExecutorSharedMemoryMapperServiceDeinitializeSignature
shared::SPSError( shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSExecutorSharedMemoryMapperServiceReleaseSignature
LLVM_ABI Error runDeallocActions(ArrayRef< WrapperFunctionCall > DAs)
Run deallocation actions.
LLVM_ABI Expected< std::vector< WrapperFunctionCall > > runFinalizeActions(AllocActions &AAs)
Run finalize actions.
uint64_t ExecutorAddrDiff
@ Finalize
Finalize memory should be allocated by the allocator, and then be overwritten and deallocated after a...
Definition MemoryFlags.h:83
sys::Memory::ProtectionFlags toSysMemoryProtectionFlags(MemProt MP)
Convert a MemProt value to a corresponding sys::Memory::ProtectionFlags value.
Definition MemoryFlags.h:44
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:557
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition Error.h:442
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
FunctionAddr VTableAddr uintptr_t uintptr_t Data
Definition InstrProf.h:221
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition Error.cpp:107
LLVM_ABI std::error_code mapWindowsError(unsigned EV)
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition Error.h:1256
Represents an address range in the exceutor process.
Represents a single allocation containing multiple segments and initialization and deinitialization a...
std::vector< SegInfo > Segments
std::vector< SharedMemorySegFinalizeRequest > Segments