From 37c8709e7b569fed126ee4448838d367a61943e6 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Thu, 16 Oct 2025 16:09:35 +1100 Subject: [PATCH] [ORC] Add SimpleRemoteMemoryMapper (new MemoryMapper implementation). SimpleRemoteMemoryMapper is a MemoryMapper implementation that manages remote memory via EPC-calls to reserve, initialize, deinitialize, and release operations. It is compatible with the SimpleExecutorMemoryManager backend, and its introduction allows MapperJITLinkMemoryManager to use this backend. It is also intended to be compatible with the orc_rt::SimpleNativeMemoryMap backend. --- .../Orc/SimpleRemoteMemoryMapper.h | 87 +++++++++++++++ llvm/lib/ExecutionEngine/Orc/CMakeLists.txt | 1 + .../Orc/SimpleRemoteMemoryMapper.cpp | 104 ++++++++++++++++++ llvm/tools/llvm-jitlink/llvm-jitlink.cpp | 58 ++++++++-- 4 files changed, 242 insertions(+), 8 deletions(-) create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.h create mode 100644 llvm/lib/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.cpp diff --git a/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.h b/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.h new file mode 100644 index 0000000000000..644c4f614108e --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.h @@ -0,0 +1,87 @@ +//===- SimpleRemoteMemoryMapper.h - Remote memory mapper --------*- 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 +// +//===----------------------------------------------------------------------===// +// +// A simple memory mapper that uses EPC calls to implement reserve, initialize, +// deinitialize, and release. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_SIMPLEREMOTEMEMORYMAPPER_H +#define LLVM_EXECUTIONENGINE_ORC_SIMPLEREMOTEMEMORYMAPPER_H + +#include "llvm/ExecutionEngine/Orc/MemoryMapper.h" + +namespace llvm::orc { + +/// Manages remote memory by making SPS-based EPC calls. +class LLVM_ABI SimpleRemoteMemoryMapper final : public MemoryMapper { +public: + struct SymbolAddrs { + ExecutorAddr Instance; + ExecutorAddr Reserve; + ExecutorAddr Initialize; + ExecutorAddr Deinitialize; + ExecutorAddr Release; + }; + + SimpleRemoteMemoryMapper(ExecutorProcessControl &EPC, SymbolAddrs SAs); + + static Expected> + Create(ExecutorProcessControl &EPC, SymbolAddrs SAs) { + return std::make_unique(EPC, SAs); + } + + unsigned int getPageSize() override { return EPC.getPageSize(); } + + /// Reserves memory in the remote process by calling a remote + /// SPS-wrapper-function with signature + /// + /// SPSExpected(uint64_t Size). + /// + /// On success, returns the base address of the reserved range. + void reserve(size_t NumBytes, OnReservedFunction OnReserved) override; + + char *prepare(jitlink::LinkGraph &G, ExecutorAddr Addr, + size_t ContentSize) override; + + /// Initializes memory within a previously reserved region (applying + /// protections and running any finalization actions) by calling a remote + /// SPS-wrapper-function with signature + /// + /// SPSExpected(SPSFinalizeRequest) + /// + /// On success, returns a key that can be used to deinitialize the region. + void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override; + + /// Given a series of keys from previous initialize calls, deinitialize + /// previously initialized memory regions (running dealloc actions, resetting + /// permissions and decommitting if possible) by calling a remote + /// SPS-wrapper-function with signature + /// + /// SPSError(SPSSequence Keys) + /// + void deinitialize(ArrayRef Allocations, + OnDeinitializedFunction OnDeInitialized) override; + + /// Given a sequence of base addresses from previous reserve calls, release + /// the underlying ranges (deinitializing any remaining regions within them) + /// by calling a remote SPS-wrapper-function with signature + /// + /// SPSError(SPSSequence Bases) + /// + void release(ArrayRef Reservations, + OnReleasedFunction OnRelease) override; + +private: + ExecutorProcessControl &EPC; + SymbolAddrs SAs; +}; + +} // namespace llvm::orc + +#endif // LLVM_EXECUTIONENGINE_ORC_SIMPLEREMOTEMEMORYMAPPER_H diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt index 0ffe3ae37da28..f34392538a7cb 100644 --- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -56,6 +56,7 @@ add_llvm_component_library(LLVMOrcJIT SectCreate.cpp SelfExecutorProcessControl.cpp SimpleRemoteEPC.cpp + SimpleRemoteMemoryMapper.cpp Speculation.cpp SpeculateAnalyses.cpp ExecutorProcessControl.cpp diff --git a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.cpp b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.cpp new file mode 100644 index 0000000000000..b82de3fd15216 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.cpp @@ -0,0 +1,104 @@ +//===---- SimpleRemoteMemoryMapper.cpp - Remote memory mapper ----*- 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/SimpleRemoteMemoryMapper.h" + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" + +namespace llvm::orc { + +SimpleRemoteMemoryMapper::SimpleRemoteMemoryMapper(ExecutorProcessControl &EPC, + SymbolAddrs SAs) + : EPC(EPC), SAs(SAs) {} + +void SimpleRemoteMemoryMapper::reserve(size_t NumBytes, + OnReservedFunction OnReserved) { + EPC.callSPSWrapperAsync( + SAs.Reserve, + [NumBytes, OnReserved = std::move(OnReserved)]( + Error SerializationErr, Expected Result) mutable { + if (SerializationErr) { + cantFail(Result.takeError()); + return OnReserved(std::move(SerializationErr)); + } + + if (Result) + OnReserved(ExecutorAddrRange(*Result, NumBytes)); + else + OnReserved(Result.takeError()); + }, + SAs.Instance, static_cast(NumBytes)); +} + +char *SimpleRemoteMemoryMapper::prepare(jitlink::LinkGraph &G, + ExecutorAddr Addr, size_t ContentSize) { + return G.allocateBuffer(ContentSize).data(); +} + +void SimpleRemoteMemoryMapper::initialize(MemoryMapper::AllocInfo &AI, + OnInitializedFunction OnInitialized) { + + tpctypes::FinalizeRequest FR; + + std::swap(FR.Actions, AI.Actions); + FR.Segments.reserve(AI.Segments.size()); + + for (auto Seg : AI.Segments) + FR.Segments.push_back({Seg.AG, AI.MappingBase + Seg.Offset, + Seg.ContentSize + Seg.ZeroFillSize, + ArrayRef(Seg.WorkingMem, Seg.ContentSize)}); + + EPC.callSPSWrapperAsync( + SAs.Initialize, + [OnInitialized = std::move(OnInitialized)]( + Error SerializationErr, Expected Result) mutable { + if (SerializationErr) { + cantFail(Result.takeError()); + return OnInitialized(std::move(SerializationErr)); + } + + OnInitialized(std::move(Result)); + }, + SAs.Instance, std::move(FR)); +} + +void SimpleRemoteMemoryMapper::deinitialize( + ArrayRef Allocations, + MemoryMapper::OnDeinitializedFunction OnDeinitialized) { + EPC.callSPSWrapperAsync( + 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 SimpleRemoteMemoryMapper::release(ArrayRef Bases, + OnReleasedFunction OnReleased) { + EPC.callSPSWrapperAsync( + SAs.Release, + [OnReleased = std::move(OnReleased)](Error SerializationErr, + Error Result) mutable { + if (SerializationErr) { + cantFail(std::move(Result)); + return OnReleased(std::move(SerializationErr)); + } + + return OnReleased(std::move(Result)); + }, + SAs.Instance, Bases); +} + +} // namespace llvm::orc diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp index b7f898ff23eba..79216e89c7cba 100644 --- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp @@ -40,6 +40,7 @@ #include "llvm/ExecutionEngine/Orc/SectCreate.h" #include "llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h" #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" +#include "llvm/ExecutionEngine/Orc/SimpleRemoteMemoryMapper.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderVTune.h" @@ -312,10 +313,19 @@ static cl::opt cl::desc("Show FailedToMaterialize errors"), cl::init(false), cl::cat(JITLinkCategory)); -static cl::opt UseSharedMemory( - "use-shared-memory", - cl::desc("Use shared memory to transfer generated code and data"), - cl::init(false), cl::cat(JITLinkCategory)); +enum class MemMgr { Default, Generic, SimpleRemote, Shared }; + +static cl::opt UseMemMgr( + "use-memmgr", cl::desc("Choose memory manager"), cl::init(MemMgr::Generic), + cl::values(clEnumValN(MemMgr::Default, "default", + "Use setup default (InProcess or EPCGeneric)"), + clEnumValN(MemMgr::Generic, "generic", + "Generic remote memory manager"), + clEnumValN(MemMgr::SimpleRemote, "simple-remote", + "Mapper memory manager with simple-remote backend"), + clEnumValN(MemMgr::Shared, "shared", + "Mapper memory manager with shared-memory manager")), + cl::cat(JITLinkCategory)); static cl::opt OverrideTriple("triple", cl::desc("Override target triple detection"), @@ -717,6 +727,27 @@ static std::unique_ptr createInProcessMemoryManager() { SlabSize)); } +Expected> +createSimpleRemoteMemoryManager(SimpleRemoteEPC &SREPC) { + SimpleRemoteMemoryMapper::SymbolAddrs SAs; + if (auto Err = SREPC.getBootstrapSymbols( + {{SAs.Instance, rt::SimpleExecutorMemoryManagerInstanceName}, + {SAs.Reserve, rt::SimpleExecutorMemoryManagerReserveWrapperName}, + {SAs.Initialize, + rt::SimpleExecutorMemoryManagerInitializeWrapperName}, + {SAs.Deinitialize, + rt::SimpleExecutorMemoryManagerDeinitializeWrapperName}, + {SAs.Release, rt::SimpleExecutorMemoryManagerReleaseWrapperName}})) + return std::move(Err); +#ifdef _WIN32 + size_t SlabSize = 1024 * 1024; +#else + size_t SlabSize = 1024 * 1024 * 1024; +#endif + return MapperJITLinkMemoryManager::CreateWithMapper( + SlabSize, SREPC, SAs); +} + Expected> createSharedMemoryManager(SimpleRemoteEPC &SREPC) { SharedMemoryMapper::SymbolAddrs SAs; @@ -745,6 +776,19 @@ createSharedMemoryManager(SimpleRemoteEPC &SREPC) { SlabSize, SREPC, SAs); } +static void setupEPCRemoteMemoryManager(SimpleRemoteEPC::Setup &S) { + switch (UseMemMgr) { + case MemMgr::Default: + case MemMgr::Generic: + break; + case MemMgr::SimpleRemote: + S.CreateMemoryManager = createSimpleRemoteMemoryManager; + break; + case MemMgr::Shared: + S.CreateMemoryManager = createSharedMemoryManager; + break; + } +} static Expected getTestObjectFileInterface(Session &S, MemoryBufferRef O) { @@ -904,8 +948,7 @@ static Expected> launchExecutor() { close(FromExecutor[WriteEnd]); auto S = SimpleRemoteEPC::Setup(); - if (UseSharedMemory) - S.CreateMemoryManager = createSharedMemoryManager; + setupEPCRemoteMemoryManager(S); return SimpleRemoteEPC::Create( std::make_unique(MaterializationThreads), @@ -994,8 +1037,7 @@ static Expected> connectToExecutor() { return SockFD.takeError(); auto S = SimpleRemoteEPC::Setup(); - if (UseSharedMemory) - S.CreateMemoryManager = createSharedMemoryManager; + setupEPCRemoteMemoryManager(S); return SimpleRemoteEPC::Create( std::make_unique(std::nullopt),