18a1362adSmaya/*
28a1362adSmaya * Copyright © 2019 Broadcom
38a1362adSmaya *
48a1362adSmaya * Permission is hereby granted, free of charge, to any person obtaining a
58a1362adSmaya * copy of this software and associated documentation files (the "Software"),
68a1362adSmaya * to deal in the Software without restriction, including without limitation
78a1362adSmaya * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88a1362adSmaya * and/or sell copies of the Software, and to permit persons to whom the
98a1362adSmaya * Software is furnished to do so, subject to the following conditions:
108a1362adSmaya *
118a1362adSmaya * The above copyright notice and this permission notice (including the next
128a1362adSmaya * paragraph) shall be included in all copies or substantial portions of the
138a1362adSmaya * Software.
148a1362adSmaya *
158a1362adSmaya * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168a1362adSmaya * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178a1362adSmaya * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
188a1362adSmaya * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
198a1362adSmaya * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
208a1362adSmaya * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
218a1362adSmaya * IN THE SOFTWARE.
228a1362adSmaya */
238a1362adSmaya
248a1362adSmaya#include "util/set.h"
258a1362adSmaya#include "util/dag.h"
268a1362adSmaya
278a1362adSmaya/**
288a1362adSmaya * Adds a directed edge from the parent node to the child.
298a1362adSmaya *
308a1362adSmaya * Both nodes should have been initialized with dag_init_node().  The edge
318a1362adSmaya * list may contain multiple edges to the same child with different data.
328a1362adSmaya */
338a1362adSmayavoid
348a1362adSmayadag_add_edge(struct dag_node *parent, struct dag_node *child, void *data)
358a1362adSmaya{
368a1362adSmaya   util_dynarray_foreach(&parent->edges, struct dag_edge, edge) {
378a1362adSmaya      if (edge->child == child && edge->data == data)
388a1362adSmaya         return;
398a1362adSmaya   }
408a1362adSmaya   /* Remove the child as a DAG head. */
418a1362adSmaya   list_delinit(&child->link);
428a1362adSmaya
438a1362adSmaya   struct dag_edge edge = {
448a1362adSmaya      .child = child,
458a1362adSmaya      .data = data,
468a1362adSmaya   };
478a1362adSmaya
488a1362adSmaya   util_dynarray_append(&parent->edges, struct dag_edge, edge);
498a1362adSmaya   child->parent_count++;
508a1362adSmaya}
518a1362adSmaya
528a1362adSmaya/* Removes a single edge from the graph, promoting the child to a DAG head.
538a1362adSmaya *
548a1362adSmaya * Note that calling this other than through dag_prune_head() means that you
558a1362adSmaya * need to be careful when iterating the edges of remaining nodes for NULL
568a1362adSmaya * children.
578a1362adSmaya */
588a1362adSmayavoid
598a1362adSmayadag_remove_edge(struct dag *dag, struct dag_edge *edge)
608a1362adSmaya{
618a1362adSmaya   if (!edge->child)
628a1362adSmaya      return;
638a1362adSmaya
648a1362adSmaya   struct dag_node *child = edge->child;
658a1362adSmaya   child->parent_count--;
668a1362adSmaya   if (child->parent_count == 0)
678a1362adSmaya      list_addtail(&child->link, &dag->heads);
688a1362adSmaya
698a1362adSmaya   edge->child = NULL;
708a1362adSmaya   edge->data = NULL;
718a1362adSmaya}
728a1362adSmaya
738a1362adSmaya/**
748a1362adSmaya * Removes a DAG head from the graph, and moves any new dag heads into the
758a1362adSmaya * heads list.
768a1362adSmaya */
778a1362adSmayavoid
788a1362adSmayadag_prune_head(struct dag *dag, struct dag_node *node)
798a1362adSmaya{
808a1362adSmaya   assert(!node->parent_count);
818a1362adSmaya
828a1362adSmaya   list_delinit(&node->link);
838a1362adSmaya
848a1362adSmaya   util_dynarray_foreach(&node->edges, struct dag_edge, edge) {
858a1362adSmaya      dag_remove_edge(dag, edge);
868a1362adSmaya   }
878a1362adSmaya}
888a1362adSmaya
898a1362adSmaya/**
908a1362adSmaya * Initializes DAG node (probably embedded in some other datastructure in the
918a1362adSmaya * user).
928a1362adSmaya */
938a1362adSmayavoid
948a1362adSmayadag_init_node(struct dag *dag, struct dag_node *node)
958a1362adSmaya{
968a1362adSmaya   util_dynarray_init(&node->edges, dag);
978a1362adSmaya   list_addtail(&node->link, &dag->heads);
988a1362adSmaya}
998a1362adSmaya
1008a1362adSmayastruct dag_traverse_bottom_up_state {
1018a1362adSmaya   struct set *seen;
1028a1362adSmaya   void *data;
1038a1362adSmaya};
1048a1362adSmaya
1058a1362adSmayastatic void
1068a1362adSmayadag_traverse_bottom_up_node(struct dag_node *node,
1078a1362adSmaya                            void (*cb)(struct dag_node *node,
1088a1362adSmaya                                       void *data),
1098a1362adSmaya                            struct dag_traverse_bottom_up_state *state)
1108a1362adSmaya{
1118a1362adSmaya   if (_mesa_set_search(state->seen, node))
1128a1362adSmaya      return;
1138a1362adSmaya
1147ec681f3Smrg   struct util_dynarray stack;
1157ec681f3Smrg   util_dynarray_init(&stack, NULL);
1167ec681f3Smrg
1177ec681f3Smrg   do {
1187ec681f3Smrg      assert(node);
1197ec681f3Smrg
1207ec681f3Smrg      while (node->edges.size != 0) {
1217ec681f3Smrg         util_dynarray_append(&stack, struct dag_node *, node);
1227ec681f3Smrg
1237ec681f3Smrg         /* Push unprocessed children onto stack in reverse order. Note that
1247ec681f3Smrg          * it's possible for any of the children nodes to already be on the
1257ec681f3Smrg          * stack.
1267ec681f3Smrg          */
1277ec681f3Smrg         util_dynarray_foreach_reverse(&node->edges, struct dag_edge, edge) {
1287ec681f3Smrg            if (!_mesa_set_search(state->seen, edge->child)) {
1297ec681f3Smrg               util_dynarray_append(&stack, struct dag_node *, edge->child);
1307ec681f3Smrg            }
1317ec681f3Smrg         }
1327ec681f3Smrg
1337ec681f3Smrg         /* Get last element pushed: either left-most child or current node.
1347ec681f3Smrg          * If it's the current node, that means that we've processed all its
1357ec681f3Smrg          * children already.
1367ec681f3Smrg          */
1377ec681f3Smrg         struct dag_node *top = util_dynarray_pop(&stack, struct dag_node *);
1387ec681f3Smrg         if (top == node)
1397ec681f3Smrg            break;
1407ec681f3Smrg         node = top;
1417ec681f3Smrg      }
1427ec681f3Smrg
1437ec681f3Smrg      /* Process the node */
1447ec681f3Smrg      cb(node, state->data);
1457ec681f3Smrg      _mesa_set_add(state->seen, node);
1467ec681f3Smrg
1477ec681f3Smrg      /* Find the next unprocessed node in the stack */
1487ec681f3Smrg      do {
1497ec681f3Smrg         node = NULL;
1507ec681f3Smrg         if (stack.size == 0)
1517ec681f3Smrg            break;
1527ec681f3Smrg
1537ec681f3Smrg         node = util_dynarray_pop(&stack, struct dag_node *);
1547ec681f3Smrg      } while (_mesa_set_search(state->seen, node));
1557ec681f3Smrg   } while (node);
1567ec681f3Smrg
1577ec681f3Smrg   util_dynarray_fini(&stack);
1588a1362adSmaya}
1598a1362adSmaya
1608a1362adSmaya/**
1618a1362adSmaya * Walks the DAG from leaves to the root, ensuring that each node is only seen
1628a1362adSmaya * once its children have been, and each node is only traversed once.
1638a1362adSmaya */
1648a1362adSmayavoid
1658a1362adSmayadag_traverse_bottom_up(struct dag *dag, void (*cb)(struct dag_node *node,
1668a1362adSmaya                                                   void *data), void *data)
1678a1362adSmaya{
1688a1362adSmaya   struct dag_traverse_bottom_up_state state = {
1698a1362adSmaya      .seen = _mesa_pointer_set_create(NULL),
1708a1362adSmaya      .data = data,
1718a1362adSmaya   };
1728a1362adSmaya
1738a1362adSmaya   list_for_each_entry(struct dag_node, node, &dag->heads, link) {
1748a1362adSmaya      dag_traverse_bottom_up_node(node, cb, &state);
1758a1362adSmaya   }
1768a1362adSmaya
1778a1362adSmaya   ralloc_free(state.seen);
1788a1362adSmaya}
1798a1362adSmaya
1808a1362adSmaya/**
1818a1362adSmaya * Creates an empty DAG datastructure.
1828a1362adSmaya */
1838a1362adSmayastruct dag *
1848a1362adSmayadag_create(void *mem_ctx)
1858a1362adSmaya{
1868a1362adSmaya   struct dag *dag = rzalloc(mem_ctx, struct dag);
1878a1362adSmaya
1888a1362adSmaya   list_inithead(&dag->heads);
1898a1362adSmaya
1908a1362adSmaya   return dag;
1918a1362adSmaya}
1928a1362adSmaya
193