diff --git a/src/compiler/nir/meson.build b/src/compiler/nir/meson.build index 9102307f388..02c45512d8d 100644 --- a/src/compiler/nir/meson.build +++ b/src/compiler/nir/meson.build @@ -392,6 +392,19 @@ if with_tests suite : ['compiler', 'nir'], ) + test( + 'nir_core', + executable( + 'nir_core_test', + files('tests/core_tests.cpp'), + cpp_args : [cpp_msvc_compat_args], + gnu_symbol_visibility : 'hidden', + include_directories : [inc_include, inc_src, inc_mapi, inc_mesa, inc_gallium, inc_gallium_aux], + dependencies : [dep_thread, idep_gtest, idep_nir, idep_mesautil], + ), + suite : ['compiler', 'nir'], + ) + test( 'nir_vars', executable( diff --git a/src/compiler/nir/nir.c b/src/compiler/nir/nir.c index b18426a10be..9fef5b1f71d 100644 --- a/src/compiler/nir/nir.c +++ b/src/compiler/nir/nir.c @@ -28,6 +28,7 @@ #include "nir.h" #include "nir_builder.h" #include "nir_control_flow_private.h" +#include "nir_worklist.h" #include "util/half_float.h" #include #include @@ -1069,6 +1070,75 @@ void nir_instr_remove_v(nir_instr *instr) } } +static bool nir_instr_remove_and_dce_live_cb(nir_ssa_def *def, void *state) +{ + bool *live = state; + + if (!nir_ssa_def_is_unused(def)) { + *live = true; + return false; + } else { + return true; + } +} + +static bool nir_instr_remove_and_dce_is_live(nir_instr *instr) +{ + /* Note: don't have to worry about jumps because they don't have dests to + * become unused. + */ + if (instr->type == nir_instr_type_intrinsic) { + nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr); + const nir_intrinsic_info *info = &nir_intrinsic_infos[intr->intrinsic]; + if (!(info->flags & NIR_INTRINSIC_CAN_ELIMINATE)) + return true; + } + + bool live = false; + nir_foreach_ssa_def(instr, nir_instr_remove_and_dce_live_cb, &live); + return live; +} + +/** + * Removes an instruction and any SSA defs that it used that are now dead, returning a nir_cursor + * where the instruction previously was. + */ +nir_cursor +nir_instr_remove_and_dce(nir_instr *instr) +{ + nir_instr_worklist *worklist = nir_instr_worklist_create(); + + nir_instr_worklist_add_ssa_srcs(worklist, instr); + nir_cursor c = nir_instr_remove(instr); + + nir_instr *dce_instr; + while ((dce_instr = nir_instr_worklist_pop_head(worklist))) { + /* Instrs can be in the worklist multiple times, so check + * that we haven't already removed this one. + */ + if (exec_node_is_tail_sentinel(&dce_instr->node)) + continue; + + if (!nir_instr_remove_and_dce_is_live(dce_instr)) { + nir_instr_worklist_add_ssa_srcs(worklist, dce_instr); + + /* If we're removing the instr where our cursor is, then we have to + * point the cursor elsewhere. + */ + if ((c.option == nir_cursor_before_instr || + c.option == nir_cursor_after_instr) && + c.instr == dce_instr) + c = nir_instr_remove(dce_instr); + else + nir_instr_remove(dce_instr); + } + } + + nir_instr_worklist_destroy(worklist); + + return c; +} + /*@}*/ void diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index 4623492dd9c..d45c5505e68 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -3951,6 +3951,8 @@ nir_instr_remove(nir_instr *instr) return cursor; } +nir_cursor nir_instr_remove_and_dce(nir_instr *instr); + /** @} */ nir_ssa_def *nir_instr_ssa_def(nir_instr *instr); diff --git a/src/compiler/nir/nir_worklist.c b/src/compiler/nir/nir_worklist.c index 75e9426daee..0c20d6ddce2 100644 --- a/src/compiler/nir/nir_worklist.c +++ b/src/compiler/nir/nir_worklist.c @@ -136,3 +136,20 @@ nir_block_worklist_pop_tail(nir_block_worklist *w) BITSET_CLEAR(w->blocks_present, w->blocks[tail]->index); return w->blocks[tail]; } + +static bool +nir_instr_worklist_add_srcs_cb(nir_src *src, void *state) +{ + nir_instr_worklist *wl = state; + + if (src->is_ssa) + nir_instr_worklist_push_tail(wl, src->ssa->parent_instr); + + return true; +} + +void +nir_instr_worklist_add_ssa_srcs(nir_instr_worklist *wl, nir_instr *instr) +{ + nir_foreach_src(instr, nir_instr_worklist_add_srcs_cb, wl); +} diff --git a/src/compiler/nir/nir_worklist.h b/src/compiler/nir/nir_worklist.h index 36014cc7752..0f402e080fd 100644 --- a/src/compiler/nir/nir_worklist.h +++ b/src/compiler/nir/nir_worklist.h @@ -154,6 +154,9 @@ nir_instr_worklist_pop_head(nir_instr_worklist *wl) return *vec_instr; } +void +nir_instr_worklist_add_ssa_srcs(nir_instr_worklist *wl, nir_instr *instr); + #define nir_foreach_instr_in_worklist(instr, wl) \ for (nir_instr *instr; (instr = nir_instr_worklist_pop_head(wl));) diff --git a/src/compiler/nir/tests/core_tests.cpp b/src/compiler/nir/tests/core_tests.cpp new file mode 100644 index 00000000000..b1c5355eda5 --- /dev/null +++ b/src/compiler/nir/tests/core_tests.cpp @@ -0,0 +1,126 @@ +/* + * Copyright © 2018 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "nir.h" +#include "nir_builder.h" + +namespace { + +class nir_core_test : public ::testing::Test { +protected: + nir_core_test(); + ~nir_core_test(); + + bool shader_contains_def(nir_ssa_def *def); + + nir_builder *b, _b; +}; + +nir_core_test::nir_core_test() +{ + glsl_type_singleton_init_or_ref(); + + static const nir_shader_compiler_options options = { }; + _b = nir_builder_init_simple_shader(MESA_SHADER_COMPUTE, &options, "builder test"); + b = &_b; +} + +nir_core_test::~nir_core_test() +{ + if (HasFailure()) { + printf("\nShader from the failed test:\n\n"); + nir_print_shader(b->shader, stdout); + } + + ralloc_free(b->shader); + + glsl_type_singleton_decref(); +} + +struct contains_def_state { + nir_ssa_def *def; + bool found; +}; + +static bool +contains_def_cb(nir_ssa_def *def, void *_state) +{ + struct contains_def_state *state = (struct contains_def_state *)_state; + if (def == state->def) + state->found = true; + + return true; +} + +bool +nir_core_test::shader_contains_def(nir_ssa_def *def) +{ + nir_foreach_block(block, b->impl) { + nir_foreach_instr(instr, block) { + struct contains_def_state state = { + .def = def, + .found = false, + }; + nir_foreach_ssa_def(instr, contains_def_cb, &state); + if (state.found) + return true; + } + } + return false; +} + +TEST_F(nir_core_test, nir_instr_remove_and_dce_test) +{ + nir_ssa_def *zero = nir_imm_int(b, 0); + nir_ssa_def *one = nir_imm_int(b, 1); + nir_ssa_def *add01 = nir_iadd(b, zero, one); + nir_ssa_def *add11 = nir_iadd(b, one, one); + + nir_cursor c = nir_instr_remove_and_dce(add01->parent_instr); + ASSERT_FALSE(shader_contains_def(add01)); + ASSERT_TRUE(shader_contains_def(add11)); + ASSERT_FALSE(shader_contains_def(zero)); + ASSERT_TRUE(shader_contains_def(one)); + + ASSERT_TRUE(nir_cursors_equal(c, nir_before_instr(add11->parent_instr))); + + nir_validate_shader(b->shader, "after remove_and_dce"); +} + +TEST_F(nir_core_test, nir_instr_remove_and_dce_all_test) +{ + nir_ssa_def *one = nir_imm_int(b, 1); + nir_ssa_def *add = nir_iadd(b, one, one); + + nir_cursor c = nir_instr_remove_and_dce(add->parent_instr); + ASSERT_FALSE(shader_contains_def(add)); + ASSERT_FALSE(shader_contains_def(one)); + + ASSERT_TRUE(nir_cursors_equal(c, nir_before_block(nir_start_block(b->impl)))); + + nir_validate_shader(b->shader, "after remove_and_dce"); +} + +}