#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); } /**********************************************************************/