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