//===------------------------ MemoryMapperTest.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 "llvm/ExecutionEngine/Orc/MemoryMapper.h" #include "llvm/Support/Process.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::orc; using namespace llvm::orc::shared; namespace { Expected reserve(MemoryMapper &M, size_t NumBytes) { std::promise> P; auto F = P.get_future(); M.reserve(NumBytes, [&](auto R) { P.set_value(std::move(R)); }); return F.get(); } Expected initialize(MemoryMapper &M, MemoryMapper::AllocInfo &AI) { std::promise> P; auto F = P.get_future(); M.initialize(AI, [&](auto R) { P.set_value(std::move(R)); }); return F.get(); } Error deinitialize(MemoryMapper &M, const std::vector &Allocations) { std::promise P; auto F = P.get_future(); M.deinitialize(Allocations, [&](auto R) { P.set_value(std::move(R)); }); return F.get(); } Error release(MemoryMapper &M, const std::vector &Reservations) { std::promise P; auto F = P.get_future(); M.release(Reservations, [&](auto R) { P.set_value(std::move(R)); }); return F.get(); } // A basic function to be used as both initializer/deinitializer orc::shared::CWrapperFunctionResult incrementWrapper(const char *ArgData, size_t ArgSize) { return WrapperFunction::handle( ArgData, ArgSize, [](ExecutorAddr A) -> Error { *A.toPtr() += 1; return Error::success(); }) .release(); } TEST(MemoryMapperTest, InitializeDeinitialize) { // These counters are used to track how many times the initializer and // deinitializer functions are called int InitializeCounter = 0; int DeinitializeCounter = 0; { std::unique_ptr Mapper = cantFail(InProcessMemoryMapper::Create()); // We will do two separate allocations auto PageSize = Mapper->getPageSize(); auto TotalSize = PageSize * 2; // Reserve address space auto Mem1 = reserve(*Mapper, TotalSize); EXPECT_THAT_ERROR(Mem1.takeError(), Succeeded()); // Test string for memory transfer std::string HW = "Hello, world!"; { // Provide working memory char *WA1 = Mapper->prepare(Mem1->Start, HW.size() + 1); std::strcpy(static_cast(WA1), HW.c_str()); } // A structure to be passed to initialize MemoryMapper::AllocInfo Alloc1; { MemoryMapper::AllocInfo::SegInfo Seg1; Seg1.Offset = 0; Seg1.ContentSize = HW.size(); Seg1.ZeroFillSize = PageSize - Seg1.ContentSize; Seg1.AG = MemProt::Read | MemProt::Write; Alloc1.MappingBase = Mem1->Start; Alloc1.Segments.push_back(Seg1); Alloc1.Actions.push_back( {cantFail(WrapperFunctionCall::Create>( ExecutorAddr::fromPtr(incrementWrapper), ExecutorAddr::fromPtr(&InitializeCounter))), cantFail(WrapperFunctionCall::Create>( ExecutorAddr::fromPtr(incrementWrapper), ExecutorAddr::fromPtr(&DeinitializeCounter)))}); } { char *WA2 = Mapper->prepare(Mem1->Start + PageSize, HW.size() + 1); std::strcpy(static_cast(WA2), HW.c_str()); } MemoryMapper::AllocInfo Alloc2; { MemoryMapper::AllocInfo::SegInfo Seg2; Seg2.Offset = PageSize; Seg2.ContentSize = HW.size(); Seg2.ZeroFillSize = PageSize - Seg2.ContentSize; Seg2.AG = MemProt::Read | MemProt::Write; Alloc2.MappingBase = Mem1->Start; Alloc2.Segments.push_back(Seg2); Alloc2.Actions.push_back( {cantFail(WrapperFunctionCall::Create>( ExecutorAddr::fromPtr(incrementWrapper), ExecutorAddr::fromPtr(&InitializeCounter))), cantFail(WrapperFunctionCall::Create>( ExecutorAddr::fromPtr(incrementWrapper), ExecutorAddr::fromPtr(&DeinitializeCounter)))}); } EXPECT_EQ(InitializeCounter, 0); EXPECT_EQ(DeinitializeCounter, 0); // Set memory protections and run initializers auto Init1 = initialize(*Mapper, Alloc1); EXPECT_THAT_ERROR(Init1.takeError(), Succeeded()); EXPECT_EQ(HW, std::string(static_cast(Init1->toPtr()))); EXPECT_EQ(InitializeCounter, 1); EXPECT_EQ(DeinitializeCounter, 0); auto Init2 = initialize(*Mapper, Alloc2); EXPECT_THAT_ERROR(Init2.takeError(), Succeeded()); EXPECT_EQ(HW, std::string(static_cast(Init2->toPtr()))); EXPECT_EQ(InitializeCounter, 2); EXPECT_EQ(DeinitializeCounter, 0); // Explicit deinitialization of first allocation std::vector DeinitAddr = {*Init1}; EXPECT_THAT_ERROR(deinitialize(*Mapper, DeinitAddr), Succeeded()); EXPECT_EQ(InitializeCounter, 2); EXPECT_EQ(DeinitializeCounter, 1); // Test explicit release { auto Mem2 = reserve(*Mapper, PageSize); EXPECT_THAT_ERROR(Mem2.takeError(), Succeeded()); char *WA = Mapper->prepare(Mem2->Start, HW.size() + 1); std::strcpy(static_cast(WA), HW.c_str()); MemoryMapper::AllocInfo Alloc3; { MemoryMapper::AllocInfo::SegInfo Seg3; Seg3.Offset = 0; Seg3.ContentSize = HW.size(); Seg3.ZeroFillSize = PageSize - Seg3.ContentSize; Seg3.AG = MemProt::Read | MemProt::Write; Alloc3.MappingBase = Mem2->Start; Alloc3.Segments.push_back(Seg3); Alloc3.Actions.push_back( {cantFail(WrapperFunctionCall::Create>( ExecutorAddr::fromPtr(incrementWrapper), ExecutorAddr::fromPtr(&InitializeCounter))), cantFail(WrapperFunctionCall::Create>( ExecutorAddr::fromPtr(incrementWrapper), ExecutorAddr::fromPtr(&DeinitializeCounter)))}); } auto Init3 = initialize(*Mapper, Alloc3); EXPECT_THAT_ERROR(Init3.takeError(), Succeeded()); EXPECT_EQ(HW, std::string(static_cast(Init3->toPtr()))); EXPECT_EQ(InitializeCounter, 3); EXPECT_EQ(DeinitializeCounter, 1); std::vector ReleaseAddrs = {Mem2->Start}; EXPECT_THAT_ERROR(release(*Mapper, ReleaseAddrs), Succeeded()); EXPECT_EQ(InitializeCounter, 3); EXPECT_EQ(DeinitializeCounter, 2); } } // Implicit deinitialization by the destructor EXPECT_EQ(InitializeCounter, 3); EXPECT_EQ(DeinitializeCounter, 3); } } // namespace