1 //===- ObjectTransformLayerTest.cpp - Unit tests for ObjectTransformLayer -===//
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
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
;
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.
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
) {
61 void verifyAddObject() {
62 EXPECT_EQ("addObject", LastCalled
);
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
);
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
) {
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";
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
) {
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";
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
);
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
) {
144 MockLocalAddress
= LocalAddress
;
145 MockTargetAddress
= TargetAddr
;
147 void verifyMapSectionAddress() {
148 EXPECT_EQ("mapSectionAddress", LastCalled
);
153 // Backing fields for remembering parameter/return values
154 std::string LastCalled
;
156 MockObjectFile MockObject
;
157 std::string MockName
;
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";
170 MockSymbol
= llvm::JITSymbol(nullptr);
171 MockLocalAddress
= nullptr;
172 MockTargetAddress
= 0;
177 // Test each operation on LegacyObjectTransformLayer.
178 TEST(LegacyObjectTransformLayerTest
, Main
) {
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
) {
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
)));
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
));
212 EXPECT_EQ(223, *Obj2
) << "Expected mutation";
214 // Test removeObjectSet
215 M
.expectRemoveObject(K2
);
216 cantFail(T1
.removeObject(K2
));
217 M
.verifyRemoveObject();
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
));
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
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
)
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
{
272 uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned,
273 llvm::StringRef
) override
{
276 uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef
,
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
) {
296 LegacyObjectTransformLayer
<decltype(BaseLayer
), decltype(IdentityTransform
)>
297 TransformLayer(llvm::AcknowledgeORCv1Deprecation
, BaseLayer
,
299 auto NullCompiler
= [](llvm::Module
&) {
300 return std::unique_ptr
<llvm::MemoryBuffer
>(nullptr);
302 LegacyIRCompileLayer
<decltype(TransformLayer
), decltype(NullCompiler
)>
303 CompileLayer(llvm::AcknowledgeORCv1Deprecation
, TransformLayer
,
306 // Make sure that the calls from LegacyIRCompileLayer to LegacyObjectTransformLayer
308 cantFail(CompileLayer
.addModule(ES
.allocateVModule(),
309 std::unique_ptr
<llvm::Module
>()));
311 // Make sure that the calls from LegacyObjectTransformLayer to ObjectLinkingLayer
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
));