#include "nir.h"
#include "nir_builder.h"
+/* This optimization is done together with copy propagation. */
+#define nir_opt_dead_write_vars nir_opt_copy_prop_vars
+
namespace {
class nir_vars_test : public ::testing::Test {
/* Allow grouping the tests while still sharing the helpers. */
class nir_copy_prop_vars_test : public nir_vars_test {};
+class nir_dead_write_vars_test : public nir_vars_test {};
} // namespace
EXPECT_EQ(store->src[1].ssa, stored_value);
}
}
+
+TEST_F(nir_dead_write_vars_test, no_dead_writes_in_block)
+{
+ nir_variable **v = create_many_int(nir_var_shader_storage, "v", 2);
+
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_FALSE(progress);
+}
+
+TEST_F(nir_dead_write_vars_test, no_dead_writes_different_components_in_block)
+{
+ nir_variable **v = create_many_ivec2(nir_var_shader_storage, "v", 3);
+
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1 << 0);
+ nir_store_var(b, v[0], nir_load_var(b, v[2]), 1 << 1);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_FALSE(progress);
+}
+
+TEST_F(nir_dead_write_vars_test, no_dead_writes_in_if_statement)
+{
+ nir_variable **v = create_many_int(nir_var_shader_storage, "v", 6);
+
+ nir_store_var(b, v[2], nir_load_var(b, v[0]), 1);
+ nir_store_var(b, v[3], nir_load_var(b, v[1]), 1);
+
+ /* Each arm of the if statement will overwrite one store. */
+ nir_if *if_stmt = nir_push_if(b, nir_imm_int(b, 0));
+ nir_store_var(b, v[2], nir_load_var(b, v[4]), 1);
+
+ nir_push_else(b, if_stmt);
+ nir_store_var(b, v[3], nir_load_var(b, v[5]), 1);
+
+ nir_pop_if(b, if_stmt);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_FALSE(progress);
+}
+
+TEST_F(nir_dead_write_vars_test, no_dead_writes_in_loop_statement)
+{
+ nir_variable **v = create_many_int(nir_var_shader_storage, "v", 3);
+
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);
+
+ /* Loop will write other value. Since it might not be executed, it doesn't
+ * kill the first write.
+ */
+ nir_loop *loop = nir_push_loop(b);
+
+ nir_if *if_stmt = nir_push_if(b, nir_imm_int(b, 0));
+ nir_jump(b, nir_jump_break);
+ nir_pop_if(b, if_stmt);
+
+ nir_store_var(b, v[0], nir_load_var(b, v[2]), 1);
+ nir_pop_loop(b, loop);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_FALSE(progress);
+}
+
+TEST_F(nir_dead_write_vars_test, dead_write_in_block)
+{
+ nir_variable **v = create_many_int(nir_var_shader_storage, "v", 3);
+
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);
+ nir_ssa_def *load_v2 = nir_load_var(b, v[2]);
+ nir_store_var(b, v[0], load_v2, 1);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_TRUE(progress);
+
+ EXPECT_EQ(1, count_intrinsics(nir_intrinsic_store_deref));
+
+ nir_intrinsic_instr *store = find_next_intrinsic(nir_intrinsic_store_deref, NULL);
+ ASSERT_TRUE(store->src[1].is_ssa);
+ EXPECT_EQ(store->src[1].ssa, load_v2);
+}
+
+TEST_F(nir_dead_write_vars_test, dead_write_components_in_block)
+{
+ nir_variable **v = create_many_ivec2(nir_var_shader_storage, "v", 3);
+
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1 << 0);
+ nir_ssa_def *load_v2 = nir_load_var(b, v[2]);
+ nir_store_var(b, v[0], load_v2, 1 << 0);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_TRUE(progress);
+
+ EXPECT_EQ(1, count_intrinsics(nir_intrinsic_store_deref));
+
+ nir_intrinsic_instr *store = find_next_intrinsic(nir_intrinsic_store_deref, NULL);
+ ASSERT_TRUE(store->src[1].is_ssa);
+ EXPECT_EQ(store->src[1].ssa, load_v2);
+}
+
+
+/* TODO: The DISABLED tests below depend on the dead write removal be able to
+ * identify dead writes between multiple blocks. This is still not
+ * implemented.
+ */
+
+TEST_F(nir_dead_write_vars_test, DISABLED_dead_write_in_two_blocks)
+{
+ nir_variable **v = create_many_int(nir_var_shader_storage, "v", 3);
+
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);
+ nir_ssa_def *load_v2 = nir_load_var(b, v[2]);
+
+ /* Causes the stores to be in different blocks. */
+ nir_pop_if(b, nir_push_if(b, nir_imm_int(b, 0)));
+
+ nir_store_var(b, v[0], load_v2, 1);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_TRUE(progress);
+
+ EXPECT_EQ(1, count_intrinsics(nir_intrinsic_store_deref));
+
+ nir_intrinsic_instr *store = find_next_intrinsic(nir_intrinsic_store_deref, NULL);
+ ASSERT_TRUE(store->src[1].is_ssa);
+ EXPECT_EQ(store->src[1].ssa, load_v2);
+}
+
+TEST_F(nir_dead_write_vars_test, DISABLED_dead_write_components_in_two_blocks)
+{
+ nir_variable **v = create_many_ivec2(nir_var_shader_storage, "v", 3);
+
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1 << 0);
+
+ /* Causes the stores to be in different blocks. */
+ nir_pop_if(b, nir_push_if(b, nir_imm_int(b, 0)));
+
+ nir_ssa_def *load_v2 = nir_load_var(b, v[2]);
+ nir_store_var(b, v[0], load_v2, 1 << 0);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_TRUE(progress);
+
+ EXPECT_EQ(1, count_intrinsics(nir_intrinsic_store_deref));
+
+ nir_intrinsic_instr *store = find_next_intrinsic(nir_intrinsic_store_deref, NULL);
+ ASSERT_TRUE(store->src[1].is_ssa);
+ EXPECT_EQ(store->src[1].ssa, load_v2);
+}
+
+TEST_F(nir_dead_write_vars_test, DISABLED_dead_writes_in_if_statement)
+{
+ nir_variable **v = create_many_int(nir_var_shader_storage, "v", 4);
+
+ /* Both branches will overwrite, making the previous store dead. */
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);
+
+ nir_if *if_stmt = nir_push_if(b, nir_imm_int(b, 0));
+ nir_ssa_def *load_v2 = nir_load_var(b, v[2]);
+ nir_store_var(b, v[0], load_v2, 1);
+
+ nir_push_else(b, if_stmt);
+ nir_ssa_def *load_v3 = nir_load_var(b, v[3]);
+ nir_store_var(b, v[0], load_v3, 1);
+
+ nir_pop_if(b, if_stmt);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_TRUE(progress);
+ EXPECT_EQ(2, count_intrinsics(nir_intrinsic_store_deref));
+
+ nir_intrinsic_instr *store = NULL;
+ store = find_next_intrinsic(nir_intrinsic_store_deref, store);
+ ASSERT_TRUE(store->src[1].is_ssa);
+ EXPECT_EQ(store->src[1].ssa, load_v2);
+
+ store = find_next_intrinsic(nir_intrinsic_store_deref, store);
+ ASSERT_TRUE(store->src[1].is_ssa);
+ EXPECT_EQ(store->src[1].ssa, load_v3);
+}
+
+TEST_F(nir_dead_write_vars_test, DISABLED_memory_barrier_in_two_blocks)
+{
+ nir_variable **v = create_many_int(nir_var_shader_storage, "v", 2);
+
+ nir_store_var(b, v[0], nir_imm_int(b, 1), 1);
+ nir_store_var(b, v[1], nir_imm_int(b, 2), 1);
+
+ /* Split into many blocks. */
+ nir_pop_if(b, nir_push_if(b, nir_imm_int(b, 0)));
+
+ /* Because it is before the barrier, this will kill the previous store to that target. */
+ nir_store_var(b, v[0], nir_imm_int(b, 3), 1);
+
+ nir_builder_instr_insert(b, &nir_intrinsic_instr_create(b->shader, nir_intrinsic_memory_barrier)->instr);
+
+ nir_store_var(b, v[1], nir_imm_int(b, 4), 1);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_TRUE(progress);
+
+ EXPECT_EQ(3, count_intrinsics(nir_intrinsic_store_deref));
+}
+
+TEST_F(nir_dead_write_vars_test, DISABLED_unrelated_barrier_in_two_blocks)
+{
+ nir_variable **v = create_many_int(nir_var_shader_storage, "v", 3);
+ nir_variable *out = create_int(nir_var_shader_out, "out");
+
+ nir_store_var(b, out, nir_load_var(b, v[1]), 1);
+ nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);
+
+ /* Split into many blocks. */
+ nir_pop_if(b, nir_push_if(b, nir_imm_int(b, 0)));
+
+ /* Emit vertex will ensure writes to output variables are considered used,
+ * but should not affect other types of variables. */
+
+ nir_builder_instr_insert(b, &nir_intrinsic_instr_create(b->shader, nir_intrinsic_emit_vertex)->instr);
+
+ nir_store_var(b, out, nir_load_var(b, v[2]), 1);
+ nir_store_var(b, v[0], nir_load_var(b, v[2]), 1);
+
+ bool progress = nir_opt_dead_write_vars(b->shader);
+ ASSERT_TRUE(progress);
+
+ /* Verify the first write to v[0] was removed. */
+ EXPECT_EQ(3, count_intrinsics(nir_intrinsic_store_deref));
+
+ nir_intrinsic_instr *store = NULL;
+ store = find_next_intrinsic(nir_intrinsic_store_deref, store);
+ EXPECT_EQ(nir_intrinsic_get_var(store, 0), out);
+ store = find_next_intrinsic(nir_intrinsic_store_deref, store);
+ EXPECT_EQ(nir_intrinsic_get_var(store, 0), out);
+ store = find_next_intrinsic(nir_intrinsic_store_deref, store);
+ EXPECT_EQ(nir_intrinsic_get_var(store, 0), v[0]);
+}