diff --git a/src/compiler/nir/tests/vars_tests.cpp b/src/compiler/nir/tests/vars_tests.cpp index 9b2877fcc92..0a2ff3f3d32 100644 --- a/src/compiler/nir/tests/vars_tests.cpp +++ b/src/compiler/nir/tests/vars_tests.cpp @@ -26,6 +26,9 @@ #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 { @@ -132,6 +135,7 @@ nir_vars_test::find_next_intrinsic(nir_intrinsic_op intrinsic, /* 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 @@ -188,3 +192,240 @@ TEST_F(nir_copy_prop_vars_test, simple_store_load) 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]); +}