[Mesa-dev] [PATCH 07/12] gallivm: One code memory pool with deferred free.

jfonseca at vmware.com jfonseca at vmware.com
Tue May 13 05:01:13 PDT 2014


From: Frank Henigman <fjhenigman at google.com>

Provide a JITMemoryManager derivative which puts all generated code into
one memory pool instead of creating a new one each time code is generated.
This saves significant memory per shader as the pool size is 512K and
a small shader occupies just several K.

This memory manager also defers freeing generated code until you tell
it to do so, making it possible to destroy the LLVM engine while keeping
the code, thus enabling future memory savings.

v2: Fix compilation errors with LLVM 3.4 (Jose)

Signed-off-by: José Fonseca <jfonseca at vmware.com>
---
 src/gallium/auxiliary/gallivm/lp_bld_init.c   |   4 +
 src/gallium/auxiliary/gallivm/lp_bld_init.h   |   1 +
 src/gallium/auxiliary/gallivm/lp_bld_misc.cpp | 273 +++++++++++++++++++++++++-
 src/gallium/auxiliary/gallivm/lp_bld_misc.h   |   6 +
 4 files changed, 283 insertions(+), 1 deletion(-)

diff --git a/src/gallium/auxiliary/gallivm/lp_bld_init.c b/src/gallium/auxiliary/gallivm/lp_bld_init.c
index a3549c1..982d1db 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_init.c
+++ b/src/gallium/auxiliary/gallivm/lp_bld_init.c
@@ -212,12 +212,15 @@ free_gallivm_state(struct gallivm_state *gallivm)
    if (!USE_GLOBAL_CONTEXT && gallivm->context)
       LLVMContextDispose(gallivm->context);
 
+   lp_free_generated_code(gallivm->code);
+
    gallivm->engine = NULL;
    gallivm->target = NULL;
    gallivm->module = NULL;
    gallivm->passmgr = NULL;
    gallivm->context = NULL;
    gallivm->builder = NULL;
+   gallivm->code = NULL;
 }
 
 
@@ -237,6 +240,7 @@ init_gallivm_engine(struct gallivm_state *gallivm)
       }
 
       ret = lp_build_create_jit_compiler_for_module(&gallivm->engine,
+                                                    &gallivm->code,
                                                     gallivm->module,
                                                     (unsigned) optlevel,
                                                     USE_MCJIT,
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_init.h b/src/gallium/auxiliary/gallivm/lp_bld_init.h
index 68f4006..e405b8a 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_init.h
+++ b/src/gallium/auxiliary/gallivm/lp_bld_init.h
@@ -44,6 +44,7 @@ struct gallivm_state
    LLVMPassManagerRef passmgr;
    LLVMContextRef context;
    LLVMBuilderRef builder;
+   struct lp_generated_code *code;
    unsigned compiled;
 };
 
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp b/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp
index fe45940..8825e54 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp
+++ b/src/gallium/auxiliary/gallivm/lp_bld_misc.cpp
@@ -151,6 +151,261 @@ lp_set_store_alignment(LLVMValueRef Inst,
 }
 
 
