blob: 1a77d750d3557c8fed97f06d1ad2c4059d6407d7 [file] [log] [blame]
/* Copyright © International Business Machines Corp., 2006
* Adelard LLP, 2007
*
* Author: Josh Triplett <josh@freedesktop.org>
* Dan Sheridan <djs@adelard.com>
*
* Licensed under the Open Software License version 1.1
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "expression.h"
#include "linearize.h"
/* Draw the subgraph for a given entrypoint. Includes details of loads
* and stores for globals, and marks return bbs */
static void graph_ep(struct entrypoint *ep)
{
struct basic_block *bb;
struct instruction *insn;
const char *fname, *sname;
fname = show_ident(ep->name->ident);
sname = stream_name(ep->entry->bb->pos.stream);
printf("subgraph cluster%p {\n"
" color=blue;\n"
" label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">\n"
" <TR><TD>%s</TD></TR>\n"
" <TR><TD><FONT POINT-SIZE=\"21\">%s()</FONT></TD></TR>\n"
" </TABLE>>;\n"
" file=\"%s\";\n"
" fun=\"%s\";\n"
" ep=bb%p;\n",
ep, sname, fname, sname, fname, ep->entry->bb);
FOR_EACH_PTR(ep->bbs, bb) {
struct basic_block *child;
int ret = 0;
const char * s = ", ls=\"[";
/* Node for the bb */
printf(" bb%p [shape=ellipse,label=%d,line=%d,col=%d",
bb, bb->pos.line, bb->pos.line, bb->pos.pos);
/* List loads and stores */
FOR_EACH_PTR(bb->insns, insn) {
switch(insn->opcode) {
case OP_STORE:
if (insn->symbol->type == PSEUDO_SYM) {
printf("%s store(%s)", s, show_ident(insn->symbol->sym->ident));
s = ",";
}
break;
case OP_LOAD:
if (insn->symbol->type == PSEUDO_SYM) {
printf("%s load(%s)", s, show_ident(insn->symbol->sym->ident));
s = ",";
}
break;
case OP_RET:
ret = 1;
break;
}
} END_FOR_EACH_PTR(insn);
if (s[1] == 0)
printf("]\"");
if (ret)
printf(",op=ret");
printf("];\n");
/* Edges between bbs; lower weight for upward edges */
FOR_EACH_PTR(bb->children, child) {
printf(" bb%p -> bb%p [op=br, %s];\n", bb, child,
(bb->pos.line > child->pos.line) ? "weight=5" : "weight=10");
} END_FOR_EACH_PTR(child);
} END_FOR_EACH_PTR(bb);
printf("}\n");
}
/* Insert edges for intra- or inter-file calls, depending on the value
* of internal. Bold edges are used for calls with destinations;
* dashed for calls to external functions */
static void graph_calls(struct entrypoint *ep, int internal)
{
struct basic_block *bb;
struct instruction *insn;
const char *fname, *sname;
fname = show_ident(ep->name->ident);
sname = stream_name(ep->entry->bb->pos.stream);
FOR_EACH_PTR(ep->bbs, bb) {
if (!bb)
continue;
if (!bb->parents && !bb->children && !bb->insns && verbose < 2)
continue;
FOR_EACH_PTR(bb->insns, insn) {
if (insn->opcode == OP_CALL &&
internal == !(insn->func->sym->ctype.modifiers & MOD_EXTERN)) {
/* Find the symbol for the callee's definition */
struct symbol * sym;
if (insn->func->type == PSEUDO_SYM) {
for (sym = insn->func->sym->ident->symbols;
sym; sym = sym->next_id) {
if (sym->namespace & NS_SYMBOL && sym->ep)
break;
}
if (sym)
printf("bb%p -> bb%p"
"[label=%d,line=%d,col=%d,op=call,style=bold,weight=30];\n",
bb, sym->ep->entry->bb,
insn->pos.line, insn->pos.line, insn->pos.pos);
else
printf("bb%p -> \"%s\" "
"[label=%d,line=%d,col=%d,op=extern,style=dashed];\n",
bb, show_pseudo(insn->func),
insn->pos.line, insn->pos.line, insn->pos.pos);
}
}
} END_FOR_EACH_PTR(insn);
} END_FOR_EACH_PTR(bb);
}
int main(int argc, char **argv)
{
struct string_list *filelist = NULL;
char *file;
struct symbol *sym;
struct symbol_list *fsyms, *all_syms=NULL;
printf("digraph call_graph {\n");
fsyms = sparse_initialize(argc, argv, &filelist);
concat_symbol_list(fsyms, &all_syms);
/* Linearize all symbols, graph internal basic block
* structures and intra-file calls */
FOR_EACH_PTR_NOTAG(filelist, file) {
fsyms = sparse(file);
concat_symbol_list(fsyms, &all_syms);
FOR_EACH_PTR(fsyms, sym) {
expand_symbol(sym);
linearize_symbol(sym);
} END_FOR_EACH_PTR(sym);
FOR_EACH_PTR(fsyms, sym) {
if (sym->ep) {
graph_ep(sym->ep);
graph_calls(sym->ep, 1);
}
} END_FOR_EACH_PTR_NOTAG(sym);
} END_FOR_EACH_PTR_NOTAG(file);
/* Graph inter-file calls */
FOR_EACH_PTR(all_syms, sym) {
if (sym->ep)
graph_calls(sym->ep, 0);
} END_FOR_EACH_PTR_NOTAG(sym);
printf("}\n");
return 0;
}