nir/deref: Handle SSBO array bindings specially

Instead of just checking for the variables to match, check that the
entire deref up to the interface type matches.

Tested-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Reviewed-by: M Henning <drawoc@darkrefraction.com>
Reviewed-by: Rhys Perry <pendingchaos02@gmail.com>
Cc: mesa-stable@lists.freedesktop.org
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/16894>
This commit is contained in:
Jason Ekstrand 2022-06-06 12:13:02 -05:00 committed by Marge Bot
parent a5b1274275
commit 8492e78f9d
2 changed files with 261 additions and 1 deletions

View file

@ -562,6 +562,17 @@ compare_deref_paths(nir_deref_path *a_path, nir_deref_path *b_path,
return result; return result;
} }
static bool
is_interface_struct_deref(const nir_deref_instr *deref)
{
if (deref->deref_type == nir_deref_type_struct) {
assert(glsl_type_is_struct_or_ifc(nir_deref_instr_parent(deref)->type));
return true;
} else {
return false;
}
}
nir_deref_compare_result nir_deref_compare_result
nir_compare_deref_paths(nir_deref_path *a_path, nir_compare_deref_paths(nir_deref_path *a_path,
nir_deref_path *b_path) nir_deref_path *b_path)
@ -572,6 +583,7 @@ nir_compare_deref_paths(nir_deref_path *a_path,
if (a_path->path[0]->deref_type != b_path->path[0]->deref_type) if (a_path->path[0]->deref_type != b_path->path[0]->deref_type)
return nir_derefs_may_alias_bit; return nir_derefs_may_alias_bit;
unsigned path_idx = 1;
if (a_path->path[0]->deref_type == nir_deref_type_var) { if (a_path->path[0]->deref_type == nir_deref_type_var) {
const nir_variable *a_var = a_path->path[0]->var; const nir_variable *a_var = a_path->path[0]->var;
const nir_variable *b_var = b_path->path[0]->var; const nir_variable *b_var = b_path->path[0]->var;
@ -585,6 +597,27 @@ nir_compare_deref_paths(nir_deref_path *a_path,
assert(a_var->data.mode == b_var->data.mode); assert(a_var->data.mode == b_var->data.mode);
switch (a_var->data.mode) { switch (a_var->data.mode) {
case nir_var_mem_ssbo: {
nir_deref_compare_result binding_compare;
if (a_var == b_var) {
binding_compare = compare_deref_paths(a_path, b_path, &path_idx,
is_interface_struct_deref);
} else {
binding_compare = nir_derefs_do_not_alias;
}
if (binding_compare & nir_derefs_equal_bit)
break;
/* If the binding derefs can't alias and at least one is RESTRICT,
* then we know they can't alias.
*/
if (!(binding_compare & nir_derefs_may_alias_bit))
return nir_derefs_do_not_alias;
return nir_derefs_may_alias_bit;
}
case nir_var_mem_shared: case nir_var_mem_shared:
if (a_var == b_var) if (a_var == b_var)
break; break;
@ -629,7 +662,6 @@ nir_compare_deref_paths(nir_deref_path *a_path,
return nir_derefs_may_alias_bit; return nir_derefs_may_alias_bit;
} }
unsigned path_idx = 1;
return compare_deref_paths(a_path, b_path, &path_idx, NULL); return compare_deref_paths(a_path, b_path, &path_idx, NULL);
} }

View file

@ -1327,6 +1327,234 @@ TEST_F(nir_copy_prop_vars_test, store_load_indirect_array_deref)
EXPECT_EQ(first->src[1].ssa, second->src[1].ssa); EXPECT_EQ(first->src[1].ssa, second->src[1].ssa);
} }
TEST_F(nir_copy_prop_vars_test, restrict_ssbo_bindings)
{
glsl_struct_field field = glsl_struct_field();
field.type = glsl_int_type();
field.name = "x";
const glsl_type *ifc_type =
glsl_type::get_interface_instance(&field, 1,
GLSL_INTERFACE_PACKING_STD430,
false /* row_major */, "b");
nir_variable *ssbo0 = create_var(nir_var_mem_ssbo, ifc_type, "ssbo0");
nir_variable *ssbo1 = create_var(nir_var_mem_ssbo, ifc_type, "ssbo1");
ssbo0->data.access = ssbo1->data.access = ACCESS_RESTRICT;
nir_variable *out = create_var(nir_var_mem_ssbo, ifc_type, "out");
out->data.access = ACCESS_RESTRICT;
nir_deref_instr *ssbo0_x =
nir_build_deref_struct(b, nir_build_deref_var(b, ssbo0), 0);
nir_store_deref(b, ssbo0_x, nir_imm_int(b, 20), 1);
nir_deref_instr *ssbo1_x =
nir_build_deref_struct(b, nir_build_deref_var(b, ssbo1), 0);
nir_store_deref(b, ssbo1_x, nir_imm_int(b, 30), 1);
/* Load ssbo0.x and store it in out.x. This load should be dropped */
nir_deref_instr *out_x =
nir_build_deref_struct(b, nir_build_deref_var(b, out), 0);
nir_store_deref(b, out_x, nir_load_deref(b, ssbo0_x), 1);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 1);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
bool progress = nir_opt_copy_prop_vars(b->shader);
EXPECT_TRUE(progress);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 0);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
/* Store to b0.x propagated to out. */
nir_intrinsic_instr *first = get_intrinsic(nir_intrinsic_store_deref, 0);
nir_intrinsic_instr *third = get_intrinsic(nir_intrinsic_store_deref, 2);
ASSERT_TRUE(first->src[1].is_ssa);
ASSERT_TRUE(third->src[1].is_ssa);
EXPECT_EQ(first->src[1].ssa, third->src[1].ssa);
}
TEST_F(nir_copy_prop_vars_test, aliasing_ssbo_bindings)
{
glsl_struct_field field = glsl_struct_field();
field.type = glsl_int_type();
field.name = "x";
const glsl_type *ifc_type =
glsl_type::get_interface_instance(&field, 1,
GLSL_INTERFACE_PACKING_STD430,
false /* row_major */, "b");
nir_variable *ssbo0 = create_var(nir_var_mem_ssbo, ifc_type, "ssbo0");
nir_variable *ssbo1 = create_var(nir_var_mem_ssbo, ifc_type, "ssbo1");
nir_variable *out = create_var(nir_var_mem_ssbo, ifc_type, "out");
out->data.access = ACCESS_RESTRICT;
nir_deref_instr *ssbo0_x =
nir_build_deref_struct(b, nir_build_deref_var(b, ssbo0), 0);
nir_store_deref(b, ssbo0_x, nir_imm_int(b, 20), 1);
nir_deref_instr *ssbo1_x =
nir_build_deref_struct(b, nir_build_deref_var(b, ssbo1), 0);
nir_store_deref(b, ssbo1_x, nir_imm_int(b, 30), 1);
/* Load ssbo0.x and store it in out.x. This load should not be dropped */
nir_deref_instr *out_x =
nir_build_deref_struct(b, nir_build_deref_var(b, out), 0);
nir_store_deref(b, out_x, nir_load_deref(b, ssbo0_x), 1);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 1);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
bool progress = nir_opt_copy_prop_vars(b->shader);
EXPECT_FALSE(progress);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 1);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
}
TEST_F(nir_copy_prop_vars_test, ssbo_array_binding_indirect)
{
glsl_struct_field field = glsl_struct_field();
field.type = glsl_int_type();
field.name = "x";
const glsl_type *ifc_type =
glsl_type::get_interface_instance(&field, 1,
GLSL_INTERFACE_PACKING_STD430,
false /* row_major */, "b");
const glsl_type *arr_ifc_type = glsl_type::get_array_instance(ifc_type, 2);
nir_variable *ssbo_arr = create_var(nir_var_mem_ssbo, arr_ifc_type,
"ssbo_arr");
ssbo_arr->data.access = ACCESS_RESTRICT;
nir_variable *out = create_var(nir_var_mem_ssbo, ifc_type, "out");
out->data.access = ACCESS_RESTRICT;
nir_ssa_def *i = nir_load_local_invocation_index(b);
nir_deref_instr *ssbo_0 =
nir_build_deref_array_imm(b, nir_build_deref_var(b, ssbo_arr), 0);
nir_deref_instr *ssbo_0_x = nir_build_deref_struct(b, ssbo_0, 0);
nir_store_deref(b, ssbo_0_x, nir_imm_int(b, 20), 1);
nir_deref_instr *ssbo_i =
nir_build_deref_array(b, nir_build_deref_var(b, ssbo_arr),
nir_load_local_invocation_index(b));
nir_deref_instr *ssbo_i_x = nir_build_deref_struct(b, ssbo_i, 0);
nir_store_deref(b, ssbo_i_x, nir_imm_int(b, 30), 1);
/* Load ssbo_arr[0].x and store it in out.x. This load should not be dropped */
nir_deref_instr *out_x =
nir_build_deref_struct(b, nir_build_deref_var(b, out), 0);
nir_store_deref(b, out_x, nir_load_deref(b, ssbo_0_x), 1);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 1);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
bool progress = nir_opt_copy_prop_vars(b->shader);
EXPECT_FALSE(progress);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 1);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
}
TEST_F(nir_copy_prop_vars_test, restrict_ssbo_array_binding)
{
glsl_struct_field field = glsl_struct_field();
field.type = glsl_int_type();
field.name = "x";
const glsl_type *ifc_type =
glsl_type::get_interface_instance(&field, 1,
GLSL_INTERFACE_PACKING_STD430,
false /* row_major */, "b");
const glsl_type *arr_ifc_type = glsl_type::get_array_instance(ifc_type, 2);
nir_variable *ssbo_arr = create_var(nir_var_mem_ssbo, arr_ifc_type,
"ssbo_arr");
ssbo_arr->data.access = ACCESS_RESTRICT;
nir_variable *out = create_var(nir_var_mem_ssbo, ifc_type, "out");
out->data.access = ACCESS_RESTRICT;
nir_ssa_def *i = nir_load_local_invocation_index(b);
nir_deref_instr *ssbo_0 =
nir_build_deref_array_imm(b, nir_build_deref_var(b, ssbo_arr), 0);
nir_deref_instr *ssbo_0_x = nir_build_deref_struct(b, ssbo_0, 0);
nir_store_deref(b, ssbo_0_x, nir_imm_int(b, 20), 1);
nir_deref_instr *ssbo_1 =
nir_build_deref_array_imm(b, nir_build_deref_var(b, ssbo_arr), 1);
nir_deref_instr *ssbo_1_x = nir_build_deref_struct(b, ssbo_1, 0);
nir_store_deref(b, ssbo_1_x, nir_imm_int(b, 30), 1);
/* Load ssbo_arr[0].x and store it in out.x. This load should be dropped */
nir_deref_instr *out_x =
nir_build_deref_struct(b, nir_build_deref_var(b, out), 0);
nir_store_deref(b, out_x, nir_load_deref(b, ssbo_0_x), 1);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 1);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
bool progress = nir_opt_copy_prop_vars(b->shader);
EXPECT_TRUE(progress);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 0);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
/* Store to b0.x propagated to out. */
nir_intrinsic_instr *first = get_intrinsic(nir_intrinsic_store_deref, 0);
nir_intrinsic_instr *third = get_intrinsic(nir_intrinsic_store_deref, 2);
ASSERT_TRUE(first->src[1].is_ssa);
ASSERT_TRUE(third->src[1].is_ssa);
EXPECT_EQ(first->src[1].ssa, third->src[1].ssa);
}
TEST_F(nir_copy_prop_vars_test, aliasing_ssbo_array_binding)
{
glsl_struct_field field = glsl_struct_field();
field.type = glsl_int_type();
field.name = "x";
const glsl_type *ifc_type =
glsl_type::get_interface_instance(&field, 1,
GLSL_INTERFACE_PACKING_STD430,
false /* row_major */, "b");
const glsl_type *arr_ifc_type = glsl_type::get_array_instance(ifc_type, 2);
nir_variable *ssbo_arr = create_var(nir_var_mem_ssbo, arr_ifc_type,
"ssbo_arr");
nir_variable *out = create_var(nir_var_mem_ssbo, ifc_type, "out");
out->data.access = ACCESS_RESTRICT;
nir_ssa_def *i = nir_load_local_invocation_index(b);
nir_deref_instr *ssbo_0 =
nir_build_deref_array_imm(b, nir_build_deref_var(b, ssbo_arr), 0);
nir_deref_instr *ssbo_0_x = nir_build_deref_struct(b, ssbo_0, 0);
nir_store_deref(b, ssbo_0_x, nir_imm_int(b, 20), 1);
nir_deref_instr *ssbo_1 =
nir_build_deref_array_imm(b, nir_build_deref_var(b, ssbo_arr), 1);
nir_deref_instr *ssbo_1_x = nir_build_deref_struct(b, ssbo_1, 0);
nir_store_deref(b, ssbo_1_x, nir_imm_int(b, 30), 1);
/* Load ssbo_arr[0].x and store it in out.x. This load should not be dropped */
nir_deref_instr *out_x =
nir_build_deref_struct(b, nir_build_deref_var(b, out), 0);
nir_store_deref(b, out_x, nir_load_deref(b, ssbo_0_x), 1);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 1);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
bool progress = nir_opt_copy_prop_vars(b->shader);
EXPECT_FALSE(progress);
nir_validate_shader(b->shader, NULL);
ASSERT_EQ(count_intrinsics(nir_intrinsic_load_deref), 1);
ASSERT_EQ(count_intrinsics(nir_intrinsic_store_deref), 3);
}
TEST_F(nir_dead_write_vars_test, no_dead_writes_in_block) TEST_F(nir_dead_write_vars_test, no_dead_writes_in_block)
{ {
nir_variable **v = create_many_int(nir_var_mem_global, "v", 2); nir_variable **v = create_many_int(nir_var_mem_global, "v", 2);