mesa/src/compiler/nir/nir_control_flow.c
Alyssa Rosenzweig 7f6491b76d nir: Combine if_uses with instruction uses
Every nir_ssa_def is part of a chain of uses, implemented with doubly linked
lists.  That means each requires 2 * 64-bit = 16 bytes per def, which is
memory intensive. Together they require 32 bytes per def. Not cool.

To cut that memory use in half, we can combine the two linked lists into a
single use list that contains both regular instruction uses and if-uses. To do
this, we augment the nir_src with a boolean "is_if", and reimplement the
abstract if-uses operations on top of that list. That boolean should fit into
the padding already in nir_src so should not actually affect memory use, and in
the future we sneak it into the bottom bit of a pointer.

However, this creates a new inefficiency: now iterating over regular uses
separate from if-uses is (nominally) more expensive. It turns out virtually
every caller of nir_foreach_if_use(_safe) also calls nir_foreach_use(_safe)
immediately before, so we rewrite most of the callers to instead call a new
single `nir_foreach_use_including_if(_safe)` which predicates the logic based on
`src->is_if`. This should mitigate the performance difference.

There's a bit of churn, but this is largely a mechanical set of changes.

Signed-off-by: Alyssa Rosenzweig <alyssa@collabora.com>
Reviewed-by: Faith Ekstrand <faith.ekstrand@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/22343>
2023-04-07 23:48:03 +00:00

881 lines
27 KiB
C

