blob: 57a9fac77d84c87e658d5f024e420549e32b5c8c [file] [log] [blame]
//===------------------------ 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<ExecutorAddrRange> reserve(MemoryMapper &M, size_t NumBytes) {
std::promise<MSVCPExpected<ExecutorAddrRange>> P;
auto F = P.get_future();
M.reserve(NumBytes, [&](auto R) { P.set_value(std::move(R)); });
return F.get();
}
Expected<ExecutorAddr> initialize(MemoryMapper &M,
MemoryMapper::AllocInfo &AI) {
std::promise<MSVCPExpected<ExecutorAddr>> 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<ExecutorAddr> &Allocations) {
std::promise<MSVCPError> 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<ExecutorAddr> &Reservations) {
std::promise<MSVCPError> 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<SPSError(SPSExecutorAddr)>::handle(
ArgData, ArgSize,
[](ExecutorAddr A) -> Error {
*A.toPtr<int *>() += 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<MemoryMapper> 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<char *>(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.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;
Alloc1.MappingBase = Mem1->Start;
Alloc1.Segments.push_back(Seg1);
Alloc1.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)))});
}
{
char *WA2 = Mapper->prepare(Mem1->Start + PageSize, HW.size() + 1);
std::strcpy(static_cast<char *>(WA2), HW.c_str());
}
MemoryMapper::AllocInfo Alloc2;
{
MemoryMapper::AllocInfo::SegInfo Seg2;
Seg2.Offset = PageSize;
Seg2.ContentSize = HW.size();
Seg2.ZeroFillSize = PageSize - Seg2.ContentSize;
Seg2.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;
Alloc2.MappingBase = Mem1->Start;
Alloc2.Segments.push_back(Seg2);
Alloc2.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);
// Set memory protections and run initializers
auto Init1 = initialize(*Mapper, Alloc1);
EXPECT_THAT_ERROR(Init1.takeError(), Succeeded());
EXPECT_EQ(HW, std::string(static_cast<char *>(Init1->toPtr<char *>())));
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<char *>(Init2->toPtr<char *>())));
EXPECT_EQ(InitializeCounter, 2);
EXPECT_EQ(DeinitializeCounter, 0);
// Explicit deinitialization of first allocation
std::vector<ExecutorAddr> 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<char *>(WA), HW.c_str());
MemoryMapper::AllocInfo Alloc3;
{
MemoryMapper::AllocInfo::SegInfo Seg3;
Seg3.Offset = 0;
Seg3.ContentSize = HW.size();
Seg3.ZeroFillSize = PageSize - Seg3.ContentSize;
Seg3.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;
Alloc3.MappingBase = Mem2->Start;
Alloc3.Segments.push_back(Seg3);
Alloc3.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)))});
}
auto Init3 = initialize(*Mapper, Alloc3);
EXPECT_THAT_ERROR(Init3.takeError(), Succeeded());
EXPECT_EQ(HW, std::string(static_cast<char *>(Init3->toPtr<char *>())));
EXPECT_EQ(InitializeCounter, 3);
EXPECT_EQ(DeinitializeCounter, 1);
std::vector<ExecutorAddr> 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