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: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26803>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user