[ORC] Add a shared-memory based orc::MemoryMapper.

This is an implementation of orc::MemoryMapper that maps shared memory
pages in both executor and controller process and writes directly to
them avoiding transferring content over EPC. All allocations are properly
deinitialized automatically on the executor side at shutdown by the
ExecutorSharedMemoryMapperService.

Reviewed By: lhames

Differential Revision: https://reviews.llvm.org/D128544
This commit is contained in:
Anubhab Ghosh
2022-07-12 16:15:19 -07:00
committed by Lang Hames
parent 6e6c1efe04
commit 5acd471698
9 changed files with 858 additions and 0 deletions

View File

@@ -109,6 +109,47 @@ private:
AllocationMap Allocations;
};
class SharedMemoryMapper final : public MemoryMapper {
public:
struct SymbolAddrs {
ExecutorAddr Instance;
ExecutorAddr Reserve;
ExecutorAddr Initialize;
ExecutorAddr Deinitialize;
ExecutorAddr Release;
};
SharedMemoryMapper(ExecutorProcessControl &EPC, SymbolAddrs SAs)
: EPC(EPC), SAs(SAs) {}
void reserve(size_t NumBytes, OnReservedFunction OnReserved) override;
char *prepare(ExecutorAddr Addr, size_t ContentSize) override;
void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override;
void deinitialize(ArrayRef<ExecutorAddr> Allocations,
OnDeinitializedFunction OnDeInitialized) override;
void release(ArrayRef<ExecutorAddr> Reservations,
OnReleasedFunction OnRelease) override;
~SharedMemoryMapper() override;
private:
struct Reservation {
void *LocalAddr;
size_t Size;
};
ExecutorProcessControl &EPC;
SymbolAddrs SAs;
std::mutex Mutex;
std::map<ExecutorAddr, Reservation> Reservations;
};
} // namespace orc
} // end namespace llvm

View File

@@ -31,6 +31,12 @@ extern const char *SimpleExecutorMemoryManagerReserveWrapperName;
extern const char *SimpleExecutorMemoryManagerFinalizeWrapperName;
extern const char *SimpleExecutorMemoryManagerDeallocateWrapperName;
extern const char *ExecutorSharedMemoryMapperServiceInstanceName;
extern const char *ExecutorSharedMemoryMapperServiceReserveWrapperName;
extern const char *ExecutorSharedMemoryMapperServiceInitializeWrapperName;
extern const char *ExecutorSharedMemoryMapperServiceDeinitializeWrapperName;
extern const char *ExecutorSharedMemoryMapperServiceReleaseWrapperName;
extern const char *MemoryWriteUInt8sWrapperName;
extern const char *MemoryWriteUInt16sWrapperName;
extern const char *MemoryWriteUInt32sWrapperName;
@@ -58,6 +64,21 @@ using SPSSimpleExecutorMemoryManagerFinalizeSignature =
using SPSSimpleExecutorMemoryManagerDeallocateSignature = shared::SPSError(
shared::SPSExecutorAddr, shared::SPSSequence<shared::SPSExecutorAddr>);
// ExecutorSharedMemoryMapperService
using SPSExecutorSharedMemoryMapperServiceReserveSignature =
shared::SPSExpected<
shared::SPSTuple<shared::SPSExecutorAddr, shared::SPSString>>(
shared::SPSExecutorAddr, uint64_t);
using SPSExecutorSharedMemoryMapperServiceInitializeSignature =
shared::SPSExpected<shared::SPSExecutorAddr>(shared::SPSExecutorAddr,
shared::SPSExecutorAddr,
shared::SPSFinalizeRequest);
using SPSExecutorSharedMemoryMapperServiceDeinitializeSignature =
shared::SPSError(shared::SPSExecutorAddr,
shared::SPSSequence<shared::SPSExecutorAddr>);
using SPSExecutorSharedMemoryMapperServiceReleaseSignature = shared::SPSError(
shared::SPSExecutorAddr, shared::SPSSequence<shared::SPSExecutorAddr>);
using SPSRunAsMainSignature = int64_t(shared::SPSExecutorAddr,
shared::SPSSequence<shared::SPSString>);