+/*
+ * Delegating is tedious but the default manager class is hidden in an
+ * anonymous namespace in LLVM, so we cannot just derive from it to change
+ * its behavior.
+ */
+class DelegatingJITMemoryManager : public llvm::JITMemoryManager {
+
+   protected:
+      virtual llvm::JITMemoryManager *mgr() const = 0;
+
+   public:
+      /*
+       * From JITMemoryManager
+       */
+      virtual void setMemoryWritable() {
+         mgr()->setMemoryWritable();
+      }
+      virtual void setMemoryExecutable() {
+         mgr()->setMemoryExecutable();
+      }
+      virtual void setPoisonMemory(bool poison) {
+         mgr()->setPoisonMemory(poison);
+      }
+      virtual void AllocateGOT() {
+         mgr()->AllocateGOT();
+         /*
+          * isManagingGOT() is not virtual in base class so we can't delegate.
+          * Instead we mirror the value of HasGOT in our instance.
+          */
+         HasGOT = mgr()->isManagingGOT();
+      }
+      virtual uint8_t *getGOTBase() const {
+         return mgr()->getGOTBase();
+      }
+      virtual uint8_t *startFunctionBody(const llvm::Function *F,
+                                         uintptr_t &ActualSize) {
+         return mgr()->startFunctionBody(F, ActualSize);
+      }
+      virtual uint8_t *allocateStub(const llvm::GlobalValue *F,
+                                    unsigned StubSize,
+                                    unsigned Alignment) {
+         return mgr()->allocateStub(F, StubSize, Alignment);
+      }
+      virtual void endFunctionBody(const llvm::Function *F,
+                                   uint8_t *FunctionStart,
+                                   uint8_t *FunctionEnd) {
+         mgr()->endFunctionBody(F, FunctionStart, FunctionEnd);
+      }
+      virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) {
+         return mgr()->allocateSpace(Size, Alignment);
+      }
+      virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) {
+         return mgr()->allocateGlobal(Size, Alignment);
+      }
+      virtual void deallocateFunctionBody(void *Body) {
+         mgr()->deallocateFunctionBody(Body);
+      }
+#if HAVE_LLVM < 0x0304
+      virtual uint8_t *startExceptionTable(const llvm::Function *F,
+                                           uintptr_t &ActualSize) {
+         return mgr()->startExceptionTable(F, ActualSize);
+      }
+      virtual void endExceptionTable(const llvm::Function *F,
+                                     uint8_t *TableStart,
+                                     uint8_t *TableEnd,
+                                     uint8_t *FrameRegister) {
+         mgr()->endExceptionTable(F, TableStart, TableEnd,
+                                  FrameRegister);
+      }
+      virtual void deallocateExceptionTable(void *ET) {
+         mgr()->deallocateExceptionTable(ET);
+      }
+#endif
+      virtual bool CheckInvariants(std::string &s) {
+         return mgr()->CheckInvariants(s);
+      }
+      virtual size_t GetDefaultCodeSlabSize() {
+         return mgr()->GetDefaultCodeSlabSize();
+      }
+      virtual size_t GetDefaultDataSlabSize() {
+         return mgr()->GetDefaultDataSlabSize();
+      }
+      virtual size_t GetDefaultStubSlabSize() {
+         return mgr()->GetDefaultStubSlabSize();
+      }
+      virtual unsigned GetNumCodeSlabs() {
+         return mgr()->GetNumCodeSlabs();
+      }
+      virtual unsigned GetNumDataSlabs() {
+         return mgr()->GetNumDataSlabs();
+      }
+      virtual unsigned GetNumStubSlabs() {
+         return mgr()->GetNumStubSlabs();
+      }
+
+      /*
+       * From RTDyldMemoryManager
+       */
+#if HAVE_LLVM >= 0x0304
+      virtual uint8_t *allocateCodeSection(uintptr_t Size,
+                                           unsigned Alignment,
+                                           unsigned SectionID,
+                                           llvm::StringRef SectionName) {
+         return mgr()->allocateCodeSection(Size, Alignment, SectionID,
+                                           SectionName);
+      }
+#else
+      virtual uint8_t *allocateCodeSection(uintptr_t Size,
+                                           unsigned Alignment,
+                                           unsigned SectionID) {
+         return mgr()->allocateCodeSection(Size, Alignment, SectionID);
+      }
+#endif
+#if HAVE_LLVM >= 0x0303
+      virtual uint8_t *allocateDataSection(uintptr_t Size,
+                                           unsigned Alignment,
+                                           unsigned SectionID,
+#if HAVE_LLVM >= 0x0304
+                                           llvm::StringRef SectionName,
+#endif
+                                           bool IsReadOnly) {
+         return mgr()->allocateDataSection(Size, Alignment, SectionID,
+#if HAVE_LLVM >= 0x0304
+                                           SectionName,
+#endif
+                                           IsReadOnly);
+      }
+#if HAVE_LLVM >= 0x0304
+      virtual void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, size_t Size) {
+         mgr()->registerEHFrames(Addr, LoadAddr, Size);
+      }
+      virtual void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr, size_t Size) {
+         mgr()->deregisterEHFrames(Addr, LoadAddr, Size);
+      }
+#else
+      virtual void registerEHFrames(llvm::StringRef SectionData) {
+         mgr()->registerEHFrames(SectionData);
+      }
+#endif
+#else
+      virtual uint8_t *allocateDataSection(uintptr_t Size,
+                                           unsigned Alignment,
+                                           unsigned SectionID) {
+         return mgr()->allocateDataSection(Size, Alignment, SectionID);
+      }
+#endif
+      virtual void *getPointerToNamedFunction(const std::string &Name,
+                                              bool AbortOnFailure=true) {
+         return mgr()->getPointerToNamedFunction(Name, AbortOnFailure);
+      }
+#if HAVE_LLVM == 0x0303
+      virtual bool applyPermissions(std::string *ErrMsg = 0) {
+         return mgr()->applyPermissions(ErrMsg);
+      }
+#elif HAVE_LLVM > 0x0303
+      virtual bool finalizeMemory(std::string *ErrMsg = 0) {
+         return mgr()->finalizeMemory(ErrMsg);
+      }
+#endif
+};
+
+
+/*
+ * Delegate memory management to one shared manager for more efficient use
+ * of memory than creating a separate pool for each LLVM engine.
+ * Keep generated code until freeGeneratedCode() is called, instead of when
+ * memory manager is destroyed, which happens during engine destruction.
+ * This allows additional memory savings as we don't have to keep the engine
+ * around in order to use the code.
+ * All methods are delegated to the shared manager except destruction and
+ * deallocating code.  For the latter we just remember what needs to be
+ * deallocated later.  The shared manager is deleted once it is empty.
+ */
+class ShaderMemoryManager : public DelegatingJITMemoryManager {
+
+   static llvm::JITMemoryManager *TheMM;
+   static unsigned NumUsers;
+
+   struct GeneratedCode {
+      typedef std::vector<void *> Vec;
+      Vec FunctionBody, ExceptionTable;
+
+      GeneratedCode() {
+         ++NumUsers;
+      }
+
+      ~GeneratedCode() {
+         /*
+          * Deallocate things as previously requested and
+          * free shared manager when no longer used.
+          */
+	 Vec::iterator i;
+
+	 assert(TheMM);
+	 for ( i = FunctionBody.begin(); i != FunctionBody.end(); ++i )
+	    TheMM->deallocateFunctionBody(*i);
+#if HAVE_LLVM < 0x0304
+	 for ( i = ExceptionTable.begin(); i != ExceptionTable.end(); ++i )
+	    TheMM->deallocateExceptionTable(*i);
+#endif
+         --NumUsers;
+         if (NumUsers == 0) {
+            delete TheMM;
+            TheMM = 0;
+         }
+      }
+   };
+
+   GeneratedCode *code;
+
+   llvm::JITMemoryManager *mgr() const {
+      if (!TheMM) {
+         TheMM = CreateDefaultMemManager();
+      }
+      return TheMM;
+   }
+
+   public:
+
+      ShaderMemoryManager() {
+         code = new GeneratedCode;
+      }
+
+      virtual ~ShaderMemoryManager() {
+         /*
+          * 'code' is purposely not deleted.  It is the user's responsibility
+          * to call getGeneratedCode() and freeGeneratedCode().
+          */
+      }
+
+      struct lp_generated_code *getGeneratedCode() {
+         return (struct lp_generated_code *) code;
+      }
+
+      static void freeGeneratedCode(struct lp_generated_code *code) {
+         delete (GeneratedCode *) code;
+      }
+
+#if HAVE_LLVM < 0x0304
+      virtual void deallocateExceptionTable(void *ET) {
+         // remember for later deallocation
+         code->ExceptionTable.push_back(ET);
+      }
+#endif
+
+      virtual void deallocateFunctionBody(void *Body) {
+         // remember for later deallocation
+         code->FunctionBody.push_back(Body);
+      }
+};
+
+llvm::JITMemoryManager *ShaderMemoryManager::TheMM = 0;
+unsigned ShaderMemoryManager::NumUsers = 0;
+
+
 /**
  * Same as LLVMCreateJITCompilerForModule, but:
  * - allows using MCJIT and enabling AVX feature where available.
@@ -164,6 +419,7 @@ lp_set_store_alignment(LLVMValueRef Inst,
 extern "C"
 LLVMBool
 lp_build_create_jit_compiler_for_module(LLVMExecutionEngineRef *OutJIT,
+                                        lp_generated_code **OutCode,
                                         LLVMModuleRef M,
                                         unsigned OptLevel,
                                         int useMCJIT,
@@ -220,7 +476,11 @@ lp_build_create_jit_compiler_for_module(LLVMExecutionEngineRef *OutJIT,
       }
       builder.setMAttrs(MAttrs);
    }
-   builder.setJITMemoryManager(JITMemoryManager::CreateDefaultMemManager());
+
+   ShaderMemoryManager *MM = new ShaderMemoryManager();
+   *OutCode = MM->getGeneratedCode();
+
+   builder.setJITMemoryManager(MM);
 
    ExecutionEngine *JIT;
 #if 0
@@ -238,6 +498,17 @@ lp_build_create_jit_compiler_for_module(LLVMExecutionEngineRef *OutJIT,
       *OutJIT = wrap(JIT);
       return 0;
    }
+   lp_free_generated_code(*OutCode);
+   *OutCode = 0;
+   delete MM;
    *OutError = strdup(Error.c_str());
    return 1;
 }
+
+
+extern "C"
+void
+lp_free_generated_code(struct lp_generated_code *code)
+{
+   ShaderMemoryManager::freeGeneratedCode(code);
+}
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_misc.h b/src/gallium/auxiliary/gallivm/lp_bld_misc.h
index 1f735fb..847894b 100644
--- a/src/gallium/auxiliary/gallivm/lp_bld_misc.h
+++ b/src/gallium/auxiliary/gallivm/lp_bld_misc.h
@@ -39,6 +39,8 @@ extern "C" {
 #endif
 
 
+struct lp_generated_code;
+
 
 extern void
 lp_set_target_options(void);
@@ -54,11 +56,15 @@ lp_build_load_volatile(LLVMBuilderRef B, LLVMValueRef PointerVal,
 
 extern int
 lp_build_create_jit_compiler_for_module(LLVMExecutionEngineRef *OutJIT,
+                                        struct lp_generated_code **OutCode,
                                         LLVMModuleRef M,
                                         unsigned OptLevel,
                                         int useMCJIT,
                                         char **OutError);
 
+extern void
+lp_free_generated_code(struct lp_generated_code *code);
+
 
 #ifdef __cplusplus
 }
-- 
1.9.1



More information about the mesa-dev mailing list