diff options
Diffstat (limited to 'src/gallium/auxiliary/gallivm/lp_bld_init.c')
-rw-r--r-- | src/gallium/auxiliary/gallivm/lp_bld_init.c | 359 |
1 files changed, 317 insertions, 42 deletions
diff --git a/src/gallium/auxiliary/gallivm/lp_bld_init.c b/src/gallium/auxiliary/gallivm/lp_bld_init.c index 0b9a6f745f..efe8d38b8f 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_init.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_init.c @@ -29,6 +29,7 @@ #include "pipe/p_compiler.h" #include "util/u_cpu_detect.h" #include "util/u_debug.h" +#include "util/u_memory.h" #include "lp_bld_debug.h" #include "lp_bld_init.h" @@ -45,6 +46,7 @@ static const struct debug_named_value lp_bld_debug_flags[] = { { "nopt", GALLIVM_DEBUG_NO_OPT, NULL }, { "perf", GALLIVM_DEBUG_PERF, NULL }, { "no_brilinear", GALLIVM_DEBUG_NO_BRILINEAR, NULL }, + { "gc", GALLIVM_DEBUG_GC, NULL }, DEBUG_NAMED_VALUE_END }; @@ -52,11 +54,7 @@ DEBUG_GET_ONCE_FLAGS_OPTION(gallivm_debug, "GALLIVM_DEBUG", lp_bld_debug_flags, #endif -LLVMModuleRef lp_build_module = NULL; -LLVMExecutionEngineRef lp_build_engine = NULL; -LLVMModuleProviderRef lp_build_provider = NULL; -LLVMTargetDataRef lp_build_target = NULL; -LLVMPassManagerRef lp_build_pass = NULL; +static boolean gallivm_initialized = FALSE; /* @@ -82,6 +80,19 @@ enum LLVM_CodeGenOpt_Level { }; +/** + * LLVM 2.6 permits only one ExecutionEngine to be created. This is it. + */ +static LLVMExecutionEngineRef GlobalEngine = NULL; + +/** + * Same gallivm state shared by all contexts. + */ +static struct gallivm_state *GlobalGallivm = NULL; + + + + extern void lp_register_oprofile_jit_event_listener(LLVMExecutionEngineRef EE); @@ -89,26 +100,148 @@ extern void lp_set_target_options(void); -void -lp_build_init(void) + +/** + * Create the LLVM (optimization) pass manager and install + * relevant optimization passes. + * \return TRUE for success, FALSE for failure + */ +static boolean +create_pass_manager(struct gallivm_state *gallivm) { -#ifdef DEBUG - gallivm_debug = debug_get_option_gallivm_debug(); + assert(!gallivm->passmgr); + + gallivm->passmgr = LLVMCreateFunctionPassManager(gallivm->provider); + if (!gallivm->passmgr) + return FALSE; + + LLVMAddTargetData(gallivm->target, gallivm->passmgr); + + if ((gallivm_debug & GALLIVM_DEBUG_NO_OPT) == 0) { + /* These are the passes currently listed in llvm-c/Transforms/Scalar.h, + * but there are more on SVN. + * TODO: Add more passes. + */ + LLVMAddCFGSimplificationPass(gallivm->passmgr); + + if (HAVE_LLVM >= 0x207 && sizeof(void*) == 4) { + /* For LLVM >= 2.7 and 32-bit build, use this order of passes to + * avoid generating bad code. + * Test with piglit glsl-vs-sqrt-zero test. + */ + LLVMAddConstantPropagationPass(gallivm->passmgr); + LLVMAddPromoteMemoryToRegisterPass(gallivm->passmgr); + } + else { + LLVMAddPromoteMemoryToRegisterPass(gallivm->passmgr); + LLVMAddConstantPropagationPass(gallivm->passmgr); + } + + if (util_cpu_caps.has_sse4_1) { + /* FIXME: There is a bug in this pass, whereby the combination + * of fptosi and sitofp (necessary for trunc/floor/ceil/round + * implementation) somehow becomes invalid code. + */ + LLVMAddInstructionCombiningPass(gallivm->passmgr); + } + LLVMAddGVNPass(gallivm->passmgr); + } + else { + /* We need at least this pass to prevent the backends to fail in + * unexpected ways. + */ + LLVMAddPromoteMemoryToRegisterPass(gallivm->passmgr); + } + + return TRUE; +} + + +/** + * Free gallivm object's LLVM allocations, but not the gallivm object itself. + */ +static void +free_gallivm_state(struct gallivm_state *gallivm) +{ +#if HAVE_LLVM >= 0x207 /* XXX or 0x208? */ + /* This leads to crashes w/ some versions of LLVM */ + LLVMModuleRef mod; + char *error; + + if (gallivm->engine && gallivm->provider) + LLVMRemoveModuleProvider(gallivm->engine, gallivm->provider, + &mod, &error); #endif - lp_set_target_options(); +#if 0 + /* XXX this seems to crash with all versions of LLVM */ + if (gallivm->provider) + LLVMDisposeModuleProvider(gallivm->provider); +#endif - LLVMInitializeNativeTarget(); + if (gallivm->passmgr) + LLVMDisposePassManager(gallivm->passmgr); - LLVMLinkInJIT(); +#if HAVE_LLVM >= 0x207 + if (gallivm->module) + LLVMDisposeModule(gallivm->module); +#endif + +#if 0 + /* Don't free the exec engine, it's a global/singleton */ + if (gallivm->engine) + LLVMDisposeExecutionEngine(gallivm->engine); +#endif + +#if 0 + /* Don't free the TargetData, it's owned by the exec engine */ + LLVMDisposeTargetData(gallivm->target); +#endif + + if (gallivm->context) + LLVMContextDispose(gallivm->context); - if (!lp_build_module) - lp_build_module = LLVMModuleCreateWithName("gallivm"); + if (gallivm->builder) + LLVMDisposeBuilder(gallivm->builder); + + gallivm->engine = NULL; + gallivm->target = NULL; + gallivm->module = NULL; + gallivm->provider = NULL; + gallivm->passmgr = NULL; + gallivm->context = NULL; + gallivm->builder = NULL; +} - if (!lp_build_provider) - lp_build_provider = LLVMCreateModuleProviderForExistingModule(lp_build_module); - if (!lp_build_engine) { +/** + * Allocate gallivm LLVM objects. + * \return TRUE for success, FALSE for failure + */ +static boolean +init_gallivm_state(struct gallivm_state *gallivm) +{ + assert(gallivm_initialized); + assert(!gallivm->context); + assert(!gallivm->module); + assert(!gallivm->provider); + + gallivm->context = LLVMContextCreate(); + if (!gallivm->context) + goto fail; + + gallivm->module = LLVMModuleCreateWithNameInContext("gallivm", + gallivm->context); + if (!gallivm->module) + goto fail; + + gallivm->provider = + LLVMCreateModuleProviderForExistingModule(gallivm->module); + if (!gallivm->provider) + goto fail; + + if (!GlobalEngine) { + /* We can only create one LLVMExecutionEngine (w/ LLVM 2.6 anyway) */ enum LLVM_CodeGenOpt_Level optlevel; char *error = NULL; @@ -119,43 +252,152 @@ lp_build_init(void) optlevel = Default; } - if (LLVMCreateJITCompiler(&lp_build_engine, lp_build_provider, - (unsigned)optlevel, &error)) { + if (LLVMCreateJITCompiler(&GlobalEngine, gallivm->provider, + (unsigned) optlevel, &error)) { _debug_printf("%s\n", error); LLVMDisposeMessage(error); - assert(0); + goto fail; } #if defined(DEBUG) || defined(PROFILE) - lp_register_oprofile_jit_event_listener(lp_build_engine); + lp_register_oprofile_jit_event_listener(GlobalEngine); #endif } - if (!lp_build_target) - lp_build_target = LLVMGetExecutionEngineTargetData(lp_build_engine); - - if (!lp_build_pass) { - lp_build_pass = LLVMCreateFunctionPassManager(lp_build_provider); - LLVMAddTargetData(lp_build_target, lp_build_pass); - - if ((gallivm_debug & GALLIVM_DEBUG_NO_OPT) == 0) { - /* These are the passes currently listed in llvm-c/Transforms/Scalar.h, - * but there are more on SVN. */ - /* TODO: Add more passes */ - LLVMAddCFGSimplificationPass(lp_build_pass); - LLVMAddPromoteMemoryToRegisterPass(lp_build_pass); - LLVMAddConstantPropagationPass(lp_build_pass); - LLVMAddInstructionCombiningPass(lp_build_pass); - LLVMAddGVNPass(lp_build_pass); - } else { - /* We need at least this pass to prevent the backends to fail in - * unexpected ways. - */ - LLVMAddPromoteMemoryToRegisterPass(lp_build_pass); + gallivm->engine = GlobalEngine; + + LLVMAddModuleProvider(gallivm->engine, gallivm->provider);//new + + gallivm->target = LLVMGetExecutionEngineTargetData(gallivm->engine); + if (!gallivm->target) + goto fail; + + if (!create_pass_manager(gallivm)) + goto fail; + + gallivm->builder = LLVMCreateBuilderInContext(gallivm->context); + if (!gallivm->builder) + goto fail; + + return TRUE; + +fail: + free_gallivm_state(gallivm); + return FALSE; +} + + +struct callback +{ + garbage_collect_callback_func func; + void *cb_data; +}; + + +#define MAX_CALLBACKS 32 +static struct callback Callbacks[MAX_CALLBACKS]; +static unsigned NumCallbacks = 0; + + +/** + * Register a function with gallivm which will be called when we + * do garbage collection. + */ +void +gallivm_register_garbage_collector_callback(garbage_collect_callback_func func, + void *cb_data) +{ + unsigned i; + + for (i = 0; i < NumCallbacks; i++) { + if (Callbacks[i].func == func && Callbacks[i].cb_data == cb_data) { + /* already in list: no-op */ + return; + } + } + + assert(NumCallbacks < MAX_CALLBACKS); + if (NumCallbacks < MAX_CALLBACKS) { + Callbacks[NumCallbacks].func = func; + Callbacks[NumCallbacks].cb_data = cb_data; + NumCallbacks++; + } +} + + +/** + * Remove a callback. + */ +void +gallivm_remove_garbage_collector_callback(garbage_collect_callback_func func, + void *cb_data) +{ + unsigned i; + + for (i = 0; i < NumCallbacks; i++) { + if (Callbacks[i].func == func && Callbacks[i].cb_data == cb_data) { + /* found, now remove it */ + NumCallbacks--; + for ( ; i < NumCallbacks; i++) { + Callbacks[i] = Callbacks[i + 1]; + } + return; } } +} + + +/** + * Call the callback functions (which are typically in the + * draw module and llvmpipe driver. + */ +static void +call_garbage_collector_callbacks(void) +{ + unsigned i; + + for (i = 0; i < NumCallbacks; i++) { + Callbacks[i].func(Callbacks[i].cb_data); + } +} + + + +/** + * Other gallium components using gallivm should call this periodically + * to let us do garbage collection (or at least try to free memory + * accumulated by the LLVM libraries). + */ +void +gallivm_garbage_collect(struct gallivm_state *gallivm) +{ + if (gallivm->context) { + if (gallivm_debug & GALLIVM_DEBUG_GC) + debug_printf("***** Doing LLVM garbage collection\n"); + + call_garbage_collector_callbacks(); + free_gallivm_state(gallivm); + init_gallivm_state(gallivm); + } +} + + +void +lp_build_init(void) +{ +#ifdef DEBUG + gallivm_debug = debug_get_option_gallivm_debug(); +#endif + + lp_set_target_options(); + + LLVMInitializeNativeTarget(); + + LLVMLinkInJIT(); util_cpu_detect(); + + gallivm_initialized = TRUE; #if 0 /* For simulating less capable machines */ @@ -166,6 +408,39 @@ lp_build_init(void) } + +/** + * Create a new gallivm_state object. + * Note that we return a singleton. + */ +struct gallivm_state * +gallivm_create(void) +{ + if (!GlobalGallivm) { + GlobalGallivm = CALLOC_STRUCT(gallivm_state); + if (GlobalGallivm) { + if (!init_gallivm_state(GlobalGallivm)) { + FREE(GlobalGallivm); + GlobalGallivm = NULL; + } + } + } + return GlobalGallivm; +} + + +/** + * Destroy a gallivm_state object. + */ +void +gallivm_destroy(struct gallivm_state *gallivm) +{ + /* No-op: don't destroy the singleton */ + (void) gallivm; +} + + + /* * Hack to allow the linking of release LLVM static libraries on a debug build. * |