<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">On Fri, Sep 14, 2018 at 10:46 PM Caio Marcelo de Oliveira Filho <<a href="mailto:caio.oliveira@intel.com" target="_blank">caio.oliveira@intel.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Note at the moment the pass called is nir_opt_copy_prop_vars, because<br>
dead write elimination is implemented there.<br>
<br>
Also added tests that involve identifying dead writes in multiple<br>
blocks (e.g. the overwrite happens in another block).  Those currently<br>
fail as expected, so are marked to be skipped.<br>
---<br>
 src/compiler/nir/tests/vars_tests.cpp | 241 ++++++++++++++++++++++++++<br>
 1 file changed, 241 insertions(+)<br>
<br>
diff --git a/src/compiler/nir/tests/vars_tests.cpp b/src/compiler/nir/tests/vars_tests.cpp<br>
index 7fbdb514349..dd913f04429 100644<br>
--- a/src/compiler/nir/tests/vars_tests.cpp<br>
+++ b/src/compiler/nir/tests/vars_tests.cpp<br>
@@ -26,6 +26,9 @@<br>
 #include "nir.h"<br>
 #include "nir_builder.h"<br>
<br>
+/* This optimization is done together with copy propagation. */<br>
+#define nir_opt_dead_write_vars nir_opt_copy_prop_vars<br>
+<br>
 namespace {<br>
<br>
 class nir_vars_test : public ::testing::Test {<br>
@@ -141,6 +144,7 @@ nir_imm_ivec2(nir_builder *build, int x, int y)<br>
<br>
 /* Allow grouping the tests while still sharing the helpers. */<br>
 class nir_copy_prop_vars_test : public nir_vars_test {};<br>
+class nir_dead_write_vars_test : public nir_vars_test {};<br>
<br>
 } // namespace<br>
<br>
@@ -197,3 +201,240 @@ TEST_F(nir_copy_prop_vars_test, simple_store_load)<br>
       EXPECT_EQ(store->src[1].ssa, stored_value);<br>
    }<br>
 }<br>
