[llvm] [cmake] Add possibility to use ChooseMSVCCRT.cmake when include LLVM library
[llvm-core.git] / unittests / ExecutionEngine / Orc / ObjectTransformLayerTest.cpp
blob2ff7e91a732342c276de0410ac5d212144981af1
1 //===- ObjectTransformLayerTest.cpp - Unit tests for ObjectTransformLayer -===//
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 //===----------------------------------------------------------------------===//
9 #include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
10 #include "llvm/ADT/STLExtras.h"
11 #include "llvm/ADT/SmallVector.h"
12 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
13 #include "llvm/ExecutionEngine/Orc/NullResolver.h"
14 #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
15 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
16 #include "llvm/IR/Module.h"
17 #include "llvm/Object/ObjectFile.h"
18 #include "gtest/gtest.h"
20 using namespace llvm::orc;
22 namespace {
24 // stand-in for object::ObjectFile
25 typedef int MockObjectFile;
27 // stand-in for llvm::MemoryBuffer set
28 typedef int MockMemoryBuffer;
30 // Mock transform that operates on unique pointers to object files, and
31 // allocates new object files rather than mutating the given ones.
32 struct AllocatingTransform {
33 std::shared_ptr<MockObjectFile>
34 operator()(std::shared_ptr<MockObjectFile> Obj) const {
35 return std::make_shared<MockObjectFile>(*Obj + 1);
39 // Mock base layer for verifying behavior of transform layer.
40 // Each method "T foo(args)" is accompanied by two auxiliary methods:
41 // - "void expectFoo(args)", to be called before calling foo on the transform
42 // layer; saves values of args, which mock layer foo then verifies against.
43 // - "void verifyFoo(T)", to be called after foo, which verifies that the
44 // transform layer called the base layer and forwarded any return value.
45 class MockBaseLayer {
46 public:
47 MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); }
49 template <typename ObjPtrT> llvm::Error addObject(VModuleKey K, ObjPtrT Obj) {
50 EXPECT_EQ(MockKey, K) << "Key should pass through";
51 EXPECT_EQ(MockObject + 1, *Obj) << "Transform should be applied";
52 LastCalled = "addObject";
53 return llvm::Error::success();
56 template <typename ObjPtrT> void expectAddObject(VModuleKey K, ObjPtrT Obj) {
57 MockKey = K;
58 MockObject = *Obj;
61 void verifyAddObject() {
62 EXPECT_EQ("addObject", LastCalled);
63 resetExpectations();
66 llvm::Error removeObject(VModuleKey K) {
67 EXPECT_EQ(MockKey, K);
68 LastCalled = "removeObject";
69 return llvm::Error::success();
72 void expectRemoveObject(VModuleKey K) { MockKey = K; }
73 void verifyRemoveObject() {
74 EXPECT_EQ("removeObject", LastCalled);
75 resetExpectations();
78 llvm::JITSymbol findSymbol(const std::string &Name,
79 bool ExportedSymbolsOnly) {
80 EXPECT_EQ(MockName, Name) << "Name should pass through";
81 EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
82 LastCalled = "findSymbol";
83 MockSymbol = llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
84 return llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
86 void expectFindSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
87 MockName = Name;
88 MockBool = ExportedSymbolsOnly;
90 void verifyFindSymbol(llvm::JITSymbol Returned) {
91 EXPECT_EQ("findSymbol", LastCalled);
92 EXPECT_EQ(cantFail(MockSymbol.getAddress()),
93 cantFail(Returned.getAddress()))
94 << "Return should pass through";
95 resetExpectations();
98 llvm::JITSymbol findSymbolIn(VModuleKey K, const std::string &Name,
99 bool ExportedSymbolsOnly) {
100 EXPECT_EQ(MockKey, K) << "VModuleKey should pass through";
101 EXPECT_EQ(MockName, Name) << "Name should pass through";
102 EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
103 LastCalled = "findSymbolIn";
104 MockSymbol = llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
105 return llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
107 void expectFindSymbolIn(VModuleKey K, const std::string &Name,
108 bool ExportedSymbolsOnly) {
109 MockKey = K;
110 MockName = Name;
111 MockBool = ExportedSymbolsOnly;
113 void verifyFindSymbolIn(llvm::JITSymbol Returned) {
114 EXPECT_EQ("findSymbolIn", LastCalled);
115 EXPECT_EQ(cantFail(MockSymbol.getAddress()),
116 cantFail(Returned.getAddress()))
117 << "Return should pass through";
118 resetExpectations();
121 llvm::Error emitAndFinalize(VModuleKey K) {
122 EXPECT_EQ(MockKey, K) << "VModuleKey should pass through";
123 LastCalled = "emitAndFinalize";
124 return llvm::Error::success();
127 void expectEmitAndFinalize(VModuleKey K) { MockKey = K; }
129 void verifyEmitAndFinalize() {
130 EXPECT_EQ("emitAndFinalize", LastCalled);
131 resetExpectations();
134 void mapSectionAddress(VModuleKey K, const void *LocalAddress,
135 llvm::JITTargetAddress TargetAddr) {
136 EXPECT_EQ(MockKey, K);
137 EXPECT_EQ(MockLocalAddress, LocalAddress);
138 EXPECT_EQ(MockTargetAddress, TargetAddr);
139 LastCalled = "mapSectionAddress";
141 void expectMapSectionAddress(VModuleKey K, const void *LocalAddress,
142 llvm::JITTargetAddress TargetAddr) {
143 MockKey = K;
144 MockLocalAddress = LocalAddress;
145 MockTargetAddress = TargetAddr;
147 void verifyMapSectionAddress() {
148 EXPECT_EQ("mapSectionAddress", LastCalled);
149 resetExpectations();
152 private:
153 // Backing fields for remembering parameter/return values
154 std::string LastCalled;
155 VModuleKey MockKey;
156 MockObjectFile MockObject;
157 std::string MockName;
158 bool MockBool;
159 llvm::JITSymbol MockSymbol;
160 const void *MockLocalAddress;
161 llvm::JITTargetAddress MockTargetAddress;
162 MockMemoryBuffer MockBuffer;
164 // Clear remembered parameters between calls
165 void resetExpectations() {
166 LastCalled = "nothing";
167 MockKey = 0;
168 MockObject = 0;
169 MockName = "bogus";
170 MockSymbol = llvm::JITSymbol(nullptr);
171 MockLocalAddress = nullptr;
172 MockTargetAddress = 0;
173 MockBuffer = 0;
177 // Test each operation on LegacyObjectTransformLayer.
178 TEST(LegacyObjectTransformLayerTest, Main) {
179 MockBaseLayer M;
181 ExecutionSession ES(std::make_shared<SymbolStringPool>());
183 // Create one object transform layer using a transform (as a functor)
184 // that allocates new objects, and deals in unique pointers.
185 LegacyObjectTransformLayer<MockBaseLayer, AllocatingTransform> T1(
186 llvm::AcknowledgeORCv1Deprecation, M);
188 // Create a second object transform layer using a transform (as a lambda)
189 // that mutates objects in place, and deals in naked pointers
190 LegacyObjectTransformLayer<MockBaseLayer,
191 std::function<std::shared_ptr<MockObjectFile>(
192 std::shared_ptr<MockObjectFile>)>>
193 T2(llvm::AcknowledgeORCv1Deprecation, M,
194 [](std::shared_ptr<MockObjectFile> Obj) {
195 ++(*Obj);
196 return Obj;
199 // Test addObject with T1 (allocating)
200 auto K1 = ES.allocateVModule();
201 auto Obj1 = std::make_shared<MockObjectFile>(211);
202 M.expectAddObject(K1, Obj1);
203 cantFail(T1.addObject(K1, std::move(Obj1)));
204 M.verifyAddObject();
206 // Test addObjectSet with T2 (mutating)
207 auto K2 = ES.allocateVModule();
208 auto Obj2 = std::make_shared<MockObjectFile>(222);
209 M.expectAddObject(K2, Obj2);
210 cantFail(T2.addObject(K2, Obj2));
211 M.verifyAddObject();
212 EXPECT_EQ(223, *Obj2) << "Expected mutation";
214 // Test removeObjectSet
215 M.expectRemoveObject(K2);
216 cantFail(T1.removeObject(K2));
217 M.verifyRemoveObject();
219 // Test findSymbol
220 std::string Name = "foo";
221 bool ExportedOnly = true;
222 M.expectFindSymbol(Name, ExportedOnly);
223 llvm::JITSymbol Sym1 = T2.findSymbol(Name, ExportedOnly);
224 M.verifyFindSymbol(std::move(Sym1));
226 // Test findSymbolIn
227 Name = "bar";
228 ExportedOnly = false;
229 M.expectFindSymbolIn(K1, Name, ExportedOnly);
230 llvm::JITSymbol Sym2 = T1.findSymbolIn(K1, Name, ExportedOnly);
231 M.verifyFindSymbolIn(std::move(Sym2));
233 // Test emitAndFinalize
234 M.expectEmitAndFinalize(K1);
235 cantFail(T2.emitAndFinalize(K1));
236 M.verifyEmitAndFinalize();
238 // Test mapSectionAddress
239 char Buffer[24];
240 llvm::JITTargetAddress MockAddress = 255;
241 M.expectMapSectionAddress(K1, Buffer, MockAddress);
242 T1.mapSectionAddress(K1, Buffer, MockAddress);
243 M.verifyMapSectionAddress();
245 // Verify transform getter (non-const)
246 auto Mutatee = std::make_shared<MockObjectFile>(277);
247 auto Out = T2.getTransform()(Mutatee);
248 EXPECT_EQ(*Mutatee, *Out) << "Expected in-place transform";
249 EXPECT_EQ(278, *Mutatee) << "Expected incrementing transform";
251 // Verify transform getter (const)
252 auto OwnedObj = std::make_shared<MockObjectFile>(288);
253 const auto &T1C = T1;
254 OwnedObj = T1C.getTransform()(std::move(OwnedObj));
255 EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform";
257 volatile bool RunStaticChecks = false;
258 if (!RunStaticChecks)
259 return;
261 // Make sure that LegacyObjectTransformLayer implements the object layer concept
262 // correctly by sandwitching one between an ObjectLinkingLayer and an
263 // LegacyIRCompileLayer, verifying that it compiles if we have a call to the
264 // IRComileLayer's addModule that should call the transform layer's
265 // addObject, and also calling the other public transform layer methods
266 // directly to make sure the methods they intend to forward to exist on
267 // the ObjectLinkingLayer.
269 // We'll need a concrete MemoryManager class.
270 class NullManager : public llvm::RuntimeDyld::MemoryManager {
271 public:
272 uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned,
273 llvm::StringRef) override {
274 return nullptr;
276 uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef,
277 bool) override {
278 return nullptr;
280 void registerEHFrames(uint8_t *, uint64_t, size_t) override {}
281 void deregisterEHFrames() override {}
282 bool finalizeMemory(std::string *) override { return false; }
285 // Construct the jit layers.
286 LegacyRTDyldObjectLinkingLayer BaseLayer(
287 llvm::AcknowledgeORCv1Deprecation, ES, [](VModuleKey) {
288 return LegacyRTDyldObjectLinkingLayer::Resources{
289 std::make_shared<llvm::SectionMemoryManager>(),
290 std::make_shared<NullResolver>()};
293 auto IdentityTransform = [](std::unique_ptr<llvm::MemoryBuffer> Obj) {
294 return Obj;
296 LegacyObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)>
297 TransformLayer(llvm::AcknowledgeORCv1Deprecation, BaseLayer,
298 IdentityTransform);
299 auto NullCompiler = [](llvm::Module &) {
300 return std::unique_ptr<llvm::MemoryBuffer>(nullptr);
302 LegacyIRCompileLayer<decltype(TransformLayer), decltype(NullCompiler)>
303 CompileLayer(llvm::AcknowledgeORCv1Deprecation, TransformLayer,
304 NullCompiler);
306 // Make sure that the calls from LegacyIRCompileLayer to LegacyObjectTransformLayer
307 // compile.
308 cantFail(CompileLayer.addModule(ES.allocateVModule(),
309 std::unique_ptr<llvm::Module>()));
311 // Make sure that the calls from LegacyObjectTransformLayer to ObjectLinkingLayer
312 // compile.
313 VModuleKey DummyKey = ES.allocateVModule();
314 cantFail(TransformLayer.emitAndFinalize(DummyKey));
315 TransformLayer.findSymbolIn(DummyKey, Name, false);
316 TransformLayer.findSymbol(Name, true);
317 TransformLayer.mapSectionAddress(DummyKey, nullptr, 0);
318 cantFail(TransformLayer.removeObject(DummyKey));