ObjectTransformLayerTest.cpp 11 KB

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