+<br>
+TEST_F(nir_dead_write_vars_test, no_dead_writes_in_block)<br>
+{<br>
+   nir_variable **v = create_many_int(nir_var_shader_storage, "v", 2);<br></blockquote><div><br></div><div>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.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_FALSE(progress);<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, no_dead_writes_different_components_in_block)<br>
+{<br>
+   nir_variable **v = create_many_ivec2(nir_var_shader_storage, "v", 3);<br>
+<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1 << 0);<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[2]), 1 << 1);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_FALSE(progress);<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, no_dead_writes_in_if_statement)<br>
+{<br>
+   nir_variable **v = create_many_int(nir_var_shader_storage, "v", 6);<br>
+<br>
+   nir_store_var(b, v[2], nir_load_var(b, v[0]), 1);<br>
+   nir_store_var(b, v[3], nir_load_var(b, v[1]), 1);<br>
+<br>
+   /* Each arm of the if statement will overwrite one store. */<br>
+   nir_if *if_stmt = nir_push_if(b, nir_imm_int(b, 0));<br></blockquote><div><br></div><div>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.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+   nir_store_var(b, v[2], nir_load_var(b, v[4]), 1);<br>
+<br>
+   nir_push_else(b, if_stmt);<br>
+   nir_store_var(b, v[3], nir_load_var(b, v[5]), 1);<br>
+<br>
+   nir_pop_if(b, if_stmt);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_FALSE(progress);<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, no_dead_writes_in_loop_statement)<br>
+{<br>
+   nir_variable **v = create_many_int(nir_var_shader_storage, "v", 3);<br>
+<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);<br>
+<br>
+   /* Loop will write other value.  Since it might not be executed, it doesn't<br>
+    * kill the first write.<br>
+    */<br>
+   nir_loop *loop = nir_push_loop(b);<br>
+<br>
+   nir_if *if_stmt = nir_push_if(b, nir_imm_int(b, 0));<br></blockquote><div><br></div><div>Same here, though you'll want to use v[1] instead of v[0] so it doesn't get copy-propagated.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+   nir_jump(b, nir_jump_break);<br>
+   nir_pop_if(b, if_stmt);<br>
+<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[2]), 1);<br>
+   nir_pop_loop(b, loop);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_FALSE(progress);<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, dead_write_in_block)<br>
+{<br>
+   nir_variable **v = create_many_int(nir_var_shader_storage, "v", 3);<br>
+<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);<br>
+   nir_ssa_def *load_v2 = nir_load_var(b, v[2]);<br>
+   nir_store_var(b, v[0], load_v2, 1);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_TRUE(progress);<br>
+<br>
+   EXPECT_EQ(1, count_intrinsics(nir_intrinsic_store_deref));<br>
+<br>
+   nir_intrinsic_instr *store = find_next_intrinsic(nir_intrinsic_store_deref, NULL);<br>
+   ASSERT_TRUE(store->src[1].is_ssa);<br>
+   EXPECT_EQ(store->src[1].ssa, load_v2);<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, dead_write_components_in_block)<br>
+{<br>
+   nir_variable **v = create_many_ivec2(nir_var_shader_storage, "v", 3);<br>
+<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1 << 0);<br>
+   nir_ssa_def *load_v2 = nir_load_var(b, v[2]);<br>
+   nir_store_var(b, v[0], load_v2, 1 << 0);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_TRUE(progress);<br>
+<br>
+   EXPECT_EQ(1, count_intrinsics(nir_intrinsic_store_deref));<br>
+<br>
+   nir_intrinsic_instr *store = find_next_intrinsic(nir_intrinsic_store_deref, NULL);<br>
+   ASSERT_TRUE(store->src[1].is_ssa);<br>
+   EXPECT_EQ(store->src[1].ssa, load_v2);<br>
+}<br>
+<br>
+<br>
+/* TODO: The DISABLED tests below depend on the dead write removal be able to<br>
+ * identify dead writes between multiple blocks.  This is still not<br>
+ * implemented.<br>
+ */<br>
+<br>
+TEST_F(nir_dead_write_vars_test, DISABLED_dead_write_in_two_blocks)<br>
+{<br>
+   nir_variable **v = create_many_int(nir_var_shader_storage, "v", 3);<br>
+<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);<br>
+   nir_ssa_def *load_v2 = nir_load_var(b, v[2]);<br>
+<br>
+   /* Causes the stores to be in different blocks. */<br>
+   nir_pop_if(b, nir_push_if(b, nir_imm_int(b, 0)));<br>
+<br>
+   nir_store_var(b, v[0], load_v2, 1);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_TRUE(progress);<br>
+<br>
+   EXPECT_EQ(1, count_intrinsics(nir_intrinsic_store_deref));<br>
+<br>
+   nir_intrinsic_instr *store = find_next_intrinsic(nir_intrinsic_store_deref, NULL);<br>
+   ASSERT_TRUE(store->src[1].is_ssa);<br>
+   EXPECT_EQ(store->src[1].ssa, load_v2);<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, DISABLED_dead_write_components_in_two_blocks)<br>
+{<br>
+   nir_variable **v = create_many_ivec2(nir_var_shader_storage, "v", 3);<br>
+<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1 << 0);<br>
+<br>
+   /* Causes the stores to be in different blocks. */<br>
+   nir_pop_if(b, nir_push_if(b, nir_imm_int(b, 0)));<br>
+<br>
+   nir_ssa_def *load_v2 = nir_load_var(b, v[2]);<br>
+   nir_store_var(b, v[0], load_v2, 1 << 0);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_TRUE(progress);<br>
+<br>
+   EXPECT_EQ(1, count_intrinsics(nir_intrinsic_store_deref));<br>
+<br>
+   nir_intrinsic_instr *store = find_next_intrinsic(nir_intrinsic_store_deref, NULL);<br>
+   ASSERT_TRUE(store->src[1].is_ssa);<br>
+   EXPECT_EQ(store->src[1].ssa, load_v2);<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, DISABLED_dead_writes_in_if_statement)<br>
+{<br>
+   nir_variable **v = create_many_int(nir_var_shader_storage, "v", 4);<br>
+<br>
+   /* Both branches will overwrite, making the previous store dead. */<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);<br>
+<br>
+   nir_if *if_stmt = nir_push_if(b, nir_imm_int(b, 0));<br></blockquote><div><br></div><div>Load something non-zero?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+   nir_ssa_def *load_v2 = nir_load_var(b, v[2]);<br>
+   nir_store_var(b, v[0], load_v2, 1);<br>
+<br>
+   nir_push_else(b, if_stmt);<br>
+   nir_ssa_def *load_v3 = nir_load_var(b, v[3]);<br>
+   nir_store_var(b, v[0], load_v3, 1);<br>
+<br>
+   nir_pop_if(b, if_stmt);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_TRUE(progress);<br>
+   EXPECT_EQ(2, count_intrinsics(nir_intrinsic_store_deref));<br>
+<br>
+   nir_intrinsic_instr *store = NULL;<br>
+   store = find_next_intrinsic(nir_intrinsic_store_deref, store);<br>
+   ASSERT_TRUE(store->src[1].is_ssa);<br>
+   EXPECT_EQ(store->src[1].ssa, load_v2);<br>
+<br>
+   store = find_next_intrinsic(nir_intrinsic_store_deref, store);<br>
+   ASSERT_TRUE(store->src[1].is_ssa);<br>
+   EXPECT_EQ(store->src[1].ssa, load_v3);<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, DISABLED_memory_barrier_in_two_blocks)<br>
+{<br>
+   nir_variable **v = create_many_int(nir_var_shader_storage, "v", 2);<br>
+<br>
+   nir_store_var(b, v[0], nir_imm_int(b, 1), 1);<br>
+   nir_store_var(b, v[1], nir_imm_int(b, 2), 1);<br>
+<br>
+   /* Split into many blocks. */<br>
+   nir_pop_if(b, nir_push_if(b, nir_imm_int(b, 0)));<br></blockquote><div><br></div><div>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.</div><div><br></div><div>Reviewed-by: Jason Ekstrand <<a href="mailto:jason@jlekstrand.net">jason@jlekstrand.net</a>><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+   /* Because it is before the barrier, this will kill the previous store to that target. */<br>
+   nir_store_var(b, v[0], nir_imm_int(b, 3), 1);<br>
+<br>
+   nir_builder_instr_insert(b, &nir_intrinsic_instr_create(b->shader, nir_intrinsic_memory_barrier)->instr);<br>
+<br>
+   nir_store_var(b, v[1], nir_imm_int(b, 4), 1);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_TRUE(progress);<br>
+<br>
+   EXPECT_EQ(3, count_intrinsics(nir_intrinsic_store_deref));<br>
+}<br>
+<br>
+TEST_F(nir_dead_write_vars_test, DISABLED_unrelated_barrier_in_two_blocks)<br>
+{<br>
+   nir_variable **v = create_many_int(nir_var_shader_storage, "v", 3);<br>
+   nir_variable *out = create_int(nir_var_shader_out, "out");<br>
+<br>
+   nir_store_var(b, out, nir_load_var(b, v[1]), 1);<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[1]), 1);<br>
+<br>
+   /* Split into many blocks. */<br>
+   nir_pop_if(b, nir_push_if(b, nir_imm_int(b, 0)));<br>
+<br>
+   /* Emit vertex will ensure writes to output variables are considered used,<br>
+    * but should not affect other types of variables. */<br>
+<br>
+   nir_builder_instr_insert(b, &nir_intrinsic_instr_create(b->shader, nir_intrinsic_emit_vertex)->instr);<br>
+<br>
+   nir_store_var(b, out, nir_load_var(b, v[2]), 1);<br>
+   nir_store_var(b, v[0], nir_load_var(b, v[2]), 1);<br>
+<br>
+   bool progress = nir_opt_dead_write_vars(b->shader);<br>
+   ASSERT_TRUE(progress);<br>
+<br>
+   /* Verify the first write to v[0] was removed. */<br>
+   EXPECT_EQ(3, count_intrinsics(nir_intrinsic_store_deref));<br>
+<br>
+   nir_intrinsic_instr *store = NULL;<br>
+   store = find_next_intrinsic(nir_intrinsic_store_deref, store);<br>
+   EXPECT_EQ(nir_intrinsic_get_var(store, 0), out);<br>
+   store = find_next_intrinsic(nir_intrinsic_store_deref, store);<br>
+   EXPECT_EQ(nir_intrinsic_get_var(store, 0), out);<br>
+   store = find_next_intrinsic(nir_intrinsic_store_deref, store);<br>
+   EXPECT_EQ(nir_intrinsic_get_var(store, 0), v[0]);<br>
+}<br>
-- <br>
2.19.0<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org" target="_blank">mesa-dev@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev" rel="noreferrer" target="_blank">https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</blockquote></div></div>