[llvm] r180146 - Adding object caching support to MCJIT
Rafael EspĂndola
rafael.espindola at gmail.com
Wed Apr 24 20:52:39 PDT 2013
Looks like this broke
http://lab.llvm.org:8011/builders/llvm-x86_64-linux-vg_leak/builds/649
so I have reverted it.
On 23 April 2013 17:26, Andrew Kaylor <andrew.kaylor at intel.com> wrote:
> Author: akaylor
> Date: Tue Apr 23 16:26:38 2013
> New Revision: 180146
>
> URL: http://llvm.org/viewvc/llvm-project?rev=180146&view=rev
> Log:
> Adding object caching support to MCJIT
>
> Added:
> llvm/trunk/include/llvm/ExecutionEngine/ObjectCache.h
> llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp
> Modified:
> llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h
> llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.cpp
> llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.h
>
> Modified: llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h?rev=180146&r1=180145&r2=180146&view=diff
> ==============================================================================
> --- llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h (original)
> +++ llvm/trunk/include/llvm/ExecutionEngine/ExecutionEngine.h Tue Apr 23 16:26:38 2013
> @@ -42,6 +42,7 @@ class JITMemoryManager;
> class MachineCodeInfo;
> class Module;
> class MutexGuard;
> +class ObjectCache;
> class DataLayout;
> class Triple;
> class Type;
> @@ -371,6 +372,12 @@ public:
> virtual void RegisterJITEventListener(JITEventListener *) {}
> virtual void UnregisterJITEventListener(JITEventListener *) {}
>
> + /// Sets the pre-compiled object cache. The ownership of the ObjectCache is
> + /// not changed. Supported by MCJIT by not JIT.
> + virtual void setObjectCache(ObjectCache *) {
> + llvm_unreachable("No support for an object cache");
> + }
> +
> /// DisableLazyCompilation - When lazy compilation is off (the default), the
> /// JIT will eagerly compile every function reachable from the argument to
> /// getPointerToFunction. If lazy compilation is turned on, the JIT will only
>
> Added: llvm/trunk/include/llvm/ExecutionEngine/ObjectCache.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/ObjectCache.h?rev=180146&view=auto
> ==============================================================================
> --- llvm/trunk/include/llvm/ExecutionEngine/ObjectCache.h (added)
> +++ llvm/trunk/include/llvm/ExecutionEngine/ObjectCache.h Tue Apr 23 16:26:38 2013
> @@ -0,0 +1,54 @@
> +//===-- ObjectCache.h - Class definition for the ObjectCache -----C++ -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_LIB_EXECUTIONENGINE_OBJECTCACHE_H
> +#define LLVM_LIB_EXECUTIONENGINE_OBJECTCACHE_H
> +
> +#include "llvm/Support/MemoryBuffer.h"
> +
> +namespace llvm {
> +
> +class Module;
> +
> +/// This is the base ObjectCache type which can be provided to an
> +/// ExecutionEngine for the purpose of avoiding compilation for Modules that
> +/// have already been compiled and an object file is available.
> +class ObjectCache {
> +public:
> + ObjectCache() { }
> +
> + virtual ~ObjectCache() { }
> +
> + /// notifyObjectCompiled - Provides a pointer to compiled code for Module M.
> + virtual void notifyObjectCompiled(const Module *M, const MemoryBuffer *Obj) = 0;
> +
> + /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that
> + /// contains the object which corresponds with Module M, or 0 if an object is
> + /// not available. The caller owns the MemoryBuffer returned by this function.
> + MemoryBuffer* getObjectCopy(const Module* M) {
> + const MemoryBuffer* Obj = getObject(M);
> + if (Obj)
> + return MemoryBuffer::getMemBufferCopy(Obj->getBuffer());
> + else
> + return 0;
> + }
> +
> +protected:
> + /// getObject - Returns a pointer to a MemoryBuffer that contains an object
> + /// that corresponds with Module M, or 0 if an object is not available.
> + /// The pointer returned by this function is not suitable for loading because
> + /// the memory is read-only and owned by the ObjectCache. To retrieve an
> + /// owning pointer to a MemoryBuffer (which is suitable for calling
> + /// RuntimeDyld::loadObject() with) use getObjectCopy() instead.
> + virtual const MemoryBuffer* getObject(const Module* M) = 0;
> +};
> +
> +}
> +
> +#endif
>
> Modified: llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.cpp?rev=180146&r1=180145&r2=180146&view=diff
> ==============================================================================
> --- llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.cpp (original)
> +++ llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.cpp Tue Apr 23 16:26:38 2013
> @@ -52,7 +52,7 @@ ExecutionEngine *MCJIT::createJIT(Module
> MCJIT::MCJIT(Module *m, TargetMachine *tm, RTDyldMemoryManager *MM,
> bool AllocateGVsWithCode)
> : ExecutionEngine(m), TM(tm), Ctx(0), MemMgr(MM), Dyld(MM),
> - isCompiled(false), M(m) {
> + IsLoaded(false), M(m), ObjCache(0) {
>
> setDataLayout(TM->getDataLayout());
> }
> @@ -64,7 +64,11 @@ MCJIT::~MCJIT() {
> delete TM;
> }
>
> -void MCJIT::emitObject(Module *m) {
> +void MCJIT::setObjectCache(ObjectCache* NewCache) {
> + ObjCache = NewCache;
> +}
> +
> +ObjectBufferStream* MCJIT::emitObject(Module *m) {
> /// Currently, MCJIT only supports a single module and the module passed to
> /// this function call is expected to be the contained module. The module
> /// is passed as a parameter here to prepare for multiple module support in
> @@ -77,30 +81,63 @@ void MCJIT::emitObject(Module *m) {
> // FIXME: Track compilation state on a per-module basis when multiple modules
> // are supported.
> // Re-compilation is not supported
> - if (isCompiled)
> - return;
> + assert(!IsLoaded);
>
> PassManager PM;
>
> PM.add(new DataLayout(*TM->getDataLayout()));
>
> // The RuntimeDyld will take ownership of this shortly
> - OwningPtr<ObjectBufferStream> Buffer(new ObjectBufferStream());
> + OwningPtr<ObjectBufferStream> CompiledObject(new ObjectBufferStream());
>
> // Turn the machine code intermediate representation into bytes in memory
> // that may be executed.
> - if (TM->addPassesToEmitMC(PM, Ctx, Buffer->getOStream(), false)) {
> + if (TM->addPassesToEmitMC(PM, Ctx, CompiledObject->getOStream(), false)) {
> report_fatal_error("Target does not support MC emission!");
> }
>
> // Initialize passes.
> PM.run(*m);
> // Flush the output buffer to get the generated code into memory
> - Buffer->flush();
> + CompiledObject->flush();
> +
> + // If we have an object cache, tell it about the new object.
> + // Note that we're using the compiled image, not the loaded image (as below).
> + if (ObjCache) {
> + ObjCache->notifyObjectCompiled(m, CompiledObject->getMemBuffer());
> + }
> +
> + return CompiledObject.take();
> +}
> +
> +void MCJIT::loadObject(Module *M) {
> +
> + // Get a thread lock to make sure we aren't trying to load multiple times
> + MutexGuard locked(lock);
> +
> + // FIXME: Track compilation state on a per-module basis when multiple modules
> + // are supported.
> + // Re-compilation is not supported
> + if (IsLoaded)
> + return;
> +
> + OwningPtr<ObjectBuffer> ObjectToLoad;
> + // Try to load the pre-compiled object from cache if possible
> + if (0 != ObjCache) {
> + OwningPtr<MemoryBuffer> PreCompiledObject(ObjCache->getObjectCopy(M));
> + if (0 != PreCompiledObject.get())
> + ObjectToLoad.reset(new ObjectBuffer(PreCompiledObject.take()));
> + }
> +
> + // If the cache did not contain a suitable object, compile the object
> + if (!ObjectToLoad) {
> + ObjectToLoad.reset(emitObject(M));
> + assert(ObjectToLoad.get() && "Compilation did not produce an object.");
> + }
>
> // Load the object into the dynamic linker.
> // handing off ownership of the buffer
> - LoadedObject.reset(Dyld.loadObject(Buffer.take()));
> + LoadedObject.reset(Dyld.loadObject(ObjectToLoad.take()));
> if (!LoadedObject)
> report_fatal_error(Dyld.getErrorString());
>
> @@ -113,7 +150,7 @@ void MCJIT::emitObject(Module *m) {
> NotifyObjectEmitted(*LoadedObject);
>
> // FIXME: Add support for per-module compilation state
> - isCompiled = true;
> + IsLoaded = true;
> }
>
> // FIXME: Add a parameter to identify which object is being finalized when
> @@ -122,10 +159,10 @@ void MCJIT::emitObject(Module *m) {
> // protection in the interface.
> void MCJIT::finalizeObject() {
> // If the module hasn't been compiled, just do that.
> - if (!isCompiled) {
> - // If the call to Dyld.resolveRelocations() is removed from emitObject()
> + if (!IsLoaded) {
> + // If the call to Dyld.resolveRelocations() is removed from loadObject()
> // we'll need to do that here.
> - emitObject(M);
> + loadObject(M);
>
> // Set page permissions.
> MemMgr->applyPermissions();
> @@ -151,8 +188,8 @@ void *MCJIT::getPointerToFunction(Functi
> // dies.
>
> // FIXME: Add support for per-module compilation state
> - if (!isCompiled)
> - emitObject(M);
> + if (!IsLoaded)
> + loadObject(M);
>
> if (F->isDeclaration() || F->hasAvailableExternallyLinkage()) {
> bool AbortOnFailure = !F->hasExternalWeakLinkage();
> @@ -284,8 +321,8 @@ GenericValue MCJIT::runFunction(Function
> void *MCJIT::getPointerToNamedFunction(const std::string &Name,
> bool AbortOnFailure) {
> // FIXME: Add support for per-module compilation state
> - if (!isCompiled)
> - emitObject(M);
> + if (!IsLoaded)
> + loadObject(M);
>
> if (!isSymbolSearchingDisabled() && MemMgr) {
> void *ptr = MemMgr->getPointerToNamedFunction(Name, false);
>
> Modified: llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.h?rev=180146&r1=180145&r2=180146&view=diff
> ==============================================================================
> --- llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.h (original)
> +++ llvm/trunk/lib/ExecutionEngine/MCJIT/MCJIT.h Tue Apr 23 16:26:38 2013
> @@ -12,6 +12,7 @@
>
> #include "llvm/ADT/SmallVector.h"
> #include "llvm/ExecutionEngine/ExecutionEngine.h"
> +#include "llvm/ExecutionEngine/ObjectCache.h"
> #include "llvm/ExecutionEngine/RuntimeDyld.h"
> #include "llvm/PassManager.h"
>
> @@ -34,16 +35,23 @@ class MCJIT : public ExecutionEngine {
> SmallVector<JITEventListener*, 2> EventListeners;
>
> // FIXME: Add support for multiple modules
> - bool isCompiled;
> + bool IsLoaded;
> Module *M;
> OwningPtr<ObjectImage> LoadedObject;
>
> + // An optional ObjectCache to be notified of compiled objects and used to
> + // perform lookup of pre-compiled code to avoid re-compilation.
> + ObjectCache *ObjCache;
> +
> public:
> ~MCJIT();
>
> /// @name ExecutionEngine interface implementation
> /// @{
>
> + /// Sets the object manager that MCJIT should use to avoid compilation.
> + virtual void setObjectCache(ObjectCache *manager);
> +
> virtual void finalizeObject();
>
> virtual void *getPointerToBasicBlock(BasicBlock *BB);
> @@ -102,7 +110,9 @@ protected:
> /// this function call is expected to be the contained module. The module
> /// is passed as a parameter here to prepare for multiple module support in
> /// the future.
> - void emitObject(Module *M);
> + ObjectBufferStream* emitObject(Module *M);
> +
> + void loadObject(Module *M);
>
> void NotifyObjectEmitted(const ObjectImage& Obj);
> void NotifyFreeingObject(const ObjectImage& Obj);
>
> Added: llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp?rev=180146&view=auto
> ==============================================================================
> --- llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp (added)
> +++ llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp Tue Apr 23 16:26:38 2013
> @@ -0,0 +1,240 @@
> +//===- MCJITObjectCacheTest.cpp - Unit tests for MCJIT object caching -----===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/ADT/OwningPtr.h"
> +#include "llvm/ADT/SmallVector.h"
> +#include "llvm/ADT/StringMap.h"
> +#include "llvm/ADT/StringSet.h"
> +#include "llvm/ExecutionEngine/JIT.h"
> +#include "llvm/ExecutionEngine/MCJIT.h"
> +#include "llvm/ExecutionEngine/ObjectCache.h"
> +#include "llvm/ExecutionEngine/SectionMemoryManager.h"
> +#include "MCJITTestBase.h"
> +#include "gtest/gtest.h"
> +
> +using namespace llvm;
> +
> +namespace {
> +
> +class TestObjectCache : public ObjectCache {
> +public:
> + TestObjectCache() : DuplicateInserted(false) { }
> +
> + virtual ~TestObjectCache() {
> + // Free any buffers we've allocated.
> + SmallVector<MemoryBuffer *, 2>::iterator it, end;
> + end = AllocatedBuffers.end();
> + for (it = AllocatedBuffers.begin(); it != end; ++it) {
> + delete *it;
> + }
> + AllocatedBuffers.clear();
> + }
> +
> + virtual void notifyObjectCompiled(const Module *M, const MemoryBuffer *Obj) {
> + // If we've seen this module before, note that.
> + const std::string ModuleID = M->getModuleIdentifier();
> + if (ObjMap.find(ModuleID) != ObjMap.end())
> + DuplicateInserted = true;
> + // Store a copy of the buffer in our map.
> + ObjMap[ModuleID] = copyBuffer(Obj);
> + }
> +
> + // Test-harness-specific functions
> + bool wereDuplicatesInserted() { return DuplicateInserted; }
> +
> + bool wasModuleLookedUp(const Module *M) {
> + return ModulesLookedUp.find(M->getModuleIdentifier())
> + != ModulesLookedUp.end();
> + }
> +
> + const MemoryBuffer* getObjectInternal(const Module* M) {
> + // Look for the module in our map.
> + const std::string ModuleID = M->getModuleIdentifier();
> + StringMap<const MemoryBuffer *>::iterator it = ObjMap.find(ModuleID);
> + if (it == ObjMap.end())
> + return 0;
> + return it->second;
> + }
> +
> +protected:
> + virtual const MemoryBuffer* getObject(const Module* M) {
> + const MemoryBuffer* BufferFound = getObjectInternal(M);
> + ModulesLookedUp.insert(M->getModuleIdentifier());
> + return BufferFound;
> + }
> +
> +private:
> + MemoryBuffer *copyBuffer(const MemoryBuffer *Buf) {
> + // Create a local copy of the buffer.
> + MemoryBuffer *NewBuffer = MemoryBuffer::getMemBufferCopy(Buf->getBuffer());
> + AllocatedBuffers.push_back(NewBuffer);
> + return NewBuffer;
> + }
> +
> + StringMap<const MemoryBuffer *> ObjMap;
> + StringSet<> ModulesLookedUp;
> + SmallVector<MemoryBuffer *, 2> AllocatedBuffers;
> + bool DuplicateInserted;
> +};
> +
> +class MCJITObjectCacheTest : public testing::Test, public MCJITTestBase {
> +protected:
> +
> + enum {
> + OriginalRC = 6,
> + ReplacementRC = 7
> + };
> +
> + virtual void SetUp() {
> + M.reset(createEmptyModule("<main>"));
> + Main = insertMainFunction(M.get(), OriginalRC);
> + }
> +
> + void compileAndRun(int ExpectedRC = OriginalRC) {
> + // This function shouldn't be called until after SetUp.
> + ASSERT_TRUE(0 != TheJIT);
> + ASSERT_TRUE(0 != Main);
> +
> + TheJIT->finalizeObject();
> + void *vPtr = TheJIT->getPointerToFunction(Main);
> +
> + static_cast<SectionMemoryManager*>(MM)->invalidateInstructionCache();
> +
> + EXPECT_TRUE(0 != vPtr)
> + << "Unable to get pointer to main() from JIT";
> +
> + int (*FuncPtr)(void) = (int(*)(void))(intptr_t)vPtr;
> + int returnCode = FuncPtr();
> + EXPECT_EQ(returnCode, ExpectedRC);
> + }
> +
> + Function *Main;
> +};
> +
> +TEST_F(MCJITObjectCacheTest, SetNullObjectCache) {
> + SKIP_UNSUPPORTED_PLATFORM;
> +
> + createJIT(M.take());
> +
> + TheJIT->setObjectCache(NULL);
> +
> + compileAndRun();
> +}
> +
> +
> +TEST_F(MCJITObjectCacheTest, VerifyBasicObjectCaching) {
> + SKIP_UNSUPPORTED_PLATFORM;
> +
> + OwningPtr<TestObjectCache> Cache(new TestObjectCache);
> +
> + // Save a copy of the module pointer before handing it off to MCJIT.
> + const Module * SavedModulePointer = M.get();
> +
> + createJIT(M.take());
> +
> + TheJIT->setObjectCache(Cache.get());
> +
> + // Verify that our object cache does not contain the module yet.
> + const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SavedModulePointer);
> + EXPECT_EQ(0, ObjBuffer);
> +
> + compileAndRun();
> +
> + // Verify that MCJIT tried to look-up this module in the cache.
> + EXPECT_TRUE(Cache->wasModuleLookedUp(SavedModulePointer));
> +
> + // Verify that our object cache now contains the module.
> + ObjBuffer = Cache->getObjectInternal(SavedModulePointer);
> + EXPECT_TRUE(0 != ObjBuffer);
> +
> + // Verify that the cache was only notified once.
> + EXPECT_FALSE(Cache->wereDuplicatesInserted());
> +}
> +
> +TEST_F(MCJITObjectCacheTest, VerifyLoadFromCache) {
> + SKIP_UNSUPPORTED_PLATFORM;
> +
> + OwningPtr<TestObjectCache> Cache(new TestObjectCache);
> +
> + // Compile this module with an MCJIT engine
> + createJIT(M.take());
> + TheJIT->setObjectCache(Cache.get());
> + TheJIT->finalizeObject();
> +
> + // Destroy the MCJIT engine we just used
> + TheJIT.reset();
> +
> + // Create a new memory manager.
> + MM = new SectionMemoryManager;
> +
> + // Create a new module and save it. Use a different return code so we can
> + // tell if MCJIT compiled this module or used the cache.
> + M.reset(createEmptyModule("<main>"));
> + Main = insertMainFunction(M.get(), ReplacementRC);
> + const Module * SecondModulePointer = M.get();
> +
> + // Create a new MCJIT instance to load this module then execute it.
> + createJIT(M.take());
> + TheJIT->setObjectCache(Cache.get());
> + compileAndRun();
> +
> + // Verify that MCJIT tried to look-up this module in the cache.
> + EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer));
> +
> + // Verify that MCJIT didn't try to cache this again.
> + EXPECT_FALSE(Cache->wereDuplicatesInserted());
> +}
> +
> +TEST_F(MCJITObjectCacheTest, VerifyNonLoadFromCache) {
> + SKIP_UNSUPPORTED_PLATFORM;
> +
> + OwningPtr<TestObjectCache> Cache(new TestObjectCache);
> +
> + // Compile this module with an MCJIT engine
> + createJIT(M.take());
> + TheJIT->setObjectCache(Cache.get());
> + TheJIT->finalizeObject();
> +
> + // Destroy the MCJIT engine we just used
> + TheJIT.reset();
> +
> + // Create a new memory manager.
> + MM = new SectionMemoryManager;
> +
> + // Create a new module and save it. Use a different return code so we can
> + // tell if MCJIT compiled this module or used the cache. Note that we use
> + // a new module name here so the module shouldn't be found in the cache.
> + M.reset(createEmptyModule("<not-main>"));
> + Main = insertMainFunction(M.get(), ReplacementRC);
> + const Module * SecondModulePointer = M.get();
> +
> + // Create a new MCJIT instance to load this module then execute it.
> + createJIT(M.take());
> + TheJIT->setObjectCache(Cache.get());
> +
> + // Verify that our object cache does not contain the module yet.
> + const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SecondModulePointer);
> + EXPECT_EQ(0, ObjBuffer);
> +
> + // Run the function and look for the replacement return code.
> + compileAndRun(ReplacementRC);
> +
> + // Verify that MCJIT tried to look-up this module in the cache.
> + EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer));
> +
> + // Verify that our object cache now contains the module.
> + ObjBuffer = Cache->getObjectInternal(SecondModulePointer);
> + EXPECT_TRUE(0 != ObjBuffer);
> +
> + // Verify that MCJIT didn't try to cache this again.
> + EXPECT_FALSE(Cache->wereDuplicatesInserted());
> +}
> +
> +} // Namespace
> +
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
More information about the llvm-commits
mailing list