xref: /llvm-project/llvm/unittests/ExecutionEngine/Orc/MemoryMapperTest.cpp (revision d3d9f7caf96680dad4ef0fd108e64ca044a0a113)
1 //===------------------------ MemoryMapperTest.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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/ExecutionEngine/Orc/MemoryMapper.h"
10 #include "llvm/Support/Process.h"
11 #include "llvm/Testing/Support/Error.h"
12 #include "gtest/gtest.h"
13 
14 using namespace llvm;
15 using namespace llvm::orc;
16 using namespace llvm::orc::shared;
17 
18 namespace {
19 
reserve(MemoryMapper & M,size_t NumBytes)20 Expected<ExecutorAddrRange> reserve(MemoryMapper &M, size_t NumBytes) {
21   std::promise<MSVCPExpected<ExecutorAddrRange>> P;
22   auto F = P.get_future();
23   M.reserve(NumBytes, [&](auto R) { P.set_value(std::move(R)); });
24   return F.get();
25 }
26 
initialize(MemoryMapper & M,MemoryMapper::AllocInfo & AI)27 Expected<ExecutorAddr> initialize(MemoryMapper &M,
28                                   MemoryMapper::AllocInfo &AI) {
29   std::promise<MSVCPExpected<ExecutorAddr>> P;
30   auto F = P.get_future();
31   M.initialize(AI, [&](auto R) { P.set_value(std::move(R)); });
32   return F.get();
33 }
34 
deinitialize(MemoryMapper & M,const std::vector<ExecutorAddr> & Allocations)35 Error deinitialize(MemoryMapper &M,
36                    const std::vector<ExecutorAddr> &Allocations) {
37   std::promise<MSVCPError> P;
38   auto F = P.get_future();
39   M.deinitialize(Allocations, [&](auto R) { P.set_value(std::move(R)); });
40   return F.get();
41 }
42 
release(MemoryMapper & M,const std::vector<ExecutorAddr> & Reservations)43 Error release(MemoryMapper &M, const std::vector<ExecutorAddr> &Reservations) {
44   std::promise<MSVCPError> P;
45   auto F = P.get_future();
46   M.release(Reservations, [&](auto R) { P.set_value(std::move(R)); });
47   return F.get();
48 }
49 
50 // A basic function to be used as both initializer/deinitializer
incrementWrapper(const char * ArgData,size_t ArgSize)51 orc::shared::CWrapperFunctionResult incrementWrapper(const char *ArgData,
52                                                      size_t ArgSize) {
53   return WrapperFunction<SPSError(SPSExecutorAddr)>::handle(
54              ArgData, ArgSize,
55              [](ExecutorAddr A) -> Error {
56                *A.toPtr<int *>() += 1;
57                return Error::success();
58              })
59       .release();
60 }
61 
TEST(MemoryMapperTest,InitializeDeinitialize)62 TEST(MemoryMapperTest, InitializeDeinitialize) {
63   // These counters are used to track how many times the initializer and
64   // deinitializer functions are called
65   int InitializeCounter = 0;
66   int DeinitializeCounter = 0;
67   {
68     std::unique_ptr<MemoryMapper> Mapper =
69         cantFail(InProcessMemoryMapper::Create());
70 
71     // We will do two separate allocations
72     auto PageSize = Mapper->getPageSize();
73     auto TotalSize = PageSize * 2;
74 
75     // Reserve address space
76     auto Mem1 = reserve(*Mapper, TotalSize);
77     EXPECT_THAT_ERROR(Mem1.takeError(), Succeeded());
78 
79     // Test string for memory transfer
80     std::string HW = "Hello, world!";
81 
82     {
83       // Provide working memory
84       char *WA1 = Mapper->prepare(Mem1->Start, HW.size() + 1);
85       std::strcpy(static_cast<char *>(WA1), HW.c_str());
86     }
87 
88     // A structure to be passed to initialize
89     MemoryMapper::AllocInfo Alloc1;
90     {
91       MemoryMapper::AllocInfo::SegInfo Seg1;
92       Seg1.Offset = 0;
93       Seg1.ContentSize = HW.size();
94       Seg1.ZeroFillSize = PageSize - Seg1.ContentSize;
95       Seg1.AG = MemProt::Read | MemProt::Write;
96 
97       Alloc1.MappingBase = Mem1->Start;
98       Alloc1.Segments.push_back(Seg1);
99       Alloc1.Actions.push_back(
100           {cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
101                ExecutorAddr::fromPtr(incrementWrapper),
102                ExecutorAddr::fromPtr(&InitializeCounter))),
103            cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
104                ExecutorAddr::fromPtr(incrementWrapper),
105                ExecutorAddr::fromPtr(&DeinitializeCounter)))});
106     }
107 
108     {
109       char *WA2 = Mapper->prepare(Mem1->Start + PageSize, HW.size() + 1);
110       std::strcpy(static_cast<char *>(WA2), HW.c_str());
111     }
112 
113     MemoryMapper::AllocInfo Alloc2;
114     {
115       MemoryMapper::AllocInfo::SegInfo Seg2;
116       Seg2.Offset = PageSize;
117       Seg2.ContentSize = HW.size();
118       Seg2.ZeroFillSize = PageSize - Seg2.ContentSize;
119       Seg2.AG = MemProt::Read | MemProt::Write;
120 
121       Alloc2.MappingBase = Mem1->Start;
122       Alloc2.Segments.push_back(Seg2);
123       Alloc2.Actions.push_back(
124           {cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
125                ExecutorAddr::fromPtr(incrementWrapper),
126                ExecutorAddr::fromPtr(&InitializeCounter))),
127            cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
128                ExecutorAddr::fromPtr(incrementWrapper),
129                ExecutorAddr::fromPtr(&DeinitializeCounter)))});
130     }
131 
132     EXPECT_EQ(InitializeCounter, 0);
133     EXPECT_EQ(DeinitializeCounter, 0);
134 
135     // Set memory protections and run initializers
136     auto Init1 = initialize(*Mapper, Alloc1);
137     EXPECT_THAT_ERROR(Init1.takeError(), Succeeded());
138     EXPECT_EQ(HW, std::string(static_cast<char *>(Init1->toPtr<char *>())));
139 
140     EXPECT_EQ(InitializeCounter, 1);
141     EXPECT_EQ(DeinitializeCounter, 0);
142 
143     auto Init2 = initialize(*Mapper, Alloc2);
144     EXPECT_THAT_ERROR(Init2.takeError(), Succeeded());
145     EXPECT_EQ(HW, std::string(static_cast<char *>(Init2->toPtr<char *>())));
146 
147     EXPECT_EQ(InitializeCounter, 2);
148     EXPECT_EQ(DeinitializeCounter, 0);
149 
150     // Explicit deinitialization of first allocation
151     std::vector<ExecutorAddr> DeinitAddr = {*Init1};
152     EXPECT_THAT_ERROR(deinitialize(*Mapper, DeinitAddr), Succeeded());
153 
154     EXPECT_EQ(InitializeCounter, 2);
155     EXPECT_EQ(DeinitializeCounter, 1);
156 
157     // Test explicit release
158     {
159       auto Mem2 = reserve(*Mapper, PageSize);
160       EXPECT_THAT_ERROR(Mem2.takeError(), Succeeded());
161 
162       char *WA = Mapper->prepare(Mem2->Start, HW.size() + 1);
163       std::strcpy(static_cast<char *>(WA), HW.c_str());
164 
165       MemoryMapper::AllocInfo Alloc3;
166       {
167         MemoryMapper::AllocInfo::SegInfo Seg3;
168         Seg3.Offset = 0;
169         Seg3.ContentSize = HW.size();
170         Seg3.ZeroFillSize = PageSize - Seg3.ContentSize;
171         Seg3.AG = MemProt::Read | MemProt::Write;
172 
173         Alloc3.MappingBase = Mem2->Start;
174         Alloc3.Segments.push_back(Seg3);
175         Alloc3.Actions.push_back(
176             {cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
177                  ExecutorAddr::fromPtr(incrementWrapper),
178                  ExecutorAddr::fromPtr(&InitializeCounter))),
179              cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
180                  ExecutorAddr::fromPtr(incrementWrapper),
181                  ExecutorAddr::fromPtr(&DeinitializeCounter)))});
182       }
183       auto Init3 = initialize(*Mapper, Alloc3);
184       EXPECT_THAT_ERROR(Init3.takeError(), Succeeded());
185       EXPECT_EQ(HW, std::string(static_cast<char *>(Init3->toPtr<char *>())));
186 
187       EXPECT_EQ(InitializeCounter, 3);
188       EXPECT_EQ(DeinitializeCounter, 1);
189 
190       std::vector<ExecutorAddr> ReleaseAddrs = {Mem2->Start};
191       EXPECT_THAT_ERROR(release(*Mapper, ReleaseAddrs), Succeeded());
192 
193       EXPECT_EQ(InitializeCounter, 3);
194       EXPECT_EQ(DeinitializeCounter, 2);
195     }
196   }
197 
198   // Implicit deinitialization by the destructor
199   EXPECT_EQ(InitializeCounter, 3);
200   EXPECT_EQ(DeinitializeCounter, 3);
201 }
202 
203 } // namespace
204