1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2019 Broadcom
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21bf215546Sopenharmony_ci * IN THE SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci#include "util/set.h"
25bf215546Sopenharmony_ci#include "util/dag.h"
26bf215546Sopenharmony_ci
27bf215546Sopenharmony_cistatic void
28bf215546Sopenharmony_ciappend_edge(struct dag_node *parent, struct dag_node *child, uintptr_t data)
29bf215546Sopenharmony_ci{
30bf215546Sopenharmony_ci   /* Remove the child as a DAG head. */
31bf215546Sopenharmony_ci   list_delinit(&child->link);
32bf215546Sopenharmony_ci
33bf215546Sopenharmony_ci   struct dag_edge edge = {
34bf215546Sopenharmony_ci      .child = child,
35bf215546Sopenharmony_ci      .data = data,
36bf215546Sopenharmony_ci   };
37bf215546Sopenharmony_ci
38bf215546Sopenharmony_ci   util_dynarray_append(&parent->edges, struct dag_edge, edge);
39bf215546Sopenharmony_ci   child->parent_count++;
40bf215546Sopenharmony_ci}
41bf215546Sopenharmony_ci
42bf215546Sopenharmony_ci/**
43bf215546Sopenharmony_ci * Adds a directed edge from the parent node to the child.
44bf215546Sopenharmony_ci *
45bf215546Sopenharmony_ci * Both nodes should have been initialized with dag_init_node().  The edge
46bf215546Sopenharmony_ci * list may contain multiple edges to the same child with different data.
47bf215546Sopenharmony_ci */
48bf215546Sopenharmony_civoid
49bf215546Sopenharmony_cidag_add_edge(struct dag_node *parent, struct dag_node *child, uintptr_t data)
50bf215546Sopenharmony_ci{
51bf215546Sopenharmony_ci   util_dynarray_foreach(&parent->edges, struct dag_edge, edge) {
52bf215546Sopenharmony_ci      if (edge->child == child && edge->data == data)
53bf215546Sopenharmony_ci         return;
54bf215546Sopenharmony_ci   }
55bf215546Sopenharmony_ci
56bf215546Sopenharmony_ci   append_edge(parent, child, data);
57bf215546Sopenharmony_ci}
58bf215546Sopenharmony_ci
59bf215546Sopenharmony_ci/**
60bf215546Sopenharmony_ci * Adds a directed edge from the parent node to the child.
61bf215546Sopenharmony_ci *
62bf215546Sopenharmony_ci * Both nodes should have been initialized with dag_init_node(). If there is
63bf215546Sopenharmony_ci * already an existing edge, the data is updated to the maximum of the
64bf215546Sopenharmony_ci * previous data and the new data. This is useful if the data represents a
65bf215546Sopenharmony_ci * delay.
66bf215546Sopenharmony_ci */
67bf215546Sopenharmony_civoid
68bf215546Sopenharmony_cidag_add_edge_max_data(struct dag_node *parent, struct dag_node *child,
69bf215546Sopenharmony_ci                      uintptr_t data)
70bf215546Sopenharmony_ci{
71bf215546Sopenharmony_ci   util_dynarray_foreach(&parent->edges, struct dag_edge, edge) {
72bf215546Sopenharmony_ci      if (edge->child == child) {
73bf215546Sopenharmony_ci         edge->data = MAX2(edge->data, data);
74bf215546Sopenharmony_ci         return;
75bf215546Sopenharmony_ci      }
76bf215546Sopenharmony_ci   }
77bf215546Sopenharmony_ci
78bf215546Sopenharmony_ci   append_edge(parent, child, data);
79bf215546Sopenharmony_ci}
80bf215546Sopenharmony_ci
81bf215546Sopenharmony_ci/* Removes a single edge from the graph, promoting the child to a DAG head.
82bf215546Sopenharmony_ci *
83bf215546Sopenharmony_ci * Note that calling this other than through dag_prune_head() means that you
84bf215546Sopenharmony_ci * need to be careful when iterating the edges of remaining nodes for NULL
85bf215546Sopenharmony_ci * children.
86bf215546Sopenharmony_ci */
87bf215546Sopenharmony_civoid
88bf215546Sopenharmony_cidag_remove_edge(struct dag *dag, struct dag_edge *edge)
89bf215546Sopenharmony_ci{
90bf215546Sopenharmony_ci   if (!edge->child)
91bf215546Sopenharmony_ci      return;
92bf215546Sopenharmony_ci
93bf215546Sopenharmony_ci   struct dag_node *child = edge->child;
94bf215546Sopenharmony_ci   child->parent_count--;
95bf215546Sopenharmony_ci   if (child->parent_count == 0)
96bf215546Sopenharmony_ci      list_addtail(&child->link, &dag->heads);
97bf215546Sopenharmony_ci
98bf215546Sopenharmony_ci   edge->child = NULL;
99bf215546Sopenharmony_ci   edge->data = 0;
100bf215546Sopenharmony_ci}
101bf215546Sopenharmony_ci
102bf215546Sopenharmony_ci/**
103bf215546Sopenharmony_ci * Removes a DAG head from the graph, and moves any new dag heads into the
104bf215546Sopenharmony_ci * heads list.
105bf215546Sopenharmony_ci */
106bf215546Sopenharmony_civoid
107bf215546Sopenharmony_cidag_prune_head(struct dag *dag, struct dag_node *node)
108bf215546Sopenharmony_ci{
109bf215546Sopenharmony_ci   assert(!node->parent_count);
110bf215546Sopenharmony_ci
111bf215546Sopenharmony_ci   list_delinit(&node->link);
112bf215546Sopenharmony_ci
113bf215546Sopenharmony_ci   util_dynarray_foreach(&node->edges, struct dag_edge, edge) {
114bf215546Sopenharmony_ci      dag_remove_edge(dag, edge);
115bf215546Sopenharmony_ci   }
116bf215546Sopenharmony_ci}
117bf215546Sopenharmony_ci
118bf215546Sopenharmony_ci/**
119bf215546Sopenharmony_ci * Initializes DAG node (probably embedded in some other datastructure in the
120bf215546Sopenharmony_ci * user).
121bf215546Sopenharmony_ci */
122bf215546Sopenharmony_civoid
123bf215546Sopenharmony_cidag_init_node(struct dag *dag, struct dag_node *node)
124bf215546Sopenharmony_ci{
125bf215546Sopenharmony_ci   util_dynarray_init(&node->edges, dag);
126bf215546Sopenharmony_ci   list_addtail(&node->link, &dag->heads);
127bf215546Sopenharmony_ci}
128bf215546Sopenharmony_ci
129bf215546Sopenharmony_cistruct dag_traverse_bottom_up_state {
130bf215546Sopenharmony_ci   struct set *seen;
131bf215546Sopenharmony_ci   void *data;
132bf215546Sopenharmony_ci};
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_cistatic void
135bf215546Sopenharmony_cidag_traverse_bottom_up_node(struct dag_node *node,
136bf215546Sopenharmony_ci                            void (*cb)(struct dag_node *node,
137bf215546Sopenharmony_ci                                       void *data),
138bf215546Sopenharmony_ci                            struct dag_traverse_bottom_up_state *state)
139bf215546Sopenharmony_ci{
140bf215546Sopenharmony_ci   if (_mesa_set_search(state->seen, node))
141bf215546Sopenharmony_ci      return;
142bf215546Sopenharmony_ci
143bf215546Sopenharmony_ci   struct util_dynarray stack;
144bf215546Sopenharmony_ci   util_dynarray_init(&stack, NULL);
145bf215546Sopenharmony_ci
146bf215546Sopenharmony_ci   do {
147bf215546Sopenharmony_ci      assert(node);
148bf215546Sopenharmony_ci
149bf215546Sopenharmony_ci      while (node->edges.size != 0) {
150bf215546Sopenharmony_ci         util_dynarray_append(&stack, struct dag_node *, node);
151bf215546Sopenharmony_ci
152bf215546Sopenharmony_ci         /* Push unprocessed children onto stack in reverse order. Note that
153bf215546Sopenharmony_ci          * it's possible for any of the children nodes to already be on the
154bf215546Sopenharmony_ci          * stack.
155bf215546Sopenharmony_ci          */
156bf215546Sopenharmony_ci         util_dynarray_foreach_reverse(&node->edges, struct dag_edge, edge) {
157bf215546Sopenharmony_ci            if (!_mesa_set_search(state->seen, edge->child)) {
158bf215546Sopenharmony_ci               util_dynarray_append(&stack, struct dag_node *, edge->child);
159bf215546Sopenharmony_ci            }
160bf215546Sopenharmony_ci         }
161bf215546Sopenharmony_ci
162bf215546Sopenharmony_ci         /* Get last element pushed: either left-most child or current node.
163bf215546Sopenharmony_ci          * If it's the current node, that means that we've processed all its
164bf215546Sopenharmony_ci          * children already.
165bf215546Sopenharmony_ci          */
166bf215546Sopenharmony_ci         struct dag_node *top = util_dynarray_pop(&stack, struct dag_node *);
167bf215546Sopenharmony_ci         if (top == node)
168bf215546Sopenharmony_ci            break;
169bf215546Sopenharmony_ci         node = top;
170bf215546Sopenharmony_ci      }
171bf215546Sopenharmony_ci
172bf215546Sopenharmony_ci      /* Process the node */
173bf215546Sopenharmony_ci      cb(node, state->data);
174bf215546Sopenharmony_ci      _mesa_set_add(state->seen, node);
175bf215546Sopenharmony_ci
176bf215546Sopenharmony_ci      /* Find the next unprocessed node in the stack */
177bf215546Sopenharmony_ci      do {
178bf215546Sopenharmony_ci         node = NULL;
179bf215546Sopenharmony_ci         if (stack.size == 0)
180bf215546Sopenharmony_ci            break;
181bf215546Sopenharmony_ci
182bf215546Sopenharmony_ci         node = util_dynarray_pop(&stack, struct dag_node *);
183bf215546Sopenharmony_ci      } while (_mesa_set_search(state->seen, node));
184bf215546Sopenharmony_ci   } while (node);
185bf215546Sopenharmony_ci
186bf215546Sopenharmony_ci   util_dynarray_fini(&stack);
187bf215546Sopenharmony_ci}
188bf215546Sopenharmony_ci
189bf215546Sopenharmony_ci/**
190bf215546Sopenharmony_ci * Walks the DAG from leaves to the root, ensuring that each node is only seen
191bf215546Sopenharmony_ci * once its children have been, and each node is only traversed once.
192bf215546Sopenharmony_ci */
193bf215546Sopenharmony_civoid
194bf215546Sopenharmony_cidag_traverse_bottom_up(struct dag *dag, void (*cb)(struct dag_node *node,
195bf215546Sopenharmony_ci                                                   void *data), void *data)
196bf215546Sopenharmony_ci{
197bf215546Sopenharmony_ci   struct dag_traverse_bottom_up_state state = {
198bf215546Sopenharmony_ci      .seen = _mesa_pointer_set_create(NULL),
199bf215546Sopenharmony_ci      .data = data,
200bf215546Sopenharmony_ci   };
201bf215546Sopenharmony_ci
202bf215546Sopenharmony_ci   list_for_each_entry(struct dag_node, node, &dag->heads, link) {
203bf215546Sopenharmony_ci      dag_traverse_bottom_up_node(node, cb, &state);
204bf215546Sopenharmony_ci   }
205bf215546Sopenharmony_ci
206bf215546Sopenharmony_ci   ralloc_free(state.seen);
207bf215546Sopenharmony_ci}
208bf215546Sopenharmony_ci
209bf215546Sopenharmony_ci/**
210bf215546Sopenharmony_ci * Creates an empty DAG datastructure.
211bf215546Sopenharmony_ci */
212bf215546Sopenharmony_cistruct dag *
213bf215546Sopenharmony_cidag_create(void *mem_ctx)
214bf215546Sopenharmony_ci{
215bf215546Sopenharmony_ci   struct dag *dag = rzalloc(mem_ctx, struct dag);
216bf215546Sopenharmony_ci
217bf215546Sopenharmony_ci   list_inithead(&dag->heads);
218bf215546Sopenharmony_ci
219bf215546Sopenharmony_ci   return dag;
220bf215546Sopenharmony_ci}
221bf215546Sopenharmony_ci
222