blob: 5815fc70b78a4c307bc799182f3605b34d3c18f1 [file] [log] [blame]
#!/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($);
}