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