/*
* 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