2016-08-29 10:02:34 +10:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2015 Thomas Helland
|
2019-03-28 12:17:51 +01:00
|
|
|
* Copyright © 2019 Valve Corporation
|
2016-08-29 10:02:34 +10:00
|
|
|
*
|
|
|
|
|
* 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;
|
2020-01-13 14:32:18 +00:00
|
|
|
nir_block *block_after_loop;
|
|
|
|
|
nir_block **exit_blocks;
|
2016-08-29 10:02:34 +10:00
|
|
|
|
2019-03-28 12:17:51 +01:00
|
|
|
/* Whether to skip loop invariant variables */
|
|
|
|
|
bool skip_invariants;
|
2019-08-05 15:24:18 +01:00
|
|
|
bool skip_bool_invariants;
|
2019-03-28 12:17:51 +01:00
|
|
|
|
2019-01-31 16:33:23 +00:00
|
|
|
bool progress;
|
2016-08-29 10:02:34 +10:00
|
|
|
} lcssa_state;
|
|
|
|
|
|
|
|
|
|
static bool
|
2019-03-28 12:17:51 +01:00
|
|
|
is_if_use_inside_loop(nir_src *use, nir_loop *loop)
|
2016-08-29 10:02:34 +10:00
|
|
|
{
|
|
|
|
|
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
|
2019-03-28 12:17:51 +01:00
|
|
|
is_use_inside_loop(nir_src *use, nir_loop *loop)
|
2016-08-29 10:02:34 +10:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-28 12:17:51 +01:00
|
|
|
static bool
|
2023-08-12 16:17:15 -04:00
|
|
|
is_defined_before_loop(nir_def *def, nir_loop *loop)
|
2019-03-28 12:17:51 +01:00
|
|
|
{
|
|
|
|
|
nir_instr *instr = def->parent_instr;
|
|
|
|
|
nir_block *block_before_loop =
|
|
|
|
|
nir_cf_node_as_block(nir_cf_node_prev(&loop->cf_node));
|
|
|
|
|
|
|
|
|
|
return instr->block->index <= block_before_loop->index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef enum instr_invariance {
|
|
|
|
|
undefined = 0,
|
|
|
|
|
invariant,
|
|
|
|
|
not_invariant,
|
|
|
|
|
} instr_invariance;
|
|
|
|
|
|
|
|
|
|
static instr_invariance
|
|
|
|
|
instr_is_invariant(nir_instr *instr, nir_loop *loop);
|
|
|
|
|
|
|
|
|
|
static bool
|
2023-08-12 16:17:15 -04:00
|
|
|
def_is_invariant(nir_def *def, nir_loop *loop)
|
2019-03-28 12:17:51 +01:00
|
|
|
{
|
|
|
|
|
if (is_defined_before_loop(def, loop))
|
|
|
|
|
return invariant;
|
|
|
|
|
|
|
|
|
|
if (def->parent_instr->pass_flags == undefined)
|
|
|
|
|
def->parent_instr->pass_flags = instr_is_invariant(def->parent_instr, loop);
|
|
|
|
|
|
|
|
|
|
return def->parent_instr->pass_flags == invariant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
src_is_invariant(nir_src *src, void *state)
|
|
|
|
|
{
|
|
|
|
|
return def_is_invariant(src->ssa, (nir_loop *)state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static instr_invariance
|
|
|
|
|
phi_is_invariant(nir_phi_instr *instr, nir_loop *loop)
|
|
|
|
|
{
|
|
|
|
|
/* Base case: it's a phi at the loop header
|
|
|
|
|
* Loop-header phis are updated in each loop iteration with
|
|
|
|
|
* the loop-carried value, and thus control-flow dependent
|
|
|
|
|
* on the loop itself.
|
|
|
|
|
*/
|
|
|
|
|
if (instr->instr.block == nir_loop_first_block(loop))
|
|
|
|
|
return not_invariant;
|
|
|
|
|
|
|
|
|
|
nir_foreach_phi_src(src, instr) {
|
|
|
|
|
if (!src_is_invariant(&src->src, loop))
|
|
|
|
|
return not_invariant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* All loop header- and LCSSA-phis should be handled by this point. */
|
|
|
|
|
nir_cf_node *prev = nir_cf_node_prev(&instr->instr.block->cf_node);
|
|
|
|
|
assert(prev && prev->type == nir_cf_node_if);
|
|
|
|
|
|
|
|
|
|
/* Invariance of phis after if-nodes also depends on the invariance
|
|
|
|
|
* of the branch condition.
|
|
|
|
|
*/
|
|
|
|
|
nir_if *if_node = nir_cf_node_as_if(prev);
|
|
|
|
|
if (!def_is_invariant(if_node->condition.ssa, loop))
|
|
|
|
|
return not_invariant;
|
|
|
|
|
|
|
|
|
|
return invariant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* An instruction is said to be loop-invariant if it
|
|
|
|
|
* - has no sideeffects and
|
|
|
|
|
* - solely depends on variables defined outside of the loop or
|
|
|
|
|
* by other invariant instructions
|
|
|
|
|
*/
|
|
|
|
|
static instr_invariance
|
|
|
|
|
instr_is_invariant(nir_instr *instr, nir_loop *loop)
|
|
|
|
|
{
|
|
|
|
|
assert(instr->pass_flags == undefined);
|
|
|
|
|
|
|
|
|
|
switch (instr->type) {
|
|
|
|
|
case nir_instr_type_load_const:
|
|
|
|
|
case nir_instr_type_ssa_undef:
|
|
|
|
|
return invariant;
|
|
|
|
|
case nir_instr_type_call:
|
|
|
|
|
return not_invariant;
|
|
|
|
|
case nir_instr_type_phi:
|
|
|
|
|
return phi_is_invariant(nir_instr_as_phi(instr), loop);
|
|
|
|
|
case nir_instr_type_intrinsic: {
|
|
|
|
|
nir_intrinsic_instr *intrinsic = nir_instr_as_intrinsic(instr);
|
|
|
|
|
if (!(nir_intrinsic_infos[intrinsic->intrinsic].flags & NIR_INTRINSIC_CAN_REORDER))
|
|
|
|
|
return not_invariant;
|
|
|
|
|
}
|
2023-08-08 12:00:35 -05:00
|
|
|
FALLTHROUGH;
|
2019-03-28 12:17:51 +01:00
|
|
|
default:
|
|
|
|
|
return nir_foreach_src(instr, src_is_invariant, loop) ? invariant : not_invariant;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return invariant;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 10:02:34 +10:00
|
|
|
static bool
|
2023-08-12 16:17:15 -04:00
|
|
|
convert_loop_exit_for_ssa(nir_def *def, void *void_state)
|
2016-08-29 10:02:34 +10:00
|
|
|
{
|
|
|
|
|
lcssa_state *state = void_state;
|
|
|
|
|
bool all_uses_inside_loop = true;
|
|
|
|
|
|
2019-03-28 12:17:51 +01:00
|
|
|
/* Don't create LCSSA-Phis for loop-invariant variables */
|
2019-08-05 15:24:18 +01:00
|
|
|
if (state->skip_invariants &&
|
|
|
|
|
(def->bit_size != 1 || state->skip_bool_invariants)) {
|
2019-03-28 12:17:51 +01:00
|
|
|
assert(def->parent_instr->pass_flags != undefined);
|
|
|
|
|
if (def->parent_instr->pass_flags == invariant)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 13:19:31 -04:00
|
|
|
nir_foreach_use_including_if(use, def) {
|
|
|
|
|
if (use->is_if) {
|
|
|
|
|
if (!is_if_use_inside_loop(use, state->loop))
|
|
|
|
|
all_uses_inside_loop = false;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 10:02:34 +10:00
|
|
|
if (use->parent_instr->type == nir_instr_type_phi &&
|
2020-01-13 14:32:18 +00:00
|
|
|
use->parent_instr->block == state->block_after_loop) {
|
2016-08-29 10:02:34 +10:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_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: Drop unused name from nir_ssa_dest_init
Since 624e799cc34 ("nir: Drop nir_ssa_def::name and nir_register::name"), SSA
defs don't have names, making the name argument unused. Drop it from the
signature and fix the call sites. This was done with the help of the following
Coccinelle semantic patch:
@@
expression A, B, C, D, E;
@@
-nir_ssa_dest_init(A, B, C, D, E);
+nir_ssa_dest_init(A, B, C, D);
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Reviewed-by: Timur Kristóf <timur.kristof@gmail.com>
Reviewed-by: Emma Anholt <emma@anholt.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23078>
2023-05-17 09:08:22 -04:00
|
|
|
nir_ssa_dest_init(&phi->instr, &phi->dest, def->num_components,
|
|
|
|
|
def->bit_size);
|
2016-08-29 10:02:34 +10:00
|
|
|
|
|
|
|
|
/* Create a phi node with as many sources pointing to the same ssa_def as
|
|
|
|
|
* the block has predecessors.
|
|
|
|
|
*/
|
2020-01-13 14:32:18 +00:00
|
|
|
uint32_t num_exits = state->block_after_loop->predecessors->entries;
|
|
|
|
|
for (uint32_t i = 0; i < num_exits; i++) {
|
2021-07-07 13:24:45 -07:00
|
|
|
nir_phi_instr_add_src(phi, state->exit_blocks[i], nir_src_for_ssa(def));
|
2016-08-29 10:02:34 +10:00
|
|
|
}
|
|
|
|
|
|
2020-01-13 14:32:18 +00:00
|
|
|
nir_instr_insert_before_block(state->block_after_loop, &phi->instr);
|
2023-08-12 16:17:15 -04:00
|
|
|
nir_def *dest = &phi->dest.ssa;
|
2019-08-14 18:52:50 +02:00
|
|
|
|
|
|
|
|
/* deref instructions need a cast after the phi */
|
|
|
|
|
if (def->parent_instr->type == nir_instr_type_deref) {
|
|
|
|
|
nir_deref_instr *cast =
|
|
|
|
|
nir_deref_instr_create(state->shader, nir_deref_type_cast);
|
|
|
|
|
|
|
|
|
|
nir_deref_instr *instr = nir_instr_as_deref(def->parent_instr);
|
2020-10-30 12:14:05 -05:00
|
|
|
cast->modes = instr->modes;
|
2019-08-14 18:52:50 +02:00
|
|
|
cast->type = instr->type;
|
|
|
|
|
cast->parent = nir_src_for_ssa(&phi->dest.ssa);
|
2020-08-27 11:59:54 -05:00
|
|
|
cast->cast.ptr_stride = nir_deref_instr_array_stride(instr);
|
2019-08-14 18:52:50 +02:00
|
|
|
|
|
|
|
|
nir_ssa_dest_init(&cast->instr, &cast->dest,
|
nir: Drop unused name from nir_ssa_dest_init
Since 624e799cc34 ("nir: Drop nir_ssa_def::name and nir_register::name"), SSA
defs don't have names, making the name argument unused. Drop it from the
signature and fix the call sites. This was done with the help of the following
Coccinelle semantic patch:
@@
expression A, B, C, D, E;
@@
-nir_ssa_dest_init(A, B, C, D, E);
+nir_ssa_dest_init(A, B, C, D);
Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Reviewed-by: Timur Kristóf <timur.kristof@gmail.com>
Reviewed-by: Emma Anholt <emma@anholt.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23078>
2023-05-17 09:08:22 -04:00
|
|
|
phi->dest.ssa.num_components, phi->dest.ssa.bit_size);
|
2020-01-13 14:32:18 +00:00
|
|
|
nir_instr_insert(nir_after_phis(state->block_after_loop), &cast->instr);
|
2019-08-14 18:52:50 +02:00
|
|
|
dest = &cast->dest.ssa;
|
|
|
|
|
}
|
2016-08-29 10:02:34 +10:00
|
|
|
|
|
|
|
|
/* Run through all uses and rewrite those outside the loop to point to
|
|
|
|
|
* the phi instead of pointing to the ssa-def.
|
|
|
|
|
*/
|
2023-04-06 13:19:31 -04:00
|
|
|
nir_foreach_use_including_if_safe(use, def) {
|
|
|
|
|
if (use->is_if) {
|
|
|
|
|
if (!is_if_use_inside_loop(use, state->loop))
|
|
|
|
|
nir_if_rewrite_condition(use->parent_if, nir_src_for_ssa(dest));
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 10:02:34 +10:00
|
|
|
if (use->parent_instr->type == nir_instr_type_phi &&
|
2020-01-13 14:32:18 +00:00
|
|
|
state->block_after_loop == use->parent_instr->block) {
|
2016-08-29 10:02:34 +10:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_use_inside_loop(use, state->loop)) {
|
2019-08-14 18:52:50 +02:00
|
|
|
nir_instr_rewrite_src(use->parent_instr, use, nir_src_for_ssa(dest));
|
2016-08-29 10:02:34 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-31 16:33:23 +00:00
|
|
|
state->progress = true;
|
2016-08-29 10:02:34 +10:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-13 14:32:18 +00:00
|
|
|
static void
|
|
|
|
|
setup_loop_state(lcssa_state *state, nir_loop *loop)
|
|
|
|
|
{
|
|
|
|
|
state->loop = loop;
|
|
|
|
|
state->block_after_loop =
|
|
|
|
|
nir_cf_node_as_block(nir_cf_node_next(&loop->cf_node));
|
|
|
|
|
|
|
|
|
|
ralloc_free(state->exit_blocks);
|
|
|
|
|
state->exit_blocks = nir_block_get_predecessors_sorted(state->block_after_loop, state);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 10:02:34 +10:00
|
|
|
static void
|
|
|
|
|
convert_to_lcssa(nir_cf_node *cf_node, lcssa_state *state)
|
|
|
|
|
{
|
|
|
|
|
switch (cf_node->type) {
|
|
|
|
|
case nir_cf_node_block:
|
|
|
|
|
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: {
|
2019-03-28 12:17:51 +01:00
|
|
|
if (state->skip_invariants) {
|
|
|
|
|
nir_foreach_block_in_cf_node(block, cf_node) {
|
|
|
|
|
nir_foreach_instr(instr, block)
|
|
|
|
|
instr->pass_flags = undefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-29 10:02:34 +10:00
|
|
|
|
2019-03-28 12:17:51 +01:00
|
|
|
/* first, convert inner loops */
|
|
|
|
|
nir_loop *loop = nir_cf_node_as_loop(cf_node);
|
2021-12-02 10:31:56 +01:00
|
|
|
assert(!nir_loop_has_continue_construct(loop));
|
2019-03-28 12:17:51 +01:00
|
|
|
foreach_list_typed(nir_cf_node, nested_node, node, &loop->body)
|
2016-08-29 10:02:34 +10:00
|
|
|
convert_to_lcssa(nested_node, state);
|
|
|
|
|
|
2020-01-13 14:32:18 +00:00
|
|
|
setup_loop_state(state, loop);
|
2020-07-10 13:37:36 +02:00
|
|
|
|
2019-03-28 12:17:51 +01:00
|
|
|
/* mark loop-invariant instructions */
|
|
|
|
|
if (state->skip_invariants) {
|
2020-07-10 13:37:36 +02:00
|
|
|
/* Without a loop all instructions are invariant.
|
|
|
|
|
* For outer loops, multiple breaks can still create phis.
|
|
|
|
|
* The variance then depends on all (nested) break conditions.
|
|
|
|
|
* We don't consider this, but assume all not_invariant.
|
|
|
|
|
*/
|
|
|
|
|
if (nir_loop_first_block(loop)->predecessors->entries == 1)
|
|
|
|
|
goto end;
|
|
|
|
|
|
2019-03-28 12:17:51 +01:00
|
|
|
nir_foreach_block_in_cf_node(block, cf_node) {
|
|
|
|
|
nir_foreach_instr(instr, block) {
|
|
|
|
|
if (instr->pass_flags == undefined)
|
|
|
|
|
instr->pass_flags = instr_is_invariant(instr, nir_cf_node_as_loop(cf_node));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nir_foreach_block_in_cf_node(block, cf_node) {
|
|
|
|
|
nir_foreach_instr(instr, block) {
|
|
|
|
|
nir_foreach_ssa_def(instr, convert_loop_exit_for_ssa, state);
|
|
|
|
|
|
|
|
|
|
/* for outer loops, invariant instructions can be variant */
|
|
|
|
|
if (state->skip_invariants && instr->pass_flags == invariant)
|
|
|
|
|
instr->pass_flags = undefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-08 12:00:35 -05:00
|
|
|
end:
|
2019-03-28 12:17:51 +01:00
|
|
|
/* For outer loops, the LCSSA-phi should be considered not invariant */
|
|
|
|
|
if (state->skip_invariants) {
|
2020-01-13 14:32:18 +00:00
|
|
|
nir_foreach_instr(instr, state->block_after_loop) {
|
2019-03-28 12:17:51 +01:00
|
|
|
if (instr->type == nir_instr_type_phi)
|
|
|
|
|
instr->pass_flags = not_invariant;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-29 10:02:34 +10:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
unreachable("unknown cf node type");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-01-31 16:33:23 +00:00
|
|
|
nir_convert_loop_to_lcssa(nir_loop *loop)
|
|
|
|
|
{
|
2021-12-02 10:31:56 +01:00
|
|
|
assert(!nir_loop_has_continue_construct(loop));
|
2016-08-29 10:02:34 +10:00
|
|
|
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);
|
2020-01-13 14:32:18 +00:00
|
|
|
setup_loop_state(state, loop);
|
2016-08-29 10:02:34 +10:00
|
|
|
state->shader = impl->function->shader;
|
2019-03-28 12:17:51 +01:00
|
|
|
state->skip_invariants = false;
|
2019-08-05 15:24:18 +01:00
|
|
|
state->skip_bool_invariants = false;
|
2016-08-29 10:02:34 +10:00
|
|
|
|
2023-08-08 12:00:35 -05:00
|
|
|
nir_foreach_block_in_cf_node(block, &loop->cf_node) {
|
2019-03-28 12:17:51 +01:00
|
|
|
nir_foreach_instr(instr, block)
|
|
|
|
|
nir_foreach_ssa_def(instr, convert_loop_exit_for_ssa, state);
|
|
|
|
|
}
|
2016-08-29 10:02:34 +10:00
|
|
|
|
|
|
|
|
ralloc_free(state);
|
|
|
|
|
}
|
2019-01-31 16:33:23 +00:00
|
|
|
|
|
|
|
|
bool
|
2019-08-05 15:24:18 +01:00
|
|
|
nir_convert_to_lcssa(nir_shader *shader, bool skip_invariants, bool skip_bool_invariants)
|
2019-01-31 16:33:23 +00:00
|
|
|
{
|
|
|
|
|
bool progress = false;
|
|
|
|
|
lcssa_state *state = rzalloc(NULL, lcssa_state);
|
|
|
|
|
state->shader = shader;
|
2019-03-28 12:17:51 +01:00
|
|
|
state->skip_invariants = skip_invariants;
|
2019-08-05 15:24:18 +01:00
|
|
|
state->skip_bool_invariants = skip_bool_invariants;
|
2019-01-31 16:33:23 +00:00
|
|
|
|
2023-06-22 13:27:59 -04:00
|
|
|
nir_foreach_function_impl(impl, shader) {
|
2019-01-31 16:33:23 +00:00
|
|
|
state->progress = false;
|
2023-06-22 13:27:59 -04:00
|
|
|
nir_metadata_require(impl, nir_metadata_block_index);
|
2019-01-31 16:33:23 +00:00
|
|
|
|
2023-06-22 13:27:59 -04:00
|
|
|
foreach_list_typed(nir_cf_node, node, node, &impl->body)
|
2019-01-31 16:33:23 +00:00
|
|
|
convert_to_lcssa(node, state);
|
|
|
|
|
|
|
|
|
|
if (state->progress) {
|
|
|
|
|
progress = true;
|
2023-06-22 13:27:59 -04:00
|
|
|
nir_metadata_preserve(impl, nir_metadata_block_index |
|
2023-08-08 12:00:35 -05:00
|
|
|
nir_metadata_dominance);
|
2019-01-31 16:33:23 +00:00
|
|
|
} else {
|
2023-06-22 13:27:59 -04:00
|
|
|
nir_metadata_preserve(impl, nir_metadata_all);
|
2019-01-31 16:33:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ralloc_free(state);
|
|
|
|
|
return progress;
|
|
|
|
|
}
|