----- Original Message ----- > Am 13.05.2014 14:01, schrieb jfons...@vmware.com: > > From: Frank Henigman <fjhenig...@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 <jfons...@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 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 > > } > > > > That also makes sense to me.
Thanks for the reviews. > Very nice work! Yes, thanks to Frank's global JITMemoryManager we for the first time get zeros leaks (on vanilla LLVM 3.4 or LLVM 3.3+crossports) and decent memory footprint. Jose _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev