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<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 > } >
That also makes sense to me. Very nice work! Roland _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev