#include "add_profiling.h"
/**********************************************************************/
/* Definitions */
/***************/
int global_argc;
char **global_argv;
int max_UID_encountered = 0;
/**********************************************************************/
/* Code */
/********/
// [....]
/****************/
void instrument_returns(tree_node *this_node, void *x)
{
// Converts "ret e1" to "temp = e1; ...insert_tn...; return temp".
if (! this_node->is_instr())
return;
instruction *this_instr = ((tree_instr *) this_node)->instr();
if (this_instr->opcode() != io_ret)
return;
in_rrr *this_return = ((in_rrr *) this_instr);
operand return_operand = this_return->src1_op();
switch (return_operand.kind())
{
case OPER_NULL:
/* Fall through to OPER_SYM case */
case OPER_SYM:
this_node->parent()->insert_before
(((tree_node *) x)->clone(this_node->scope()), this_node->list_e());
break;
case OPER_INSTR:
{
assert(return_operand.is_expr());
block b1 = block::new_sym(return_operand.type());
sym_node *temp_sym = b1.get_sym();
block b2(b1 = block(return_operand.clone(this_node->scope())));
tree_node_list *tnl = b2.make_tree_node_list();
this_node->parent()->insert_before(tnl, this_node->list_e());
this_node->parent()->insert_before
(((tree_node *) x)->clone(this_node->scope()), this_node->list_e());
return_operand.remove();
this_return->set_src1((var_sym *) temp_sym);
}
break;
default:
assert(0); /* Should never be reached */
}
}
/****************/
void instrument_TNL(tree_proc *tp, tree_node_list *tnl, int UID,
char *pre, char *post)
{
proc_sym *qhead_sym =
((global_symtab *) global_symbol_table)->lookup_proc(pre);
proc_sym *qtail_sym =
((global_symtab *) global_symbol_table)->lookup_proc(post);
assert (qhead_sym != NULL);
assert (qtail_sym != NULL);
block::set_proc(tp);
block qhead(qhead_sym);
block qtail(qtail_sym);
block uid(UID);
block verbose(debug_level);
block call_qhead;
call_qhead.set(block::CALL(qhead,uid,verbose));
tnl->push(call_qhead.make_tree_node());
block call_qtail;
call_qtail.set(block::CALL(qtail,uid,verbose));
tree_node *tn = call_qtail.make_tree_node();
tnl->append(new tree_node_list_e(tn));
if (debug_level >= 3) print_TNL(tnl);
tnl->map(instrument_returns, tn);
if (debug_level >= 3) print_TNL(tnl);
// In cases where the TNL ends with a return, this code inserts
// multiple calls to qtail. Could be fixed.... XXX
}
/****************/
void wrap_main(tree_proc *tp, tree_node_list *tnl,
char *pre, char *base_filename, int counters, int verbose1,
char *post, int verbose2)
{
/* Similar to instrument_TNL in some ways, but not similar
enough to make it sensible to combine the two */
proc_sym *qhead_sym =
((global_symtab *) global_symbol_table)->lookup_proc(pre);
proc_sym *qtail_sym =
((global_symtab *) global_symbol_table)->lookup_proc(post);
assert (qhead_sym != NULL);
assert (qtail_sym != NULL);
type_node *char_array = file_symbol_table->install_type
(new array_type(type_char, 0, strlen(base_filename)-1));
var_sym *base_filename_sym = file_symbol_table->new_var
(char_array, "qp_basefilename");
#define VD_DATA_ALIGNMENT 8
var_def *base_filename_def =
new var_def(base_filename_sym,VD_DATA_ALIGNMENT);
immed_list *il = new immed_list();
for (int loop = 0; loop<strlen(base_filename); loop++)
il->append(new immed_list_e(*(new immed(base_filename[loop]))));
il->push(new immed_list_e(*(new immed(8))));
base_filename_def->append_annote(k_multi_init, (void *) il);
file_symbol_table->add_def(base_filename_def);
block::set_proc(tp);
block call_qhead;
call_qhead.set(block::CALL(new block(qhead_sym),
new block(base_filename_sym),
new block(counters),
new block(verbose1)));
tnl->push(call_qhead.make_tree_node());
block call_qtail;
call_qtail.set(block::CALL(new block(qtail_sym),
new block(verbose2)));
tree_node *tn = call_qtail.make_tree_node();
tnl->append(new tree_node_list_e(tn));
if (debug_level >= 3) print_TNL(tnl);
tnl->map(instrument_returns, tn);
if (debug_level >= 3) print_TNL(tnl);
// In cases where the TNL ends with a return, this code inserts
// multiple calls to qtail. Could be fixed.... XXX
/* XXX: deal with calls to exit(); extra headache: multiple counters
may still be "ticking". I could rewrite qp_dump() to deal with
that, I suppose. */
}
/**********************************************************************/
int lookup_set_UID(annote *the_an, char *k_annotation_type)
{
CStringSet *the_set = pIL2pCStringSet(the_an->immeds());
annote_list_iter anli = annote_list_iter(global_symbol_table->annotes());
while (! anli.is_empty())
{
annote *an = anli.step();
if (an->name() == k_annotation_type)
{
immed_list *il = an->immeds();
immed_list_e *ile = new immed_list_e(il->pop());
assert(ile->contents.is_integer());
CStringSet *set = pIL2pCStringSet(il);
il->push(ile);
if (*set == *the_set)
{
int UID = ile->contents.integer();
if (UID > max_UID_encountered)
max_UID_encountered = UID;
return UID;
}
}
}
assert(0); /* Shouldn't ever be reached */
return -1; /* Make compiler happy */
}
/**********************************************************************/
void check_block(tree_node *t, tree_proc *tp)
{
annote *an_statement = NULL;
annote *an_set = NULL;
int UID = 0;
switch (t->kind())
{
case TREE_IF:
an_statement = t->annotes()->peek_annote(k_qif_statement);
if (an_statement == NULL)
return; /* Not a quasistatic if at all */
an_set = t->annotes()->peek_annote(k_qif_set);
if (an_set == NULL)
return; /* Not the first quasistatic if in a chain */
(void) t->annotes()->get_annote(k_qif_set_analyzed);
/* The k_qif_set_analyzed annotation was useful during the
stage of calculating the set, but is now useless. The
get_annote function is a mutator, not just observer. */
UID = lookup_set_UID(an_set, k_qif_set);
instrument_TNL(tp, ((tree_if *) t)->then_part(),
UID, "qp_start", "qp_stop");
while (1)
{
tree_node_list *pElseTNL = ((tree_if *) t)->else_part();
if (pElseTNL->count() == 0)
break; /* "... NO FINAL QELSE" */
if ((pElseTNL->count() == 1)
&& (pElseTNL->head()->contents->is_block()))
{
instrument_TNL(tp, pElseTNL, UID, "qp_start", "qp_stop");
break;
}
assert(pElseTNL->count() > 1);
tree_node *pTN = pElseTNL->head()->next()->contents;
if (pTN->is_if()
&& (pTN->annotes()->peek_annote(k_qif_statement) != NULL))
{
t = pTN;
instrument_TNL(tp, ((tree_if *) t)->then_part(),
UID, "qp_start", "qp_stop");
/* Do *NOT* break here */
}
else /* "... FINAL QELSE" */
{
instrument_TNL(tp, pElseTNL, UID, "qp_start", "qp_stop");
break;
}
}
break;
case TREE_BLOCK:
an_statement = t->annotes()->peek_annote(k_qinfluence_statement);
if (an_statement == NULL)
return; /* Not a qinfluence statement at all */
an_set = t->annotes()->peek_annote(k_qinfluence_set);
assert(an_set != NULL);
UID = lookup_set_UID(an_set, k_qinfluence_set);
instrument_TNL(tp, ((tree_block *) t)->body(),
UID, "qp_start", "qp_stop");
break;
default:
; /* Do nothing */
}
}
/**********************************************************************/
void do_proc(tree_proc *tp)
{
if (file_symbol_table == NULL)
{
file_symbol_table = tp->scope();
while (file_symbol_table->kind() != SYMTAB_FILE)
file_symbol_table = file_symbol_table->parent();
}
if (global_symbol_table == NULL)
{
global_symbol_table = file_symbol_table;
while (global_symbol_table->kind() != SYMTAB_GLOBAL)
global_symbol_table = global_symbol_table->parent();
}
tp->map(check_block, tp);
/* Important that tp->map comes before a call to wrap_main,
in case main itself needs to be instrumented. */
if (strcmp(tp->proc()->name(),"main") == 0)
wrap_main(tp, tp->body(),
"qp_initialize", global_argv[global_argc-1],
max_UID_encountered+1, debug_level,
"qp_dump", debug_level);
}
/**********************************************************************/
int main(int argc, char *argv[])
{
global_argc = argc;
global_argv = argv;
start_suif(argc, argv);
register_qannotations();
process_command_line(argc, argv);
if (argc-optind != 2)
{
fprintf(stderr, "%s: wrong number of filenames given on command line.\n",
argv[0]);
exit(1);
}
#define WRITE_BACK_TO_FILE TRUE
my_suif_proc_iter(argc, argv, do_proc, NULL, NULL, WRITE_BACK_TO_FILE);
}
/**********************************************************************/