nir/lower_io_to_temporaries: fix interp_deref_at_* lowering

The pass converts:
    ...
    %.. = load_deref(input)
to:
    temp = copy_deref(input) // beginning of the shader
    ...
    %.. = load_deref(temp)

If interp_deref_at_* occurs between copy_deref and load_deref,
the interp_deref_at_* lowering overwrites temp, so all future
load_deref(temp) return the result of interp_deref_at_* instead of
copy_deref, which is incorrect.

The issue manifests when the same input is used by both load_deref
and interp_deref_at_* in the same shader and when interp_deref_at_*
happens to be before load_deref.

This fixes it by using a completely new temporary for each instance
of interp_deref_at_*.

Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/32344>
This commit is contained in:
Marek Olšák 2024-11-25 20:41:53 -05:00 committed by Marge Bot
parent c23abb12e8
commit 8fc640b256

View file

@ -236,14 +236,27 @@ fixup_interpolation_instr(struct lower_io_state *state,
nir_variable *input = entry->data;
nir_deref_instr *input_root = nir_build_deref_var(b, input);
/* We can't reuse the original temporary because it contains the original
* value of the input that's used throughout the whole shader to replace
* the original load_deref, so we can't overwrite it. Create a new
* temporary that's only used for this interp_deref_at_* opcode.
*/
char *var_name = ralloc_asprintf(NULL, "%s-interp", input->name);
nir_variable *var = nir_local_variable_create(b->impl, input->type, var_name);
ralloc_free(var_name);
nir_deref_instr *new_temp_root = nir_build_deref_var(b, var);
/* Emit the interpolation instructions. */
emit_interp(b, interp_path.path + 1, temp_root, input_root, interp);
emit_interp(b, interp_path.path + 1, new_temp_root, input_root, interp);
/* Now the temporary contains the interpolation results, and we can just
* load from it. We can reuse the original deref, since it points to the
* correct part of the temporary.
* load from it.
*
* Clone the original deref chain, but use the new temporary variable.
*/
nir_def *load = nir_load_deref(b, nir_src_as_deref(interp->src[0]));
nir_deref_instr *new_temp_leaf =
nir_clone_deref_instr(b, var, nir_src_as_deref(interp->src[0]));
nir_def *load = nir_load_deref(b, new_temp_leaf);
nir_def_replace(&interp->def, load);
nir_deref_path_finish(&interp_path);