View File

@@ -0,0 +1,78 @@
//===----------- ExecutorSharedMemoryMapperService.h ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORSHAREDMEMORYMAPPERSERVICE
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORSHAREDMEMORYMAPPERSERVICE
#include "llvm/ADT/DenseMap.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorBootstrapService.h"
#include <atomic>
#include <mutex>
#if defined(_WIN32)
#include <windows.h>
#endif
namespace llvm {
namespace orc {
namespace rt_bootstrap {
class ExecutorSharedMemoryMapperService final
: public ExecutorBootstrapService {
public:
~ExecutorSharedMemoryMapperService(){};
Expected<std::pair<ExecutorAddr, std::string>> reserve(uint64_t Size);
Expected<ExecutorAddr> initialize(ExecutorAddr Reservation,
tpctypes::FinalizeRequest &FR);
Error deinitialize(const std::vector<ExecutorAddr> &Bases);
Error release(const std::vector<ExecutorAddr> &Bases);
Error shutdown() override;
void addBootstrapSymbols(StringMap<ExecutorAddr> &M) override;
private:
struct Allocation {
std::vector<shared::WrapperFunctionCall> DeinitializationActions;
};
using AllocationMap = DenseMap<ExecutorAddr, Allocation>;
struct Reservation {
size_t Size;
std::vector<ExecutorAddr> Allocations;
#if defined(_WIN32)
HANDLE SharedMemoryFile;
#endif
};
using ReservationMap = DenseMap<void *, Reservation>;
static llvm::orc::shared::CWrapperFunctionResult
reserveWrapper(const char *ArgData, size_t ArgSize);
static llvm::orc::shared::CWrapperFunctionResult
initializeWrapper(const char *ArgData, size_t ArgSize);
static llvm::orc::shared::CWrapperFunctionResult
deinitializeWrapper(const char *ArgData, size_t ArgSize);
static llvm::orc::shared::CWrapperFunctionResult
releaseWrapper(const char *ArgData, size_t ArgSize);
std::atomic<int> SharedMemoryCount{0};
std::mutex Mutex;
ReservationMap Reservations;
AllocationMap Allocations;
};
} // namespace rt_bootstrap
} // namespace orc
} // namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORSHAREDMEMORYMAPPERSERVICE

View File

