[Mesa-dev] [PATCH 04/11] nir: Add tests for dead write elimination
Jason Ekstrand
jason at jlekstrand.net
Mon Oct 8 20:26:15 UTC 2018
On Fri, Sep 14, 2018 at 10:46 PM Caio Marcelo de Oliveira Filho <
caio.oliveira at 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 <jason at 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 at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/mesa-dev/attachments/20181008/166c70bc/attachment-0001.html>
More information about the mesa-dev
mailing list