LLVM 22.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
14
15#if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
16#include <fcntl.h>
17#include <sys/mman.h>
18#if defined(__MVS__)
19#include "llvm/Support/BLAKE3.h"
20#include <sys/shm.h>
21#endif
22#include <unistd.h>
23#elif defined(_WIN32)
24#include <windows.h>
25#endif
26
27namespace llvm {
28namespace orc {
29
31
33 : PageSize(PageSize) {}
34
37 auto PageSize = sys::Process::getPageSize();
38 if (!PageSize)
39 return PageSize.takeError();
40 return std::make_unique<InProcessMemoryMapper>(*PageSize);
41}
42
43void InProcessMemoryMapper::reserve(size_t NumBytes,
44 OnReservedFunction OnReserved) {
45 std::error_code EC;
47 NumBytes, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
48
49 if (EC)
50 return OnReserved(errorCodeToError(EC));
51
52 {
53 std::lock_guard<std::mutex> Lock(Mutex);
54 Reservations[MB.base()].Size = MB.allocatedSize();
55 }
56
57 OnReserved(
58 ExecutorAddrRange(ExecutorAddr::fromPtr(MB.base()), MB.allocatedSize()));
59}
60
62 size_t ContentSize) {
63 return Addr.toPtr<char *>();
64}
65
67 OnInitializedFunction OnInitialized) {
68 ExecutorAddr MinAddr(~0ULL);
69 ExecutorAddr MaxAddr(0);
70
71 // FIXME: Release finalize lifetime segments.
72 for (auto &Segment : AI.Segments) {
73 auto Base = AI.MappingBase + Segment.Offset;
74 auto Size = Segment.ContentSize + Segment.ZeroFillSize;
75
76 if (Base < MinAddr)
77 MinAddr = Base;
78
79 if (Base + Size > MaxAddr)
80 MaxAddr = Base + Size;
81
82 std::memset((Base + Segment.ContentSize).toPtr<void *>(), 0,
83 Segment.ZeroFillSize);
84
86 {Base.toPtr<void *>(), Size},
87 toSysMemoryProtectionFlags(Segment.AG.getMemProt()))) {
88 return OnInitialized(errorCodeToError(EC));
89 }
90 if ((Segment.AG.getMemProt() & MemProt::Exec) == MemProt::Exec)
92 }
93
94 std::vector<shared::WrapperFunctionCall> DeinitializeActions;
95 {
96 std::promise<MSVCPExpected<std::vector<shared::WrapperFunctionCall>>> P;
97 auto F = P.get_future();
99 AI.Actions, [&](Expected<std::vector<shared::WrapperFunctionCall>> R) {
100 P.set_value(std::move(R));
101 });
102 if (auto DeinitializeActionsOrErr = F.get())
103 DeinitializeActions = std::move(*DeinitializeActionsOrErr);
104 else
105 return OnInitialized(DeinitializeActionsOrErr.takeError());
106 }
107
108 {
109 std::lock_guard<std::mutex> Lock(Mutex);
110
111 // This is the maximum range whose permission have been possibly modified
112 auto &Alloc = Allocations[MinAddr];
113 Alloc.Size = MaxAddr - MinAddr;
114 Alloc.DeinitializationActions = std::move(DeinitializeActions);
115 Reservations[AI.MappingBase.toPtr<void *>()].Allocations.push_back(MinAddr);
116 }
117
118 OnInitialized(MinAddr);
119}
120
124 Error AllErr = Error::success();
125
126 {
127 std::lock_guard<std::mutex> Lock(Mutex);
128
129 for (auto Base : llvm::reverse(Bases)) {
130
132 Allocations[Base].DeinitializationActions, [&](Error Err) {
133 AllErr = joinErrors(std::move(AllErr), std::move(Err));
134 });
135
136 // Reset protections to read/write so the area can be reused
138 {Base.toPtr<void *>(), Allocations[Base].Size},
141 AllErr = joinErrors(std::move(AllErr), errorCodeToError(EC));
142 }
143
144 Allocations.erase(Base);
145 }
146 }
147
148 OnDeinitialized(std::move(AllErr));
149}
150
152 OnReleasedFunction OnReleased) {
153 Error Err = Error::success();
154
155 for (auto Base : Bases) {
156 std::vector<ExecutorAddr> AllocAddrs;
157 size_t Size;
158 {
159 std::lock_guard<std::mutex> Lock(Mutex);
160 auto &R = Reservations[Base.toPtr<void *>()];
161 Size = R.Size;
162 AllocAddrs.swap(R.Allocations);
163 }
164
165 // deinitialize sub allocations
166 std::promise<MSVCPError> P;
167 auto F = P.get_future();
168 deinitialize(AllocAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
169 if (Error E = F.get()) {
170 Err = joinErrors(std::move(Err), std::move(E));
171 }
172
173 // free the memory
174 auto MB = sys::MemoryBlock(Base.toPtr<void *>(), Size);
175
177 if (EC) {
178 Err = joinErrors(std::move(Err), errorCodeToError(EC));
179 }
180
181 std::lock_guard<std::mutex> Lock(Mutex);
182 Reservations.erase(Base.toPtr<void *>());
183 }
184
185 OnReleased(std::move(Err));
186}
187
189 std::vector<ExecutorAddr> ReservationAddrs;
190 {
191 std::lock_guard<std::mutex> Lock(Mutex);
192
193 ReservationAddrs.reserve(Reservations.size());
194 for (const auto &R : Reservations) {
195 ReservationAddrs.push_back(ExecutorAddr::fromPtr(R.getFirst()));
196 }
197 }
198
199 std::promise<MSVCPError> P;
200 auto F = P.get_future();
201 release(ReservationAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
202 cantFail(F.get());
203}
204
205// SharedMemoryMapper
206
208 SymbolAddrs SAs, size_t PageSize)
209 : EPC(EPC), SAs(SAs), PageSize(PageSize) {
210#if (!defined(LLVM_ON_UNIX) || defined(__ANDROID__)) && !defined(_WIN32)
211 llvm_unreachable("SharedMemoryMapper is not supported on this platform yet");
212#endif
213}
214
217#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
218 auto PageSize = sys::Process::getPageSize();
219 if (!PageSize)
220 return PageSize.takeError();
221
222 return std::make_unique<SharedMemoryMapper>(EPC, SAs, *PageSize);
223#else
225 "SharedMemoryMapper is not supported on this platform yet",
227#endif
228}
229
230void SharedMemoryMapper::reserve(size_t NumBytes,
231 OnReservedFunction OnReserved) {
232#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
233
234 int SharedMemoryId = -1;
235 EPC.callSPSWrapperAsync<
237 SAs.Reserve,
238 [this, NumBytes, OnReserved = std::move(OnReserved), SharedMemoryId](
239 Error SerializationErr,
241 if (SerializationErr) {
242 cantFail(Result.takeError());
243 return OnReserved(std::move(SerializationErr));
244 }
245
246 if (!Result)
247 return OnReserved(Result.takeError());
248
249 ExecutorAddr RemoteAddr;
250 std::string SharedMemoryName;
251 std::tie(RemoteAddr, SharedMemoryName) = std::move(*Result);
252
253 void *LocalAddr = nullptr;
254
255#if defined(LLVM_ON_UNIX)
256
257#if defined(__MVS__)
259 reinterpret_cast<const uint8_t *>(SharedMemoryName.c_str()),
260 SharedMemoryName.size());
261 auto HashedName = BLAKE3::hash<sizeof(key_t)>(Data);
262 key_t Key = *reinterpret_cast<key_t *>(HashedName.data());
263 SharedMemoryId =
264 shmget(Key, NumBytes, IPC_CREAT | __IPC_SHAREAS | 0700);
265 if (SharedMemoryId < 0) {
266 return OnReserved(errorCodeToError(
267 std::error_code(errno, std::generic_category())));
268 }
269 LocalAddr = shmat(SharedMemoryId, nullptr, 0);
270 if (LocalAddr == reinterpret_cast<void *>(-1)) {
271 return OnReserved(errorCodeToError(
272 std::error_code(errno, std::generic_category())));
273 }
274#else
275 int SharedMemoryFile = shm_open(SharedMemoryName.c_str(), O_RDWR, 0700);
276 if (SharedMemoryFile < 0) {
277 return OnReserved(errorCodeToError(errnoAsErrorCode()));
278 }
279
280 // this prevents other processes from accessing it by name
281 shm_unlink(SharedMemoryName.c_str());
282
283 LocalAddr = mmap(nullptr, NumBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
284 SharedMemoryFile, 0);
285 if (LocalAddr == MAP_FAILED) {
286 return OnReserved(errorCodeToError(errnoAsErrorCode()));
287 }
288
289 close(SharedMemoryFile);
290#endif
291
292#elif defined(_WIN32)
293
294 std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
295 SharedMemoryName.end());
296 HANDLE SharedMemoryFile = OpenFileMappingW(
297 FILE_MAP_ALL_ACCESS, FALSE, WideSharedMemoryName.c_str());
298 if (!SharedMemoryFile)
299 return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
300
301 LocalAddr =
302 MapViewOfFile(SharedMemoryFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
303 if (!LocalAddr) {
304 CloseHandle(SharedMemoryFile);
305 return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
306 }
307
308 CloseHandle(SharedMemoryFile);
309
310#endif
311 {
312 std::lock_guard<std::mutex> Lock(Mutex);
313 Reservations.insert(
314 {RemoteAddr, {LocalAddr, NumBytes, SharedMemoryId}});
315 }
316
317 OnReserved(ExecutorAddrRange(RemoteAddr, NumBytes));
318 },
319 SAs.Instance, static_cast<uint64_t>(NumBytes));
320
321#else
322 OnReserved(make_error<StringError>(
323 "SharedMemoryMapper is not supported on this platform yet",
325#endif
326}
327
329 size_t ContentSize) {
330 auto R = Reservations.upper_bound(Addr);
331 assert(R != Reservations.begin() && "Attempt to prepare unreserved range");
332 R--;
333
334 ExecutorAddrDiff Offset = Addr - R->first;
335
336 return static_cast<char *>(R->second.LocalAddr) + Offset;
337}
338
340 OnInitializedFunction OnInitialized) {
341 auto Reservation = Reservations.upper_bound(AI.MappingBase);
342 assert(Reservation != Reservations.begin() && "Attempt to initialize unreserved range");
343 Reservation--;
344
345 auto AllocationOffset = AI.MappingBase - Reservation->first;
346
348
349 AI.Actions.swap(FR.Actions);
350
351 FR.Segments.reserve(AI.Segments.size());
352
353 for (auto Segment : AI.Segments) {
354 char *Base = static_cast<char *>(Reservation->second.LocalAddr) +
355 AllocationOffset + Segment.Offset;
356 std::memset(Base + Segment.ContentSize, 0, Segment.ZeroFillSize);
357
359 SegReq.RAG = {Segment.AG.getMemProt(),
360 Segment.AG.getMemLifetime() == MemLifetime::Finalize};
361 SegReq.Addr = AI.MappingBase + Segment.Offset;
362 SegReq.Size = Segment.ContentSize + Segment.ZeroFillSize;
363
364 FR.Segments.push_back(SegReq);
365 }
366
367 EPC.callSPSWrapperAsync<
369 SAs.Initialize,
370 [OnInitialized = std::move(OnInitialized)](
371 Error SerializationErr, Expected<ExecutorAddr> Result) mutable {
372 if (SerializationErr) {
373 cantFail(Result.takeError());
374 return OnInitialized(std::move(SerializationErr));
375 }
376
377 OnInitialized(std::move(Result));
378 },
379 SAs.Instance, Reservation->first, std::move(FR));
380}
381
383 ArrayRef<ExecutorAddr> Allocations,
385 EPC.callSPSWrapperAsync<
387 SAs.Deinitialize,
388 [OnDeinitialized = std::move(OnDeinitialized)](Error SerializationErr,
389 Error Result) mutable {
390 if (SerializationErr) {
391 cantFail(std::move(Result));
392 return OnDeinitialized(std::move(SerializationErr));
393 }
394
395 OnDeinitialized(std::move(Result));
396 },
397 SAs.Instance, Allocations);
398}
399
401 OnReleasedFunction OnReleased) {
402#if (defined(LLVM_ON_UNIX) && !defined(__ANDROID__)) || defined(_WIN32)
403 Error Err = Error::success();
404
405 {
406 std::lock_guard<std::mutex> Lock(Mutex);
407
408 for (auto Base : Bases) {
409
410#if defined(LLVM_ON_UNIX)
411
412#if defined(__MVS__)
413 if (shmdt(Reservations[Base].LocalAddr) < 0 ||
414 shmctl(Reservations[Base].SharedMemoryId, IPC_RMID, NULL) < 0)
415 Err = joinErrors(std::move(Err), errorCodeToError(errnoAsErrorCode()));
416#else
417 if (munmap(Reservations[Base].LocalAddr, Reservations[Base].Size) != 0)
418 Err = joinErrors(std::move(Err), errorCodeToError(errnoAsErrorCode()));
419#endif
420
421#elif defined(_WIN32)
422
423 if (!UnmapViewOfFile(Reservations[Base].LocalAddr))
424 Err = joinErrors(std::move(Err),
425 errorCodeToError(mapWindowsError(GetLastError())));
426
427#endif
428
429 Reservations.erase(Base);
430 }
431 }
432
433 EPC.callSPSWrapperAsync<
435 SAs.Release,
436 [OnReleased = std::move(OnReleased),
437 Err = std::move(Err)](Error SerializationErr, Error Result) mutable {
438 if (SerializationErr) {
439 cantFail(std::move(Result));
440 return OnReleased(
441 joinErrors(std::move(Err), std::move(SerializationErr)));
442 }
443
444 return OnReleased(joinErrors(std::move(Err), std::move(Result)));
445 },
446 SAs.Instance, Bases);
447#else
448 OnReleased(make_error<StringError>(
449 "SharedMemoryMapper is not supported on this platform yet",
451#endif
452}
453
455 std::lock_guard<std::mutex> Lock(Mutex);
456 for (const auto &R : Reservations) {
457
458#if defined(LLVM_ON_UNIX) && !defined(__ANDROID__)
459
460#if defined(__MVS__)
461 shmdt(R.second.LocalAddr);
462#else
463 munmap(R.second.LocalAddr, R.second.Size);
464#endif
465
466#elif defined(_WIN32)
467
468 UnmapViewOfFile(R.second.LocalAddr);
469
470#else
471
472 (void)R;
473
474#endif
475 }
476}
477
478} // namespace orc
479
480} // 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:55
#define G(x, y, z)
Definition MD5.cpp:56
#define P(N)
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:41
static BLAKE3Result< NumBytes > hash(ArrayRef< uint8_t > Data)
Returns a BLAKE3 hash for the given data.
Definition BLAKE3.h:86
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
Definition OrcRTBridge.h:82
shared::SPSExpected< shared::SPSExecutorAddr >( shared::SPSExecutorAddr, shared::SPSExecutorAddr, shared::SPSSharedMemoryFinalizeRequest) SPSExecutorSharedMemoryMapperServiceInitializeSignature
Definition OrcRTBridge.h:86
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSExecutorSharedMemoryMapperServiceDeinitializeSignature
Definition OrcRTBridge.h:90
shared::SPSError( shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSExecutorSharedMemoryMapperServiceReleaseSignature
Definition OrcRTBridge.h:93
LLVM_ABI void runDeallocActions(ArrayRef< WrapperFunctionCall > DAs, OnRunDeallocActionsComeleteFn OnComplete)
Run deallocation actions.
LLVM_ABI void runFinalizeActions(AllocActions &AAs, OnRunFinalizeActionsCompleteFn OnComplete)
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:477
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:98
auto reverse(ContainerTy &&C)
Definition STLExtras.h:406
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:189
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition Error.cpp:111
LLVM_ABI std::error_code mapWindowsError(unsigned EV)
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition Error.h:1240
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