From 1649a4d92ba880242f5891c278ec8e8352b1e42b Mon Sep 17 00:00:00 2001 From: Jesse Natalie Date: Fri, 8 Dec 2023 14:02:35 -0800 Subject: [PATCH] microsoft/clc: When possible, compute a part-constant "pointer" value for kernel inputs When a kernel input is a pointer to global or constant memory, it's expected that the invoker provides the pointer value in the form of (buffer_index << 32) | offset. The buffer index, however, is statically knowable in the compiler, as long as a buffer is bound. Since it's undefined behavior to dereference the pointer with no buffer bound, we can replace any deref chain that terminates in an access with one that uses a constant buffer index. Part-of: --- src/microsoft/clc/clc_compiler.c | 4 +- src/microsoft/clc/clc_nir.c | 81 ++++++++++++++++++++++++++++++++ src/microsoft/clc/clc_nir.h | 1 + 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/microsoft/clc/clc_compiler.c b/src/microsoft/clc/clc_compiler.c index d8f18129630..8c21b04fd7d 100644 --- a/src/microsoft/clc/clc_compiler.c +++ b/src/microsoft/clc/clc_compiler.c @@ -804,7 +804,7 @@ clc_spirv_to_dxil(struct clc_libclc *lib, var->data.driver_location + size); if (out_dxil->kernel->args[i].address_qualifier == CLC_KERNEL_ARG_ADDRESS_GLOBAL || out_dxil->kernel->args[i].address_qualifier == CLC_KERNEL_ARG_ADDRESS_CONSTANT) { - metadata->args[i].globconstptr.buf_id = uav_id++; + metadata->args[i].globconstptr.buf_id = var->data.binding = uav_id++; } else if (glsl_type_is_sampler(var->type)) { unsigned address_mode = conf ? conf->args[i].sampler.addressing_mode : 0u; int_sampler_states[sampler_id].wrap[0] = @@ -906,6 +906,8 @@ clc_spirv_to_dxil(struct clc_libclc *lib, } NIR_PASS_V(nir, nir_lower_memcpy); + NIR_PASS_V(nir, clc_nir_lower_global_pointers_to_constants); + // Attempt to preserve derefs to constants by moving them to shader_temp NIR_PASS_V(nir, dxil_nir_lower_constant_to_temp); // While inserting new var derefs for our "logical" addressing mode, temporarily diff --git a/src/microsoft/clc/clc_nir.c b/src/microsoft/clc/clc_nir.c index dc3c2c90e0a..76818573bfa 100644 --- a/src/microsoft/clc/clc_nir.c +++ b/src/microsoft/clc/clc_nir.c @@ -25,6 +25,7 @@ #include "nir.h" #include "glsl_types.h" #include "nir_builder.h" +#include "nir_deref.h" #include "clc_nir.h" #include "clc_compiler.h" @@ -261,3 +262,83 @@ clc_lower_printf_base(nir_shader *nir, unsigned uav_id) return printf_var != NULL; } + +/* Find patterns of: + * - deref_var for one of the kernel inputs + * - load_deref to get a pointer to global/constant memory + * - cast the pointer into a deref + * - use a basic deref chain that terminates in a load/store/atomic + * + * When this pattern is found, replace the load_deref with a constant value, + * based on which kernel argument is being loaded from. This can only be done + * for chains that terminate in a pointer access, since the presence of null + * pointers should be detected by actually performing the load and inspecting + * the resulting pointer value. + */ +static bool +lower_deref_base_to_constant(nir_builder *b, nir_intrinsic_instr *intr, void *context) +{ + switch (intr->intrinsic) { + case nir_intrinsic_load_deref: + case nir_intrinsic_store_deref: + case nir_intrinsic_deref_atomic: + case nir_intrinsic_deref_atomic_swap: + break; + default: + return false; + } + + nir_deref_instr *deref = nir_src_as_deref(intr->src[0]); + if (!nir_deref_mode_must_be(deref, nir_var_mem_global | nir_var_mem_constant)) + return false; + + nir_deref_path path; + nir_deref_path_init(&path, deref, NULL); + bool ret = false; + + if (path.path[0]->deref_type != nir_deref_type_cast) + goto done; + if (!nir_deref_mode_must_be(path.path[0], nir_var_mem_global | nir_var_mem_constant)) + goto done; + + nir_instr *cast_src = path.path[0]->parent.ssa->parent_instr; + if (cast_src->type != nir_instr_type_intrinsic) + goto done; + + nir_intrinsic_instr *cast_src_intr = nir_instr_as_intrinsic(cast_src); + if (cast_src_intr->intrinsic != nir_intrinsic_load_deref) + goto done; + + nir_deref_instr *load_deref_src = nir_src_as_deref(cast_src_intr->src[0]); + if (load_deref_src->deref_type != nir_deref_type_var || + load_deref_src->modes != nir_var_uniform) + goto done; + + nir_variable *var = load_deref_src->var; + + ret = true; + b->cursor = nir_before_instr(&path.path[0]->instr); + nir_def *original_offset = nir_unpack_64_2x32_split_x(b, &cast_src_intr->def); + nir_def *constant_ptr = nir_pack_64_2x32_split(b, original_offset, nir_imm_int(b, var->data.binding)); + nir_deref_instr *new_path = nir_build_deref_cast_with_alignment(b, constant_ptr, path.path[0]->modes, path.path[0]->type, path.path[0]->cast.ptr_stride, + path.path[0]->cast.align_mul, path.path[0]->cast.align_offset); + + for (unsigned i = 1; path.path[i]; ++i) { + b->cursor = nir_after_instr(&path.path[i]->instr); + new_path = nir_build_deref_follower(b, new_path, path.path[i]); + } + nir_src_rewrite(&intr->src[0], &new_path->def); + +done: + nir_deref_path_finish(&path); + return ret; +} + +bool +clc_nir_lower_global_pointers_to_constants(nir_shader *nir) +{ + return nir_shader_intrinsics_pass(nir, lower_deref_base_to_constant, + nir_metadata_block_index | + nir_metadata_dominance | + nir_metadata_loop_analysis, NULL); +} diff --git a/src/microsoft/clc/clc_nir.h b/src/microsoft/clc/clc_nir.h index 7f5a352bb02..4e1756e0ddf 100644 --- a/src/microsoft/clc/clc_nir.h +++ b/src/microsoft/clc/clc_nir.h @@ -32,4 +32,5 @@ clc_nir_lower_system_values(nir_shader *nir, nir_variable *var); bool clc_nir_lower_kernel_input_loads(nir_shader *nir, nir_variable *var); bool clc_lower_printf_base(nir_shader *nir, unsigned uav_id); +bool clc_nir_lower_global_pointers_to_constants(nir_shader *nir); #endif