On Fri, Sep 14, 2018 at 10:46 PM Caio Marcelo de Oliveira Filho <
caio.olive...@intel.com> wrote:

> 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.
> ---
>  src/compiler/nir/tests/vars_tests.cpp | 241 ++++++++++++++++++++++++++
>  1 file changed, 241 insertions(+)
>
> diff --git a/src/compiler/nir/tests/vars_tests.cpp
> b/src/compiler/nir/tests/vars_tests.cpp
> index 7fbdb514349..dd913f04429 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 {
> @@ -141,6 +144,7 @@ nir_imm_ivec2(nir_builder *build, int x, int y)
>
>  /* 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
>
> @@ -197,3 +201,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);
>

Using inputs and outputs is probably a tad bit safer than shader_storage
but I don't think it matters too much.  This is probably fine.


> +
> +   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));
>

Maybe nir_push_if(b, nir_load_var(b, v[0])); so that it's not a loop with
one dead side.  I doubt this pass will ever get that smart but it's easy
enough to just prevent the possibility.


> +   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));
>

Same here, though you'll want to use v[1] instead of v[0] so it doesn't get
copy-propagated.


> +   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));
>

Load something non-zero?


> +   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)));
>

Ok, I'm starting to get the idea that protecting ourselves from control
flow optimizations is probably a lost cause.  You can ignore all those
comments.

Reviewed-by: Jason Ekstrand <ja...@jlekstrand.net>


> +
> +   /* 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]);
> +}
> --
> 2.19.0
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
>
_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to