| /* |
| * Copyright 2011 Christoph Bumiller |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF |
| * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include "nv50_ir_graph.h" |
| #include <limits> |
| #include <list> |
| #include <stack> |
| #include "nv50_ir.h" |
| |
| namespace nv50_ir { |
| |
| Graph::Graph() |
| { |
| root = NULL; |
| size = 0; |
| sequence = 0; |
| } |
| |
| Graph::~Graph() |
| { |
| for (IteratorRef it = safeIteratorDFS(); !it->end(); it->next()) |
| reinterpret_cast<Node *>(it->get())->cut(); |
| } |
| |
| void Graph::insert(Node *node) |
| { |
| if (!root) |
| root = node; |
| |
| node->graph = this; |
| size++; |
| } |
| |
| void Graph::Edge::unlink() |
| { |
| if (origin) { |
| prev[0]->next[0] = next[0]; |
| next[0]->prev[0] = prev[0]; |
| if (origin->out == this) |
| origin->out = (next[0] == this) ? NULL : next[0]; |
| |
| --origin->outCount; |
| } |
| if (target) { |
| prev[1]->next[1] = next[1]; |
| next[1]->prev[1] = prev[1]; |
| if (target->in == this) |
| target->in = (next[1] == this) ? NULL : next[1]; |
| |
| --target->inCount; |
| } |
| } |
| |
| const char *Graph::Edge::typeStr() const |
| { |
| switch (type) { |
| case TREE: return "tree"; |
| case FORWARD: return "forward"; |
| case BACK: return "back"; |
| case CROSS: return "cross"; |
| case DUMMY: return "dummy"; |
| case UNKNOWN: |
| default: |
| return "unk"; |
| } |
| } |
| |
| Graph::Node::Node(void *priv) : data(priv), |
| in(0), out(0), graph(0), |
| visited(0), |
| inCount(0), outCount(0) |
| { |
| // nothing to do |
| } |
| |
| void Graph::Node::attach(Node *node, Edge::Type kind) |
| { |
| Edge *edge = new Edge(this, node, kind); |
| |
| // insert head |
| if (this->out) { |
| edge->next[0] = this->out; |
| edge->prev[0] = this->out->prev[0]; |
| edge->prev[0]->next[0] = edge; |
| this->out->prev[0] = edge; |
| } |
| this->out = edge; |
| |
| if (node->in) { |
| edge->next[1] = node->in; |
| edge->prev[1] = node->in->prev[1]; |
| edge->prev[1]->next[1] = edge; |
| node->in->prev[1] = edge; |
| } |
| node->in = edge; |
| |
| ++this->outCount; |
| ++node->inCount; |
| |
| assert(graph || node->graph); |
| if (!node->graph) |
| graph->insert(node); |
| if (!graph) |
| node->graph->insert(this); |
| |
| if (kind == Edge::UNKNOWN) |
| graph->classifyEdges(); |
| } |
| |
| bool Graph::Node::detach(Graph::Node *node) |
| { |
| EdgeIterator ei = this->outgoing(); |
| for (; !ei.end(); ei.next()) |
| if (ei.getNode() == node) |
| break; |
| if (ei.end()) { |
| ERROR("no such node attached\n"); |
| return false; |
| } |
| delete ei.getEdge(); |
| return true; |
| } |
| |
| // Cut a node from the graph, deleting all attached edges. |
| void Graph::Node::cut() |
| { |
| while (out) |
| delete out; |
| while (in) |
| delete in; |
| |
| if (graph) { |
| if (graph->root == this) |
| graph->root = NULL; |
| graph = NULL; |
| } |
| } |
| |
| Graph::Edge::Edge(Node *org, Node *tgt, Type kind) |
| { |
| target = tgt; |
| origin = org; |
| type = kind; |
| |
| next[0] = next[1] = this; |
| prev[0] = prev[1] = this; |
| } |
| |
| bool |
| Graph::Node::reachableBy(const Node *node, const Node *term) const |
| { |
| std::stack<const Node *> stack; |
| const Node *pos = NULL; |
| const int seq = graph->nextSequence(); |
| |
| stack.push(node); |
| |
| while (!stack.empty()) { |
| pos = stack.top(); |
| stack.pop(); |
| |
| if (pos == this) |
| return true; |
| if (pos == term) |
| continue; |
| |
| for (EdgeIterator ei = pos->outgoing(); !ei.end(); ei.next()) { |
| if (ei.getType() == Edge::BACK || ei.getType() == Edge::DUMMY) |
| continue; |
| if (ei.getNode()->visit(seq)) |
| stack.push(ei.getNode()); |
| } |
| } |
| return pos == this; |
| } |
| |
| class DFSIterator : public Iterator |
| { |
| public: |
| DFSIterator(Graph *graph, const bool preorder) |
| { |
| unsigned int seq = graph->nextSequence(); |
| |
| nodes = new Graph::Node * [graph->getSize() + 1]; |
| count = 0; |
| pos = 0; |
| nodes[graph->getSize()] = 0; |
| |
| if (graph->getRoot()) { |
| graph->getRoot()->visit(seq); |
| search(graph->getRoot(), preorder, seq); |
| } |
| } |
| |
| ~DFSIterator() |
| { |
| if (nodes) |
| delete[] nodes; |
| } |
| |
| void search(Graph::Node *node, const bool preorder, const int sequence) |
| { |
| if (preorder) |
| nodes[count++] = node; |
| |
| for (Graph::EdgeIterator ei = node->outgoing(); !ei.end(); ei.next()) |
| if (ei.getNode()->visit(sequence)) |
| search(ei.getNode(), preorder, sequence); |
| |
| if (!preorder) |
| nodes[count++] = node; |
| } |
| |
| virtual bool end() const { return pos >= count; } |
| virtual void next() { if (pos < count) ++pos; } |
| virtual void *get() const { return nodes[pos]; } |
| virtual void reset() { pos = 0; } |
| |
| protected: |
| Graph::Node **nodes; |
| int count; |
| int pos; |
| }; |
| |
| IteratorRef Graph::iteratorDFS(bool preorder) |
| { |
| return IteratorRef(new DFSIterator(this, preorder)); |
| } |
| |
| IteratorRef Graph::safeIteratorDFS(bool preorder) |
| { |
| return this->iteratorDFS(preorder); |
| } |
| |
| class CFGIterator : public Iterator |
| { |
| public: |
| CFGIterator(Graph *graph) |
| { |
| nodes = new Graph::Node * [graph->getSize() + 1]; |
| count = 0; |
| pos = 0; |
| nodes[graph->getSize()] = 0; |
| |
| // TODO: argh, use graph->sequence instead of tag and just raise it by > 1 |
| for (IteratorRef it = graph->iteratorDFS(); !it->end(); it->next()) |
| reinterpret_cast<Graph::Node *>(it->get())->tag = 0; |
| |
| if (graph->getRoot()) |
| search(graph->getRoot(), graph->nextSequence()); |
| } |
| |
| ~CFGIterator() |
| { |
| if (nodes) |
| delete[] nodes; |
| } |
| |
| virtual void *get() const { return nodes[pos]; } |
| virtual bool end() const { return pos >= count; } |
| virtual void next() { if (pos < count) ++pos; } |
| virtual void reset() { pos = 0; } |
| |
| private: |
| void search(Graph::Node *node, const int sequence) |
| { |
| Stack bb, cross; |
| |
| bb.push(node); |
| |
| while (bb.getSize()) { |
| node = reinterpret_cast<Graph::Node *>(bb.pop().u.p); |
| assert(node); |
| if (!node->visit(sequence)) |
| continue; |
| node->tag = 0; |
| |
| for (Graph::EdgeIterator ei = node->outgoing(); !ei.end(); ei.next()) { |
| switch (ei.getType()) { |
| case Graph::Edge::TREE: |
| case Graph::Edge::FORWARD: |
| case Graph::Edge::DUMMY: |
| if (++(ei.getNode()->tag) == ei.getNode()->incidentCountFwd()) |
| bb.push(ei.getNode()); |
| break; |
| case Graph::Edge::BACK: |
| continue; |
| case Graph::Edge::CROSS: |
| if (++(ei.getNode()->tag) == 1) |
| cross.push(ei.getNode()); |
| break; |
| default: |
| assert(!"unknown edge kind in CFG"); |
| break; |
| } |
| } |
| nodes[count++] = node; |
| |
| if (bb.getSize() == 0) |
| cross.moveTo(bb); |
| } |
| } |
| |
| private: |
| Graph::Node **nodes; |
| int count; |
| int pos; |
| }; |
| |
| IteratorRef Graph::iteratorCFG() |
| { |
| return IteratorRef(new CFGIterator(this)); |
| } |
| |
| IteratorRef Graph::safeIteratorCFG() |
| { |
| return this->iteratorCFG(); |
| } |
| |
| void Graph::classifyEdges() |
| { |
| int seq; |
| |
| for (IteratorRef it = iteratorDFS(true); !it->end(); it->next()) { |
| Node *node = reinterpret_cast<Node *>(it->get()); |
| node->visit(0); |
| node->tag = 0; |
| } |
| |
| classifyDFS(root, (seq = 0)); |
| |
| sequence = seq; |
| } |
| |
| void Graph::classifyDFS(Node *curr, int& seq) |
| { |
| Graph::Edge *edge; |
| Graph::Node *node; |
| |
| curr->visit(++seq); |
| curr->tag = 1; |
| |
| for (edge = curr->out; edge; edge = edge->next[0]) { |
| node = edge->target; |
| if (edge->type == Edge::DUMMY) |
| continue; |
| |
| if (node->getSequence() == 0) { |
| edge->type = Edge::TREE; |
| classifyDFS(node, seq); |
| } else |
| if (node->getSequence() > curr->getSequence()) { |
| edge->type = Edge::FORWARD; |
| } else { |
| edge->type = node->tag ? Edge::BACK : Edge::CROSS; |
| } |
| } |
| |
| for (edge = curr->in; edge; edge = edge->next[1]) { |
| node = edge->origin; |
| if (edge->type == Edge::DUMMY) |
| continue; |
| |
| if (node->getSequence() == 0) { |
| edge->type = Edge::TREE; |
| classifyDFS(node, seq); |
| } else |
| if (node->getSequence() > curr->getSequence()) { |
| edge->type = Edge::FORWARD; |
| } else { |
| edge->type = node->tag ? Edge::BACK : Edge::CROSS; |
| } |
| } |
| |
| curr->tag = 0; |
| } |
| |
| // @dist is indexed by Node::tag, returns -1 if no path found |
| int |
| Graph::findLightestPathWeight(Node *a, Node *b, const std::vector<int> &weight) |
| { |
| std::vector<int> path(weight.size(), std::numeric_limits<int>::max()); |
| std::list<Node *> nodeList; |
| const int seq = nextSequence(); |
| |
| path[a->tag] = 0; |
| for (Node *c = a; c && c != b;) { |
| const int p = path[c->tag] + weight[c->tag]; |
| for (EdgeIterator ei = c->outgoing(); !ei.end(); ei.next()) { |
| Node *t = ei.getNode(); |
| if (t->getSequence() < seq) { |
| if (path[t->tag] == std::numeric_limits<int>::max()) |
| nodeList.push_front(t); |
| if (p < path[t->tag]) |
| path[t->tag] = p; |
| } |
| } |
| c->visit(seq); |
| Node *next = NULL; |
| for (std::list<Node *>::iterator n = nodeList.begin(); |
| n != nodeList.end(); ++n) { |
| if (!next || path[(*n)->tag] < path[next->tag]) |
| next = *n; |
| if ((*n) == c) { |
| // erase visited |
| n = nodeList.erase(n); |
| --n; |
| } |
| } |
| c = next; |
| } |
| if (path[b->tag] == std::numeric_limits<int>::max()) |
| return -1; |
| return path[b->tag]; |
| } |
| |
| } // namespace nv50_ir |