Home | History | Annotate | Download | only in MCJIT
      1 //===- MCJITObjectCacheTest.cpp - Unit tests for MCJIT object caching -----===//
      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 "MCJITTestBase.h"
     11 #include "llvm/ADT/SmallVector.h"
     12 #include "llvm/ADT/StringMap.h"
     13 #include "llvm/ADT/StringSet.h"
     14 #include "llvm/ExecutionEngine/JIT.h"
     15 #include "llvm/ExecutionEngine/MCJIT.h"
     16 #include "llvm/ExecutionEngine/ObjectCache.h"
     17 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
     18 #include "gtest/gtest.h"
     19 
     20 using namespace llvm;
     21 
     22 namespace {
     23 
     24 class TestObjectCache : public ObjectCache {
     25 public:
     26   TestObjectCache() : DuplicateInserted(false) { }
     27 
     28   virtual ~TestObjectCache() {
     29     // Free any buffers we've allocated.
     30     SmallVectorImpl<MemoryBuffer *>::iterator it, end;
     31     end = AllocatedBuffers.end();
     32     for (it = AllocatedBuffers.begin(); it != end; ++it) {
     33       delete *it;
     34     }
     35     AllocatedBuffers.clear();
     36   }
     37 
     38   virtual void notifyObjectCompiled(const Module *M, const MemoryBuffer *Obj) {
     39     // If we've seen this module before, note that.
     40     const std::string ModuleID = M->getModuleIdentifier();
     41     if (ObjMap.find(ModuleID) != ObjMap.end())
     42       DuplicateInserted = true;
     43     // Store a copy of the buffer in our map.
     44     ObjMap[ModuleID] = copyBuffer(Obj);
     45   }
     46 
     47   virtual MemoryBuffer* getObject(const Module* M) {
     48     const MemoryBuffer* BufferFound = getObjectInternal(M);
     49     ModulesLookedUp.insert(M->getModuleIdentifier());
     50     if (!BufferFound)
     51       return nullptr;
     52     // Our test cache wants to maintain ownership of its object buffers
     53     // so we make a copy here for the execution engine.
     54     return MemoryBuffer::getMemBufferCopy(BufferFound->getBuffer());
     55   }
     56 
     57   // Test-harness-specific functions
     58   bool wereDuplicatesInserted() { return DuplicateInserted; }
     59 
     60   bool wasModuleLookedUp(const Module *M) {
     61     return ModulesLookedUp.find(M->getModuleIdentifier())
     62                                       != ModulesLookedUp.end();
     63   }
     64 
     65   const MemoryBuffer* getObjectInternal(const Module* M) {
     66     // Look for the module in our map.
     67     const std::string ModuleID = M->getModuleIdentifier();
     68     StringMap<const MemoryBuffer *>::iterator it = ObjMap.find(ModuleID);
     69     if (it == ObjMap.end())
     70       return nullptr;
     71     return it->second;
     72   }
     73 
     74 private:
     75   MemoryBuffer *copyBuffer(const MemoryBuffer *Buf) {
     76     // Create a local copy of the buffer.
     77     MemoryBuffer *NewBuffer = MemoryBuffer::getMemBufferCopy(Buf->getBuffer());
     78     AllocatedBuffers.push_back(NewBuffer);
     79     return NewBuffer;
     80   }
     81 
     82   StringMap<const MemoryBuffer *> ObjMap;
     83   StringSet<>                     ModulesLookedUp;
     84   SmallVector<MemoryBuffer *, 2>  AllocatedBuffers;
     85   bool                            DuplicateInserted;
     86 };
     87 
     88 class MCJITObjectCacheTest : public testing::Test, public MCJITTestBase {
     89 protected:
     90 
     91   enum {
     92     OriginalRC = 6,
     93     ReplacementRC = 7
     94   };
     95 
     96   virtual void SetUp() {
     97     M.reset(createEmptyModule("<main>"));
     98     Main = insertMainFunction(M.get(), OriginalRC);
     99   }
    100 
    101   void compileAndRun(int ExpectedRC = OriginalRC) {
    102     // This function shouldn't be called until after SetUp.
    103     ASSERT_TRUE(bool(TheJIT));
    104     ASSERT_TRUE(nullptr != Main);
    105 
    106     // We may be using a null cache, so ensure compilation is valid.
    107     TheJIT->finalizeObject();
    108     void *vPtr = TheJIT->getPointerToFunction(Main);
    109 
    110     EXPECT_TRUE(nullptr != vPtr)
    111       << "Unable to get pointer to main() from JIT";
    112 
    113     int (*FuncPtr)(void) = (int(*)(void))(intptr_t)vPtr;
    114     int returnCode = FuncPtr();
    115     EXPECT_EQ(returnCode, ExpectedRC);
    116   }
    117 
    118   Function *Main;
    119 };
    120 
    121 TEST_F(MCJITObjectCacheTest, SetNullObjectCache) {
    122   SKIP_UNSUPPORTED_PLATFORM;
    123 
    124   createJIT(M.release());
    125 
    126   TheJIT->setObjectCache(nullptr);
    127 
    128   compileAndRun();
    129 }
    130 
    131 
    132 TEST_F(MCJITObjectCacheTest, VerifyBasicObjectCaching) {
    133   SKIP_UNSUPPORTED_PLATFORM;
    134 
    135   std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);
    136 
    137   // Save a copy of the module pointer before handing it off to MCJIT.
    138   const Module * SavedModulePointer = M.get();
    139 
    140   createJIT(M.release());
    141 
    142   TheJIT->setObjectCache(Cache.get());
    143 
    144   // Verify that our object cache does not contain the module yet.
    145   const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SavedModulePointer);
    146   EXPECT_EQ(nullptr, ObjBuffer);
    147 
    148   compileAndRun();
    149 
    150   // Verify that MCJIT tried to look-up this module in the cache.
    151   EXPECT_TRUE(Cache->wasModuleLookedUp(SavedModulePointer));
    152 
    153   // Verify that our object cache now contains the module.
    154   ObjBuffer = Cache->getObjectInternal(SavedModulePointer);
    155   EXPECT_TRUE(nullptr != ObjBuffer);
    156 
    157   // Verify that the cache was only notified once.
    158   EXPECT_FALSE(Cache->wereDuplicatesInserted());
    159 }
    160 
    161 TEST_F(MCJITObjectCacheTest, VerifyLoadFromCache) {
    162   SKIP_UNSUPPORTED_PLATFORM;
    163 
    164   std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);
    165 
    166   // Compile this module with an MCJIT engine
    167   createJIT(M.release());
    168   TheJIT->setObjectCache(Cache.get());
    169   TheJIT->finalizeObject();
    170 
    171   // Destroy the MCJIT engine we just used
    172   TheJIT.reset();
    173 
    174   // Create a new memory manager.
    175   MM = new SectionMemoryManager;
    176 
    177   // Create a new module and save it. Use a different return code so we can
    178   // tell if MCJIT compiled this module or used the cache.
    179   M.reset(createEmptyModule("<main>"));
    180   Main = insertMainFunction(M.get(), ReplacementRC);
    181   const Module * SecondModulePointer = M.get();
    182 
    183   // Create a new MCJIT instance to load this module then execute it.
    184   createJIT(M.release());
    185   TheJIT->setObjectCache(Cache.get());
    186   compileAndRun();
    187 
    188   // Verify that MCJIT tried to look-up this module in the cache.
    189   EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer));
    190 
    191   // Verify that MCJIT didn't try to cache this again.
    192   EXPECT_FALSE(Cache->wereDuplicatesInserted());
    193 }
    194 
    195 TEST_F(MCJITObjectCacheTest, VerifyNonLoadFromCache) {
    196   SKIP_UNSUPPORTED_PLATFORM;
    197 
    198   std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);
    199 
    200   // Compile this module with an MCJIT engine
    201   createJIT(M.release());
    202   TheJIT->setObjectCache(Cache.get());
    203   TheJIT->finalizeObject();
    204 
    205   // Destroy the MCJIT engine we just used
    206   TheJIT.reset();
    207 
    208   // Create a new memory manager.
    209   MM = new SectionMemoryManager;
    210 
    211   // Create a new module and save it. Use a different return code so we can
    212   // tell if MCJIT compiled this module or used the cache. Note that we use
    213   // a new module name here so the module shouldn't be found in the cache.
    214   M.reset(createEmptyModule("<not-main>"));
    215   Main = insertMainFunction(M.get(), ReplacementRC);
    216   const Module * SecondModulePointer = M.get();
    217 
    218   // Create a new MCJIT instance to load this module then execute it.
    219   createJIT(M.release());
    220   TheJIT->setObjectCache(Cache.get());
    221 
    222   // Verify that our object cache does not contain the module yet.
    223   const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SecondModulePointer);
    224   EXPECT_EQ(nullptr, ObjBuffer);
    225 
    226   // Run the function and look for the replacement return code.
    227   compileAndRun(ReplacementRC);
    228 
    229   // Verify that MCJIT tried to look-up this module in the cache.
    230   EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer));
    231 
    232   // Verify that our object cache now contains the module.
    233   ObjBuffer = Cache->getObjectInternal(SecondModulePointer);
    234   EXPECT_TRUE(nullptr != ObjBuffer);
    235 
    236   // Verify that MCJIT didn't try to cache this again.
    237   EXPECT_FALSE(Cache->wereDuplicatesInserted());
    238 }
    239 
    240 } // Namespace
    241 
    242