From: Caio Marcelo de Oliveira Filho Date: Wed, 29 Aug 2018 23:30:09 +0000 (-0700) Subject: nir: Add tests for dead write elimination X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a02fd7000d4f1d6912d9916eea3d72b01aa4990f;p=mesa.git nir: Add tests for dead write elimination Note at the moment the pass called is nir_opt_copy_prop_vars, because dead write elimination is implemented there. Also added tests that involve identifying dead writes in multiple blocks (e.g. the overwrite happens in another block). Those currently fail as expected, so are marked to be skipped. Reviewed-by: Jason Ekstrand --- 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]); +}