@@ -8,6 +8,17 @@
#include "llvm/ExecutionEngine/Orc/MemoryMapper.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/Support/WindowsError.h"
#if defined(LLVM_ON_UNIX)
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <windows.h>
#endif
namespace llvm {
namespace orc {
@@ -147,6 +158,225 @@ InProcessMemoryMapper::~InProcessMemoryMapper() {
cantFail(F.get());
}
// SharedMemoryMapper
void SharedMemoryMapper::reserve(size_t NumBytes,
OnReservedFunction OnReserved) {
#if defined(LLVM_ON_UNIX) || defined(_WIN32)
EPC.callSPSWrapperAsync<
rt::SPSExecutorSharedMemoryMapperServiceReserveSignature>(
SAs.Reserve,
[this, NumBytes, OnReserved = std::move(OnReserved)](
Error SerializationErr,
Expected<std::pair<ExecutorAddr, std::string>> Result) mutable {
if (SerializationErr) {
cantFail(Result.takeError());
return OnReserved(std::move(SerializationErr));
}
if (!Result)
return OnReserved(Result.takeError());
ExecutorAddr RemoteAddr;
std::string SharedMemoryName;
std::tie(RemoteAddr, SharedMemoryName) = std::move(*Result);
void *LocalAddr = nullptr;
#if defined(LLVM_ON_UNIX)
int SharedMemoryFile = shm_open(SharedMemoryName.c_str(), O_RDWR, 0700);
if (SharedMemoryFile < 0) {
return OnReserved(errorCodeToError(
std::error_code(errno, std::generic_category())));
}
// this prevents other processes from accessing it by name
shm_unlink(SharedMemoryName.c_str());
LocalAddr = mmap(nullptr, NumBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
SharedMemoryFile, 0);
if (LocalAddr == MAP_FAILED) {
return OnReserved(errorCodeToError(
std::error_code(errno, std::generic_category())));
}
close(SharedMemoryFile);
#elif defined(_WIN32)
std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
SharedMemoryName.end());
HANDLE SharedMemoryFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE,
WideSharedMemoryName.c_str());
if (!SharedMemoryFile)
return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
LocalAddr =
MapViewOfFile(SharedMemoryFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (!LocalAddr) {
CloseHandle(SharedMemoryFile);
return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
}
CloseHandle(SharedMemoryFile);
#endif
{
std::lock_guard<std::mutex> Lock(Mutex);
Reservations.insert({RemoteAddr, {LocalAddr, NumBytes}});
}
OnReserved(ExecutorAddrRange(RemoteAddr, NumBytes));
},
SAs.Instance, static_cast<uint64_t>(NumBytes));
#else
OnReserved(make_error<StringError>(
"SharedMemoryMapper is not supported on this platform yet",
inconvertibleErrorCode()));
#endif
}
char *SharedMemoryMapper::prepare(ExecutorAddr Addr, size_t ContentSize) {
auto R = Reservations.upper_bound(Addr);
assert(R != Reservations.begin() && "Attempt to prepare unknown range");
R--;
ExecutorAddrDiff Offset = Addr - R->first;
return static_cast<char *>(R->second.LocalAddr) + Offset;
}
void SharedMemoryMapper::initialize(MemoryMapper::AllocInfo &AI,
OnInitializedFunction OnInitialized) {
auto Reservation = Reservations.find(AI.MappingBase);
assert(Reservation != Reservations.end() &&
"Attempt to initialize unreserved range");
tpctypes::FinalizeRequest FR;
AI.Actions.swap(FR.Actions);
FR.Segments.reserve(AI.Segments.size());
for (auto Segment : AI.Segments) {
char *Base =
static_cast<char *>(Reservation->second.LocalAddr) + Segment.Offset;
std::memset(Base + Segment.ContentSize, 0, Segment.ZeroFillSize);
tpctypes::SegFinalizeRequest SegReq;
SegReq.Prot = tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(Segment.Prot));
SegReq.Addr = AI.MappingBase + Segment.Offset;
SegReq.Size = Segment.ContentSize + Segment.ZeroFillSize;
FR.Segments.push_back(SegReq);
}
EPC.callSPSWrapperAsync<
rt::SPSExecutorSharedMemoryMapperServiceInitializeSignature>(
SAs.Initialize,
[OnInitialized = std::move(OnInitialized)](
Error SerializationErr, Expected<ExecutorAddr> Result) mutable {
if (SerializationErr) {
cantFail(Result.takeError());
return OnInitialized(std::move(SerializationErr));
}
OnInitialized(std::move(Result));
},
SAs.Instance, AI.MappingBase, std::move(FR));
}
void SharedMemoryMapper::deinitialize(
ArrayRef<ExecutorAddr> Allocations,
MemoryMapper::OnDeinitializedFunction OnDeinitialized) {
EPC.callSPSWrapperAsync<
rt::SPSExecutorSharedMemoryMapperServiceDeinitializeSignature>(
SAs.Deinitialize,
[OnDeinitialized = std::move(OnDeinitialized)](Error SerializationErr,
Error Result) mutable {
if (SerializationErr) {
cantFail(std::move(Result));
return OnDeinitialized(std::move(SerializationErr));
}
OnDeinitialized(std::move(Result));
},
SAs.Instance, Allocations);
}
void SharedMemoryMapper::release(ArrayRef<ExecutorAddr> Bases,
OnReleasedFunction OnReleased) {
#if defined(LLVM_ON_UNIX) || defined(_WIN32)
Error Err = Error::success();
{
std::lock_guard<std::mutex> Lock(Mutex);
for (auto Base : Bases) {
#if defined(LLVM_ON_UNIX)
if (munmap(Reservations[Base].LocalAddr, Reservations[Base].Size) != 0)
Err = joinErrors(std::move(Err), errorCodeToError(std::error_code(
errno, std::generic_category())));
#elif defined(_WIN32)
if (!UnmapViewOfFile(Reservations[Base].LocalAddr))
joinErrors(std::move(Err),
errorCodeToError(mapWindowsError(GetLastError())));
#endif
Reservations.erase(Base);
}
}
EPC.callSPSWrapperAsync<
rt::SPSExecutorSharedMemoryMapperServiceReleaseSignature>(
SAs.Release,
[OnReleased = std::move(OnReleased),
Err = std::move(Err)](Error SerializationErr, Error Result) mutable {
if (SerializationErr) {
cantFail(std::move(Result));
return OnReleased(
joinErrors(std::move(Err), std::move(SerializationErr)));
}
return OnReleased(joinErrors(std::move(Err), std::move(Result)));
},
SAs.Instance, Bases);
#else
OnReleased(make_error<StringError>(
"SharedMemoryMapper is not supported on this platform yet",
inconvertibleErrorCode()));
#endif
}
SharedMemoryMapper::~SharedMemoryMapper() {
std::vector<ExecutorAddr> ReservationAddrs;
if (!Reservations.empty()) {
std::lock_guard<std::mutex> Lock(Mutex);
{
ReservationAddrs.reserve(Reservations.size());
for (const auto &R : Reservations) {
ReservationAddrs.push_back(R.first);
}
}
}
std::promise<MSVCPError> P;
auto F = P.get_future();
release(ReservationAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
// FIXME: Release can actually fail. The error should be propagated.
// Meanwhile, a better option is to explicitly call release().
cantFail(F.get());
}
} // namespace orc
} // namespace llvm

