From ec8423a4b174450575152dfb3f9c80ba1b8729af Mon Sep 17 00:00:00 2001 From: Thomas Helland Date: Mon, 29 Aug 2016 10:02:34 +1000 Subject: [PATCH] nir: Add a LCSAA-pass V2: Do a "depth first search" to convert to LCSSA V3: Small comment fixup V4: Rebase, adapt to removal of function overloads V5: Rebase, adapt to relocation of nir to compiler/nir Still need to adapt to potential if-uses Work around nir_validate issue V6 (Timothy): - tidy lcssa and stop leaking memory - dont rewrite the src for the lcssa phi node - validate lcssa phi srcs to avoid postvalidate assert - don't add new phi if one already exists - more lcssa phi validation fixes - Rather than marking ssa defs inside a loop just mark blocks inside a loop. This is simpler and fixes lcssa for intrinsics which do not have a destination. - don't create LCSSA phis for loops we won't unroll - require loop metadata for lcssa pass - handle case were the ssa defs use outside the loop is already a phi V7: (Timothy) - pass indirect mask to metadata call v8: (Timothy) - make convert to lcssa a helper function rather than a nir pass - replace inside loop bitset with on the fly block index logic. - remove lcssa phi validation special cases - inline code from useless helpers, suggested by Jason. - always do lcssa on loops, suggested by Jason. - stop making lcssa phis special. Add as many source as the block has predecessors, suggested by Jason. V9: (Timothy) - fix regression with the is_lcssa_phi field not being initialised to false now that ralloc() doesn't zero out memory. V10: (Timothy) - remove extra braces in SSA example, pointed out by Topi V11: (Timothy) - add missing support for LCSSA phis in if conditions. V12: (Timothy) - small tidy up suggested by Jason. - always create lcssa phi even if it just points to an lcssa phi from an inner loop Reviewed-by: Jason Ekstrand --- src/compiler/Makefile.sources | 1 + src/compiler/nir/nir.h | 2 + src/compiler/nir/nir_to_lcssa.c | 203 ++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 src/compiler/nir/nir_to_lcssa.c diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources index edc70f7c4c2..f551449ed1c 100644 --- a/src/compiler/Makefile.sources +++ b/src/compiler/Makefile.sources @@ -256,6 +256,7 @@ NIR_FILES = \ nir/nir_split_var_copies.c \ nir/nir_sweep.c \ nir/nir_to_ssa.c \ + nir/nir_to_lcssa.c \ nir/nir_validate.c \ nir/nir_vla.h \ nir/nir_worklist.c \ diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h index c7da1e779db..21769463e7f 100644 --- a/src/compiler/nir/nir.h +++ b/src/compiler/nir/nir.h @@ -2526,6 +2526,8 @@ void nir_convert_to_ssa(nir_shader *shader); bool nir_repair_ssa_impl(nir_function_impl *impl); bool nir_repair_ssa(nir_shader *shader); +void nir_convert_loop_to_lcssa(nir_loop *loop); + /* If phi_webs_only is true, only convert SSA values involved in phi nodes to * registers. If false, convert all values (even those not involved in a phi * node) to registers. diff --git a/src/compiler/nir/nir_to_lcssa.c b/src/compiler/nir/nir_to_lcssa.c new file mode 100644 index 00000000000..9b3539193ea --- /dev/null +++ b/src/compiler/nir/nir_to_lcssa.c @@ -0,0 +1,203 @@ +/* + * Copyright © 2015 Thomas Helland + * + * 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. + */ + +/* + * This pass converts the ssa-graph into "Loop Closed SSA form". This is + * done by placing phi nodes at the exits of the loop for all values + * that are used outside the loop. The result is it transforms: + * + * loop { -> loop { + * ssa2 = .... -> ssa2 = ... + * if (cond) -> if (cond) + * break; -> break; + * ssa3 = ssa2 * ssa4 -> ssa3 = ssa2 * ssa4 + * } -> } + * ssa6 = ssa2 + 4 -> ssa5 = phi(ssa2) + * ssa6 = ssa5 + 4 + */ + +#include "nir.h" + +typedef struct { + /* The nir_shader we are transforming */ + nir_shader *shader; + + /* The loop we store information for */ + nir_loop *loop; + +} lcssa_state; + +static bool +is_if_use_inside_loop(nir_src *use, nir_loop* loop) +{ + nir_block *block_before_loop = + nir_cf_node_as_block(nir_cf_node_prev(&loop->cf_node)); + nir_block *block_after_loop = + nir_cf_node_as_block(nir_cf_node_next(&loop->cf_node)); + + nir_block *prev_block = + nir_cf_node_as_block(nir_cf_node_prev(&use->parent_if->cf_node)); + if (prev_block->index <= block_before_loop->index || + prev_block->index >= block_after_loop->index) { + return false; + } + + return true; +} + +static bool +is_use_inside_loop(nir_src *use, nir_loop* loop) +{ + nir_block *block_before_loop = + nir_cf_node_as_block(nir_cf_node_prev(&loop->cf_node)); + nir_block *block_after_loop = + nir_cf_node_as_block(nir_cf_node_next(&loop->cf_node)); + + if (use->parent_instr->block->index <= block_before_loop->index || + use->parent_instr->block->index >= block_after_loop->index) { + return false; + } + + return true; +} + +static bool +convert_loop_exit_for_ssa(nir_ssa_def *def, void *void_state) +{ + lcssa_state *state = void_state; + bool all_uses_inside_loop = true; + + nir_block *block_after_loop = + nir_cf_node_as_block(nir_cf_node_next(&state->loop->cf_node)); + + nir_foreach_use(use, def) { + if (use->parent_instr->type == nir_instr_type_phi && + use->parent_instr->block == block_after_loop) { + continue; + } + + if (!is_use_inside_loop(use, state->loop)) { + all_uses_inside_loop = false; + } + } + + nir_foreach_if_use(use, def) { + if (!is_if_use_inside_loop(use, state->loop)) { + all_uses_inside_loop = false; + } + } + + /* There where no sources that had defs outside the loop */ + if (all_uses_inside_loop) + return true; + + /* Initialize a phi-instruction */ + nir_phi_instr *phi = nir_phi_instr_create(state->shader); + nir_ssa_dest_init(&phi->instr, &phi->dest, + def->num_components, def->bit_size, "LCSSA-phi"); + + /* Create a phi node with as many sources pointing to the same ssa_def as + * the block has predecessors. + */ + struct set_entry *entry; + set_foreach(block_after_loop->predecessors, entry) { + nir_phi_src *phi_src = ralloc(phi, nir_phi_src); + phi_src->src = nir_src_for_ssa(def); + phi_src->pred = (nir_block *) entry->key; + + exec_list_push_tail(&phi->srcs, &phi_src->node); + } + + nir_instr_insert_before_block(block_after_loop, &phi->instr); + + /* Run through all uses and rewrite those outside the loop to point to + * the phi instead of pointing to the ssa-def. + */ + nir_foreach_use_safe(use, def) { + if (use->parent_instr->type == nir_instr_type_phi && + block_after_loop == use->parent_instr->block) { + continue; + } + + if (!is_use_inside_loop(use, state->loop)) { + nir_instr_rewrite_src(use->parent_instr, use, + nir_src_for_ssa(&phi->dest.ssa)); + } + } + + nir_foreach_if_use_safe(use, def) { + if (!is_if_use_inside_loop(use, state->loop)) { + nir_if_rewrite_condition(use->parent_if, + nir_src_for_ssa(&phi->dest.ssa)); + } + } + + return true; +} + +static void +convert_to_lcssa(nir_cf_node *cf_node, lcssa_state *state) +{ + switch (cf_node->type) { + case nir_cf_node_block: + nir_foreach_instr(instr, nir_cf_node_as_block(cf_node)) + nir_foreach_ssa_def(instr, convert_loop_exit_for_ssa, state); + return; + case nir_cf_node_if: { + nir_if *if_stmt = nir_cf_node_as_if(cf_node); + foreach_list_typed(nir_cf_node, nested_node, node, &if_stmt->then_list) + convert_to_lcssa(nested_node, state); + foreach_list_typed(nir_cf_node, nested_node, node, &if_stmt->else_list) + convert_to_lcssa(nested_node, state); + return; + } + case nir_cf_node_loop: { + nir_loop *parent_loop = state->loop; + state->loop = nir_cf_node_as_loop(cf_node); + + foreach_list_typed(nir_cf_node, nested_node, node, &state->loop->body) + convert_to_lcssa(nested_node, state); + + state->loop = parent_loop; + return; + } + default: + unreachable("unknown cf node type"); + } +} + +void +nir_convert_loop_to_lcssa(nir_loop *loop) { + nir_function_impl *impl = nir_cf_node_get_function(&loop->cf_node); + + nir_metadata_require(impl, nir_metadata_block_index); + + lcssa_state *state = rzalloc(NULL, lcssa_state); + state->loop = loop; + state->shader = impl->function->shader; + + foreach_list_typed(nir_cf_node, node, node, &state->loop->body) + convert_to_lcssa(node, state); + + ralloc_free(state); +} -- 2.30.2