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