View File

@@ -18,6 +18,7 @@ const char *SimpleExecutorDylibManagerOpenWrapperName =
"__llvm_orc_SimpleExecutorDylibManager_open_wrapper";
const char *SimpleExecutorDylibManagerLookupWrapperName =
"__llvm_orc_SimpleExecutorDylibManager_lookup_wrapper";
const char *SimpleExecutorMemoryManagerInstanceName =
"__llvm_orc_SimpleExecutorMemoryManager_Instance";
const char *SimpleExecutorMemoryManagerReserveWrapperName =
@@ -26,6 +27,18 @@ const char *SimpleExecutorMemoryManagerFinalizeWrapperName =
"__llvm_orc_SimpleExecutorMemoryManager_finalize_wrapper";
const char *SimpleExecutorMemoryManagerDeallocateWrapperName =
"__llvm_orc_SimpleExecutorMemoryManager_deallocate_wrapper";
const char *ExecutorSharedMemoryMapperServiceInstanceName =
"__llvm_orc_ExecutorSharedMemoryMapperService_Instance";
const char *ExecutorSharedMemoryMapperServiceReserveWrapperName =
"__llvm_orc_ExecutorSharedMemoryMapperService_Reserve";
const char *ExecutorSharedMemoryMapperServiceInitializeWrapperName =
"__llvm_orc_ExecutorSharedMemoryMapperService_Initialize";
const char *ExecutorSharedMemoryMapperServiceDeinitializeWrapperName =
"__llvm_orc_ExecutorSharedMemoryMapperService_Deinitialize";
const char *ExecutorSharedMemoryMapperServiceReleaseWrapperName =
"__llvm_orc_ExecutorSharedMemoryMapperService_Release";
const char *MemoryWriteUInt8sWrapperName =
"__llvm_orc_bootstrap_mem_write_uint8s_wrapper";
const char *MemoryWriteUInt16sWrapperName =
@@ -36,10 +49,12 @@ const char *MemoryWriteUInt64sWrapperName =
"__llvm_orc_bootstrap_mem_write_uint64s_wrapper";
const char *MemoryWriteBuffersWrapperName =
"__llvm_orc_bootstrap_mem_write_buffers_wrapper";
const char *RegisterEHFrameSectionWrapperName =
"__llvm_orc_bootstrap_register_ehframe_section_wrapper";
const char *DeregisterEHFrameSectionWrapperName =
"__llvm_orc_bootstrap_deregister_ehframe_section_wrapper";
const char *RunAsMainWrapperName = "__llvm_orc_bootstrap_run_as_main_wrapper";
} // end namespace rt

