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