[Mesa-dev] [PATCH 01/47] nir: rewrite nir_foreach_block and friends

Connor Abbott cwabbott0 at gmail.com
Wed Apr 13 21:06:33 UTC 2016


On Wed, Apr 13, 2016 at 3:20 PM, Jason Ekstrand <jason at jlekstrand.net> wrote:
>
>
> On Wed, Apr 13, 2016 at 12:15 PM, Connor Abbott <cwabbott0 at gmail.com> wrote:
>>
>> On Wed, Apr 13, 2016 at 2:49 PM, Jason Ekstrand <jason at jlekstrand.net>
>> wrote:
>> >
>> >
>> > On Wed, Apr 13, 2016 at 8:15 AM, Jason Ekstrand <jason at jlekstrand.net>
>> > wrote:
>> >>
>> >>
>> >> On Apr 13, 2016 4:57 AM, "Rob Clark" <robdclark at gmail.com> wrote:
>> >> >
>> >> > On Wed, Apr 13, 2016 at 12:34 AM, Connor Abbott <cwabbott0 at gmail.com>
>> >> > wrote:
>> >> > > Previously, these were functions which took a callback. This meant
>> >> > > that
>> >> > > the per-block code had to be in a separate function, and all the
>> >> > > data
>> >> > > that you wanted to pass in had to be a single void *. They walked
>> >> > > the
>> >> > > control flow tree recursively, doing a depth-first search, and
>> >> > > called
>> >> > > the callback in a preorder, matching the order of the original
>> >> > > source
>> >> > > code. But since each node in the control flow tree has a pointer to
>> >> > > its
>> >> > > parent, we can implement a "get-next" and "get-previous" method
>> >> > > that
>> >> > > does the same thing that the recursive function did with no state
>> >> > > at
>> >> > > all. This lets us rewrite nir_foreach_block() as a simple for loop,
>> >> > > which lets us greatly simplify its users in some cases. This does
>> >> > > require us to rewrite every user, although the transformation from
>> >> > > the
>> >> > > old nir_foreach_block() to the new nir_foreach_block() is mostly
>> >> > > trivial.
>> >> > >
>> >> > > One subtlety, though, is that the new nir_foreach_block() won't
>> >> > > handle
>> >> > > the case where the current block is deleted, which the old one
>> >> > > could.
>> >> > > There's a new nir_foreach_block_safe() which implements the
>> >> > > standard
>> >> > > trick for solving this. Most users don't modify control flow,
>> >> > > though,
>> >> > > so
>> >> > > they won't need it. Right now, only opt_select_peephole needs it.
>> >> > >
>> >> > > Signed-off-by: Connor Abbott <cwabbott0 at gmail.com>
>> >> > > ---
>> >> > >  src/compiler/nir/nir.c | 186
>> >> > > +++++++++++++++++++++++++++++--------------------
>> >> > >  src/compiler/nir/nir.h |  57 ++++++++++++---
>> >> > >  2 files changed, 159 insertions(+), 84 deletions(-)
>> >> > >
>> >> > > diff --git a/src/compiler/nir/nir.c b/src/compiler/nir/nir.c
>> >> > > index b67916d..ea8aa88 100644
>> >> > > --- a/src/compiler/nir/nir.c
>> >> > > +++ b/src/compiler/nir/nir.c
>> >> > > @@ -1468,109 +1468,143 @@
>> >> > > nir_ssa_def_rewrite_uses_after(nir_ssa_def
>> >> > > *def, nir_src new_src,
>> >> > >        nir_if_rewrite_condition(use_src->parent_if, new_src);
>> >> > >  }
>> >> > >
>> >> > > -static bool foreach_cf_node(nir_cf_node *node,
>> >> > > nir_foreach_block_cb
>> >> > > cb,
>> >> > > -                            bool reverse, void *state);
>> >> > > -
>> >> > > -static inline bool
>> >> > > -foreach_if(nir_if *if_stmt, nir_foreach_block_cb cb, bool reverse,
>> >> > > void *state)
>> >> > > +nir_block *
>> >> > > +nir_block_cf_tree_next(nir_block *block)
>> >> > >  {
>> >> > > -   if (reverse) {
>> >> > > -      foreach_list_typed_reverse_safe(nir_cf_node, node, node,
>> >> > > -                                      &if_stmt->else_list) {
>> >> > > -         if (!foreach_cf_node(node, cb, reverse, state))
>> >> > > -            return false;
>> >> > > -      }
>> >> > > +   if (block == NULL) {
>> >> > > +      /* nir_foreach_block_safe() will call this function on a
>> >> > > NULL
>> >> > > block
>> >> > > +       * after the last iteration, but it won't use the result so
>> >> > > just return
>> >> > > +       * NULL here.
>> >> > > +       */
>> >> > > +      return NULL;
>> >> > > +   }
>> >> > >
>> >> > > -      foreach_list_typed_reverse_safe(nir_cf_node, node, node,
>> >> > > -                                      &if_stmt->then_list) {
>> >> > > -         if (!foreach_cf_node(node, cb, reverse, state))
>> >> > > -            return false;
>> >> > > -      }
>> >> > > -   } else {
>> >> > > -      foreach_list_typed_safe(nir_cf_node, node, node,
>> >> > > &if_stmt->then_list) {
>> >> > > -         if (!foreach_cf_node(node, cb, reverse, state))
>> >> > > -            return false;
>> >> > > -      }
>> >> > > +   nir_cf_node *cf_next = nir_cf_node_next(&block->cf_node);
>> >> > > +   if (cf_next)
>> >> > > +      return nir_cf_node_cf_tree_first(cf_next);
>> >> > >
>> >> > > -      foreach_list_typed_safe(nir_cf_node, node, node,
>> >> > > &if_stmt->else_list) {
>> >> > > -         if (!foreach_cf_node(node, cb, reverse, state))
>> >> > > -            return false;
>> >> > > -      }
>> >> > > +   nir_cf_node *parent = block->cf_node.parent;
>> >> > > +
>> >> > > +   switch (parent->type) {
>> >> > > +   case nir_cf_node_if: {
>> >> > > +      /* Are we at the end of the if? Go to the beginning of the
>> >> > > else
>> >> > > */
>> >> > > +      nir_if *if_stmt = nir_cf_node_as_if(parent);
>> >> > > +      if (&block->cf_node == nir_if_last_then_node(if_stmt))
>> >> > > +         return
>> >> > > nir_cf_node_as_block(nir_if_first_else_node(if_stmt));
>> >> > > +      /* We must be at the end of the else, fallthrough */
>> >> > >     }
>> >> > >
>> >> > > -   return true;
>> >> > > +   case nir_cf_node_loop: {
>> >> > > +      nir_cf_node *node = nir_cf_node_next(parent);
>> >> > > +      return nir_cf_node_as_block(node);
>> >> > > +   }
>> >> > > +
>> >> > > +   case nir_cf_node_function:
>> >> > > +      return NULL;
>> >> > > +
>> >> > > +   default:
>> >> > > +      unreachable("unknown cf node type");
>> >> > > +   }
>> >> > >  }
>> >> > >
>> >> > > -static inline bool
>> >> > > -foreach_loop(nir_loop *loop, nir_foreach_block_cb cb, bool
>> >> > > reverse,
>> >> > > void *state)
>> >> > > +nir_block *
>> >> > > +nir_block_cf_tree_prev(nir_block *block)
>> >> > >  {
>> >> > > -   if (reverse) {
>> >> > > -      foreach_list_typed_reverse_safe(nir_cf_node, node, node,
>> >> > > &loop->body) {
>> >> > > -         if (!foreach_cf_node(node, cb, reverse, state))
>> >> > > -            return false;
>> >> > > -      }
>> >> > > -   } else {
>> >> > > -      foreach_list_typed_safe(nir_cf_node, node, node,
>> >> > > &loop->body) {
>> >> > > -         if (!foreach_cf_node(node, cb, reverse, state))
>> >> > > -            return false;
>> >> > > -      }
>> >> > > +   if (block == NULL) {
>> >> > > +      /* do this for consistency with nir_block_cf_tree_next() */
>> >> > > +      return NULL;
>> >> > >     }
>> >> > >
>> >> > > -   return true;
>> >> > > +   nir_cf_node *cf_prev = nir_cf_node_prev(&block->cf_node);
>> >> > > +   if (cf_prev)
>> >> > > +      return nir_cf_node_cf_tree_last(cf_prev);
>> >> > > +
>> >> > > +   nir_cf_node *parent = block->cf_node.parent;
>> >> > > +
>> >> > > +   switch (parent->type) {
>> >> > > +   case nir_cf_node_if: {
>> >> > > +      /* Are we at the beginning of the else? Go to the end of the
>> >> > > if
>> >> > > */
>> >> > > +      nir_if *if_stmt = nir_cf_node_as_if(parent);
>> >> > > +      if (&block->cf_node == nir_if_first_else_node(if_stmt))
>> >> > > +         return
>> >> > > nir_cf_node_as_block(nir_if_last_then_node(if_stmt));
>> >> > > +      /* We must be at the beginning of the if, fallthrough */
>> >> > > +   }
>> >> > > +
>> >> > > +   case nir_cf_node_loop: {
>> >> > > +      nir_cf_node *node = nir_cf_node_prev(parent);
>> >> > > +      return nir_cf_node_as_block(node);
>> >> > > +   }
>> >> > > +
>> >> > > +   case nir_cf_node_function:
>> >> > > +      return NULL;
>> >> > > +
>> >> > > +   default:
>> >> > > +      unreachable("unknown cf node type");
>> >> > > +   }
>> >> > >  }
>> >> > >
>> >> > > -static bool
>> >> > > -foreach_cf_node(nir_cf_node *node, nir_foreach_block_cb cb,
>> >> > > -                bool reverse, void *state)
>> >> > > +nir_block *nir_cf_node_cf_tree_first(nir_cf_node *node)
>> >> > >  {
>> >> > >     switch (node->type) {
>> >> > > -   case nir_cf_node_block:
>> >> > > -      return cb(nir_cf_node_as_block(node), state);
>> >> > > -   case nir_cf_node_if:
>> >> > > -      return foreach_if(nir_cf_node_as_if(node), cb, reverse,
>> >> > > state);
>> >> > > -   case nir_cf_node_loop:
>> >> > > -      return foreach_loop(nir_cf_node_as_loop(node), cb, reverse,
>> >> > > state);
>> >> > > -      break;
>> >> > > +   case nir_cf_node_function: {
>> >> > > +      nir_function_impl *impl = nir_cf_node_as_function(node);
>> >> > > +      return nir_start_block(impl);
>> >> > > +   }
>> >> > >
>> >> > > -   default:
>> >> > > -      unreachable("Invalid CFG node type");
>> >> > > -      break;
>> >> > > +   case nir_cf_node_if: {
>> >> > > +      nir_if *if_stmt = nir_cf_node_as_if(node);
>> >> > > +      return
>> >> > > nir_cf_node_as_block(nir_if_first_then_node(if_stmt));
>> >> > >     }
>> >> > >
>> >> > > -   return false;
>> >> > > -}
>> >> > > +   case nir_cf_node_loop: {
>> >> > > +      nir_loop *loop = nir_cf_node_as_loop(node);
>> >> > > +      return nir_cf_node_as_block(nir_loop_first_cf_node(loop));
>> >> > > +   }
>> >> > > +
>> >> > > +   case nir_cf_node_block: {
>> >> > > +      return nir_cf_node_as_block(node);
>> >> > > +   }
>> >> > >
>> >> > > -bool
>> >> > > -nir_foreach_block_in_cf_node(nir_cf_node *node,
>> >> > > nir_foreach_block_cb
>> >> > > cb,
>> >> > > -                             void *state)
>> >> > > -{
>> >> > > -   return foreach_cf_node(node, cb, false, state);
>> >> > > +   default:
>> >> > > +      unreachable("unknown node type");
>> >> > > +   }
>> >> > >  }
>> >> > >
>> >> > > -bool
>> >> > > -nir_foreach_block(nir_function_impl *impl, nir_foreach_block_cb
>> >> > > cb,
>> >> > > void *state)
>> >> > > +nir_block *nir_cf_node_cf_tree_last(nir_cf_node *node)
>> >> > >  {
>> >> > > -   foreach_list_typed_safe(nir_cf_node, node, node, &impl->body) {
>> >> > > -      if (!foreach_cf_node(node, cb, false, state))
>> >> > > -         return false;
>> >> > > +   switch (node->type) {
>> >> > > +   case nir_cf_node_function: {
>> >> > > +      nir_function_impl *impl = nir_cf_node_as_function(node);
>> >> > > +      return nir_impl_last_block(impl);
>> >> > >     }
>> >> > >
>> >> > > -   return cb(impl->end_block, state);
>> >> > > -}
>> >> > > +   case nir_cf_node_if: {
>> >> > > +      nir_if *if_stmt = nir_cf_node_as_if(node);
>> >> > > +      return nir_cf_node_as_block(nir_if_last_else_node(if_stmt));
>> >> > > +   }
>> >> > >
>> >> > > -bool
>> >> > > -nir_foreach_block_reverse(nir_function_impl *impl,
>> >> > > nir_foreach_block_cb cb,
>> >> > > -                          void *state)
>> >> > > -{
>> >> > > -   if (!cb(impl->end_block, state))
>> >> > > -      return false;
>> >> > > +   case nir_cf_node_loop: {
>> >> > > +      nir_loop *loop = nir_cf_node_as_loop(node);
>> >> > > +      return nir_cf_node_as_block(nir_loop_first_cf_node(loop));
>> >> > > +   }
>> >> > > +
>> >> > > +   case nir_cf_node_block: {
>> >> > > +      return nir_cf_node_as_block(node);
>> >> > > +   }
>> >> > >
>> >> > > -   foreach_list_typed_reverse_safe(nir_cf_node, node, node,
>> >> > > &impl->body) {
>> >> > > -      if (!foreach_cf_node(node, cb, true, state))
>> >> > > -         return false;
>> >> > > +   default:
>> >> > > +      unreachable("unknown node type");
>> >> > >     }
>> >> > > +}
>> >> > >
>> >> > > -   return true;
>> >> > > +nir_block *nir_cf_node_cf_tree_next(nir_cf_node *node)
>> >> > > +{
>> >> > > +   if (node->type == nir_cf_node_block)
>> >> > > +      return nir_cf_node_cf_tree_first(nir_cf_node_next(node));
>> >> > > +   else if (node->type == nir_cf_node_function)
>> >> > > +      return NULL;
>> >> > > +   else
>> >> > > +      return nir_cf_node_as_block(nir_cf_node_next(node));
>> >> > >  }
>> >> > >
>> >> > >  nir_if *
>> >> > > diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h
>> >> > > index c19ae59..54cbcba 100644
>> >> > > --- a/src/compiler/nir/nir.h
>> >> > > +++ b/src/compiler/nir/nir.h
>> >> > > @@ -1513,6 +1513,12 @@ nir_start_block(nir_function_impl *impl)
>> >> > >     return (nir_block *) exec_list_get_head(&impl->body);
>> >> > >  }
>> >> > >
>> >> > > +static inline nir_block *
>> >> > > +nir_impl_last_block(nir_function_impl *impl)
>> >> > > +{
>> >> > > +   return (nir_block *) exec_list_get_tail(&impl->body);
>> >> > > +}
>> >> > > +
>> >> > >  static inline nir_cf_node *
>> >> > >  nir_cf_node_next(nir_cf_node *node)
>> >> > >  {
>> >> > > @@ -2077,14 +2083,49 @@ void nir_ssa_def_rewrite_uses(nir_ssa_def
>> >> > > *def, nir_src new_src);
>> >> > >  void nir_ssa_def_rewrite_uses_after(nir_ssa_def *def, nir_src
>> >> > > new_src,
>> >> > >                                      nir_instr *after_me);
>> >> > >
>> >> > > -/* visits basic blocks in source-code order */
>> >> > > -typedef bool (*nir_foreach_block_cb)(nir_block *block, void
>> >> > > *state);
>> >> > > -bool nir_foreach_block(nir_function_impl *impl,
>> >> > > nir_foreach_block_cb
>> >> > > cb,
>> >> > > -                       void *state);
>> >> > > -bool nir_foreach_block_reverse(nir_function_impl *impl,
>> >> > > nir_foreach_block_cb cb,
>> >> > > -                               void *state);
>> >> > > -bool nir_foreach_block_in_cf_node(nir_cf_node *node,
>> >> > > nir_foreach_block_cb cb,
>> >> > > -                                  void *state);
>> >> >
>> >> >
>> >> > so, I like the new iterator macros, but this is one giant massive
>> >> > unbisectable flag-day change :-(
>> >> >
>> >> > I'd prefer an approach that kept the old fxns implemented in terms of
>> >> > the new macros at the beginning of the patchset, then removed them
>> >> > once everyone else was converted.  Which is slightly annoying since
>> >> > you'd kinda want to use the same names.  But less of a pita wrt new
>> >> > nir passes that haven't been pushed yet (I've got a bunch.. I'm not
>> >> > sure if all of Jason's vulkan stuff has landed yet either)
>> >>
>> >> I think enough Vulkan stuff has landed to make this not terrible but
>> >> you're right about the scope of things.  How about starting with a
>> >> patch
>> >> that renames nir_foreach_block to something else, say
>> >> nir_foreach_block_call.  Then patch 1 then a patch to implement
>> >> nir_foreach_block_call in terms of the macro and then the switchover
>> >> patches.  The rename will have to touch everything but it will be
>> >> trivially
>> >> correct. The rest can be incremental.
>> >>
>> >> That said, I'm still going to look through the patches to see what I
>> >> think
>> >> of the end result.
>> >>
>> >> > BR,
>> >> > -R
>> >> >
>> >> > > +/*
>> >> > > + * finds the next basic block in source-code order, returns NULL
>> >> > > if
>> >> > > there is
>> >> > > + * none
>> >> > > + */
>> >> > > +
>> >> > > +nir_block *nir_block_cf_tree_next(nir_block *block);
>> >> > > +
>> >> > > +/* Performs the opposite of nir_block_cf_tree_next() */
>> >> > > +
>> >> > > +nir_block *nir_block_cf_tree_prev(nir_block *block);
>> >> > > +
>> >> > > +/* Gets the first block in a CF node in source-code order */
>> >> > > +
>> >> > > +nir_block *nir_cf_node_cf_tree_first(nir_cf_node *node);
>> >> > > +
>> >> > > +/* Gets the last block in a CF node in source-code order */
>> >> > > +
>> >> > > +nir_block *nir_cf_node_cf_tree_last(nir_cf_node *node);
>> >> > > +
>> >> > > +/* Gets the next block after a CF node in source-code order */
>> >> > > +
>> >> > > +nir_block *nir_cf_node_cf_tree_next(nir_cf_node *node);
>> >> > > +
>> >> > > +/* Macros for loops that visit blocks in source-code order */
>> >> > > +
>> >> > > +#define nir_foreach_block(impl, block) \
>> >> > > +   for (nir_block *block = nir_start_block(impl); block != NULL; \
>> >> > > +        block = nir_block_cf_tree_next(block))
>> >
>> >
>> > I'm sorry in advance for this comment, but can we put impl and block in
>> > the
>> > other order.  This is something that's been bothering me for some time
>> > now.
>> > NIR's iterators are inconsistent as to which order the parameters go.
>> > This
>> > isn't your fault at all; I'm the one who added most of them. :-(  In
>> > most
>> > languages that have a foreach such as C++11, Java, and python, it's
>> > usually
>> > "for (thing : container)" or "for thing in container:".  I think we
>> > should
>> > follow that convention.
>> >
>> > I'll write the patches to fix the others, but let's do this one
>> > correctly.
>>
>> Well, from looking through nir.h it seems like most of the iterators
>> use the same order I did. The only ones that don't are
>> nir_foreach_variable() and nir_foreach_variable_safe().
>
>
> Yes, I know and I personally find that order more natural.  But I think
> consistency with other languages is important too.
>
>>
>> I'd rather
>> keep them consistent with each other now, and then change everything
>> at once. As-is, it would stick out like a sore thumb, especially since
>> after this series it's a relatively common pattern to do:
>>
>> nir_foreach_block(impl, block) {
>>    nir_foreach_instr(block, instr) {
>>       ...
>>    }
>> }
>>
>> After all, changing the other iterators would have to be a flag-day
>> rename touching basically everything already, so it can't be much
>> worse if you change nir_foreach_block() as well.
>
>
> I guess.  However, I don't see why we need to add something now and change
> the order later.  Other than who has to go make the changes. :-P

Well, I could make the change, but at this point it would be a bit
more involved. I'd have to figure out how to get the list of files
changed with each commit and then run a sed job on only them
(otherwise I'd re-swap the arguments for everything else), and then
amend each commit. Probably doable, but it would take some work to
make it automated. On the other hand, it's just a simple(r) sed job
for you, and you'll have to write a lot of very similar sed jobs
anyways.

>
>>
>> >
>> >> > > +
>> >> > > +#define nir_foreach_block_safe(impl, block) \
>> >> > > +   for (nir_block *block = nir_start_block(impl), \
>> >> > > +        *next = nir_block_cf_tree_next(block); \
>> >> > > +        block != NULL; \
>> >> > > +        block = next, next = nir_block_cf_tree_next(block))
>> >> > > +
>> >> > > +#define nir_foreach_block_reverse(impl, block) \
>> >> > > +   for (nir_block *block = nir_impl_last_block(impl); block !=
>> >> > > NULL;
>> >> > > \
>> >> > > +        block = nir_block_cf_tree_prev(block))
>> >> > > +
>> >> > > +#define nir_foreach_block_in_cf_node(node, block) \
>> >> > > +   for (nir_block *block = nir_cf_node_cf_tree_first(node); \
>> >> > > +        block != nir_cf_node_cf_tree_next(node); \
>> >> > > +        block = nir_block_cf_tree_next(block))
>> >> > >
>> >> > >  /* If the following CF node is an if, this function returns that
>> >> > > if.
>> >> > >   * Otherwise, it returns NULL.
>> >> > > --
>> >> > > 2.5.0
>> >> > >
>> >> > > _______________________________________________
>> >> > > mesa-dev mailing list
>> >> > > mesa-dev at lists.freedesktop.org
>> >> > > https://lists.freedesktop.org/mailman/listinfo/mesa-dev
>> >> > _______________________________________________
>> >> > mesa-dev mailing list
>> >> > mesa-dev at lists.freedesktop.org
>> >> > https://lists.freedesktop.org/mailman/listinfo/mesa-dev
>> >
>> >
>
>


More information about the mesa-dev mailing list