View File

@@ -1,4 +1,5 @@
add_llvm_component_library(LLVMOrcTargetProcess
ExecutorSharedMemoryMapperService.cpp
JITLoaderGDB.cpp
OrcRTBootstrap.cpp
RegisterEHFrames.cpp

View File

@@ -0,0 +1,342 @@
//===---------- ExecutorSharedMemoryMapperService.cpp -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/WindowsError.h"
#include <sstream>
#if defined(LLVM_ON_UNIX)
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
#if defined(_WIN32)
static DWORD getWindowsProtectionFlags(unsigned Flags) {
switch (Flags & llvm::sys::Memory::MF_RWE_MASK) {
case llvm::sys::Memory::MF_READ:
return PAGE_READONLY;
case llvm::sys::Memory::MF_WRITE:
// Note: PAGE_WRITE is not supported by VirtualProtect
return PAGE_READWRITE;
case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE:
return PAGE_READWRITE;
case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_EXEC:
return PAGE_EXECUTE_READ;
case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE |
llvm::sys::Memory::MF_EXEC:
return PAGE_EXECUTE_READWRITE;
case llvm::sys::Memory::MF_EXEC:
return PAGE_EXECUTE;
default:
llvm_unreachable("Illegal memory protection flag specified!");
}
// Provide a default return value as required by some compilers.
return PAGE_NOACCESS;
}
#endif
namespace llvm {
namespace orc {
namespace rt_bootstrap {
Expected<std::pair<ExecutorAddr, std::string>>
ExecutorSharedMemoryMapperService::reserve(uint64_t Size) {
#if defined(LLVM_ON_UNIX) || defined(_WIN32)
#if defined(LLVM_ON_UNIX)
std::string SharedMemoryName;
{
std::stringstream SharedMemoryNameStream;
SharedMemoryNameStream << "/jitlink_" << sys::Process::getProcessId() << '_'
<< (++SharedMemoryCount);
SharedMemoryName = SharedMemoryNameStream.str();
}
int SharedMemoryFile =
shm_open(SharedMemoryName.c_str(), O_RDWR | O_CREAT | O_EXCL, 0700);
if (SharedMemoryFile < 0)
return errorCodeToError(std::error_code(errno, std::generic_category()));
// by default size is 0
if (ftruncate(SharedMemoryFile, Size) < 0)
return errorCodeToError(std::error_code(errno, std::generic_category()));
void *Addr = mmap(nullptr, Size, PROT_NONE, MAP_SHARED, SharedMemoryFile, 0);
if (Addr == MAP_FAILED)
return errorCodeToError(std::error_code(errno, std::generic_category()));
close(SharedMemoryFile);
#elif defined(_WIN32)
std::string SharedMemoryName;
{
std::stringstream SharedMemoryNameStream;
SharedMemoryNameStream << "jitlink_" << sys::Process::getProcessId() << '_'
<< (++SharedMemoryCount);
SharedMemoryName = SharedMemoryNameStream.str();
}
std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
SharedMemoryName.end());
HANDLE SharedMemoryFile = CreateFileMapping(
INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, Size >> 32,
Size & 0xffffffff, WideSharedMemoryName.c_str());
if (!SharedMemoryFile)
return errorCodeToError(mapWindowsError(GetLastError()));
void *Addr = MapViewOfFile(SharedMemoryFile,
FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);
if (!Addr) {
CloseHandle(SharedMemoryFile);
return errorCodeToError(mapWindowsError(GetLastError()));
}
#endif
{
std::lock_guard<std::mutex> Lock(Mutex);
Reservations[Addr].Size = Size;
#if defined(_WIN32)
Reservations[Addr].SharedMemoryFile = SharedMemoryFile;
#endif
}
return std::make_pair(ExecutorAddr::fromPtr(Addr),
std::move(SharedMemoryName));
#else
return make_error<StringError>(
"SharedMemoryMapper is not supported on this platform yet",
inconvertibleErrorCode());
#endif
}
Expected<ExecutorAddr>
ExecutorSharedMemoryMapperService::initialize(ExecutorAddr Reservation,
tpctypes::FinalizeRequest &FR) {
#if defined(LLVM_ON_UNIX) || defined(_WIN32)
ExecutorAddr MinAddr(~0ULL);
// Contents are already in place
for (auto &Segment : FR.Segments) {
if (Segment.Addr < MinAddr)
MinAddr = Segment.Addr;
#if defined(LLVM_ON_UNIX)
int NativeProt = 0;
if (Segment.Prot & tpctypes::WPF_Read)
NativeProt |= PROT_READ;
if (Segment.Prot & tpctypes::WPF_Write)
NativeProt |= PROT_WRITE;
if (Segment.Prot & tpctypes::WPF_Exec)
NativeProt |= PROT_EXEC;
if (mprotect(Segment.Addr.toPtr<void *>(), Segment.Size, NativeProt))
return errorCodeToError(std::error_code(errno, std::generic_category()));
#elif defined(_WIN32)
DWORD NativeProt =
getWindowsProtectionFlags(fromWireProtectionFlags(Segment.Prot));
if (!VirtualProtect(Segment.Addr.toPtr<void *>(), Segment.Size, NativeProt,
&NativeProt))
return errorCodeToError(mapWindowsError(GetLastError()));
#endif
if (Segment.Prot & tpctypes::WPF_Exec)
sys::Memory::InvalidateInstructionCache(Segment.Addr.toPtr<void *>(),
Segment.Size);
}
// Run finalization actions and get deinitlization action list.
auto DeinitializeActions = shared::runFinalizeActions(FR.Actions);
if (!DeinitializeActions) {
return DeinitializeActions.takeError();
}
{
std::lock_guard<std::mutex> Lock(Mutex);
Allocations[MinAddr].DeinitializationActions =
std::move(*DeinitializeActions);
Reservations[Reservation.toPtr<void *>()].Allocations.push_back(MinAddr);
}
return MinAddr;
#else
return make_error<StringError>(
"SharedMemoryMapper is not supported on this platform yet",
inconvertibleErrorCode());
#endif
}
Error ExecutorSharedMemoryMapperService::deinitialize(
const std::vector<ExecutorAddr> &Bases) {
Error AllErr = Error::success();
{
std::lock_guard<std::mutex> Lock(Mutex);
for (auto Base : Bases) {
if (Error Err = shared::runDeallocActions(
Allocations[Base].DeinitializationActions)) {
AllErr = joinErrors(std::move(AllErr), std::move(Err));
}
Allocations.erase(Base);
}
}
return AllErr;
}
Error ExecutorSharedMemoryMapperService::release(
const std::vector<ExecutorAddr> &Bases) {
#if defined(LLVM_ON_UNIX) || defined(_WIN32)
Error Err = Error::success();
for (auto Base : Bases) {
std::vector<ExecutorAddr> AllocAddrs;
size_t Size;
#if defined(_WIN32)
HANDLE SharedMemoryFile;
#endif
{
std::lock_guard<std::mutex> Lock(Mutex);
auto &R = Reservations[Base.toPtr<void *>()];
Size = R.Size;
#if defined(_WIN32)
SharedMemoryFile = R.SharedMemoryFile;
#endif
AllocAddrs.swap(R.Allocations);
}
// deinitialize sub allocations
if (Error E = deinitialize(AllocAddrs))
Err = joinErrors(std::move(Err), std::move(E));
#if defined(LLVM_ON_UNIX)
if (munmap(Base.toPtr<void *>(), Size) != 0)
Err = joinErrors(std::move(Err), errorCodeToError(std::error_code(
errno, std::generic_category())));
#elif defined(_WIN32)
if (!UnmapViewOfFile(Base.toPtr<void *>()))
Err = joinErrors(std::move(Err),
errorCodeToError(mapWindowsError(GetLastError())));
CloseHandle(SharedMemoryFile);
#endif
std::lock_guard<std::mutex> Lock(Mutex);
Reservations.erase(Base.toPtr<void *>());
}
return Err;
#else
return make_error<StringError>(
"SharedMemoryMapper is not supported on this platform yet",
inconvertibleErrorCode());
#endif
}
Error ExecutorSharedMemoryMapperService::shutdown() {
std::vector<ExecutorAddr> ReservationAddrs;
if (!Reservations.empty()) {
std::lock_guard<std::mutex> Lock(Mutex);
{
ReservationAddrs.reserve(Reservations.size());
for (const auto &R : Reservations) {
ReservationAddrs.push_back(ExecutorAddr::fromPtr(R.getFirst()));
}
}
}
return release(ReservationAddrs);
return Error::success();
}
void ExecutorSharedMemoryMapperService::addBootstrapSymbols(
StringMap<ExecutorAddr> &M) {
M[rt::ExecutorSharedMemoryMapperServiceInstanceName] =
ExecutorAddr::fromPtr(this);
M[rt::ExecutorSharedMemoryMapperServiceReserveWrapperName] =
ExecutorAddr::fromPtr(&reserveWrapper);
M[rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName] =
ExecutorAddr::fromPtr(&initializeWrapper);
M[rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName] =
ExecutorAddr::fromPtr(&deinitializeWrapper);
M[rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName] =
ExecutorAddr::fromPtr(&releaseWrapper);
}
llvm::orc::shared::CWrapperFunctionResult
ExecutorSharedMemoryMapperService::reserveWrapper(const char *ArgData,
size_t ArgSize) {
return shared::WrapperFunction<
rt::SPSExecutorSharedMemoryMapperServiceReserveSignature>::
handle(ArgData, ArgSize,
shared::makeMethodWrapperHandler(
&ExecutorSharedMemoryMapperService::reserve))
.release();
}
llvm::orc::shared::CWrapperFunctionResult
ExecutorSharedMemoryMapperService::initializeWrapper(const char *ArgData,
size_t ArgSize) {
return shared::WrapperFunction<
rt::SPSExecutorSharedMemoryMapperServiceInitializeSignature>::
handle(ArgData, ArgSize,
shared::makeMethodWrapperHandler(
&ExecutorSharedMemoryMapperService::initialize))
.release();
}
llvm::orc::shared::CWrapperFunctionResult
ExecutorSharedMemoryMapperService::deinitializeWrapper(const char *ArgData,
size_t ArgSize) {
return shared::WrapperFunction<
rt::SPSExecutorSharedMemoryMapperServiceDeinitializeSignature>::
handle(ArgData, ArgSize,
shared::makeMethodWrapperHandler(
&ExecutorSharedMemoryMapperService::deinitialize))
.release();
}
llvm::orc::shared::CWrapperFunctionResult
ExecutorSharedMemoryMapperService::releaseWrapper(const char *ArgData,
size_t ArgSize) {
return shared::WrapperFunction<
rt::SPSExecutorSharedMemoryMapperServiceReleaseSignature>::
handle(ArgData, ArgSize,
shared::makeMethodWrapperHandler(
&ExecutorSharedMemoryMapperService::release))
.release();
}
} // namespace rt_bootstrap
} // end namespace orc
} // end namespace llvm

