diff --git a/llvm/include/llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h b/llvm/include/llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h @@ -0,0 +1,53 @@ +//===--------------- MapperJITLinkMemoryManager.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 +// +//===----------------------------------------------------------------------===// +// +// Implements JITLinkMemoryManager using MemoryMapper +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_MAPPERJITLINKMEMORYMANAGER_H +#define LLVM_EXECUTIONENGINE_ORC_MAPPERJITLINKMEMORYMANAGER_H + +#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/MemoryMapper.h" + +namespace llvm { +namespace orc { + +class MapperJITLinkMemoryManager : public jitlink::JITLinkMemoryManager { +public: + MapperJITLinkMemoryManager(std::unique_ptr Mapper); + + template + static std::unique_ptr + createWithMapper(Args &&...A) { + auto Mapper = std::make_unique(std::forward(A)...); + return std::make_unique(std::move(Mapper)); + } + + void allocate(const jitlink::JITLinkDylib *JD, jitlink::LinkGraph &G, + OnAllocatedFunction OnAllocated) override; + // synchronous overload + using JITLinkMemoryManager::allocate; + + void deallocate(std::vector Allocs, + OnDeallocatedFunction OnDeallocated) override; + // synchronous overload + using JITLinkMemoryManager::deallocate; + +private: + class InFlightAlloc; + + std::unique_ptr Mapper; +}; + +} // end namespace orc +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_ORC_MAPPERJITLINKMEMORYMANAGER_H diff --git a/llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h b/llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h --- a/llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h @@ -14,6 +14,7 @@ #define LLVM_EXECUTIONENGINE_ORC_MEMORYMAPPER_H #include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/Support/Process.h" #include @@ -41,6 +42,9 @@ using OnReservedFunction = unique_function)>; + // Page size of the target process + virtual unsigned int getPageSize() = 0; + /// Reserves address space in executor process virtual void reserve(size_t NumBytes, OnReservedFunction OnReserved) = 0; @@ -76,7 +80,9 @@ class InProcessMemoryMapper final : public MemoryMapper { public: - InProcessMemoryMapper() {} + InProcessMemoryMapper(); + + unsigned int getPageSize() override { return PageSize; } void reserve(size_t NumBytes, OnReservedFunction OnReserved) override; @@ -107,6 +113,8 @@ std::mutex Mutex; ReservationMap Reservations; AllocationMap Allocations; + + static size_t PageSize; }; } // namespace orc diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -23,6 +23,7 @@ LookupAndRecordAddrs.cpp LLJIT.cpp MachOPlatform.cpp + MapperJITLinkMemoryManager.cpp MemoryMapper.cpp ELFNixPlatform.cpp Mangling.cpp diff --git a/llvm/lib/ExecutionEngine/Orc/MapperJITLinkMemoryManager.cpp b/llvm/lib/ExecutionEngine/Orc/MapperJITLinkMemoryManager.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/MapperJITLinkMemoryManager.cpp @@ -0,0 +1,134 @@ +//=== MapperJITLinkMemoryManager.cpp - Memory management with MemoryMapper ===// +// +// 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/MapperJITLinkMemoryManager.h" + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/Support/Process.h" + +#include + +using namespace llvm::jitlink; + +namespace llvm { +namespace orc { + +class MapperJITLinkMemoryManager::InFlightAlloc + : public JITLinkMemoryManager::InFlightAlloc { +public: + InFlightAlloc(MapperJITLinkMemoryManager &Parent, LinkGraph &G, + ExecutorAddr AllocAddr, + std::vector Segs) + : Parent(Parent), G(G), AllocAddr(AllocAddr), Segs(std::move(Segs)) {} + + void finalize(OnFinalizedFunction OnFinalize) override { + MemoryMapper::AllocInfo AI; + AI.MappingBase = AllocAddr; + + std::swap(AI.Segments, Segs); + std::swap(AI.Actions, G.allocActions()); + + Parent.Mapper->initialize(AI, [&](Expected Result) { + if (!Result) { + return OnFinalize(Result.takeError()); + } + + OnFinalize(FinalizedAlloc(*Result)); + }); + } + + void abandon(OnAbandonedFunction OnFinalize) override { + Parent.Mapper->release({AllocAddr}, std::move(OnFinalize)); + } + +private: + MapperJITLinkMemoryManager &Parent; + LinkGraph &G; + ExecutorAddr AllocAddr; + std::vector Segs; +}; + +MapperJITLinkMemoryManager::MapperJITLinkMemoryManager( + std::unique_ptr Mapper) + : Mapper(std::move(Mapper)) {} + +void MapperJITLinkMemoryManager::allocate(const JITLinkDylib *JD, LinkGraph &G, + OnAllocatedFunction OnAllocated) { + BasicLayout BL(G); + + // find required address space + auto SegsSizes = BL.getContiguousPageBasedLayoutSizes(Mapper->getPageSize()); + if (!SegsSizes) { + OnAllocated(SegsSizes.takeError()); + return; + } + + // Check if total size fits in address space + if (SegsSizes->total() > std::numeric_limits::max()) { + OnAllocated(make_error( + formatv("Total requested size {:x} for graph {} exceeds address space", + SegsSizes->total(), G.getName()))); + return; + } + + Mapper->reserve( + SegsSizes->total(), + [this, &G, BL = std::move(BL), OnAllocated = std::move(OnAllocated)]( + Expected Result) mutable { + if (!Result) { + return OnAllocated(Result.takeError()); + } + + auto NextSegAddr = Result->Start; + + std::vector SegInfos; + + for (auto &KV : BL.segments()) { + auto &AG = KV.first; + auto &Seg = KV.second; + + auto TotalSize = Seg.ContentSize + Seg.ZeroFillSize; + + Seg.Addr = NextSegAddr; + Seg.WorkingMem = Mapper->prepare(NextSegAddr, TotalSize); + + NextSegAddr += alignTo(TotalSize, Mapper->getPageSize()); + + MemoryMapper::AllocInfo::SegInfo SI; + SI.Offset = Seg.Addr - Result->Start; + SI.ContentSize = Seg.ContentSize; + SI.ZeroFillSize = Seg.ZeroFillSize; + SI.Prot = (toSysMemoryProtectionFlags(AG.getMemProt())); + SI.WorkingMem = Seg.WorkingMem; + + SegInfos.push_back(SI); + } + + if (auto Err = BL.apply()) { + OnAllocated(std::move(Err)); + return; + } + + OnAllocated(std::make_unique(*this, G, Result->Start, + std::move(SegInfos))); + }); +} + +void MapperJITLinkMemoryManager::deallocate( + std::vector Allocs, OnDeallocatedFunction OnDeallocated) { + std::vector Bases; + Bases.reserve(Allocs.size()); + for (auto &FA : Allocs) { + Bases.push_back(FA.getAddress()); + FA.release(); + } + Mapper->release(Bases, std::move(OnDeallocated)); +} + +} // end namespace orc +} // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp b/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp --- a/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp +++ b/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp @@ -13,6 +13,13 @@ MemoryMapper::~MemoryMapper() {} +size_t InProcessMemoryMapper::PageSize = 0; + +InProcessMemoryMapper::InProcessMemoryMapper() { + if (!PageSize) + PageSize = cantFail(sys::Process::getPageSize()); +} + void InProcessMemoryMapper::reserve(size_t NumBytes, OnReservedFunction OnReserved) { std::error_code EC; diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt --- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -24,6 +24,7 @@ JITTargetMachineBuilderTest.cpp LazyCallThroughAndReexportsTest.cpp LookupAndRecordAddrsTest.cpp + MapperJITLinkMemoryManagerTest.cpp MemoryMapperTest.cpp ObjectLinkingLayerTest.cpp OrcCAPITest.cpp diff --git a/llvm/unittests/ExecutionEngine/Orc/MapperJITLinkMemoryManagerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/MapperJITLinkMemoryManagerTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/Orc/MapperJITLinkMemoryManagerTest.cpp @@ -0,0 +1,48 @@ +//===-------------- MapperJITLinkMemoryManagerTest.cpp ----------------===// +// +// 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/MapperJITLinkMemoryManager.h" + +#include "llvm/Testing/Support/Error.h" + +#include + +using namespace llvm; +using namespace llvm::jitlink; +using namespace llvm::orc; +using namespace llvm::orc::shared; + +namespace { + +TEST(MapperJITLinkMemoryManagerTest, InProcess) { + auto MemMgr = + MapperJITLinkMemoryManager::createWithMapper(); + + StringRef Hello = "hello"; + auto SSA = jitlink::SimpleSegmentAlloc::Create( + *MemMgr, nullptr, {{jitlink::MemProt::Read, {Hello.size(), Align(1)}}}); + EXPECT_THAT_EXPECTED(SSA, Succeeded()); + auto SegInfo = SSA->getSegInfo(jitlink::MemProt::Read); + memcpy(SegInfo.WorkingMem.data(), Hello.data(), Hello.size()); + + auto FA = SSA->finalize(); + EXPECT_THAT_EXPECTED(FA, Succeeded()); + + ExecutorAddr TargetAddr(SegInfo.Addr); + + const char *TargetMem = TargetAddr.toPtr(); + StringRef TargetHello(TargetMem, Hello.size()); + EXPECT_EQ(Hello, TargetHello); + + auto Err2 = MemMgr->deallocate(std::move(*FA)); + EXPECT_THAT_ERROR(std::move(Err2), Succeeded()); +} + +} // namespace