From bb0efdd4d8968a71957b563e7dbf98cefa80a986 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 28 Jun 2024 12:43:29 +0800 Subject: [PATCH] llvmpipe: add shader cache support for ORCJIT implementation Signed-off-by: Icenowy Zheng Reviewed-by: Dave Airlie Part-of: --- src/gallium/auxiliary/draw/draw_llvm.c | 19 ++++- src/gallium/auxiliary/gallivm/lp_bld_init.c | 11 +++ src/gallium/auxiliary/gallivm/lp_bld_init.h | 3 + .../auxiliary/gallivm/lp_bld_init_orc.cpp | 76 ++++++++++++++++++- src/gallium/drivers/llvmpipe/lp_state_cs.c | 4 +- src/gallium/drivers/llvmpipe/lp_state_fs.c | 4 +- .../llvmpipe/lp_state_fs_linear_llvm.c | 4 +- 7 files changed, 111 insertions(+), 10 deletions(-) diff --git a/src/gallium/auxiliary/draw/draw_llvm.c b/src/gallium/auxiliary/draw/draw_llvm.c index 12c246aadc9..46eb9dfb9e8 100644 --- a/src/gallium/auxiliary/draw/draw_llvm.c +++ b/src/gallium/auxiliary/draw/draw_llvm.c @@ -1636,8 +1636,10 @@ draw_llvm_generate(struct draw_llvm *llvm, struct draw_llvm_variant *variant) if (LLVMGetTypeKind(arg_types[i]) == LLVMPointerTypeKind) lp_add_function_attr(variant_func, i + 1, LP_FUNC_ATTR_NOALIAS); - if (gallivm->cache && gallivm->cache->data_size) + if (gallivm->cache && gallivm->cache->data_size) { + gallivm_stub_func(gallivm, variant_func); return; + } context_ptr = LLVMGetParam(variant_func, 0); resources_ptr = LLVMGetParam(variant_func, 1); @@ -2367,8 +2369,11 @@ draw_gs_llvm_generate(struct draw_llvm *llvm, if (LLVMGetTypeKind(arg_types[i]) == LLVMPointerTypeKind) lp_add_function_attr(variant_func, i + 1, LP_FUNC_ATTR_NOALIAS); - if (gallivm->cache && gallivm->cache->data_size) + if (gallivm->cache && gallivm->cache->data_size) { + gallivm_stub_func(gallivm, variant_func); return; + } + context_ptr = LLVMGetParam(variant_func, 0); resources_ptr = LLVMGetParam(variant_func, 1); input_array = LLVMGetParam(variant_func, 2); @@ -2961,8 +2966,11 @@ draw_tcs_llvm_generate(struct draw_llvm *llvm, } } - if (gallivm->cache && gallivm->cache->data_size) + if (gallivm->cache && gallivm->cache->data_size) { + gallivm_stub_func(gallivm, variant_func); return; + } + resources_ptr = LLVMGetParam(variant_func, 0); input_array = LLVMGetParam(variant_func, 1); output_array = LLVMGetParam(variant_func, 2); @@ -3523,8 +3531,11 @@ draw_tes_llvm_generate(struct draw_llvm *llvm, if (LLVMGetTypeKind(arg_types[i]) == LLVMPointerTypeKind) lp_add_function_attr(variant_func, i + 1, LP_FUNC_ATTR_NOALIAS); - if (gallivm->cache && gallivm->cache->data_size) + if (gallivm->cache && gallivm->cache->data_size) { + gallivm_stub_func(gallivm, variant_func); return; + } + resources_ptr = LLVMGetParam(variant_func, 0); input_array = LLVMGetParam(variant_func, 1); io_ptr = LLVMGetParam(variant_func, 2); diff --git a/src/gallium/auxiliary/gallivm/lp_bld_init.c b/src/gallium/auxiliary/gallivm/lp_bld_init.c index c7d55cce7d1..8a4c5b0b58e 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_init.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_init.c @@ -495,3 +495,14 @@ gallivm_jit_function(struct gallivm_state *gallivm, return jit_func; } + +void +gallivm_stub_func(struct gallivm_state *gallivm, LLVMValueRef func) +{ + /* + * MCJIT can accept an empty function, nothing is needed here. + * The only code is to silence unused var warning. + */ + (void) gallivm; + (void) func; +} diff --git a/src/gallium/auxiliary/gallivm/lp_bld_init.h b/src/gallium/auxiliary/gallivm/lp_bld_init.h index 15549d10962..6894e9ecac7 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_init.h +++ b/src/gallium/auxiliary/gallivm/lp_bld_init.h @@ -116,6 +116,9 @@ func_pointer gallivm_jit_function(struct gallivm_state *gallivm, LLVMValueRef func, const char *func_name); +void +gallivm_stub_func(struct gallivm_state *gallivm, LLVMValueRef func); + unsigned gallivm_get_perf_flags(void); void lp_init_clock_hook(struct gallivm_state *gallivm); diff --git a/src/gallium/auxiliary/gallivm/lp_bld_init_orc.cpp b/src/gallium/auxiliary/gallivm/lp_bld_init_orc.cpp index a5723824eaa..59bc0aa69d8 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_init_orc.cpp +++ b/src/gallium/auxiliary/gallivm/lp_bld_init_orc.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "llvm/ExecutionEngine/JITLink/JITLink.h" #include #include @@ -62,6 +64,41 @@ namespace { +class LPObjectCacheORC : public llvm::ObjectCache { +private: + bool has_object; + std::string mid; + struct lp_cached_code *cache_out; +public: + LPObjectCacheORC(struct lp_cached_code *cache) { + cache_out = cache; + has_object = false; + } + + ~LPObjectCacheORC() { + } + void notifyObjectCompiled(const llvm::Module *M, llvm::MemoryBufferRef Obj) override { + const std::string ModuleID = M->getModuleIdentifier(); + if (has_object) + fprintf(stderr, "CACHE ALREADY HAS MODULE OBJECT\n"); + if (mid == ModuleID) + fprintf(stderr, "CACHING ANOTHER MODULE\n"); + has_object = true; + mid = ModuleID; + cache_out->data_size = Obj.getBufferSize(); + cache_out->data = malloc(cache_out->data_size); + memcpy(cache_out->data, Obj.getBufferStart(), cache_out->data_size); + } + + std::unique_ptr getObject(const llvm::Module *M) override { + const std::string ModuleID = M->getModuleIdentifier(); + if (cache_out->data_size) + return llvm::MemoryBuffer::getMemBuffer(llvm::StringRef((const char *)cache_out->data, cache_out->data_size), "", false); + return NULL; + } + +}; + class LPJit; class LLVMEnsureMultithreaded { @@ -215,6 +252,12 @@ public: ExitOnErr(es.removeJITDylib(* ::unwrap(jd))); } + static void set_object_cache(llvm::ObjectCache *objcache) { + auto &ircl = LPJit::get_instance()->lljit->getIRCompileLayer(); + auto &irc = ircl.getCompiler(); + auto &sc = dynamic_cast(irc); + sc.setObjectCache(objcache); + } LLVMTargetMachineRef tm; private: @@ -481,10 +524,7 @@ init_gallivm_state(struct gallivm_state *gallivm, const char *name, if (!lp_build_init()) return false; - // cache is not implemented gallivm->cache = cache; - if (gallivm->cache) - gallivm->cache->data_size = 0; gallivm->_ts_context = context->ref; gallivm->context = LLVMOrcThreadSafeContextGetContext(context->ref); @@ -543,6 +583,12 @@ gallivm_free_ir(struct gallivm_state *gallivm) if (gallivm->builder) LLVMDisposeBuilder(gallivm->builder); + if (gallivm->cache) { + if (gallivm->cache->jit_obj_cache) + lp_free_objcache(gallivm->cache->jit_obj_cache); + free(gallivm->cache->data); + } + gallivm->target = NULL; gallivm->module=NULL; gallivm->module_name=NULL; @@ -551,6 +597,7 @@ gallivm_free_ir(struct gallivm_state *gallivm) gallivm->_ts_context=NULL; gallivm->cache=NULL; LPJit::deregister_gallivm_state(gallivm); + LPJit::set_object_cache(NULL); } void @@ -580,6 +627,14 @@ gallivm_compile_module(struct gallivm_state *gallivm) LPJit::register_gallivm_state(gallivm); gallivm->module = nullptr; + if (gallivm->cache) { + if (!gallivm->cache->jit_obj_cache) { + LPObjectCacheORC *objcache = new LPObjectCacheORC(gallivm->cache); + gallivm->cache->jit_obj_cache = (void *)objcache; + } + auto *objcache = (LPObjectCacheORC *)gallivm->cache->jit_obj_cache; + LPJit::set_object_cache(objcache); + } /* defer compilation till first lookup by gallivm_jit_function */ } @@ -590,3 +645,18 @@ gallivm_jit_function(struct gallivm_state *gallivm, return pointer_to_func( LPJit::lookup_in_jd(func_name, gallivm->_per_module_jd)); } + +void +gallivm_stub_func(struct gallivm_state *gallivm, LLVMValueRef func) +{ + /* + * ORCJIT cannot accept a function with absolutely no content at all. + * Generate a "void func() {}" stub here. + */ + LLVMBasicBlockRef block = LLVMAppendBasicBlockInContext(gallivm->context, + func, "entry"); + LLVMBuilderRef builder = gallivm->builder; + assert(builder); + LLVMPositionBuilderAtEnd(builder, block); + LLVMBuildRetVoid(builder); +} diff --git a/src/gallium/drivers/llvmpipe/lp_state_cs.c b/src/gallium/drivers/llvmpipe/lp_state_cs.c index 64203f7e56c..fb04d498d46 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_cs.c +++ b/src/gallium/drivers/llvmpipe/lp_state_cs.c @@ -409,8 +409,10 @@ generate_compute(struct llvmpipe_context *lp, } } - if (variant->gallivm->cache->data_size) + if (variant->gallivm->cache->data_size) { + gallivm_stub_func(gallivm, function); return; + } context_ptr = LLVMGetParam(function, CS_ARG_CONTEXT); resources_ptr = LLVMGetParam(function, CS_ARG_RESOURCES); diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs.c b/src/gallium/drivers/llvmpipe/lp_state_fs.c index 7728b53366a..6b739606527 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_fs.c +++ b/src/gallium/drivers/llvmpipe/lp_state_fs.c @@ -3217,8 +3217,10 @@ generate_fragment(struct llvmpipe_context *lp, if (LLVMGetTypeKind(arg_types[i]) == LLVMPointerTypeKind) lp_add_function_attr(function, i + 1, LP_FUNC_ATTR_NOALIAS); - if (variant->gallivm->cache->data_size) + if (variant->gallivm->cache->data_size) { + gallivm_stub_func(gallivm, function); return; + } context_ptr = LLVMGetParam(function, 0); resources_ptr = LLVMGetParam(function, 1); diff --git a/src/gallium/drivers/llvmpipe/lp_state_fs_linear_llvm.c b/src/gallium/drivers/llvmpipe/lp_state_fs_linear_llvm.c index 70db126994d..1776fdb8bf3 100644 --- a/src/gallium/drivers/llvmpipe/lp_state_fs_linear_llvm.c +++ b/src/gallium/drivers/llvmpipe/lp_state_fs_linear_llvm.c @@ -301,8 +301,10 @@ llvmpipe_fs_variant_linear_llvm(struct llvmpipe_context *lp, } } - if (variant->gallivm->cache->data_size) + if (variant->gallivm->cache->data_size) { + gallivm_stub_func(gallivm, function); return; + } LLVMValueRef context_ptr = LLVMGetParam(function, 0); LLVMValueRef x = LLVMGetParam(function, 1);