#!/usr/bin/gvpr -f
// Compute the reverse partition of the chosen function
//
// Run with graph ... | return-paths | subg-rev -a functionname


BEGIN {
	// Find the immediate parent subgraph of this object
	graph_t find_owner(obj_t o, graph_t g)
	{
		graph_t g1;
		for (g1 = fstsubg(g); g1; g1 = nxtsubg(g1))
			if(isIn(g1,o)) return g1;
		return NULL;
	}
}

BEG_G {
	graph_t calls[]; // Crude hash table for tracking who calls what
	graph_t sg = subg ($, "reachable");
	graph_t target, g, g2;
	edge_t e;
	int i;

	$tvtype = TV_rev;

	// find the ep corresponding to ARG[0]
	for (g = fstsubg($G); g; g = nxtsubg(g)) {
		if(g.fun == ARGV[0]) {
			node_t n;
			n = node($,g.ep);
			$tvroot = n;
			n.style = "filled";
			target = g;
			break;
		}
	}
	if(!target) {
		printf(2, "Function %s not found\n", ARGV[0]);
		exit(1);
	}

	// Add the incoming call edges to the allowed call list
	i = 0;
	for(e = fstin(n); e; e = nxtin(e)) {
		if (e.op = "call") {
			g2 = find_owner(e.tail, $G);
			calls[sprintf("%s%d", g2.name, ++i)] = g;
		}
	}
}


E [op == "ret"] {

	// This is a return edge. Allow the corresponding call
	g = find_owner(head, $G);
	i = 0;
	while(calls[sprintf("%s%d", g.name, ++i)]) {
	}
	calls[sprintf("%s%d", g.name, i)] = find_owner(tail, $G);
	g2 = find_owner(tail, $G);
}


N [split == 1] {

	// Ignore returns back to the target function
	for (e = fstin($); e; e = nxtin(e))
		if (e.op == "ret" && isIn(target,e.tail))
			delete($G,e);
}

N {
	$tvroot = NULL;

	for (e = fstin($); e; e = nxtin(e)) {
		if (e.op == "call") {
			int found = 0;
			g = find_owner(e.tail, $G);
			i = 0;
			while(calls[sprintf("%s%d", g.name, ++i)]) {
				if (isIn(calls[sprintf("%s%d", g.name, i)],e.head))
					found = 1;
			}
			g2 = find_owner(e.head, $G);
			if (!found) delete($G, e);
		}
	}

	for (g = fstsubg($G); g; g = nxtsubg(g)) {
		if(isIn(g,$) && g != sg) {
			subnode (copy(sg, g), $);
		}
	}
}

END_G {
	induce(sg);
	write(sg);
}
