1 /* $Id: cf.c,v 1.10 1994/06/24 10:20:12 ceriel Exp $ */
3 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
4 * See the copyright notice in the ACK home directory, in the file "Copyright".
6 /* C O N T R O L F L O W
8 * M A I N R O U T I N E
17 #include "../share/types.h"
18 #include "../share/debug.h"
19 #include "../share/map.h"
20 #include "../share/files.h"
21 #include "../share/global.h"
22 #include "../share/alloc.h"
23 #include "../share/lset.h"
24 #include "../share/cset.h"
25 #include "../share/get.h"
26 #include "../share/put.h"
27 #include "../share/def.h"
33 #define newcfbx() (bext_p) newstruct(bext_cf)
34 #define oldcfbx(x) oldstruct(bext_cf,x)
36 extern char em_flag[];
38 STATIC cset lpi_set; /* set of procedures used in LPI instruction */
39 STATIC cset cai_set; /* set of all procedures doing a CAI */
42 /* The procedure getbblocks reads the EM textfile and
43 * partitions every procedure into a number of basic blocks.
55 /* These global variables are used by getbblocks and nextblock. */
57 STATIC bblock_p b, *bp; /* b is the current basic block, bp is
58 * the address where the next block has
61 STATIC line_p lnp, *lp; /* lnp is the current line, lp is
62 * the address where the next line
65 STATIC short state; /* We use a finite state machine with the
67 * LABEL0: after the first (successive)
69 * LABEL1: after at least two successive
71 * NORMAL: after a normal instruction.
72 * JUMP: after a branch (conditional,
73 * unconditional or CSA/CSB).
74 * END: after an END pseudo
75 * AFTERPRO: after we've read a PRO pseudo
82 /* allocate a new basic block structure and
86 b = *bp = freshblock();
89 b->b_succ = Lempty_set();
90 b->b_pred = Lempty_set();
91 b->b_extend = newcfbx(); /* basic block extension for CF */
92 b->b_extend->bx_cf.bx_bucket = Lempty_set();
93 b->b_extend->bx_cf.bx_semi = 0;
96 fprintf(stderr,"new basic block, id = %d\n",lastbid);
101 STATIC short kind(lnp)
104 /* determine if lnp is a label, branch, end or otherwise */
109 if ((instr = INSTR(lnp)) == op_lab) return (short) LABEL;
110 if (instr == ps_end) return (short) END;
111 if (instr > sp_lmnem) return (short) NORMAL; /* pseudo */
112 if ((flow = (em_flag[instr-sp_fmnem] & EM_FLO)) == FLO_C ||
113 flow == FLO_T) return (short) JUMP; /* conditional/uncond. jump */
114 return (short) NORMAL;
118 STATIC line_p doread_line(p_out)
121 /* read a line, and check pseudos for procedure addresses */
123 register line_p lnp = read_line(p_out);
125 if (lnp && TYPE(lnp) == OPLIST && INSTR(lnp) != ps_mes) {
126 register arg_p arg = ARG(lnp);
129 if (arg->a_type == ARGPROC) {
130 Cadd(arg->a_a.a_proc->p_id, &lpi_set);
131 arg->a_a.a_proc->p_flags1 |= PF_LPI;
139 STATIC bool getbblocks(fp,kind_out,n_out,g_out,l_out)
146 bblock_p head = (bblock_p) 0;
147 line_p headl = (line_p) 0;
149 curproc = (proc_p) 0;
150 /* curproc will get a value when we encounter a PRO pseudo.
151 * If there is no such pseudo, we're reading only data
152 * declarations or messages (outside any proc.).
155 lastbid = (block_id) 0; /* block identier */
156 state = INIT; /* initial state */
161 fprintf(stderr,"state = %d\n",state);
166 /* Fall through !! */
168 lbmap[INSTRLAB(lnp)] = b;
169 /* The lbmap table contains for each
170 * label_id the basic block of that label.
172 lnp = doread_line(&curproc);
180 lnp = doread_line(&curproc);
181 if ( (state = kind(lnp)) == LABEL) {
182 /* If we come accross a label
183 * here, it must be the beginning
184 * of a new basic block.
195 lnp = doread_line(&curproc);
196 /* fall through ... */
198 switch(state = kind(lnp)) {
211 fprintf(stderr,"at end of proc, %d blocks\n",lastbid);
213 if (head == (bblock_p) 0) {
219 *n_out = (short) lastbid;
220 /* number of basic blocks */
224 lnp = doread_line(&curproc);
225 if (feof(curinp)) return FALSE;
226 if (INSTR(lnp) == ps_pro) {
239 STATIC interproc_analysis(p)
242 /* Interprocedural analysis of a procedure p determines:
243 * - all procedures called by p (the 'call graph')
244 * - the set of objects changed by p (directly)
245 * - whether p does a load-indirect (loi,lof etc.)
246 * - whether p does a store-indirect (sti, stf etc.)
247 * The changed/used variables information will be
248 * transitively closed, i.e. if P calls Q and Q changes
249 * a variable X, the P changes X too.
250 * (The same applies for used variables and for use/store
252 * The transitive closure will be computed by main
253 * after all procedures have been processed.
260 /* Allocate memory for structs and sets */
263 p->p_change = newchange();
264 p->p_change->c_ext = Cempty_set(olength);
265 p->p_calling = Cempty_set(plength);
267 for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
268 inloop = (Lnrelems(b->b_loops) > 0);
269 for (lnp = b->b_start; lnp != (line_p) 0; lnp = lnp->l_next) {
270 /* for all instructions of p do */
273 Cadd(PROC(lnp)->p_id, &p->p_calling);
274 /* add called proc to p_calling */
276 CALLED_IN_LOOP(PROC(lnp));
280 Cadd(p->p_id,&cai_set);
283 Cadd(PROC(lnp)->p_id, &lpi_set);
284 /* All procedures that have their names used
285 * in an lpi instruction, may be called via
288 PROC(lnp)->p_flags1 |= PF_LPI;
295 Cadd(OBJ(lnp)->o_id, &p->p_change->c_ext);
296 /* Add changed object to c_ext */
303 p->p_use->u_flags |= UF_INDIR;
304 /* p does a load-indirect */
311 p->p_change->c_flags |= CF_INDIR;
312 /* p does a store-indirect */
316 p->p_use->u_flags |= UF_INDIR;
317 p->p_change->c_flags |= CF_INDIR;
321 printf("mon not yet implemented\n");
325 curproc->p_flags1 |= PF_ENVIRON;
329 if (SHORT(lnp) == 0) {
330 curproc->p_flags1 |= PF_ENVIRON;
334 if (aoff(ARG(lnp),0) == ms_gto) {
335 ENTERED_WITH_GTO(curproc);
344 STATIC cf_cleanproc(p)
347 /* Remove the extended data structures of p */
353 for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
354 oldcfbx(b->b_extend);
356 for (pi = Lfirst(p->p_loops); pi != (Lindex) 0; pi = Lnext(pi,
358 lp = (loop_p) Lelem(pi);
359 oldcflpx(lp->lp_extend);
365 #define CH_CHANGE_INDIR(ch) ((ch->c_flags & CF_INDIR) != 0)
366 #define USE_INDIR(us) ((us->u_flags & UF_INDIR) != 0)
367 #define CALLS_UNKNOWN(p) (p->p_flags1 & (byte) PF_CALUNKNOWN)
368 #define ENVIRON(p) (p->p_flags1 & (byte) PF_ENVIRON)
371 STATIC bool add_info(q,p)
374 /* Determine the consequences for used/changed variables info
375 * of the fact that p calls q. If e.g. q changes a variable X
376 * then p changes this variable too. This routine is an
377 * auxiliary routine of the transitive closure process.
378 * The returned value indicates if there was any change in
379 * the information of p.
391 if (!BODY_KNOWN(q)) {
392 /* q is a procedure of which the body is not available
395 if (CALLS_UNKNOWN(p)) {
397 /* p already called an unknown procedure */
399 p->p_flags1 |= PF_CALUNKNOWN;
403 if (CALLS_UNKNOWN(q)) {
404 /* q calls a procedure of which the body is not available
407 if (!CALLS_UNKNOWN(p)) {
408 p->p_flags1 |= PF_CALUNKNOWN;
412 if (IS_CALLED_IN_LOOP(p) && !IS_CALLED_IN_LOOP(q)) {
416 if (!Cis_subset(chq->c_ext, chp->c_ext)) {
417 /* q changes global variables (objects) that
418 * p did not (yet) change. Add all variables
419 * changed by q to the c_ext set of p.
421 Cjoin(chq->c_ext, &chp->c_ext);
424 if (CH_CHANGE_INDIR(chq) && !CH_CHANGE_INDIR(chp)) {
425 /* q does a change-indirect (sil etc.)
426 * and p did not (yet).
428 chp->c_flags |= CF_INDIR;
431 if (USE_INDIR(usq) && !USE_INDIR(usp)) {
432 /* q does a use-indirect (lil etc.)
433 * and p dis not (yet).
435 usp->u_flags |= UF_INDIR;
438 if (ENVIRON(q) && !ENVIRON(p)) {
439 /* q uses or changes local variables in its
440 * environment while p does not (yet).
442 p->p_flags1 |= PF_ENVIRON;
450 STATIC trans_clos(head)
453 /* Compute the transitive closure of the used/changed
454 * variable information.
463 for (p = head; p != (proc_p) 0; p = p->p_next) {
464 if (!BODY_KNOWN(p)) continue;
465 for (i = Cfirst(p->p_calling); i != (Cindex) 0;
466 i = Cnext(i,p->p_calling)) {
484 for (i = Cfirst(cai_set); i != (Cindex) 0; i = Cnext(i,cai_set)) {
485 p = pmap[Celem(i)]; /* p does a CAI */
486 Cjoin(lpi_set, &p->p_calling);
498 FILE *f, *f2, *gf2; /* The EM input, EM output, basic block output */
504 fproc = getptable(pname); /* proc table */
505 fdblock = getdtable(dname); /* data block table */
506 lpi_set = Cempty_set(plength);
507 cai_set = Cempty_set(plength);
508 if ((f = fopen(lname,"r")) == NULL) {
509 error("cannot open %s", lname);
511 if ((f2 = fopen(lname2,"w")) == NULL) {
512 error("cannot open %s", lname2);
514 if ((gf2 = fopen(bname2,"w")) == NULL) {
515 error("cannot open %s",bname2);
517 while (getbblocks(f,&kind,&n,&g,&l)) {
518 /* read EM text of one unit and
519 * (if it is a procedure)
520 * partition it into n basic blocks.
523 putunit(LDATA,(proc_p) 0,l,gf2,f2);
525 curproc->p_start = g;
526 /* The global variable curproc points to the
527 * current procedure. It is set by getbblocks
529 control_flow(g); /* compute pred and succ */
530 dominators(g,n); /* compute immediate dominators */
531 loop_detection(curproc); /* compute loops */
532 interproc_analysis(curproc);
533 /* Interprocedural analysis */
534 cf_cleanproc(curproc);
535 putunit(LTEXT,curproc,(line_p) 0,gf2,f2);
536 /* output control flow graph + text */
544 /* Compute transitive closure of used/changed
545 * variables information for every procedure.
547 if ((f = fopen(dname2,"w")) == NULL) {
548 error("cannot open %s",dname2);
550 putdtable(fdblock,f);
551 if ((f = fopen(pname2,"w")) == NULL) {
552 error("cannot open %s",pname2);
554 putptable(fproc,f,TRUE);