/*
* Copyright © 2014 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.
*
* Authors:
* Connor Abbott (cwabbott0@gmail.com)
*
*/
#include "nir_control_flow_private.h"
/**
* \name Control flow modification
*
* These functions modify the control flow tree while keeping the control flow
* graph up-to-date. The invariants respected are:
* 1. Each then statement, else statement, or loop body must have at least one
* control flow node.
* 2. Each if-statement and loop must have one basic block before it and one
* after.
* 3. Two basic blocks cannot be directly next to each other.
* 4. If a basic block has a jump instruction, there must be only one and it
* must be at the end of the block.
*
* The purpose of the second one is so that we have places to insert code during
* GCM, as well as eliminating the possibility of critical edges.
*/
/*@{*/
static inline void
block_add_pred(nir_block *block, nir_block *pred)
{
_mesa_set_add(block->predecessors, pred);
}
static inline void
block_remove_pred(nir_block *block, nir_block *pred)
{
struct set_entry *entry = _mesa_set_search(block->predecessors, pred);
assert(entry);
_mesa_set_remove(block->predecessors, entry);
}
static void
link_blocks(nir_block *pred, nir_block *succ1, nir_block *succ2)
{
pred->successors[0] = succ1;
if (succ1 != NULL)
block_add_pred(succ1, pred);
pred->successors[1] = succ2;
if (succ2 != NULL)
block_add_pred(succ2, pred);
}
static void
unlink_blocks(nir_block *pred, nir_block *succ)
{
if (pred->successors[0] == succ) {
pred->successors[0] = pred->successors[1];
pred->successors[1] = NULL;
} else {
assert(pred->successors[1] == succ);
pred->successors[1] = NULL;
}
block_remove_pred(succ, pred);
}
static void
unlink_block_successors(nir_block *block)
{
if (block->successors[1] != NULL)
unlink_blocks(block, block->successors[1]);
if (block->successors[0] != NULL)
unlink_blocks(block, block->successors[0]);
}
static void
link_non_block_to_block(nir_cf_node *node, nir_block *block)
{
if (node->type == nir_cf_node_if) {
/*
* We're trying to link an if to a block after it; this just means linking
* the last block of the then and else branches.
*/
nir_if *if_stmt = nir_cf_node_as_if(node);
nir_block *last_then_block = nir_if_last_then_block(if_stmt);
nir_block *last_else_block = nir_if_last_else_block(if_stmt);
if (!nir_block_ends_in_jump(last_then_block)) {
unlink_block_successors(last_then_block);
link_blocks(last_then_block, block, NULL);
}
if (!nir_block_ends_in_jump(last_else_block)) {
unlink_block_successors(last_else_block);
link_blocks(last_else_block, block, NULL);
}
} else {
assert(node->type == nir_cf_node_loop);
}
}
static void
link_block_to_non_block(nir_block *block, nir_cf_node *node)
{
if (node->type == nir_cf_node_if) {
/*
* We're trying to link a block to an if after it; this just means linking
* the block to the first block of the then and else branches.
*/
nir_if *if_stmt = nir_cf_node_as_if(node);
nir_block *first_then_block = nir_if_first_then_block(if_stmt);
nir_block *first_else_block = nir_if_first_else_block(if_stmt);
unlink_block_successors(block);
link_blocks(block, first_then_block, first_else_block);
} else if (node->type == nir_cf_node_loop) {
/*
* For similar reasons as the corresponding case in
* link_non_block_to_block(), don't worry about if the loop header has
* any predecessors that need to be unlinked.
*/
nir_loop *loop = nir_cf_node_as_loop(node);
nir_block *loop_header_block = nir_loop_first_block(loop);
unlink_block_successors(block);
link_blocks(block, loop_header_block, NULL);
}
}
/**
* Replace a block's successor with a different one.
*/
static void
replace_successor(nir_block *block, nir_block *old_succ, nir_block *new_succ)
{
if (block->successors[0] == old_succ) {
block->successors[0] = new_succ;
} else {
assert(block->successors[1] == old_succ);
block->successors[1] = new_succ;
}
block_remove_pred(old_succ, block);
block_add_pred(new_succ, block);
}
/**
* Takes a basic block and inserts a new empty basic block before it, making its
* predecessors point to the new block. This essentially splits the block into
* an empty header and a body so that another non-block CF node can be inserted
* between the two. Note that this does *not* link the two basic blocks, so
* some kind of cleanup *must* be performed after this call.
*/
static nir_block *
split_block_beginning(nir_block *block)
{
nir_block *new_block = nir_block_create(ralloc_parent(block));
new_block->cf_node.parent = block->cf_node.parent;
exec_node_insert_node_before(&block->cf_node.node, &new_block->cf_node.node);
set_foreach(block->predecessors, entry) {
nir_block *pred = (nir_block *) entry->key;
replace_successor(pred, block, new_block);
}
/* Any phi nodes must stay part of the new block, or else their
* sources will be messed up.
*/
nir_foreach_instr_safe(instr, block) {
if (instr->type != nir_instr_type_phi)
break;
exec_node_remove(&instr->node);
instr->block = new_block;
exec_list_push_tail(&new_block->instr_list, &instr->node);
}
return new_block;
}
static void
rewrite_phi_preds(nir_block *block, nir_block *old_pred, nir_block *new_pred)
{
nir_foreach_instr_safe(instr, block) {
if (instr->type != nir_instr_type_phi)
break;
nir_phi_instr *phi = nir_instr_as_phi(instr);
nir_foreach_phi_src(src, phi) {
if (src->pred == old_pred) {
src->pred = new_pred;
break;
}
}
}
}
void
nir_insert_phi_undef(nir_block *block, nir_block *pred)
{
nir_function_impl *impl = nir_cf_node_get_function(&block->cf_node);
nir_foreach_instr(instr, block) {
if (instr->type != nir_instr_type_phi)
break;
nir_phi_instr *phi = nir_instr_as_phi(instr);
nir_ssa_undef_instr *undef =
nir_ssa_undef_instr_create(impl->function->shader,
phi->dest.ssa.num_components,
phi->dest.ssa.bit_size);
nir_instr_insert_before_cf_list(&impl->body, &undef->instr);
nir_phi_src *src = nir_phi_instr_add_src(phi, pred, nir_src_for_ssa(&undef->def));
list_addtail(&src->src.use_link, &undef->def.uses);
}
}
/**
* Moves the successors of source to the successors of dest, leaving both
* successors of source NULL.
*/
static void
move_successors(nir_block *source, nir_block *dest)
{
nir_block *succ1 = source->successors[0];
nir_block *succ2 = source->successors[1];
if (succ1) {
unlink_blocks(source, succ1);
rewrite_phi_preds(succ1, source, dest);
}
if (succ2) {
unlink_blocks(source, succ2);
rewrite_phi_preds(succ2, source, dest);
}
unlink_block_successors(dest);
link_blocks(dest, succ1, succ2);
}
/* Given a basic block with no successors that has been inserted into the
* control flow tree, gives it the successors it would normally have assuming
* it doesn't end in a jump instruction. Also inserts phi sources with undefs
* if necessary.
*/
static void
block_add_normal_succs(nir_block *block)
{
if (exec_node_is_tail_sentinel(block->cf_node.node.next)) {
nir_cf_node *parent = block->cf_node.parent;
if (parent->type == nir_cf_node_if) {
nir_cf_node *next = nir_cf_node_next(parent);
nir_block *next_block = nir_cf_node_as_block(next);
link_blocks(block, next_block, NULL);
} else if (parent->type == nir_cf_node_loop) {
nir_loop *loop = nir_cf_node_as_loop(parent);
nir_block *cont_block;
if (block == nir_loop_last_block(loop)) {
cont_block = nir_loop_continue_target(loop);
} else {
assert(block == nir_loop_last_continue_block(loop));
cont_block = nir_loop_first_block(loop);
}
link_blocks(block, cont_block, NULL);
nir_insert_phi_undef(cont_block, block);
} else {
nir_function_impl *impl = nir_cf_node_as_function(parent);
link_blocks(block, impl->end_block, NULL);
}
} else {
nir_cf_node *next = nir_cf_node_next(&block->cf_node);
if (next->type == nir_cf_node_if) {
nir_if *next_if = nir_cf_node_as_if(next);
nir_block *first_then_block = nir_if_first_then_block(next_if);
nir_block *first_else_block = nir_if_first_else_block(next_if);
link_blocks(block, first_then_block, first_else_block);
} else if (next->type == nir_cf_node_loop) {
nir_loop *next_loop = nir_cf_node_as_loop(next);
nir_block *first_block = nir_loop_first_block(next_loop);
link_blocks(block, first_block, NULL);
nir_insert_phi_undef(first_block, block);
}
}
}
static nir_block *
split_block_end(nir_block *block)
{
nir_block *new_block = nir_block_create(ralloc_parent(block));
new_block->cf_node.parent = block->cf_node.parent;
exec_node_insert_after(&block->cf_node.node, &new_block->cf_node.node);
if (nir_block_ends_in_jump(block)) {
/* Figure out what successor block would've had if it didn't have a jump
* instruction, and make new_block have that successor.
*/
block_add_normal_succs(new_block);
} else {
move_successors(block, new_block);
}
return new_block;
}
static nir_block *
split_block_before_instr(nir_instr *instr)
{
assert(instr->type != nir_instr_type_phi);
nir_block *new_block = split_block_beginning(instr->block);
nir_foreach_instr_safe(cur_instr, instr->block) {
if (cur_instr == instr)
break;
exec_node_remove(&cur_instr->node);
cur_instr->block = new_block;
exec_list_push_tail(&new_block->instr_list, &cur_instr->node);
}
return new_block;
}
/* Splits a basic block at the point specified by the cursor. The "before" and
* "after" arguments are filled out with the blocks resulting from the split
* if non-NULL. Note that the "beginning" of the block is actually interpreted
* as before the first non-phi instruction, and it's illegal to split a block
* before a phi instruction.
*/
static void
split_block_cursor(nir_cursor cursor,
nir_block **_before, nir_block **_after)
{
nir_block *before, *after;
switch (cursor.option) {
case nir_cursor_before_block:
after = cursor.block;
before = split_block_beginning(cursor.block);
break;
case nir_cursor_after_block:
before = cursor.block;
after = split_block_end(cursor.block);
break;
case nir_cursor_before_instr:
after = cursor.instr->block;
before = split_block_before_instr(cursor.instr);
break;
case nir_cursor_after_instr:
/* We lower this to split_block_before_instr() so that we can keep the
* after-a-jump-instr case contained to split_block_end().
*/
if (nir_instr_is_last(cursor.instr)) {
before = cursor.instr->block;
after = split_block_end(cursor.instr->block);
} else {
after = cursor.instr->block;
before = split_block_before_instr(nir_instr_next(cursor.instr));
}
break;
default:
unreachable("not reached");
}
if (_before)
*_before = before;
if (_after)
*_after = after;
}
/**
* Inserts a non-basic block between two basic blocks and links them together.
*/
static void
insert_non_block(nir_block *before, nir_cf_node *node, nir_block *after)
{
node->parent = before->cf_node.parent;
exec_node_insert_after(&before->cf_node.node, &node->node);
if (!nir_block_ends_in_jump(before))
link_block_to_non_block(before, node);
link_non_block_to_block(node, after);
}
/* walk up the control flow tree to find the innermost enclosed loop */
static nir_loop *
nearest_loop(nir_cf_node *node)
{
while (node->type != nir_cf_node_loop) {
node = node->parent;
}
return nir_cf_node_as_loop(node);
}
void
nir_loop_add_continue_construct(nir_loop *loop)
{
assert(!nir_loop_has_continue_construct(loop));
nir_block *cont = nir_block_create(ralloc_parent(loop));
exec_list_push_tail(&loop->continue_list, &cont->cf_node.node);
cont->cf_node.parent = &loop->cf_node;
/* change predecessors and successors */
nir_block *header = nir_loop_first_block(loop);
nir_block *preheader = nir_block_cf_tree_prev(header);
set_foreach(header->predecessors, entry) {
nir_block *pred = (nir_block *) entry->key;
if (pred != preheader)
replace_successor(pred, header, cont);
}
link_blocks(cont, header, NULL);
}
void
nir_loop_remove_continue_construct(nir_loop *loop)
{
assert(nir_cf_list_is_empty_block(&loop->continue_list));
/* change predecessors and successors */
nir_block *header = nir_loop_first_block(loop);
nir_block *cont = nir_loop_first_continue_block(loop);
set_foreach(cont->predecessors, entry) {
nir_block *pred = (nir_block*) entry->key;
replace_successor(pred, cont, header);
}
block_remove_pred(header, cont);
exec_node_remove(&cont->cf_node.node);
}
static void
remove_phi_src(nir_block *block, nir_block *pred)
{
nir_foreach_instr(instr, block) {
if (instr->type != nir_instr_type_phi)
break;
nir_phi_instr *phi = nir_instr_as_phi(instr);
nir_foreach_phi_src_safe(src, phi) {
if (src->pred == pred) {
list_del(&src->src.use_link);
exec_node_remove(&src->node);
gc_free(src);
}
}
}
}
/*
* update the CFG after a jump instruction has been added to the end of a block
*/
void
nir_handle_add_jump(nir_block *block)
{
nir_instr *instr = nir_block_last_instr(block);
nir_jump_instr *jump_instr = nir_instr_as_jump(instr);
if (block->successors[0])
remove_phi_src(block->successors[0], block);
if (block->successors[1])
remove_phi_src(block->successors[1], block);
unlink_block_successors(block);
nir_function_impl *impl = nir_cf_node_get_function(&block->cf_node);
nir_metadata_preserve(impl, nir_metadata_none);
switch (jump_instr->type) {
case nir_jump_return:
case nir_jump_halt:
link_blocks(block, impl->end_block, NULL);
break;
case nir_jump_break: {
nir_loop *loop = nearest_loop(&block->cf_node);
nir_cf_node *after = nir_cf_node_next(&loop->cf_node);
nir_block *after_block = nir_cf_node_as_block(after);
link_blocks(block, after_block, NULL);
break;
}
case nir_jump_continue: {
nir_loop *loop = nearest_loop(&block->cf_node);
nir_block *cont_block = nir_loop_continue_target(loop);
link_blocks(block, cont_block, NULL);
break;
}
case nir_jump_goto:
link_blocks(block, jump_instr->target, NULL);
break;
case nir_jump_goto_if:
link_blocks(block, jump_instr->else_target, jump_instr->target);
break;
default:
unreachable("Invalid jump type");
}
}
/* Removes the successor of a block with a jump. Note that the jump to be
* eliminated may be free-floating.
*/
static void
unlink_jump(nir_block *block, nir_jump_type type, bool add_normal_successors)
{
if (block->successors[0])
remove_phi_src(block->successors[0], block);
if (block->successors[1])
remove_phi_src(block->successors[1], block);
unlink_block_successors(block);
if (add_normal_successors)
block_add_normal_succs(block);
}
void
nir_handle_remove_jump(nir_block *block, nir_jump_type type)
{
unlink_jump(block, type, true);
nir_function_impl *impl = nir_cf_node_get_function(&block->cf_node);
nir_metadata_preserve(impl, nir_metadata_none);
}
static void
update_if_uses(nir_cf_node *node)
{
if (node->type != nir_cf_node_if)
return;
nir_if *if_stmt = nir_cf_node_as_if(node);
nir_src_set_parent_if(&if_stmt->condition, if_stmt);
if (if_stmt->condition.is_ssa) {
list_addtail(&if_stmt->condition.use_link,
&if_stmt->condition.ssa->uses);
} else {
list_addtail(&if_stmt->condition.use_link,
&if_stmt->condition.reg.reg->uses);
}
}
/**
* Stitch two basic blocks together into one. The aggregate must have the same
* predecessors as the first and the same successors as the second.
*
* Returns a cursor pointing at the end of the before block (i.e.m between the
* two blocks) once stiched together.
*/
static nir_cursor
stitch_blocks(nir_block *before, nir_block *after)
{
/*
* We move after into before, so we have to deal with up to 2 successors vs.
* possibly a large number of predecessors.
*
* TODO: special case when before is empty and after isn't?
*/
if (nir_block_ends_in_jump(before)) {
assert(exec_list_is_empty(&after->instr_list));
if (after->successors[0])
remove_phi_src(after->successors[0], after);
if (after->successors[1])
remove_phi_src(after->successors[1], after);
unlink_block_successors(after);
exec_node_remove(&after->cf_node.node);
return nir_after_block(before);
} else {
nir_instr *last_before_instr = nir_block_last_instr(before);
move_successors(after, before);
foreach_list_typed(nir_instr, instr, node, &after->instr_list) {
instr->block = before;
}
exec_list_append(&before->instr_list, &after->instr_list);
exec_node_remove(&after->cf_node.node);
return last_before_instr ? nir_after_instr(last_before_instr) :
nir_before_block(before);
}
}
void
nir_cf_node_insert(nir_cursor cursor, nir_cf_node *node)
{
nir_block *before, *after;
split_block_cursor(cursor, &before, &after);
if (node->type == nir_cf_node_block) {
nir_block *block = nir_cf_node_as_block(node);
exec_node_insert_after(&before->cf_node.node, &block->cf_node.node);
block->cf_node.parent = before->cf_node.parent;
/* stitch_blocks() assumes that any block that ends with a jump has
* already been setup with the correct successors, so we need to set
* up jumps here as the block is being inserted.
*/
if (nir_block_ends_in_jump(block))
nir_handle_add_jump(block);
stitch_blocks(block, after);
stitch_blocks(before, block);
} else {
update_if_uses(node);
insert_non_block(before, node, after);
}
}
static bool
replace_ssa_def_uses(nir_ssa_def *def, void *void_impl)
{
nir_function_impl *impl = void_impl;
nir_ssa_undef_instr *undef =
nir_ssa_undef_instr_create(impl->function->shader,
def->num_components,
def->bit_size);
nir_instr_insert_before_cf_list(&impl->body, &undef->instr);
nir_ssa_def_rewrite_uses(def, &undef->def);
return true;
}
static void
cleanup_cf_node(nir_cf_node *node, nir_function_impl *impl)
{
switch (node->type) {
case nir_cf_node_block: {
nir_block *block = nir_cf_node_as_block(node);
/* We need to walk the instructions and clean up defs/uses */
nir_foreach_instr_safe(instr, block) {
if (instr->type == nir_instr_type_jump) {
nir_jump_instr *jump = nir_instr_as_jump(instr);
unlink_jump(block, jump->type, false);
if (jump->type == nir_jump_goto_if)
nir_instr_rewrite_src(instr, &jump->condition, NIR_SRC_INIT);
} else {
nir_foreach_ssa_def(instr, replace_ssa_def_uses, impl);
nir_instr_remove(instr);
}
}
break;
}
case nir_cf_node_if: {
nir_if *if_stmt = nir_cf_node_as_if(node);
foreach_list_typed(nir_cf_node, child, node, &if_stmt->then_list)
cleanup_cf_node(child, impl);
foreach_list_typed(nir_cf_node, child, node, &if_stmt->else_list)
cleanup_cf_node(child, impl);
list_del(&if_stmt->condition.use_link);
break;
}
case nir_cf_node_loop: {
nir_loop *loop = nir_cf_node_as_loop(node);
foreach_list_typed(nir_cf_node, child, node, &loop->body)
cleanup_cf_node(child, impl);
foreach_list_typed(nir_cf_node, child, node, &loop->continue_list)
cleanup_cf_node(child, impl);
break;
}
case nir_cf_node_function: {
nir_function_impl *impl = nir_cf_node_as_function(node);
foreach_list_typed(nir_cf_node, child, node, &impl->body)
cleanup_cf_node(child, impl);
break;
}
default:
unreachable("Invalid CF node type");
}
}
/**
* Extracts everything between two cursors. Returns the cursor which is
* equivalent to the old begin/end curosors.
*/
nir_cursor
nir_cf_extract(nir_cf_list *extracted, nir_cursor begin, nir_cursor end)
{
nir_block *block_begin, *block_end, *block_before, *block_after;
if (nir_cursors_equal(begin, end)) {
exec_list_make_empty(&extracted->list);
extracted->impl = NULL; /* we shouldn't need this */
return begin;
}
split_block_cursor(begin, &block_before, &block_begin);
/* Splitting a block twice with two cursors created before either split is
* tricky and there are a couple of places it can go wrong if both cursors
* point to the same block. One is if the second cursor is an block-based
* cursor and, thanks to the split above, it ends up pointing to the wrong
* block. If it's a before_block cursor and it's in the same block as
* begin, then begin must also be a before_block cursor and it should be
* caught by the nir_cursors_equal check above and we won't get here. If
* it's an after_block cursor, we need to re-adjust to ensure that it
* points to the second one of the split blocks, regardless of which it is.
*/
if (end.option == nir_cursor_after_block && end.block == block_before)
end.block = block_begin;
split_block_cursor(end, &block_end, &block_after);
/* The second place this can all go wrong is that it could be that the
* second split places the original block after the new block in which case
* the block_begin pointer that we saved off above is pointing to the block
* at the end rather than the block in the middle like it's supposed to be.
* In this case, we have to re-adjust begin_block to point to the middle
* one.
*/
if (block_begin == block_after)
block_begin = block_end;
extracted->impl = nir_cf_node_get_function(&block_begin->cf_node);
exec_list_make_empty(&extracted->list);
/* Dominance and other block-related information is toast. */
nir_metadata_preserve(extracted->impl, nir_metadata_none);
nir_cf_node *cf_node = &block_begin->cf_node;
nir_cf_node *cf_node_end = &block_end->cf_node;
while (true) {
nir_cf_node *next = nir_cf_node_next(cf_node);
exec_node_remove(&cf_node->node);
cf_node->parent = NULL;
exec_list_push_tail(&extracted->list, &cf_node->node);
if (cf_node == cf_node_end)
break;
cf_node = next;
}
return stitch_blocks(block_before, block_after);
}
static void
relink_jump_halt_cf_node(nir_cf_node *node, nir_block *end_block)
{
switch (node->type) {
case nir_cf_node_block: {
nir_block *block = nir_cf_node_as_block(node);
nir_instr *last_instr = nir_block_last_instr(block);
if (last_instr == NULL || last_instr->type != nir_instr_type_jump)
break;
nir_jump_instr *jump = nir_instr_as_jump(last_instr);
/* We can't move a CF list from one function to another while we still
* have returns.
*/
assert(jump->type != nir_jump_return);
if (jump->type == nir_jump_halt) {
unlink_block_successors(block);
link_blocks(block, end_block, NULL);
}
break;
}
case nir_cf_node_if: {
nir_if *if_stmt = nir_cf_node_as_if(node);
foreach_list_typed(nir_cf_node, child, node, &if_stmt->then_list)
relink_jump_halt_cf_node(child, end_block);
foreach_list_typed(nir_cf_node, child, node, &if_stmt->else_list)
relink_jump_halt_cf_node(child, end_block);
break;
}
case nir_cf_node_loop: {
nir_loop *loop = nir_cf_node_as_loop(node);
foreach_list_typed(nir_cf_node, child, node, &loop->body)
relink_jump_halt_cf_node(child, end_block);
foreach_list_typed(nir_cf_node, child, node, &loop->continue_list)
relink_jump_halt_cf_node(child, end_block);
break;
}
case nir_cf_node_function:
unreachable("Cannot insert a function in a function");
default:
unreachable("Invalid CF node type");
}
}
/**
* Inserts a list at a given cursor. Returns the cursor at the end of the
* insertion (i.e., at the end of the instructions contained in cf_list).
*/
nir_cursor
nir_cf_reinsert(nir_cf_list *cf_list, nir_cursor cursor)
{
nir_block *before, *after;
if (exec_list_is_empty(&cf_list->list))
return cursor;
nir_function_impl *cursor_impl =
nir_cf_node_get_function(&nir_cursor_current_block(cursor)->cf_node);
if (cf_list->impl != cursor_impl) {
foreach_list_typed(nir_cf_node, node, node, &cf_list->list)
relink_jump_halt_cf_node(node, cursor_impl->end_block);
}
split_block_cursor(cursor, &before, &after);
foreach_list_typed_safe(nir_cf_node, node, node, &cf_list->list) {
exec_node_remove(&node->node);
node->parent = before->cf_node.parent;
exec_node_insert_node_before(&after->cf_node.node, &node->node);
}
stitch_blocks(before,
nir_cf_node_as_block(nir_cf_node_next(&before->cf_node)));
return stitch_blocks(nir_cf_node_as_block(nir_cf_node_prev(&after->cf_node)),
after);
}
void
nir_cf_delete(nir_cf_list *cf_list)
{
foreach_list_typed(nir_cf_node, node, node, &cf_list->list) {
cleanup_cf_node(node, cf_list->impl);
}
}