For simple loop terminators we can evaluate all further uses of the condition in the loop because we know we must have either exited the loop or we have a known value.
shader-db results IVB (all changes from dolphin uber shaders): total instructions in shared programs: 10022822 -> 10018187 (-0.05%) instructions in affected programs: 115380 -> 110745 (-4.02%) helped: 54 HURT: 0 total cycles in shared programs: 232376154 -> 220065064 (-5.30%) cycles in affected programs: 143176202 -> 130865112 (-8.60%) helped: 54 HURT: 0 total spills in shared programs: 4383 -> 4370 (-0.30%) spills in affected programs: 1656 -> 1643 (-0.79%) helped: 9 HURT: 18 total fills in shared programs: 4610 -> 4581 (-0.63%) fills in affected programs: 374 -> 345 (-7.75%) helped: 6 HURT: 0 --- src/compiler/nir/nir_opt_if.c | 115 ++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/src/compiler/nir/nir_opt_if.c b/src/compiler/nir/nir_opt_if.c index 8863c42d9e7..83f2141dff6 100644 --- a/src/compiler/nir/nir_opt_if.c +++ b/src/compiler/nir/nir_opt_if.c @@ -430,6 +430,98 @@ opt_if_evaluate_condition_use(nir_if *nif, void *mem_ctx) return progress; } +static bool +evaluate_term_condition_use(unsigned prev_blk_idx, + unsigned after_loop_blk_idx, + unsigned nir_boolean, + nir_src *use_src, void *mem_ctx, + bool if_condition) +{ + bool progress = false; + + if (if_condition) { + unsigned blk_idx_before_if = + nir_cf_node_as_block(nir_cf_node_prev(&use_src->parent_if->cf_node))->index; + if (prev_blk_idx <= blk_idx_before_if && + after_loop_blk_idx > blk_idx_before_if) { + replace_if_condition_use_with_const(use_src, nir_boolean, mem_ctx, + if_condition); + progress = true; + } + + } else { + unsigned use_blk_idx = use_src->parent_instr->block->index; + if (prev_blk_idx < use_blk_idx && after_loop_blk_idx > use_blk_idx) { + replace_if_condition_use_with_const(use_src, nir_boolean, mem_ctx, + if_condition); + progress = true; + } + } + + return progress; +} + +/** + * Since we know loop terminators either exit the loop or continue we can + * evaluate any further uses of the if-statements condition in the continue + * path. + */ +static bool +opt_if_evaluate_condition_use_loop_terminator(nir_if *nif, nir_loop *loop, + void *mem_ctx) +{ + if (!loop) + return false; + + nir_block *break_blk = NULL; + bool continue_from_then = true; + + nir_block *last_then = nir_if_last_then_block(nif); + nir_block *last_else = nir_if_last_else_block(nif); + + if (nir_block_ends_in_break(last_then)) { + break_blk = last_then; + continue_from_then = false; + } else if (nir_block_ends_in_break(last_else)) { + break_blk = last_else; + } + + /* Continue if the if-statement contained no jumps at all */ + if (!break_blk) + return false; + + if (!nir_is_trivial_loop_if(nif, break_blk)) + return false; + + const unsigned nir_boolean = continue_from_then ? NIR_TRUE : NIR_FALSE; + bool progress = false; + + nir_block *prev_block = + nir_cursor_current_block(nir_before_cf_node(&nif->cf_node)); + + nir_block *after_loop = + nir_cursor_current_block(nir_after_cf_node(&loop->cf_node)); + + /* Evaluate any uses of the loop terminator condition */ + assert(nif->condition.is_ssa); + nir_foreach_use_safe(use_src, nif->condition.ssa) { + progress = + evaluate_term_condition_use(prev_block->index, after_loop->index, + nir_boolean, use_src, mem_ctx, false); + } + + nir_foreach_if_use_safe(use_src, nif->condition.ssa) { + if (use_src->parent_if != nif) { + progress = + evaluate_term_condition_use(prev_block->index, after_loop->index, + nir_boolean, use_src, mem_ctx, true); + } + } + + + return progress; +} + static bool opt_if_cf_list(nir_builder *b, struct exec_list *cf_list) { @@ -468,9 +560,11 @@ opt_if_cf_list(nir_builder *b, struct exec_list *cf_list) * not do anything to cause the metadata to become invalid. */ static bool -opt_if_safe_cf_list(nir_builder *b, struct exec_list *cf_list, void *mem_ctx) +opt_if_safe_cf_list(nir_builder *b, struct exec_list *cf_list, + nir_loop *curr_loop, void *mem_ctx) { bool progress = false; + foreach_list_typed(nir_cf_node, cf_node, node, cf_list) { switch (cf_node->type) { case nir_cf_node_block: @@ -478,15 +572,23 @@ opt_if_safe_cf_list(nir_builder *b, struct exec_list *cf_list, void *mem_ctx) case nir_cf_node_if: { nir_if *nif = nir_cf_node_as_if(cf_node); - progress |= opt_if_safe_cf_list(b, &nif->then_list, mem_ctx); - progress |= opt_if_safe_cf_list(b, &nif->else_list, mem_ctx); + progress |= opt_if_safe_cf_list(b, &nif->then_list, curr_loop, + mem_ctx); + progress |= opt_if_safe_cf_list(b, &nif->else_list, curr_loop, + mem_ctx); progress |= opt_if_evaluate_condition_use(nif, mem_ctx); + progress |= opt_if_evaluate_condition_use_loop_terminator(nif, + curr_loop, + mem_ctx); break; } case nir_cf_node_loop: { - nir_loop *loop = nir_cf_node_as_loop(cf_node); - progress |= opt_if_safe_cf_list(b, &loop->body, mem_ctx); + nir_loop *prev_loop = curr_loop; + nir_loop *curr_loop = nir_cf_node_as_loop(cf_node); + progress |= opt_if_safe_cf_list(b, &curr_loop->body, curr_loop, + mem_ctx); + curr_loop = prev_loop; break; } @@ -513,7 +615,8 @@ nir_opt_if(nir_shader *shader) void *mem_ctx = ralloc_parent(function->impl); nir_metadata_require(function->impl, nir_metadata_block_index); - progress = opt_if_safe_cf_list(&b, &function->impl->body, mem_ctx); + progress = opt_if_safe_cf_list(&b, &function->impl->body, NULL, + mem_ctx); nir_metadata_preserve(function->impl, nir_metadata_block_index); if (opt_if_cf_list(&b, &function->impl->body)) { -- 2.17.1 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev