diff --git a/src/compiler/nir/nir_deref.c b/src/compiler/nir/nir_deref.c index eecb3d19413..eb3a2352b3f 100644 --- a/src/compiler/nir/nir_deref.c +++ b/src/compiler/nir/nir_deref.c @@ -562,6 +562,17 @@ compare_deref_paths(nir_deref_path *a_path, nir_deref_path *b_path, 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_compare_deref_paths(nir_deref_path *a_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) return nir_derefs_may_alias_bit; + unsigned path_idx = 1; if (a_path->path[0]->deref_type == nir_deref_type_var) { const nir_variable *a_var = a_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); 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: if (a_var == b_var) break; @@ -629,7 +662,6 @@ nir_compare_deref_paths(nir_deref_path *a_path, return nir_derefs_may_alias_bit; } - unsigned path_idx = 1; return compare_deref_paths(a_path, b_path, &path_idx, NULL); } diff --git a/src/compiler/nir/tests/vars_tests.cpp b/src/compiler/nir/tests/vars_tests.cpp index eb7e7547ba9..bdd19ffb9c6 100644 --- a/src/compiler/nir/tests/vars_tests.cpp +++ b/src/compiler/nir/tests/vars_tests.cpp @@ -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); } +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) { nir_variable **v = create_many_int(nir_var_mem_global, "v", 2);