1 //===- ObjectTransformLayerTest.cpp - Unit tests for ObjectTransformLayer -===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
11 #include "llvm/ADT/STLExtras.h"
12 #include "llvm/ADT/SmallVector.h"
13 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
14 #include "llvm/ExecutionEngine/Orc/NullResolver.h"
15 #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
16 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
17 #include "llvm/IR/Module.h"
18 #include "llvm/Object/ObjectFile.h"
19 #include "gtest/gtest.h"
21 using namespace llvm::orc
;
25 // stand-in for object::ObjectFile
26 typedef int MockObjectFile
;
28 // stand-in for llvm::MemoryBuffer set
29 typedef int MockMemoryBuffer
;
31 // Mock transform that operates on unique pointers to object files, and
32 // allocates new object files rather than mutating the given ones.
33 struct AllocatingTransform
{
34 std::shared_ptr
<MockObjectFile
>
35 operator()(std::shared_ptr
<MockObjectFile
> Obj
) const {
36 return std::make_shared
<MockObjectFile
>(*Obj
+ 1);
40 // Mock base layer for verifying behavior of transform layer.
41 // Each method "T foo(args)" is accompanied by two auxiliary methods:
42 // - "void expectFoo(args)", to be called before calling foo on the transform
43 // layer; saves values of args, which mock layer foo then verifies against.
44 // - "void verifyFoo(T)", to be called after foo, which verifies that the
45 // transform layer called the base layer and forwarded any return value.
48 MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); }
50 template <typename ObjPtrT
> llvm::Error
addObject(VModuleKey K
, ObjPtrT Obj
) {
51 EXPECT_EQ(MockKey
, K
) << "Key should pass through";
52 EXPECT_EQ(MockObject
+ 1, *Obj
) << "Transform should be applied";
53 LastCalled
= "addObject";
54 return llvm::Error::success();
57 template <typename ObjPtrT
> void expectAddObject(VModuleKey K
, ObjPtrT Obj
) {
62 void verifyAddObject() {
63 EXPECT_EQ("addObject", LastCalled
);
67 llvm::Error
removeObject(VModuleKey K
) {
68 EXPECT_EQ(MockKey
, K
);
69 LastCalled
= "removeObject";
70 return llvm::Error::success();
73 void expectRemoveObject(VModuleKey K
) { MockKey
= K
; }
74 void verifyRemoveObject() {
75 EXPECT_EQ("removeObject", LastCalled
);
79 llvm::JITSymbol
findSymbol(const std::string
&Name
,
80 bool ExportedSymbolsOnly
) {
81 EXPECT_EQ(MockName
, Name
) << "Name should pass through";
82 EXPECT_EQ(MockBool
, ExportedSymbolsOnly
) << "Flag should pass through";
83 LastCalled
= "findSymbol";
84 MockSymbol
= llvm::JITSymbol(122, llvm::JITSymbolFlags::None
);
85 return llvm::JITSymbol(122, llvm::JITSymbolFlags::None
);
87 void expectFindSymbol(const std::string
&Name
, bool ExportedSymbolsOnly
) {
89 MockBool
= ExportedSymbolsOnly
;
91 void verifyFindSymbol(llvm::JITSymbol Returned
) {
92 EXPECT_EQ("findSymbol", LastCalled
);
93 EXPECT_EQ(cantFail(MockSymbol
.getAddress()),
94 cantFail(Returned
.getAddress()))
95 << "Return should pass through";
99 llvm::JITSymbol
findSymbolIn(VModuleKey K
, const std::string
&Name
,
100 bool ExportedSymbolsOnly
) {
101 EXPECT_EQ(MockKey
, K
) << "VModuleKey should pass through";
102 EXPECT_EQ(MockName
, Name
) << "Name should pass through";
103 EXPECT_EQ(MockBool
, ExportedSymbolsOnly
) << "Flag should pass through";
104 LastCalled
= "findSymbolIn";
105 MockSymbol
= llvm::JITSymbol(122, llvm::JITSymbolFlags::None
);
106 return llvm::JITSymbol(122, llvm::JITSymbolFlags::None
);
108 void expectFindSymbolIn(VModuleKey K
, const std::string
&Name
,
109 bool ExportedSymbolsOnly
) {
112 MockBool
= ExportedSymbolsOnly
;
114 void verifyFindSymbolIn(llvm::JITSymbol Returned
) {
115 EXPECT_EQ("findSymbolIn", LastCalled
);
116 EXPECT_EQ(cantFail(MockSymbol
.getAddress()),
117 cantFail(Returned
.getAddress()))
118 << "Return should pass through";
122 llvm::Error
emitAndFinalize(VModuleKey K
) {
123 EXPECT_EQ(MockKey
, K
) << "VModuleKey should pass through";
124 LastCalled
= "emitAndFinalize";
125 return llvm::Error::success();
128 void expectEmitAndFinalize(VModuleKey K
) { MockKey
= K
; }
130 void verifyEmitAndFinalize() {
131 EXPECT_EQ("emitAndFinalize", LastCalled
);
135 void mapSectionAddress(VModuleKey K
, const void *LocalAddress
,
136 llvm::JITTargetAddress TargetAddr
) {
137 EXPECT_EQ(MockKey
, K
);
138 EXPECT_EQ(MockLocalAddress
, LocalAddress
);
139 EXPECT_EQ(MockTargetAddress
, TargetAddr
);
140 LastCalled
= "mapSectionAddress";
142 void expectMapSectionAddress(VModuleKey K
, const void *LocalAddress
,
143 llvm::JITTargetAddress TargetAddr
) {
145 MockLocalAddress
= LocalAddress
;
146 MockTargetAddress
= TargetAddr
;
148 void verifyMapSectionAddress() {
149 EXPECT_EQ("mapSectionAddress", LastCalled
);
154 // Backing fields for remembering parameter/return values
155 std::string LastCalled
;
157 MockObjectFile MockObject
;
158 std::string MockName
;
160 llvm::JITSymbol MockSymbol
;
161 const void *MockLocalAddress
;
162 llvm::JITTargetAddress MockTargetAddress
;
163 MockMemoryBuffer MockBuffer
;
165 // Clear remembered parameters between calls
166 void resetExpectations() {
167 LastCalled
= "nothing";
171 MockSymbol
= llvm::JITSymbol(nullptr);
172 MockLocalAddress
= nullptr;
173 MockTargetAddress
= 0;
178 // Test each operation on ObjectTransformLayer.
179 TEST(ObjectTransformLayerTest
, Main
) {
182 ExecutionSession
ES(std::make_shared
<SymbolStringPool
>());
184 // Create one object transform layer using a transform (as a functor)
185 // that allocates new objects, and deals in unique pointers.
186 ObjectTransformLayer
<MockBaseLayer
, AllocatingTransform
> T1(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 ObjectTransformLayer
<MockBaseLayer
,
191 std::function
<std::shared_ptr
<MockObjectFile
>(
192 std::shared_ptr
<MockObjectFile
>)>>
193 T2(M
, [](std::shared_ptr
<MockObjectFile
> Obj
) {
198 // Test addObject with T1 (allocating)
199 auto K1
= ES
.allocateVModule();
200 auto Obj1
= std::make_shared
<MockObjectFile
>(211);
201 M
.expectAddObject(K1
, Obj1
);
202 cantFail(T1
.addObject(K1
, std::move(Obj1
)));
205 // Test addObjectSet with T2 (mutating)
206 auto K2
= ES
.allocateVModule();
207 auto Obj2
= std::make_shared
<MockObjectFile
>(222);
208 M
.expectAddObject(K2
, Obj2
);
209 cantFail(T2
.addObject(K2
, Obj2
));
211 EXPECT_EQ(223, *Obj2
) << "Expected mutation";
213 // Test removeObjectSet
214 M
.expectRemoveObject(K2
);
215 cantFail(T1
.removeObject(K2
));
216 M
.verifyRemoveObject();
219 std::string Name
= "foo";
220 bool ExportedOnly
= true;
221 M
.expectFindSymbol(Name
, ExportedOnly
);
222 llvm::JITSymbol Sym1
= T2
.findSymbol(Name
, ExportedOnly
);
223 M
.verifyFindSymbol(std::move(Sym1
));
227 ExportedOnly
= false;
228 M
.expectFindSymbolIn(K1
, Name
, ExportedOnly
);
229 llvm::JITSymbol Sym2
= T1
.findSymbolIn(K1
, Name
, ExportedOnly
);
230 M
.verifyFindSymbolIn(std::move(Sym2
));
232 // Test emitAndFinalize
233 M
.expectEmitAndFinalize(K1
);
234 cantFail(T2
.emitAndFinalize(K1
));
235 M
.verifyEmitAndFinalize();
237 // Test mapSectionAddress
239 llvm::JITTargetAddress MockAddress
= 255;
240 M
.expectMapSectionAddress(K1
, Buffer
, MockAddress
);
241 T1
.mapSectionAddress(K1
, Buffer
, MockAddress
);
242 M
.verifyMapSectionAddress();
244 // Verify transform getter (non-const)
245 auto Mutatee
= std::make_shared
<MockObjectFile
>(277);
246 auto Out
= T2
.getTransform()(Mutatee
);
247 EXPECT_EQ(*Mutatee
, *Out
) << "Expected in-place transform";
248 EXPECT_EQ(278, *Mutatee
) << "Expected incrementing transform";
250 // Verify transform getter (const)
251 auto OwnedObj
= std::make_shared
<MockObjectFile
>(288);
252 const auto &T1C
= T1
;
253 OwnedObj
= T1C
.getTransform()(std::move(OwnedObj
));
254 EXPECT_EQ(289, *OwnedObj
) << "Expected incrementing transform";
256 volatile bool RunStaticChecks
= false;
257 if (!RunStaticChecks
)
260 // Make sure that ObjectTransformLayer implements the object layer concept
261 // correctly by sandwitching one between an ObjectLinkingLayer and an
262 // IRCompileLayer, verifying that it compiles if we have a call to the
263 // IRComileLayer's addModule that should call the transform layer's
264 // addObject, and also calling the other public transform layer methods
265 // directly to make sure the methods they intend to forward to exist on
266 // the ObjectLinkingLayer.
268 // We'll need a concrete MemoryManager class.
269 class NullManager
: public llvm::RuntimeDyld::MemoryManager
{
271 uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned,
272 llvm::StringRef
) override
{
275 uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef
,
279 void registerEHFrames(uint8_t *, uint64_t, size_t) override
{}
280 void deregisterEHFrames() override
{}
281 bool finalizeMemory(std::string
*) override
{ return false; }
284 // Construct the jit layers.
285 RTDyldObjectLinkingLayer
BaseLayer(ES
, [](VModuleKey
) {
286 return RTDyldObjectLinkingLayer::Resources
{
287 std::make_shared
<llvm::SectionMemoryManager
>(),
288 std::make_shared
<NullResolver
>()};
291 auto IdentityTransform
= [](std::unique_ptr
<llvm::MemoryBuffer
> Obj
) {
294 ObjectTransformLayer
<decltype(BaseLayer
), decltype(IdentityTransform
)>
295 TransformLayer(BaseLayer
, IdentityTransform
);
296 auto NullCompiler
= [](llvm::Module
&) {
297 return std::unique_ptr
<llvm::MemoryBuffer
>(nullptr);
299 IRCompileLayer
<decltype(TransformLayer
), decltype(NullCompiler
)>
300 CompileLayer(TransformLayer
, NullCompiler
);
302 // Make sure that the calls from IRCompileLayer to ObjectTransformLayer
304 cantFail(CompileLayer
.addModule(ES
.allocateVModule(),
305 std::unique_ptr
<llvm::Module
>()));
307 // Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer
309 VModuleKey DummyKey
= ES
.allocateVModule();
310 cantFail(TransformLayer
.emitAndFinalize(DummyKey
));
311 TransformLayer
.findSymbolIn(DummyKey
, Name
, false);
312 TransformLayer
.findSymbol(Name
, true);
313 TransformLayer
.mapSectionAddress(DummyKey
, nullptr, 0);
314 cantFail(TransformLayer
.removeObject(DummyKey
));