204 lines
7.0 KiB
C++
204 lines
7.0 KiB
C++
//===------------------------ 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.AG = MemProt::Read | MemProt::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.AG = MemProt::Read | MemProt::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.AG = MemProt::Read | MemProt::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
|