View File

@@ -30,6 +30,7 @@ add_llvm_unittest(OrcJITTests
OrcTestCommon.cpp
ResourceTrackerTest.cpp
RTDyldObjectLinkingLayerTest.cpp
SharedMemoryMapperTest.cpp
SimpleExecutorMemoryManagerTest.cpp
SimplePackedSerializationTest.cpp
SymbolStringPoolTest.cpp

View File

@@ -0,0 +1,129 @@
//===- SharedMemoryMapperTest.cpp -- Tests for SharedMemoryMapper ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "OrcTestCommon.h"
#include "llvm/ExecutionEngine/Orc/MemoryMapper.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h"
#include "llvm/Testing/Support/Error.h"
using namespace llvm;
using namespace llvm::orc;
using namespace llvm::orc::shared;
using namespace llvm::orc::rt_bootstrap;
// A basic function to be used as both initializer/deinitializer
orc::shared::CWrapperFunctionResult incrementWrapper(const char *ArgData,
size_t ArgSize) {
return WrapperFunction<SPSError(SPSExecutorAddr)>::handle(
ArgData, ArgSize,
[](ExecutorAddr A) -> Error {
*A.toPtr<int *>() += 1;
return Error::success();
})
.release();
}
TEST(SharedMemoryMapperTest, MemReserveInitializeDeinitializeRelease) {
// These counters are used to track how many times the initializer and
// deinitializer functions are called
int InitializeCounter = 0;
int DeinitializeCounter = 0;
auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());
ExecutorSharedMemoryMapperService MapperService;
SharedMemoryMapper::SymbolAddrs SAs;
{
StringMap<ExecutorAddr> Map;
MapperService.addBootstrapSymbols(Map);
SAs.Instance = Map[rt::ExecutorSharedMemoryMapperServiceInstanceName];
SAs.Reserve = Map[rt::ExecutorSharedMemoryMapperServiceReserveWrapperName];
SAs.Initialize =
Map[rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName];
SAs.Deinitialize =
Map[rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName];
SAs.Release = Map[rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName];
}
std::string TestString = "Hello, World!";
// barrier
std::promise<void> P;
auto F = P.get_future();
{
auto PageSize = cantFail(sys::Process::getPageSize());
size_t ReqSize = PageSize;
std::unique_ptr<MemoryMapper> Mapper =
std::make_unique<SharedMemoryMapper>(*SelfEPC, SAs);
Mapper->reserve(ReqSize, [&](Expected<ExecutorAddrRange> Result) {
EXPECT_THAT_ERROR(Result.takeError(), Succeeded());
auto Reservation = std::move(*Result);
{
char *Addr = Mapper->prepare(Reservation.Start, TestString.size() + 1);
std::strcpy(Addr, TestString.c_str());
}
MemoryMapper::AllocInfo AI;
{
MemoryMapper::AllocInfo::SegInfo SI;
SI.Offset = 0;
SI.ContentSize = TestString.size() + 1;
SI.ZeroFillSize = PageSize - SI.ContentSize;
SI.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;
AI.MappingBase = Reservation.Start;
AI.Segments.push_back(SI);
AI.Actions.push_back(
{cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
ExecutorAddr::fromPtr(incrementWrapper),
ExecutorAddr::fromPtr(&InitializeCounter))),
cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
ExecutorAddr::fromPtr(incrementWrapper),
ExecutorAddr::fromPtr(&DeinitializeCounter)))});
}
EXPECT_EQ(InitializeCounter, 0);
EXPECT_EQ(DeinitializeCounter, 0);
Mapper->initialize(AI, [&, Reservation](Expected<ExecutorAddr> Result) {
EXPECT_THAT_ERROR(Result.takeError(), Succeeded());
EXPECT_EQ(TestString, std::string(static_cast<char *>(
Reservation.Start.toPtr<char *>())));
EXPECT_EQ(InitializeCounter, 1);
EXPECT_EQ(DeinitializeCounter, 0);
Mapper->deinitialize({*Result}, [&, Reservation](Error Err) {
EXPECT_THAT_ERROR(std::move(Err), Succeeded());
EXPECT_EQ(InitializeCounter, 1);
EXPECT_EQ(DeinitializeCounter, 1);
Mapper->release({Reservation.Start}, [&](Error Err) {
EXPECT_THAT_ERROR(std::move(Err), Succeeded());
P.set_value();
});
});
});
});
// This will block the test if any of the above callbacks are not executed
F.wait();
// Mapper must be destructed before calling shutdown to avoid double free
}
EXPECT_THAT_ERROR(MapperService.shutdown(), Succeeded());
cantFail(SelfEPC->disconnect());
}