340 lines · cpp
1//===-- SPSNativeMemoryMapTest.cpp ----------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Test SPS serialization for MemoryFlags APIs.10//11//===----------------------------------------------------------------------===//12 13#include "orc-rt/SimpleNativeMemoryMap.h"14#include "orc-rt/SPSAllocAction.h"15#include "orc-rt/SPSMemoryFlags.h"16 17#include "AllocActionTestUtils.h"18#include "DirectCaller.h"19#include "gtest/gtest.h"20 21#include <future>22 23using namespace orc_rt;24 25namespace orc_rt {26 27struct SPSSimpleNativeMemoryMapSegment;28 29/// A SimpleNativeMemoryMap::InitializeRequest::Segment plus segment content (if30/// segment content type is regular).31struct TestSNMMSegment32 : public SimpleNativeMemoryMap::InitializeRequest::Segment {33 34 TestSNMMSegment(AllocGroup AG, char *Address, size_t Size,35 std::vector<char> C = {})36 : SimpleNativeMemoryMap::InitializeRequest::Segment(37 {AG, Address, Size, {}}),38 OwnedContent(std::move(C)) {39 this->Content = {OwnedContent.data(), OwnedContent.size()};40 }41 42 std::vector<char> OwnedContent;43};44 45template <>46class SPSSerializationTraits<SPSSimpleNativeMemoryMapSegment, TestSNMMSegment> {47 using SPSType =48 SPSTuple<SPSAllocGroup, SPSExecutorAddr, uint64_t, SPSSequence<char>>;49 50public:51 static size_t size(const TestSNMMSegment &S) {52 return SPSType::AsArgList::size(S.AG, ExecutorAddr::fromPtr(S.Address),53 static_cast<uint64_t>(S.Size), S.Content);54 }55 56 static bool serialize(SPSOutputBuffer &OB, const TestSNMMSegment &S) {57 return SPSType::AsArgList::serialize(58 OB, S.AG, ExecutorAddr::fromPtr(S.Address),59 static_cast<uint64_t>(S.Size), S.Content);60 }61};62 63struct SPSSimpleNativeMemoryMapInitializeRequest;64 65struct TestSNMMInitializeRequest {66 std::vector<TestSNMMSegment> Segments;67 std::vector<AllocActionPair> AAPs;68};69 70template <>71class SPSSerializationTraits<SPSSimpleNativeMemoryMapInitializeRequest,72 TestSNMMInitializeRequest> {73 using SPSType = SPSTuple<SPSSequence<SPSSimpleNativeMemoryMapSegment>,74 SPSSequence<SPSAllocActionPair>>;75 76public:77 static size_t size(const TestSNMMInitializeRequest &IR) {78 return SPSType::AsArgList::size(IR.Segments, IR.AAPs);79 }80 static bool serialize(SPSOutputBuffer &OB,81 const TestSNMMInitializeRequest &IR) {82 return SPSType::AsArgList::serialize(OB, IR.Segments, IR.AAPs);83 }84};85 86} // namespace orc_rt87 88template <typename T> move_only_function<void(T)> waitFor(std::future<T> &F) {89 std::promise<T> P;90 F = P.get_future();91 return [P = std::move(P)](T Val) mutable { P.set_value(std::move(Val)); };92}93 94TEST(SimpleNativeMemoryMapTest, CreateAndDestroy) {95 // Test that we can create and destroy a SimpleNativeMemoryMap instance as96 // expected.97 auto SNMM = std::make_unique<SimpleNativeMemoryMap>();98}99 100template <typename OnCompleteFn>101static void snmm_reserve(OnCompleteFn &&OnComplete,102 SimpleNativeMemoryMap *Instance, size_t Size) {103 using SPSSig = SPSExpected<SPSExecutorAddr>(SPSExecutorAddr, SPSSize);104 SPSWrapperFunction<SPSSig>::call(105 DirectCaller(nullptr, orc_rt_SimpleNativeMemoryMap_reserve_sps_wrapper),106 std::forward<OnCompleteFn>(OnComplete), Instance, Size);107}108 109template <typename OnCompleteFn>110static void snmm_releaseMultiple(OnCompleteFn &&OnComplete,111 SimpleNativeMemoryMap *Instance,112 span<void *> Addr) {113 using SPSSig = SPSError(SPSExecutorAddr, SPSSequence<SPSExecutorAddr>);114 SPSWrapperFunction<SPSSig>::call(115 DirectCaller(nullptr,116 orc_rt_SimpleNativeMemoryMap_releaseMultiple_sps_wrapper),117 std::forward<OnCompleteFn>(OnComplete), Instance, Addr);118}119 120template <typename OnCompleteFn>121static void snmm_initialize(OnCompleteFn &&OnComplete,122 SimpleNativeMemoryMap *Instance,123 TestSNMMInitializeRequest IR) {124 using SPSSig = SPSExpected<SPSExecutorAddr>(125 SPSExecutorAddr, SPSSimpleNativeMemoryMapInitializeRequest);126 SPSWrapperFunction<SPSSig>::call(127 DirectCaller(nullptr,128 orc_rt_SimpleNativeMemoryMap_initialize_sps_wrapper),129 std::forward<OnCompleteFn>(OnComplete), Instance, std::move(IR));130}131 132template <typename OnCompleteFn>133static void snmm_deinitializeMultiple(OnCompleteFn &&OnComplete,134 SimpleNativeMemoryMap *Instance,135 span<void *> Base) {136 using SPSSig = SPSError(SPSExecutorAddr, SPSSequence<SPSExecutorAddr>);137 SPSWrapperFunction<SPSSig>::call(138 DirectCaller(139 nullptr,140 orc_rt_SimpleNativeMemoryMap_deinitializeMultiple_sps_wrapper),141 std::forward<OnCompleteFn>(OnComplete), Instance, Base);142}143 144TEST(SimpleNativeMemoryMapTest, ReserveAndRelease) {145 // Test that we can reserve and release a slab of address space as expected,146 // without finalizing any memory within it.147 auto SNMM = std::make_unique<SimpleNativeMemoryMap>();148 std::future<Expected<Expected<void *>>> ReserveAddr;149 snmm_reserve(waitFor(ReserveAddr), SNMM.get(), 1024 * 1024 * 1024);150 auto Addr = cantFail(cantFail(ReserveAddr.get()));151 152 std::future<Expected<Error>> ReleaseResult;153 snmm_releaseMultiple(waitFor(ReleaseResult), SNMM.get(), {&Addr, 1});154 cantFail(cantFail(ReleaseResult.get()));155}156 157// Write the given value to the address pointed to by P.158static orc_rt_WrapperFunctionBuffer159write_value_sps_allocaction(const char *ArgData, size_t ArgSize) {160 return SPSAllocActionFunction<SPSExecutorAddr, uint64_t>::handle(161 ArgData, ArgSize,162 [](ExecutorAddr P, uint64_t Val) {163 *P.toPtr<uint64_t *>() = Val;164 return WrapperFunctionBuffer();165 })166 .release();167}168 169// Read the uint64_t value at Src and write it to Dst.170// Increments int via pointer.171static orc_rt_WrapperFunctionBuffer172read_value_sps_allocaction(const char *ArgData, size_t ArgSize) {173 return SPSAllocActionFunction<SPSExecutorAddr, SPSExecutorAddr>::handle(174 ArgData, ArgSize,175 [](ExecutorAddr Dst, ExecutorAddr Src) {176 *Dst.toPtr<uint64_t *>() = *Src.toPtr<uint64_t *>();177 return WrapperFunctionBuffer();178 })179 .release();180}181 182TEST(SimpleNativeMemoryMap, FullPipelineForOneRWSegment) {183 // Test that we can:184 // 1. reserve some address space.185 // 2. initialize a range within it as read/write, and that finalize actions186 // are applied as expected.187 // 3. deinitialize the initialized range, with deallocation actions applied as188 // expected.189 // 4. release the address range.190 191 auto SNMM = std::make_unique<SimpleNativeMemoryMap>();192 std::future<Expected<Expected<void *>>> ReserveAddr;193 snmm_reserve(waitFor(ReserveAddr), SNMM.get(), 1024 * 1024 * 1024);194 void *Addr = cantFail(cantFail(ReserveAddr.get()));195 196 std::future<Expected<Expected<void *>>> InitializeKey;197 TestSNMMInitializeRequest IR;198 char *InitializeBase = // Initialize addr at non-zero (64kb) offset from base.199 reinterpret_cast<char *>(Addr) + 64 * 1024;200 uint64_t SentinelValue1 = 0; // Read from pre-filled content201 uint64_t SentinelValue2 =202 0; // Written in initialize, read back during dealloc.203 uint64_t SentinelValue3 = 42; // Read from zero-filled region.204 205 // Build initial content vector.206 std::vector<char> Content;207 Content.resize(sizeof(uint64_t) * 2);208 memcpy(Content.data(), &SentinelValue3, sizeof(uint64_t));209 memcpy(Content.data() + sizeof(uint64_t), &SentinelValue1, sizeof(uint64_t));210 211 IR.Segments.push_back({MemProt::Read | MemProt::Write, InitializeBase,212 64 * 1024, std::move(Content)});213 214 // Read initial content into Sentinel 1.215 IR.AAPs.push_back({216 *MakeAllocAction<SPSExecutorAddr, SPSExecutorAddr>::from(217 read_value_sps_allocaction, ExecutorAddr::fromPtr(&SentinelValue1),218 ExecutorAddr::fromPtr(InitializeBase)),219 {} // No dealloc action.220 });221 222 // Write value in finalize action, then read back into Sentinel 2.223 IR.AAPs.push_back(224 {*MakeAllocAction<SPSExecutorAddr, uint64_t>::from(225 write_value_sps_allocaction,226 ExecutorAddr::fromPtr(InitializeBase) + sizeof(uint64_t),227 uint64_t(42)),228 *MakeAllocAction<SPSExecutorAddr, SPSExecutorAddr>::from(229 read_value_sps_allocaction, ExecutorAddr::fromPtr(&SentinelValue2),230 ExecutorAddr::fromPtr(InitializeBase) + sizeof(uint64_t))});231 232 // Read first 64 bits of the zero-fill region.233 IR.AAPs.push_back({234 *MakeAllocAction<SPSExecutorAddr, SPSExecutorAddr>::from(235 read_value_sps_allocaction, ExecutorAddr::fromPtr(&SentinelValue3),236 ExecutorAddr::fromPtr(InitializeBase) + sizeof(uint64_t) * 2),237 {} // No dealloc action.238 });239 240 snmm_initialize(waitFor(InitializeKey), SNMM.get(), std::move(IR));241 void *InitializeKeyAddr = cantFail(cantFail(InitializeKey.get()));242 243 EXPECT_EQ(SentinelValue1, 42U);244 EXPECT_EQ(SentinelValue2, 0U);245 EXPECT_EQ(SentinelValue3, 0U);246 247 std::future<Expected<Error>> DeallocResult;248 snmm_deinitializeMultiple(waitFor(DeallocResult), SNMM.get(),249 {&InitializeKeyAddr, 1});250 cantFail(cantFail(DeallocResult.get()));251 252 EXPECT_EQ(SentinelValue1, 42U);253 EXPECT_EQ(SentinelValue2, 42U);254 EXPECT_EQ(SentinelValue3, 0U);255 256 std::future<Expected<Error>> ReleaseResult;257 snmm_releaseMultiple(waitFor(ReleaseResult), SNMM.get(), {&Addr, 1});258 cantFail(cantFail(ReleaseResult.get()));259}260 261TEST(SimpleNativeMemoryMap, ReserveInitializeShutdown) {262 // Test that memory is deinitialized in the case where we reserve and263 // initialize some memory, then just shut down the memory manager.264 265 auto SNMM = std::make_unique<SimpleNativeMemoryMap>();266 std::future<Expected<Expected<void *>>> ReserveAddr;267 snmm_reserve(waitFor(ReserveAddr), SNMM.get(), 1024 * 1024 * 1024);268 void *Addr = cantFail(cantFail(ReserveAddr.get()));269 270 std::future<Expected<Expected<void *>>> InitializeKey;271 TestSNMMInitializeRequest IR;272 char *InitializeBase = // Initialize addr at non-zero (64kb) offset from base.273 reinterpret_cast<char *>(Addr) + 64 * 1024;274 uint64_t SentinelValue = 0;275 276 IR.Segments.push_back(277 {MemProt::Read | MemProt::Write, InitializeBase, 64 * 1024});278 279 IR.AAPs.push_back(280 {*MakeAllocAction<SPSExecutorAddr, uint64_t>::from(281 write_value_sps_allocaction, ExecutorAddr::fromPtr(InitializeBase),282 uint64_t(42)),283 *MakeAllocAction<SPSExecutorAddr, SPSExecutorAddr>::from(284 read_value_sps_allocaction, ExecutorAddr::fromPtr(&SentinelValue),285 ExecutorAddr::fromPtr(InitializeBase))});286 snmm_initialize(waitFor(InitializeKey), SNMM.get(), std::move(IR));287 cantFail(cantFail(InitializeKey.get()));288 289 EXPECT_EQ(SentinelValue, 0U);290 291 std::future<Error> ShutdownResult;292 SNMM->shutdown(waitFor(ShutdownResult));293 cantFail(ShutdownResult.get());294 295 EXPECT_EQ(SentinelValue, 42);296}297 298TEST(SimpleNativeMemoryMap, ReserveInitializeDetachShutdown) {299 // Test that memory is deinitialized in the case where we reserve and300 // initialize some memory, then just shut down the memory manager.301 302 auto SNMM = std::make_unique<SimpleNativeMemoryMap>();303 std::future<Expected<Expected<void *>>> ReserveAddr;304 snmm_reserve(waitFor(ReserveAddr), SNMM.get(), 1024 * 1024 * 1024);305 void *Addr = cantFail(cantFail(ReserveAddr.get()));306 307 std::future<Expected<Expected<void *>>> InitializeKey;308 TestSNMMInitializeRequest IR;309 char *InitializeBase = // Initialize addr at non-zero (64kb) offset from base.310 reinterpret_cast<char *>(Addr) + 64 * 1024;311 uint64_t SentinelValue = 0;312 313 IR.Segments.push_back(314 {MemProt::Read | MemProt::Write, InitializeBase, 64 * 1024});315 316 IR.AAPs.push_back(317 {*MakeAllocAction<SPSExecutorAddr, uint64_t>::from(318 write_value_sps_allocaction, ExecutorAddr::fromPtr(InitializeBase),319 uint64_t(42)),320 *MakeAllocAction<SPSExecutorAddr, SPSExecutorAddr>::from(321 read_value_sps_allocaction, ExecutorAddr::fromPtr(&SentinelValue),322 ExecutorAddr::fromPtr(InitializeBase))});323 snmm_initialize(waitFor(InitializeKey), SNMM.get(), std::move(IR));324 cantFail(cantFail(InitializeKey.get()));325 326 EXPECT_EQ(SentinelValue, 0U);327 328 std::future<Error> DetachResult;329 SNMM->detach(waitFor(DetachResult));330 cantFail(DetachResult.get());331 332 EXPECT_EQ(SentinelValue, 0);333 334 std::future<Error> ShutdownResult;335 SNMM->shutdown(waitFor(ShutdownResult));336 cantFail(ShutdownResult.get());337 338 EXPECT_EQ(SentinelValue, 42);339}340