Home | History | Annotate | Download | only in Orc
      1 //===- ObjectTransformLayerTest.cpp - Unit tests for ObjectTransformLayer -===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "llvm/ADT/STLExtras.h"
     11 #include "llvm/ADT/SmallVector.h"
     12 #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
     13 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
     14 #include "llvm/ExecutionEngine/Orc/NullResolver.h"
     15 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
     16 #include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
     17 #include "llvm/Object/ObjectFile.h"
     18 #include "gtest/gtest.h"
     19 
     20 using namespace llvm::orc;
     21 
     22 namespace {
     23 
     24 // Stand-in for RuntimeDyld::MemoryManager
     25 typedef int MockMemoryManager;
     26 
     27 // Stand-in for RuntimeDyld::SymbolResolver
     28 typedef int MockSymbolResolver;
     29 
     30 // stand-in for object::ObjectFile
     31 typedef int MockObjectFile;
     32 
     33 // stand-in for llvm::MemoryBuffer set
     34 typedef int MockMemoryBufferSet;
     35 
     36 // Mock transform that operates on unique pointers to object files, and
     37 // allocates new object files rather than mutating the given ones.
     38 struct AllocatingTransform {
     39   std::unique_ptr<MockObjectFile>
     40   operator()(std::unique_ptr<MockObjectFile> Obj) const {
     41     return llvm::make_unique<MockObjectFile>(*Obj + 1);
     42   }
     43 };
     44 
     45 // Mock base layer for verifying behavior of transform layer.
     46 // Each method "T foo(args)" is accompanied by two auxiliary methods:
     47 //  - "void expectFoo(args)", to be called before calling foo on the transform
     48 //      layer; saves values of args, which mock layer foo then verifies against.
     49 // - "void verifyFoo(T)", to be called after foo, which verifies that the
     50 //      transform layer called the base layer and forwarded any return value.
     51 class MockBaseLayer {
     52 public:
     53   typedef int ObjSetHandleT;
     54 
     55   MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); }
     56 
     57   template <typename ObjSetT, typename MemoryManagerPtrT,
     58             typename SymbolResolverPtrT>
     59   ObjSetHandleT addObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr,
     60                              SymbolResolverPtrT Resolver) {
     61     EXPECT_EQ(MockManager, *MemMgr) << "MM should pass through";
     62     EXPECT_EQ(MockResolver, *Resolver) << "Resolver should pass through";
     63     size_t I = 0;
     64     for (auto &ObjPtr : Objects) {
     65       EXPECT_EQ(MockObjects[I] + 1, *ObjPtr) << "Transform should be applied";
     66       I++;
     67     }
     68     EXPECT_EQ(MockObjects.size(), I) << "Number of objects should match";
     69     LastCalled = "addObjectSet";
     70     MockObjSetHandle = 111;
     71     return MockObjSetHandle;
     72   }
     73   template <typename ObjSetT>
     74   void expectAddObjectSet(ObjSetT &Objects, MockMemoryManager *MemMgr,
     75                           MockSymbolResolver *Resolver) {
     76     MockManager = *MemMgr;
     77     MockResolver = *Resolver;
     78     for (auto &ObjPtr : Objects) {
     79       MockObjects.push_back(*ObjPtr);
     80     }
     81   }
     82   void verifyAddObjectSet(ObjSetHandleT Returned) {
     83     EXPECT_EQ("addObjectSet", LastCalled);
     84     EXPECT_EQ(MockObjSetHandle, Returned) << "Return should pass through";
     85     resetExpectations();
     86   }
     87 
     88   void removeObjectSet(ObjSetHandleT H) {
     89     EXPECT_EQ(MockObjSetHandle, H);
     90     LastCalled = "removeObjectSet";
     91   }
     92   void expectRemoveObjectSet(ObjSetHandleT H) { MockObjSetHandle = H; }
     93   void verifyRemoveObjectSet() {
     94     EXPECT_EQ("removeObjectSet", LastCalled);
     95     resetExpectations();
     96   }
     97 
     98   JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
     99     EXPECT_EQ(MockName, Name) << "Name should pass through";
    100     EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
    101     LastCalled = "findSymbol";
    102     MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None);
    103     return MockSymbol;
    104   }
    105   void expectFindSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
    106     MockName = Name;
    107     MockBool = ExportedSymbolsOnly;
    108   }
    109   void verifyFindSymbol(llvm::orc::JITSymbol Returned) {
    110     EXPECT_EQ("findSymbol", LastCalled);
    111     EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress())
    112         << "Return should pass through";
    113     resetExpectations();
    114   }
    115 
    116   JITSymbol findSymbolIn(ObjSetHandleT H, const std::string &Name,
    117                          bool ExportedSymbolsOnly) {
    118     EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through";
    119     EXPECT_EQ(MockName, Name) << "Name should pass through";
    120     EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
    121     LastCalled = "findSymbolIn";
    122     MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None);
    123     return MockSymbol;
    124   }
    125   void expectFindSymbolIn(ObjSetHandleT H, const std::string &Name,
    126                           bool ExportedSymbolsOnly) {
    127     MockObjSetHandle = H;
    128     MockName = Name;
    129     MockBool = ExportedSymbolsOnly;
    130   }
    131   void verifyFindSymbolIn(llvm::orc::JITSymbol Returned) {
    132     EXPECT_EQ("findSymbolIn", LastCalled);
    133     EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress())
    134         << "Return should pass through";
    135     resetExpectations();
    136   }
    137 
    138   void emitAndFinalize(ObjSetHandleT H) {
    139     EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through";
    140     LastCalled = "emitAndFinalize";
    141   }
    142   void expectEmitAndFinalize(ObjSetHandleT H) { MockObjSetHandle = H; }
    143   void verifyEmitAndFinalize() {
    144     EXPECT_EQ("emitAndFinalize", LastCalled);
    145     resetExpectations();
    146   }
    147 
    148   void mapSectionAddress(ObjSetHandleT H, const void *LocalAddress,
    149                          TargetAddress TargetAddr) {
    150     EXPECT_EQ(MockObjSetHandle, H);
    151     EXPECT_EQ(MockLocalAddress, LocalAddress);
    152     EXPECT_EQ(MockTargetAddress, TargetAddr);
    153     LastCalled = "mapSectionAddress";
    154   }
    155   void expectMapSectionAddress(ObjSetHandleT H, const void *LocalAddress,
    156                                TargetAddress TargetAddr) {
    157     MockObjSetHandle = H;
    158     MockLocalAddress = LocalAddress;
    159     MockTargetAddress = TargetAddr;
    160   }
    161   void verifyMapSectionAddress() {
    162     EXPECT_EQ("mapSectionAddress", LastCalled);
    163     resetExpectations();
    164   }
    165 
    166 private:
    167   // Backing fields for remembering parameter/return values
    168   std::string LastCalled;
    169   MockMemoryManager MockManager;
    170   MockSymbolResolver MockResolver;
    171   std::vector<MockObjectFile> MockObjects;
    172   ObjSetHandleT MockObjSetHandle;
    173   std::string MockName;
    174   bool MockBool;
    175   JITSymbol MockSymbol;
    176   const void *MockLocalAddress;
    177   TargetAddress MockTargetAddress;
    178   MockMemoryBufferSet MockBufferSet;
    179 
    180   // Clear remembered parameters between calls
    181   void resetExpectations() {
    182     LastCalled = "nothing";
    183     MockManager = 0;
    184     MockResolver = 0;
    185     MockObjects.clear();
    186     MockObjSetHandle = 0;
    187     MockName = "bogus";
    188     MockSymbol = JITSymbol(nullptr);
    189     MockLocalAddress = nullptr;
    190     MockTargetAddress = 0;
    191     MockBufferSet = 0;
    192   }
    193 };
    194 
    195 // Test each operation on ObjectTransformLayer.
    196 TEST(ObjectTransformLayerTest, Main) {
    197   MockBaseLayer M;
    198 
    199   // Create one object transform layer using a transform (as a functor)
    200   // that allocates new objects, and deals in unique pointers.
    201   ObjectTransformLayer<MockBaseLayer, AllocatingTransform> T1(M);
    202 
    203   // Create a second object transform layer using a transform (as a lambda)
    204   // that mutates objects in place, and deals in naked pointers
    205   ObjectTransformLayer<MockBaseLayer,
    206                        std::function<MockObjectFile *(MockObjectFile *)>>
    207   T2(M, [](MockObjectFile *Obj) {
    208     ++(*Obj);
    209     return Obj;
    210   });
    211 
    212   // Instantiate some mock objects to use below
    213   MockObjectFile MockObject1 = 211;
    214   MockObjectFile MockObject2 = 222;
    215   MockMemoryManager MockManager = 233;
    216   MockSymbolResolver MockResolver = 244;
    217 
    218   // Test addObjectSet with T1 (allocating, unique pointers)
    219   std::vector<std::unique_ptr<MockObjectFile>> Objs1;
    220   Objs1.push_back(llvm::make_unique<MockObjectFile>(MockObject1));
    221   Objs1.push_back(llvm::make_unique<MockObjectFile>(MockObject2));
    222   auto MM = llvm::make_unique<MockMemoryManager>(MockManager);
    223   auto SR = llvm::make_unique<MockSymbolResolver>(MockResolver);
    224   M.expectAddObjectSet(Objs1, MM.get(), SR.get());
    225   auto H = T1.addObjectSet(std::move(Objs1), std::move(MM), std::move(SR));
    226   M.verifyAddObjectSet(H);
    227 
    228   // Test addObjectSet with T2 (mutating, naked pointers)
    229   llvm::SmallVector<MockObjectFile *, 2> Objs2Vec;
    230   Objs2Vec.push_back(&MockObject1);
    231   Objs2Vec.push_back(&MockObject2);
    232   llvm::MutableArrayRef<MockObjectFile *> Objs2(Objs2Vec);
    233   M.expectAddObjectSet(Objs2, &MockManager, &MockResolver);
    234   H = T2.addObjectSet(Objs2, &MockManager, &MockResolver);
    235   M.verifyAddObjectSet(H);
    236   EXPECT_EQ(212, MockObject1) << "Expected mutation";
    237   EXPECT_EQ(223, MockObject2) << "Expected mutation";
    238 
    239   // Test removeObjectSet
    240   M.expectRemoveObjectSet(H);
    241   T1.removeObjectSet(H);
    242   M.verifyRemoveObjectSet();
    243 
    244   // Test findSymbol
    245   std::string Name = "foo";
    246   bool ExportedOnly = true;
    247   M.expectFindSymbol(Name, ExportedOnly);
    248   JITSymbol Symbol = T2.findSymbol(Name, ExportedOnly);
    249   M.verifyFindSymbol(Symbol);
    250 
    251   // Test findSymbolIn
    252   Name = "bar";
    253   ExportedOnly = false;
    254   M.expectFindSymbolIn(H, Name, ExportedOnly);
    255   Symbol = T1.findSymbolIn(H, Name, ExportedOnly);
    256   M.verifyFindSymbolIn(Symbol);
    257 
    258   // Test emitAndFinalize
    259   M.expectEmitAndFinalize(H);
    260   T2.emitAndFinalize(H);
    261   M.verifyEmitAndFinalize();
    262 
    263   // Test mapSectionAddress
    264   char Buffer[24];
    265   TargetAddress MockAddress = 255;
    266   M.expectMapSectionAddress(H, Buffer, MockAddress);
    267   T1.mapSectionAddress(H, Buffer, MockAddress);
    268   M.verifyMapSectionAddress();
    269 
    270   // Verify transform getter (non-const)
    271   MockObjectFile Mutatee = 277;
    272   MockObjectFile *Out = T2.getTransform()(&Mutatee);
    273   EXPECT_EQ(&Mutatee, Out) << "Expected in-place transform";
    274   EXPECT_EQ(278, Mutatee) << "Expected incrementing transform";
    275 
    276   // Verify transform getter (const)
    277   auto OwnedObj = llvm::make_unique<MockObjectFile>(288);
    278   const auto &T1C = T1;
    279   OwnedObj = T1C.getTransform()(std::move(OwnedObj));
    280   EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform";
    281 
    282   volatile bool RunStaticChecks = false;
    283   if (!RunStaticChecks)
    284     return;
    285 
    286   // Make sure that ObjectTransformLayer implements the object layer concept
    287   // correctly by sandwitching one between an ObjectLinkingLayer and an
    288   // IRCompileLayer, verifying that it compiles if we have a call to the
    289   // IRComileLayer's addModuleSet that should call the transform layer's
    290   // addObjectSet, and also calling the other public transform layer methods
    291   // directly to make sure the methods they intend to forward to exist on
    292   // the ObjectLinkingLayer.
    293 
    294   // We'll need a concrete MemoryManager class.
    295   class NullManager : public llvm::RuntimeDyld::MemoryManager {
    296   public:
    297     uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned,
    298                                  llvm::StringRef) override {
    299       return nullptr;
    300     }
    301     uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef,
    302                                  bool) override {
    303       return nullptr;
    304     }
    305     void registerEHFrames(uint8_t *, uint64_t, size_t) override {}
    306     void deregisterEHFrames(uint8_t *, uint64_t, size_t) override {}
    307     bool finalizeMemory(std::string *) override { return false; }
    308   };
    309 
    310   // Construct the jit layers.
    311   ObjectLinkingLayer<> BaseLayer;
    312   auto IdentityTransform = [](
    313       std::unique_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>>
    314           Obj) { return Obj; };
    315   ObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)>
    316       TransformLayer(BaseLayer, IdentityTransform);
    317   auto NullCompiler = [](llvm::Module &) {
    318     return llvm::object::OwningBinary<llvm::object::ObjectFile>();
    319   };
    320   IRCompileLayer<decltype(TransformLayer)> CompileLayer(TransformLayer,
    321                                                         NullCompiler);
    322 
    323   // Make sure that the calls from IRCompileLayer to ObjectTransformLayer
    324   // compile.
    325   NullResolver Resolver;
    326   NullManager Manager;
    327   CompileLayer.addModuleSet(std::vector<llvm::Module *>(), &Manager, &Resolver);
    328 
    329   // Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer
    330   // compile.
    331   decltype(TransformLayer)::ObjSetHandleT ObjSet;
    332   TransformLayer.emitAndFinalize(ObjSet);
    333   TransformLayer.findSymbolIn(ObjSet, Name, false);
    334   TransformLayer.findSymbol(Name, true);
    335   TransformLayer.mapSectionAddress(ObjSet, nullptr, 0);
    336   TransformLayer.removeObjectSet(ObjSet);
    337 }
    338 }
    339