#!/usr/bin/gvpr -f
// Split call sites into call site and return site nodes and add
// return edges
//
// Run with graph ... | return-paths

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 {
	node_t calls[]; // Crude hash table for tracking who calls what
	graph_t g,g2;
	edge_t e,e2;
	string idx;
	node_t n, n2;
	int i;

	$tvtype = TV_en;
}

// Each call edge which hasn't already been seen
E [op == "call" && tail.split != 1] {
	int offset=0;

	// Clear the label of this call
	label = "";
	g = find_owner(tail, $G);

	// Consider each outgoing call. Split the node accordingly
	n = tail;
	for (e = fstout(tail); e; e = nxtout(e)) {
		if (e.op == "call") {

			// Split node
			n2 = node(g, sprintf("%s%s%d", tail.name, "x", offset++));
			copyA(tail, n2);
			n2.line = e.line;
			n2.label = e.line;
			n2.col = e.col;
			n2.split = 1;
			n2.op = "target";

			// Record this call
			g2 = find_owner(e.head, $G);
			i = 0;
			while(calls[sprintf("%s%d", g2.name, ++i)]) {
			}
			calls[sprintf("%s%d", g2.name, i)] = n2;

			// Connect original to split
			e2 = edge(n, n2, "call");
			e2.style = "dotted";
			e2.weight = 50;

			// Replace this outedge
			if (n != tail) {
				e2 = edge(n, e.head, "transformed-call");
				copyA(e,e2);
				e2.label = "";
				delete($G,e);
			}

			// Record where we were
			n = n2;
		}
	}

	// Consider the outgoing control flow: move down to the bottom of
	// the call sequence nodes
	for (e = fstout(tail); e; e = nxtout(e)) {
		if (e.op == "br") {
			// Replace this outedge
			e2 = edge(n,e.head,"transformed");
			copyA(e,e2);
			delete($G,e);
		}
	}
}

// Each return node: add edges back to the caller
N [op == "ret"] {
	for (g = fstsubg($G); g; g = nxtsubg(g)) {
		if(isIn(g,$)) {
			i = 0;
			while(calls[sprintf("%s%d", g.name, ++i)]) {
				e = edge($, calls[sprintf("%s%d", g.name, i)], "return");
				e.style = "dotted";
				e.op = "ret";
				e.line = e.tail.line;
				e.weight = 5;
			}
		}
	}
}


END_G {
	write($);
}
