| //===---------------- 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/ExecutionEngine/Orc/MemoryMapper.h" |
| #include "llvm/Testing/Support/Error.h" |
| |
| #include <vector> |
| |
| using namespace llvm; |
| using namespace llvm::jitlink; |
| using namespace llvm::orc; |
| using namespace llvm::orc::shared; |
| |
| namespace { |
| |
| class CounterMapper final : public MemoryMapper { |
| public: |
| CounterMapper(std::unique_ptr<MemoryMapper> Mapper) |
| : Mapper(std::move(Mapper)) {} |
| |
| unsigned int getPageSize() override { return Mapper->getPageSize(); } |
| |
| void reserve(size_t NumBytes, OnReservedFunction OnReserved) override { |
| ++ReserveCount; |
| return Mapper->reserve(NumBytes, std::move(OnReserved)); |
| } |
| |
| void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override { |
| ++InitCount; |
| return Mapper->initialize(AI, std::move(OnInitialized)); |
| } |
| |
| char *prepare(ExecutorAddr Addr, size_t ContentSize) override { |
| return Mapper->prepare(Addr, ContentSize); |
| } |
| |
| void deinitialize(ArrayRef<ExecutorAddr> Allocations, |
| OnDeinitializedFunction OnDeInitialized) override { |
| ++DeinitCount; |
| return Mapper->deinitialize(Allocations, std::move(OnDeInitialized)); |
| } |
| |
| void release(ArrayRef<ExecutorAddr> Reservations, |
| OnReleasedFunction OnRelease) override { |
| ++ReleaseCount; |
| |
| return Mapper->release(Reservations, std::move(OnRelease)); |
| } |
| |
| int ReserveCount = 0, InitCount = 0, DeinitCount = 0, ReleaseCount = 0; |
| |
| private: |
| std::unique_ptr<MemoryMapper> Mapper; |
| }; |
| |
| TEST(MapperJITLinkMemoryManagerTest, InProcess) { |
| auto Mapper = std::make_unique<CounterMapper>( |
| cantFail(InProcessMemoryMapper::Create())); |
| |
| auto *Counter = static_cast<CounterMapper *>(Mapper.get()); |
| |
| auto MemMgr = std::make_unique<MapperJITLinkMemoryManager>(16 * 1024 * 1024, |
| std::move(Mapper)); |
| |
| EXPECT_EQ(Counter->ReserveCount, 0); |
| EXPECT_EQ(Counter->InitCount, 0); |
| |
| StringRef Hello = "hello"; |
| auto SSA1 = jitlink::SimpleSegmentAlloc::Create( |
| *MemMgr, nullptr, {{MemProt::Read, {Hello.size(), Align(1)}}}); |
| EXPECT_THAT_EXPECTED(SSA1, Succeeded()); |
| |
| EXPECT_EQ(Counter->ReserveCount, 1); |
| EXPECT_EQ(Counter->InitCount, 0); |
| |
| auto SegInfo1 = SSA1->getSegInfo(MemProt::Read); |
| memcpy(SegInfo1.WorkingMem.data(), Hello.data(), Hello.size()); |
| |
| auto FA1 = SSA1->finalize(); |
| EXPECT_THAT_EXPECTED(FA1, Succeeded()); |
| |
| EXPECT_EQ(Counter->ReserveCount, 1); |
| EXPECT_EQ(Counter->InitCount, 1); |
| |
| auto SSA2 = jitlink::SimpleSegmentAlloc::Create( |
| *MemMgr, nullptr, {{MemProt::Read, {Hello.size(), Align(1)}}}); |
| EXPECT_THAT_EXPECTED(SSA2, Succeeded()); |
| |
| // last reservation should be reused |
| EXPECT_EQ(Counter->ReserveCount, 1); |
| EXPECT_EQ(Counter->InitCount, 1); |
| |
| auto SegInfo2 = SSA2->getSegInfo(MemProt::Read); |
| memcpy(SegInfo2.WorkingMem.data(), Hello.data(), Hello.size()); |
| auto FA2 = SSA2->finalize(); |
| EXPECT_THAT_EXPECTED(FA2, Succeeded()); |
| |
| EXPECT_EQ(Counter->ReserveCount, 1); |
| EXPECT_EQ(Counter->InitCount, 2); |
| |
| ExecutorAddr TargetAddr1(SegInfo1.Addr); |
| ExecutorAddr TargetAddr2(SegInfo2.Addr); |
| |
| const char *TargetMem1 = TargetAddr1.toPtr<const char *>(); |
| StringRef TargetHello1(TargetMem1, Hello.size()); |
| EXPECT_EQ(Hello, TargetHello1); |
| |
| const char *TargetMem2 = TargetAddr2.toPtr<const char *>(); |
| StringRef TargetHello2(TargetMem2, Hello.size()); |
| EXPECT_EQ(Hello, TargetHello2); |
| |
| EXPECT_EQ(Counter->DeinitCount, 0); |
| |
| auto Err2 = MemMgr->deallocate(std::move(*FA1)); |
| EXPECT_THAT_ERROR(std::move(Err2), Succeeded()); |
| |
| EXPECT_EQ(Counter->DeinitCount, 1); |
| |
| auto Err3 = MemMgr->deallocate(std::move(*FA2)); |
| EXPECT_THAT_ERROR(std::move(Err3), Succeeded()); |
| |
| EXPECT_EQ(Counter->DeinitCount, 2); |
| } |
| |
| TEST(MapperJITLinkMemoryManagerTest, Coalescing) { |
| auto Mapper = cantFail(InProcessMemoryMapper::Create()); |
| auto MemMgr = std::make_unique<MapperJITLinkMemoryManager>(16 * 1024 * 1024, |
| std::move(Mapper)); |
| |
| auto SSA1 = jitlink::SimpleSegmentAlloc::Create( |
| *MemMgr, nullptr, {{MemProt::Read, {1024, Align(1)}}}); |
| EXPECT_THAT_EXPECTED(SSA1, Succeeded()); |
| auto SegInfo1 = SSA1->getSegInfo(MemProt::Read); |
| ExecutorAddr TargetAddr1(SegInfo1.Addr); |
| auto FA1 = SSA1->finalize(); |
| EXPECT_THAT_EXPECTED(FA1, Succeeded()); |
| |
| auto SSA2 = jitlink::SimpleSegmentAlloc::Create( |
| *MemMgr, nullptr, {{MemProt::Read, {1024, Align(1)}}}); |
| EXPECT_THAT_EXPECTED(SSA2, Succeeded()); |
| auto FA2 = SSA2->finalize(); |
| EXPECT_THAT_EXPECTED(FA2, Succeeded()); |
| |
| auto Err2 = MemMgr->deallocate(std::move(*FA1)); |
| EXPECT_THAT_ERROR(std::move(Err2), Succeeded()); |
| |
| auto Err3 = MemMgr->deallocate(std::move(*FA2)); |
| EXPECT_THAT_ERROR(std::move(Err3), Succeeded()); |
| |
| auto SSA3 = jitlink::SimpleSegmentAlloc::Create( |
| *MemMgr, nullptr, {{MemProt::Read, {2048, Align(1)}}}); |
| EXPECT_THAT_EXPECTED(SSA3, Succeeded()); |
| |
| auto SegInfo3 = SSA3->getSegInfo(MemProt::Read); |
| ExecutorAddr TargetAddr3(SegInfo3.Addr); |
| |
| auto FA3 = SSA3->finalize(); |
| EXPECT_THAT_EXPECTED(FA3, Succeeded()); |
| |
| // previous two freed 1024 blocks should be fused to form a 2048 block |
| EXPECT_EQ(TargetAddr1, TargetAddr3); |
| |
| auto Err4 = MemMgr->deallocate(std::move(*FA3)); |
| EXPECT_THAT_ERROR(std::move(Err4), Succeeded()); |
| } |
| |
| } // namespace |