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