<p dir="ltr"><br>
On Apr 12, 2016 9:36 PM, "Connor Abbott" <<a href="mailto:cwabbott0@gmail.com">cwabbott0@gmail.com</a>> wrote:<br>
><br>
> Previously, these were functions which took a callback. This meant that<br>
> the per-block code had to be in a separate function, and all the data<br>
> that you wanted to pass in had to be a single void *. They walked the<br>
> control flow tree recursively, doing a depth-first search, and called<br>
> the callback in a preorder, matching the order of the original source<br>
> code. But since each node in the control flow tree has a pointer to its<br>
> parent, we can implement a "get-next" and "get-previous" method that<br>
> does the same thing that the recursive function did with no state at<br>
> all. This lets us rewrite nir_foreach_block() as a simple for loop,<br>
> which lets us greatly simplify its users in some cases. This does<br>
> require us to rewrite every user, although the transformation from the<br>
> old nir_foreach_block() to the new nir_foreach_block() is mostly<br>
> trivial.<br>
><br>
> One subtlety, though, is that the new nir_foreach_block() won't handle<br>
> the case where the current block is deleted, which the old one could.<br>
> There's a new nir_foreach_block_safe() which implements the standard<br>
> trick for solving this. Most users don't modify control flow, though, so<br>
> they won't need it. Right now, only opt_select_peephole needs it.<br>
><br>
> Signed-off-by: Connor Abbott <<a href="mailto:cwabbott0@gmail.com">cwabbott0@gmail.com</a>><br>
> ---<br>
> src/compiler/nir/nir.c | 186 +++++++++++++++++++++++++++++--------------------<br>
> src/compiler/nir/nir.h | 57 ++++++++++++---<br>
> 2 files changed, 159 insertions(+), 84 deletions(-)<br>
><br>
> diff --git a/src/compiler/nir/nir.c b/src/compiler/nir/nir.c<br>
> index b67916d..ea8aa88 100644<br>
> --- a/src/compiler/nir/nir.c<br>
> +++ b/src/compiler/nir/nir.c<br>
> @@ -1468,109 +1468,143 @@ nir_ssa_def_rewrite_uses_after(nir_ssa_def *def, nir_src new_src,<br>
> nir_if_rewrite_condition(use_src->parent_if, new_src);<br>
> }<br>
><br>
> -static bool foreach_cf_node(nir_cf_node *node, nir_foreach_block_cb cb,<br>
> - bool reverse, void *state);<br>
> -<br>
> -static inline bool<br>
> -foreach_if(nir_if *if_stmt, nir_foreach_block_cb cb, bool reverse, void *state)<br>
> +nir_block *<br>
> +nir_block_cf_tree_next(nir_block *block)<br>
> {<br>
> - if (reverse) {<br>
> - foreach_list_typed_reverse_safe(nir_cf_node, node, node,<br>
> - &if_stmt->else_list) {<br>
> - if (!foreach_cf_node(node, cb, reverse, state))<br>
> - return false;<br>
> - }<br>
> + if (block == NULL) {<br>
> + /* nir_foreach_block_safe() will call this function on a NULL block<br>
> + * after the last iteration, but it won't use the result so just return<br>
> + * NULL here.<br>
> + */<br>
> + return NULL;<br>
> + }<br>
><br>
> - foreach_list_typed_reverse_safe(nir_cf_node, node, node,<br>
> - &if_stmt->then_list) {<br>
> - if (!foreach_cf_node(node, cb, reverse, state))<br>
> - return false;<br>
> - }<br>
> - } else {<br>
> - foreach_list_typed_safe(nir_cf_node, node, node, &if_stmt->then_list) {<br>
> - if (!foreach_cf_node(node, cb, reverse, state))<br>
> - return false;<br>
> - }<br>
> + nir_cf_node *cf_next = nir_cf_node_next(&block->cf_node);<br>
> + if (cf_next)<br>
> + return nir_cf_node_cf_tree_first(cf_next);<br>
><br>
> - foreach_list_typed_safe(nir_cf_node, node, node, &if_stmt->else_list) {<br>
> - if (!foreach_cf_node(node, cb, reverse, state))<br>
> - return false;<br>
> - }<br>
> + nir_cf_node *parent = block->cf_node.parent;<br>
> +<br>
> + switch (parent->type) {<br>
> + case nir_cf_node_if: {<br>
> + /* Are we at the end of the if? Go to the beginning of the else */<br>
> + nir_if *if_stmt = nir_cf_node_as_if(parent);<br>
> + if (&block->cf_node == nir_if_last_then_node(if_stmt))<br>
> + return nir_cf_node_as_block(nir_if_first_else_node(if_stmt));<br>
> + /* We must be at the end of the else, fallthrough */</p>
<p dir="ltr">Maybe assert this?</p>
<p dir="ltr">> }<br>
><br>
> - return true;<br>
> + case nir_cf_node_loop: {<br>
> + nir_cf_node *node = nir_cf_node_next(parent);<br>
> + return nir_cf_node_as_block(node);</p>
<p dir="ltr">No need for the temporary; just compose functions.</p>
<p dir="ltr">> + }</p>
<p dir="ltr">And you can ditch the braces if you do. In general in this patch I'd like to see the unneeded braces go. </p>
<p dir="ltr">> +<br>
> + case nir_cf_node_function:<br>
> + return NULL;<br>
> +<br>
> + default:<br>
> + unreachable("unknown cf node type");<br>
> + }<br>
> }<br>
><br>
> -static inline bool<br>
> -foreach_loop(nir_loop *loop, nir_foreach_block_cb cb, bool reverse, void *state)<br>
> +nir_block *<br>
> +nir_block_cf_tree_prev(nir_block *block)<br>
> {<br>
> - if (reverse) {<br>
> - foreach_list_typed_reverse_safe(nir_cf_node, node, node, &loop->body) {<br>
> - if (!foreach_cf_node(node, cb, reverse, state))<br>
> - return false;<br>
> - }<br>
> - } else {<br>
> - foreach_list_typed_safe(nir_cf_node, node, node, &loop->body) {<br>
> - if (!foreach_cf_node(node, cb, reverse, state))<br>
> - return false;<br>
> - }<br>
> + if (block == NULL) {<br>
> + /* do this for consistency with nir_block_cf_tree_next() */<br>
> + return NULL;<br>
> }<br>
><br>
> - return true;<br>
> + nir_cf_node *cf_prev = nir_cf_node_prev(&block->cf_node);<br>
> + if (cf_prev)<br>
> + return nir_cf_node_cf_tree_last(cf_prev);<br>
> +<br>
> + nir_cf_node *parent = block->cf_node.parent;<br>
> +<br>
> + switch (parent->type) {<br>
> + case nir_cf_node_if: {<br>
> + /* Are we at the beginning of the else? Go to the end of the if */<br>
> + nir_if *if_stmt = nir_cf_node_as_if(parent);<br>
> + if (&block->cf_node == nir_if_first_else_node(if_stmt))<br>
> + return nir_cf_node_as_block(nir_if_last_then_node(if_stmt));<br>
> + /* We must be at the beginning of the if, fallthrough */</p>
<p dir="ltr">Assert</p>
<p dir="ltr">> + }<br>
> +<br>
> + case nir_cf_node_loop: {<br>
> + nir_cf_node *node = nir_cf_node_prev(parent);<br>
> + return nir_cf_node_as_block(node);</p>
<p dir="ltr">Compose</p>
<p dir="ltr">> + }<br>
> +<br>
> + case nir_cf_node_function:<br>
> + return NULL;<br>
> +<br>
> + default:<br>
> + unreachable("unknown cf node type");<br>
> + }<br>
> }<br>
><br>
> -static bool<br>
> -foreach_cf_node(nir_cf_node *node, nir_foreach_block_cb cb,<br>
> - bool reverse, void *state)<br>
> +nir_block *nir_cf_node_cf_tree_first(nir_cf_node *node)<br>
> {<br>
> switch (node->type) {<br>
> - case nir_cf_node_block:<br>
> - return cb(nir_cf_node_as_block(node), state);<br>
> - case nir_cf_node_if:<br>
> - return foreach_if(nir_cf_node_as_if(node), cb, reverse, state);<br>
> - case nir_cf_node_loop:<br>
> - return foreach_loop(nir_cf_node_as_loop(node), cb, reverse, state);<br>
> - break;<br>
> + case nir_cf_node_function: {<br>
> + nir_function_impl *impl = nir_cf_node_as_function(node);<br>
> + return nir_start_block(impl);</p>
<p dir="ltr">Compose</p>
<p dir="ltr">> + }<br>
><br>
> - default:<br>
> - unreachable("Invalid CFG node type");<br>
> - break;<br>
> + case nir_cf_node_if: {<br>
> + nir_if *if_stmt = nir_cf_node_as_if(node);<br>
> + return nir_cf_node_as_block(nir_if_first_then_node(if_stmt));<br>
> }<br>
><br>
> - return false;<br>
> -}<br>
> + case nir_cf_node_loop: {<br>
> + nir_loop *loop = nir_cf_node_as_loop(node);<br>
> + return nir_cf_node_as_block(nir_loop_first_cf_node(loop));<br>
> + }<br>
> +<br>
> + case nir_cf_node_block: {<br>
> + return nir_cf_node_as_block(node);<br>
> + }<br>
><br>
> -bool<br>
> -nir_foreach_block_in_cf_node(nir_cf_node *node, nir_foreach_block_cb cb,<br>
> - void *state)<br>
> -{<br>
> - return foreach_cf_node(node, cb, false, state);<br>
> + default:<br>
> + unreachable("unknown node type");<br>
> + }<br>
> }<br>
><br>
> -bool<br>
> -nir_foreach_block(nir_function_impl *impl, nir_foreach_block_cb cb, void *state)<br>
> +nir_block *nir_cf_node_cf_tree_last(nir_cf_node *node)<br>
> {<br>
> - foreach_list_typed_safe(nir_cf_node, node, node, &impl->body) {<br>
> - if (!foreach_cf_node(node, cb, false, state))<br>
> - return false;<br>
> + switch (node->type) {<br>
> + case nir_cf_node_function: {<br>
> + nir_function_impl *impl = nir_cf_node_as_function(node);<br>
> + return nir_impl_last_block(impl);</p>
<p dir="ltr">Compose</p>
<p dir="ltr">> }<br>
><br>
> - return cb(impl->end_block, state);<br>
> -}<br>
> + case nir_cf_node_if: {<br>
> + nir_if *if_stmt = nir_cf_node_as_if(node);<br>
> + return nir_cf_node_as_block(nir_if_last_else_node(if_stmt));<br>
> + }<br>
><br>
> -bool<br>
> -nir_foreach_block_reverse(nir_function_impl *impl, nir_foreach_block_cb cb,<br>
> - void *state)<br>
> -{<br>
> - if (!cb(impl->end_block, state))<br>
> - return false;<br>
> + case nir_cf_node_loop: {<br>
> + nir_loop *loop = nir_cf_node_as_loop(node);<br>
> + return nir_cf_node_as_block(nir_loop_first_cf_node(loop));</p>
<p dir="ltr">Should be loop_last_cf_node. :-)</p>
<p dir="ltr">> + }<br>
> +<br>
> + case nir_cf_node_block: {<br>
> + return nir_cf_node_as_block(node);<br>
> + }<br>
><br>
> - foreach_list_typed_reverse_safe(nir_cf_node, node, node, &impl->body) {<br>
> - if (!foreach_cf_node(node, cb, true, state))<br>
> - return false;<br>
> + default:<br>
> + unreachable("unknown node type");<br>
> }<br>
> +}<br>
><br>
> - return true;<br>
> +nir_block *nir_cf_node_cf_tree_next(nir_cf_node *node)<br>
> +{<br>
> + if (node->type == nir_cf_node_block)<br>
> + return nir_cf_node_cf_tree_first(nir_cf_node_next(node));<br>
> + else if (node->type == nir_cf_node_function)<br>
> + return NULL;<br>
> + else<br>
> + return nir_cf_node_as_block(nir_cf_node_next(node));<br>
> }<br>
><br>
> nir_if *<br>
> diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h<br>
> index c19ae59..54cbcba 100644<br>
> --- a/src/compiler/nir/nir.h<br>
> +++ b/src/compiler/nir/nir.h<br>
> @@ -1513,6 +1513,12 @@ nir_start_block(nir_function_impl *impl)<br>
> return (nir_block *) exec_list_get_head(&impl->body);<br>
> }<br>
><br>
> +static inline nir_block *<br>
> +nir_impl_last_block(nir_function_impl *impl)<br>
> +{<br>
> + return (nir_block *) exec_list_get_tail(&impl->body);<br>
> +}<br>
> +<br>
> static inline nir_cf_node *<br>
> nir_cf_node_next(nir_cf_node *node)<br>
> {<br>
> @@ -2077,14 +2083,49 @@ void nir_ssa_def_rewrite_uses(nir_ssa_def *def, nir_src new_src);<br>
> void nir_ssa_def_rewrite_uses_after(nir_ssa_def *def, nir_src new_src,<br>
> nir_instr *after_me);<br>
><br>
> -/* visits basic blocks in source-code order */<br>
> -typedef bool (*nir_foreach_block_cb)(nir_block *block, void *state);<br>
> -bool nir_foreach_block(nir_function_impl *impl, nir_foreach_block_cb cb,<br>
> - void *state);<br>
> -bool nir_foreach_block_reverse(nir_function_impl *impl, nir_foreach_block_cb cb,<br>
> - void *state);<br>
> -bool nir_foreach_block_in_cf_node(nir_cf_node *node, nir_foreach_block_cb cb,<br>
> - void *state);<br>
> +/*<br>
> + * finds the next basic block in source-code order, returns NULL if there is<br>
> + * none<br>
> + */<br>
> +<br>
> +nir_block *nir_block_cf_tree_next(nir_block *block);<br>
> +<br>
> +/* Performs the opposite of nir_block_cf_tree_next() */<br>
> +<br>
> +nir_block *nir_block_cf_tree_prev(nir_block *block);<br>
> +<br>
> +/* Gets the first block in a CF node in source-code order */<br>
> +<br>
> +nir_block *nir_cf_node_cf_tree_first(nir_cf_node *node);<br>
> +<br>
> +/* Gets the last block in a CF node in source-code order */<br>
> +<br>
> +nir_block *nir_cf_node_cf_tree_last(nir_cf_node *node);<br>
> +<br>
> +/* Gets the next block after a CF node in source-code order */<br>
> +<br>
> +nir_block *nir_cf_node_cf_tree_next(nir_cf_node *node);<br>
> +<br>
> +/* Macros for loops that visit blocks in source-code order */<br>
> +<br>
> +#define nir_foreach_block(impl, block) \<br>
> + for (nir_block *block = nir_start_block(impl); block != NULL; \<br>
> + block = nir_block_cf_tree_next(block))<br>
> +<br>
> +#define nir_foreach_block_safe(impl, block) \<br>
> + for (nir_block *block = nir_start_block(impl), \<br>
> + *next = nir_block_cf_tree_next(block); \<br>
> + block != NULL; \<br>
> + block = next, next = nir_block_cf_tree_next(block))<br>
> +<br>
> +#define nir_foreach_block_reverse(impl, block) \<br>
> + for (nir_block *block = nir_impl_last_block(impl); block != NULL; \<br>
> + block = nir_block_cf_tree_prev(block))<br>
> +<br>
> +#define nir_foreach_block_in_cf_node(node, block) \<br>
> + for (nir_block *block = nir_cf_node_cf_tree_first(node); \<br>
> + block != nir_cf_node_cf_tree_next(node); \<br>
> + block = nir_block_cf_tree_next(block))<br>
><br>
> /* If the following CF node is an if, this function returns that if.<br>
> * Otherwise, it returns NULL.<br>
> --<br>
> 2.5.0<br>
><br>
> _______________________________________________<br>
> mesa-dev mailing list<br>
><a href="mailto:mesa-dev@lists.freedesktop.org"> mesa-dev@lists.freedesktop.org</a><br>
><a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev"> https://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</p>