/* C O N T R O L F L O W * * M A I N R O U T I N E */ #include #include "../share/types.h" #include "../share/debug.h" #include "../share/map.h" #include "../share/files.h" #include "../share/global.h" #include "../share/alloc.h" #include "../share/lset.h" #include "../share/cset.h" #include "../share/get.h" #include "../share/put.h" #include "../../../h/em_mnem.h" #include "cf.h" #include "cf_succ.h" #include "cf_idom.h" #include "cf_loop.h" STATIC cset lpi_set; /* set of procedures used in LPI instruction */ STATIC cset cai_set; /* set of all procedures doing a CAI */ STATIC interproc_analysis(p) proc_p p; { /* Interprocedural analysis of a procedure p determines: * - all procedures called by p (the 'call graph') * - the set of objects changed by p (directly) * - whether p does a load-indirect (loi,lof etc.) * - whether p does a store-indirect (sti, stf etc.) * The changed/used variables information will be * transitively closed, i.e. if P calls Q and Q changes * a variable X, the P changes X too. * (The same applies for used variables and for use/store * indirect). * The transitive closure will be computed by main * after all procedures have been processed. */ bblock_p b; line_p lnp; bool inloop; /* Allocate memory for structs and sets */ p->p_use = newuse(); p->p_change = newchange(); p->p_change->c_ext = Cempty_set(olength); p->p_calling = Cempty_set(plength); for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) { inloop = (Lnrelems(b->b_loops) > 0); for (lnp = b->b_start; lnp != (line_p) 0; lnp = lnp->l_next) { /* for all instructions of p do */ switch(INSTR(lnp)) { case op_cal: Cadd(PROC(lnp)->p_id, &p->p_calling); /* add called proc to p_calling */ if (inloop) { CALLED_IN_LOOP(PROC(lnp)); } break; case op_cai: Cadd(p->p_id,&cai_set); break; case op_lpi: Cadd(PROC(lnp)->p_id, &lpi_set); /* All procedures that have their names used * in an lpi instruction, may be called via * a cai instruction. */ PROC(lnp)->p_flags1 |= PF_LPI; break; case op_ste: case op_sde: case op_ine: case op_dee: case op_zre: Cadd(OBJ(lnp)->o_id, &p->p_change->c_ext); /* Add changed object to c_ext */ break; case op_lil: case op_lof: case op_loi: case op_los: case op_lar: p->p_use->u_flags |= UF_INDIR; /* p does a load-indirect */ break; case op_sil: case op_stf: case op_sti: case op_sts: case op_sar: p->p_change->c_flags |= CF_INDIR; /* p does a store-indirect */ break; case op_blm: case op_bls: p->p_use->u_flags |= UF_INDIR; p->p_change->c_flags |= CF_INDIR; /* p does both */ break; case op_mon: printf("mon not yet implemented\n"); break; case op_lxl: case op_lxa: curproc->p_flags1 |= PF_ENVIRON; break; } } } } STATIC cf_cleanproc(p) proc_p p; { /* Remove the extended data structures of p */ register bblock_p b; register Lindex pi; loop_p lp; for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) { oldcfbx(b->b_extend); } for (pi = Lfirst(p->p_loops); pi != (Lindex) 0; pi = Lnext(pi, p->p_loops)) { lp = (loop_p) Lelem(pi); oldcflpx(lp->lp_extend); } } #define CHANGE_INDIR(ch) ((ch->c_flags & CF_INDIR) != 0) #define USE_INDIR(us) ((us->u_flags & UF_INDIR) != 0) #define CALLS_UNKNOWN(p) (p->p_flags1 & (byte) PF_CALUNKNOWN) #define BODY_KNOWN(p) (p->p_flags1 & (byte) PF_BODYSEEN) #define ENVIRON(p) (p->p_flags1 & (byte) PF_ENVIRON) STATIC bool add_info(q,p) proc_p q,p; { /* Determine the consequences for used/changed variables info * of the fact that p calls q. If e.g. q changes a variable X * then p changes this variable too. This routine is an * auxiliary routine of the transitive closure process. * The returned value indicates if there was any change in * the information of p. */ change_p chp, chq; use_p usp, usq; bool diff = FALSE; chp = p->p_change; chq = q->p_change; usp = p->p_use; usq = q->p_use; if (!BODY_KNOWN(q)) { /* q is a procedure of which the body is not available * as EM text. */ if (CALLS_UNKNOWN(p)) { return FALSE; /* p already called an unknown procedure */ } else { p->p_flags1 |= PF_CALUNKNOWN; return TRUE; } } if (CALLS_UNKNOWN(q)) { /* q calls a procedure of which the body is not available * as EM text. */ if (!CALLS_UNKNOWN(p)) { p->p_flags1 |= PF_CALUNKNOWN; diff = TRUE; } } if (IS_CALLED_IN_LOOP(p) && !IS_CALLED_IN_LOOP(q)) { CALLED_IN_LOOP(q); diff = TRUE; } if (!Cis_subset(chq->c_ext, chp->c_ext)) { /* q changes global variables (objects) that * p did not (yet) change. Add all variables * changed by q to the c_ext set of p. */ Cjoin(chq->c_ext, &chp->c_ext); diff = TRUE; } if (CHANGE_INDIR(chq) && !CHANGE_INDIR(chp)) { /* q does a change-indirect (sil etc.) * and p did not (yet). */ chp->c_flags |= CF_INDIR; diff = TRUE; } if (USE_INDIR(usq) && !USE_INDIR(usp)) { /* q does a use-indirect (lil etc.) * and p dis not (yet). */ usp->u_flags |= UF_INDIR; diff = TRUE; } if (ENVIRON(q) && !ENVIRON(p)) { /* q uses or changes local variables in its * environment while p does not (yet). */ p->p_flags1 |= PF_ENVIRON; diff = TRUE; } return diff; } STATIC trans_clos(head) proc_p head; { /* Compute the transitive closure of the used/changed * variable information. */ register proc_p p,q; Cindex i; bool changes = TRUE; while(changes) { changes = FALSE; for (p = head; p != (proc_p) 0; p = p->p_next) { if (!BODY_KNOWN(p)) continue; for (i = Cfirst(p->p_calling); i != (Cindex) 0; i = Cnext(i,p->p_calling)) { q = pmap[Celem(i)]; if (add_info(q,p)) { changes = TRUE; } } } } } indir_calls() { Cindex i; proc_p p; for (i = Cfirst(cai_set); i != (Cindex) 0; i = Cnext(i,cai_set)) { p = pmap[Celem(i)]; /* p does a CAI */ Cjoin(lpi_set, &p->p_calling); } Cdeleteset(lpi_set); Cdeleteset(cai_set); } main(argc,argv) int argc; char *argv[]; { FILE *f, *f2, *gf2; /* The EM input, EM output, basic block output */ bblock_p g; short n, kind; line_p l; linecount = 0; fproc = getptable(pname); /* proc table */ fdblock = getdtable(dname); /* data block table */ lpi_set = Cempty_set(plength); cai_set = Cempty_set(plength); if ((f = fopen(lname,"r")) == NULL) { error("cannot open %s", lname); } if ((f2 = fopen(lname2,"w")) == NULL) { error("cannot open %s", lname2); } if ((gf2 = fopen(bname2,"w")) == NULL) { error("cannot open %s",bname2); } while (getbblocks(f,&kind,&n,&g,&l)) { /* read EM text of one unit and * (if it is a procedure) * partition it into n basic blocks. */ if (kind == LDATA) { putunit(LDATA,(proc_p) 0,l,gf2,f2); } else { curproc->p_start = g; /* The global variable curproc points to the * current procedure. It is set by getbblocks */ control_flow(g); /* compute pred and succ */ dominators(g,n); /* compute immediate dominators */ loop_detection(curproc); /* compute loops */ interproc_analysis(curproc); /* Interprocedural analysis */ cf_cleanproc(curproc); putunit(LTEXT,curproc,(line_p) 0,gf2,f2); /* output control flow graph + text */ } } fclose(f); fclose(f2); fclose(gf2); indir_calls(); trans_clos(fproc); /* Compute transitive closure of used/changed * variables information for every procedure. */ if ((f = fopen(dname2,"w")) == NULL) { error("cannot open %s",dname2); } putdtable(fdblock,f); if ((f = fopen(pname2,"w")) == NULL) { error("cannot open %s",pname2); } putptable(fproc,f,TRUE); exit(0); }