[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