565 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			565 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* $Id$ */
 | |
| /*
 | |
|  * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | |
|  * See the copyright notice in the ACK home directory, in the file "Copyright".
 | |
|  */
 | |
| /*  U S E  -  D E F I N I T I O N   A N A L Y S I S */
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <em_spec.h>
 | |
| #include "../share/types.h"
 | |
| #include "ud.h"
 | |
| #include "../share/debug.h"
 | |
| #include "../share/global.h"
 | |
| #include "../share/lset.h"
 | |
| #include "../share/cset.h"
 | |
| #include "../share/def.h"
 | |
| #include "../share/files.h"
 | |
| #include "../share/map.h"
 | |
| #include "../share/get.h"
 | |
| #include "../share/put.h"
 | |
| #include "../share/alloc.h"
 | |
| #include "../share/aux.h"
 | |
| #include "../share/init_glob.h"
 | |
| #include "../share/locals.h"
 | |
| #include "../share/go.h"
 | |
| #include "ud_defs.h"
 | |
| #include "ud_const.h"
 | |
| #include "ud_copy.h"
 | |
| 
 | |
| /* core allocation macros */
 | |
| #define newudbx()	(bext_p) newstruct(bext_ud)
 | |
| #define oldudbx(x)	oldstruct(bext_ud,x)
 | |
| 
 | |
| short nrglobals;
 | |
| short nrvars;
 | |
| 
 | |
| int Svalue,Svariable;
 | |
| 
 | |
| cond_p globl_cond_tab,local_cond_tab;
 | |
| 
 | |
| STATIC cond_p getcondtab(f)
 | |
| 	FILE *f;
 | |
| {
 | |
| 	int l,i;
 | |
| 	cond_p tab;
 | |
| 
 | |
| 	fscanf(f,"%d",&l);
 | |
| 	tab = newcondtab(l);
 | |
| 	for (i = 0; i < l; i++) {
 | |
| 		fscanf(f,"%hd %hd %hd",&tab[i].mc_cond,&tab[i].mc_tval,
 | |
| 			 &tab[i].mc_sval);
 | |
| 	}
 | |
| 	assert(tab[l-1].mc_cond == DEFAULT);
 | |
| 	return tab;
 | |
| }
 | |
| 
 | |
| 
 | |
| STATIC ud_machinit(f)
 | |
| 	FILE *f;
 | |
| {
 | |
| 	char s[100];
 | |
| 
 | |
| 	for (;;) {
 | |
| 		while(getc(f) != '\n');
 | |
| 		fscanf(f,"%s",s);
 | |
| 		if (strcmp(s,"%%UD") == 0)break;
 | |
| 	}
 | |
| 	globl_cond_tab = getcondtab(f);
 | |
| 	local_cond_tab = getcondtab(f);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| STATIC bool test_cond(cond,val)
 | |
| 	short cond;
 | |
| 	offset val;
 | |
| {
 | |
| 	switch(cond) {
 | |
| 		case DEFAULT:
 | |
| 			return TRUE;
 | |
| 		case FITBYTE:
 | |
| 			return val >= -128 && val < 128;
 | |
| 	}
 | |
| 	assert(FALSE);
 | |
| 	/* NOTREACHED */
 | |
| }
 | |
| 
 | |
| 
 | |
| STATIC short map_value(tab,val,time)
 | |
| 	struct cond_tab tab[];
 | |
| 	offset val;
 | |
| 	bool time;
 | |
| {
 | |
| 	cond_p p;
 | |
| 
 | |
| 	for (p = &tab[0]; ; p++) {
 | |
| 		if (test_cond(p->mc_cond,val)) {
 | |
| 			return (time ? p->mc_tval : p->mc_sval);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| STATIC init_root(root)
 | |
| 	bblock_p root;
 | |
| {
 | |
| 	/* Initialise the IN OUT sets of the entry block of the
 | |
| 	 * current procedure. Global variables and parameters
 | |
| 	 * already have a value at this point, although we do
 | |
| 	 * not know which value. Therefor, implicit definitions
 | |
| 	 * to all global variables and parameters are
 | |
| 	 * put in IN.
 | |
| 	 */
 | |
| 
 | |
| 	short v;
 | |
| 
 | |
| 	for (v = 1; v <= nrglobals; v++) {
 | |
| 		Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)), &IN(root));
 | |
| 	}
 | |
| 	for (v = 1; v <= nrlocals; v++) {
 | |
| 		if (locals[v]->lc_off >= 0) {
 | |
| 			Cadd(IMPLICIT_DEF(LOC_TO_VARNR(v)),&IN(root));
 | |
| 		}
 | |
| 	}
 | |
| 	/* OUT(root) = IN(root) - KILL(root) + GEN(root) */
 | |
| 	Ccopy_set(IN(root),&OUT(root));
 | |
| 	Csubtract(KILL(root),&OUT(root));
 | |
| 	Cjoin(GEN(root),&OUT(root));
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| STATIC unite_outs(bbset,setp)
 | |
| 	lset bbset;
 | |
| 	cset *setp;
 | |
| {
 | |
| 	/* Take the union of OUT(b), for all b in bbset,
 | |
| 	 * and put the result in setp.
 | |
| 	 */
 | |
| 
 | |
| 	Lindex i;
 | |
| 
 | |
| 	Cclear_set(setp);
 | |
| 	for (i = Lfirst(bbset); i != (Lindex) 0; i = Lnext(i,bbset)) {
 | |
| 		Cjoin(OUT((bblock_p) Lelem(i)), setp);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| STATIC solve_equations(p)
 | |
| 	proc_p p;
 | |
| {
 | |
| 	/* Solve the data flow equations for reaching
 | |
| 	 * definitions of procedure p.
 | |
| 	 * These equations are:
 | |
| 	 *  (1)  OUT(b) = IN(b) - KILL(b) + GEN(b)
 | |
| 	 *  (2)  IN(b)  = OUT(p1) + .. + OUT(pn) ; 
 | |
| 	 *       where PRED(b) = {p1, .. , pn}
 | |
| 	 * We use the iterative algorithm of Aho&Ullman to
 | |
| 	 * solve the equations.
 | |
| 	 */
 | |
| 
 | |
| 	register bblock_p b;
 | |
| 	bool     change;
 | |
| 	cset     newin;
 | |
| 
 | |
| 	/* initializations */
 | |
| 	newin = Cempty_set(nrdefs);
 | |
| 	for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
 | |
| 		IN(b) = Cempty_set(nrdefs);
 | |
| 		OUT(b) = Cempty_set(nrdefs);
 | |
| 		Ccopy_set(GEN(b), &OUT(b));
 | |
| 	}
 | |
| 	init_root(p->p_start);
 | |
| 	/* Global variables and parameters have already a value
 | |
| 	 * at the procedure entry block.
 | |
| 	 */
 | |
| 	change = TRUE;
 | |
| 	/* main loop */
 | |
| 	while (change) {
 | |
| 		change = FALSE;
 | |
| 		for (b = p->p_start->b_next; b != (bblock_p) 0; b = b->b_next) {
 | |
| 			unite_outs(b->b_pred, &newin);
 | |
| 			/* newin = OUT(p1) + .. + OUT(pn) */
 | |
| 			if (!Cequal(newin,IN(b))) {
 | |
| 				change = TRUE;
 | |
| 				Ccopy_set(newin, &IN(b));
 | |
| 				Ccopy_set(IN(b),   &OUT(b));
 | |
| 				Csubtract(KILL(b), &OUT(b));
 | |
| 				Cjoin(GEN(b),      &OUT(b));
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
 | |
| 		Cdeleteset(KILL(b));
 | |
| 		Cdeleteset(OUT(b));
 | |
| 	}
 | |
| 	Cdeleteset(newin);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| short global_addr_cost()
 | |
| {
 | |
| 	return add_timespace(map_value(globl_cond_tab,(offset) 0,TRUE),
 | |
| 			     map_value(globl_cond_tab,(offset) 0,FALSE));
 | |
| }
 | |
| 
 | |
| short local_addr_cost(off)
 | |
| 	offset off;
 | |
| {
 | |
| 	return add_timespace(map_value(local_cond_tab,off,TRUE),
 | |
| 			     map_value(local_cond_tab,off,FALSE));
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| STATIC bool fold_is_desirable(old,new)
 | |
| 	line_p old,new;
 | |
| {
 | |
| 	/* See if it is desirable to replace the variable used by the
 | |
| 	 * EM instruction 'old' by the variable used by 'new'.
 | |
| 	 * We do not replace 'cheaply addressable variables' by 'expensively
 | |
| 	 * addressable variables'. E.g. if we're optimizing object code size,
 | |
| 	 * we do not replace a local variable by a global variable on a VAX,
 | |
| 	 * because the former occupies 1 or 2 bytes and the latter occupies
 | |
| 	 * 4 bytes.
 | |
| 	 * If 2 local variables are equally expensive to address, we replace
 | |
| 	 * the first one by the second only if the first one is used at
 | |
| 	 * least as many times as the second one.
 | |
| 	 */
 | |
| 
 | |
| 	local_p oldloc,newloc;
 | |
| 	short old_cost,new_cost,nr;
 | |
| 	bool ok;
 | |
| 
 | |
| 	if (TYPE(old) == OPOBJECT) {
 | |
| 		/* old variable is a global variable */
 | |
| 		return TYPE(new) != OPOBJECT && 
 | |
| 		       global_addr_cost() >=
 | |
| 		       local_addr_cost(off_set(new));
 | |
| 	}
 | |
| 	find_local(off_set(old),&nr,&ok);
 | |
| 	assert(ok);
 | |
| 	oldloc = locals[nr];
 | |
| 	old_cost = local_addr_cost(off_set(old));
 | |
| 	if (TYPE(new) == OPOBJECT) {
 | |
| 		return oldloc->lc_score == 2 || /* old var. can be eliminated */
 | |
| 		       old_cost > global_addr_cost();
 | |
| 	}
 | |
| 	find_local(off_set(new),&nr,&ok);
 | |
| 	assert(ok);
 | |
| 	newloc = locals[nr];
 | |
| 	new_cost = local_addr_cost(off_set(new));
 | |
| 	return old_cost > new_cost ||
 | |
| 	       (old_cost == new_cost && oldloc->lc_score < newloc->lc_score);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| #ifdef TRACE
 | |
| /*********** TRACING ROUTINES ***********/
 | |
| 
 | |
| pr_localtab() {
 | |
| 	short i;
 | |
| 	local_p lc;
 | |
| 
 | |
| 	printf("LOCAL-TABLE (%d)\n\n",nrlocals);
 | |
| 	for (i = 1; i <= nrlocals; i++) {
 | |
| 		lc = locals[i];
 | |
| 		printf("LOCAL %d\n",i);
 | |
| 		printf("	offset= %ld\n",lc->lc_off);
 | |
| 		printf("	size=   %d\n",lc->lc_size);
 | |
| 		printf("	flags=  %d\n",lc->lc_flags);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| pr_globals()
 | |
| {
 | |
| 	dblock_p d;
 | |
| 	obj_p obj;
 | |
| 
 | |
| 	printf("GLOBALS (%d)\n\n",nrglobals);
 | |
| 	printf("ID	GLOBNR\n");
 | |
| 	for (d = fdblock; d != (dblock_p) 0; d = d->d_next) {
 | |
| 		for (obj = d->d_objlist; obj != (obj_p) 0; obj = obj->o_next) {
 | |
| 			if (obj->o_globnr != 0) {
 | |
| 			   printf("%d	%d\n", obj->o_id,obj->o_globnr);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| extern char em_mnem[];
 | |
| 
 | |
| pr_defs()
 | |
| {
 | |
| 	short i;
 | |
| 	line_p l;
 | |
| 
 | |
| 	printf("DEF TABLE\n\n");
 | |
| 	for (i = 1; i <= nrexpldefs; i++) {
 | |
| 		l = defs[i];
 | |
| 		printf("%d	%s ",EXPL_TO_DEFNR(i),
 | |
| 			&em_mnem[(INSTR(l)-sp_fmnem)*4]);
 | |
| 		switch(TYPE(l)) {
 | |
| 			case OPSHORT:
 | |
| 				printf("%d\n",SHORT(l));
 | |
| 				break;
 | |
| 			case OPOFFSET:
 | |
| 				printf("%ld\n",OFFSET(l));
 | |
| 				break;
 | |
| 			case OPOBJECT:
 | |
| 				printf("%d\n",OBJ(l)->o_id);
 | |
| 				break;
 | |
| 			default:
 | |
| 				assert(FALSE);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| pr_set(name,k,s,n)
 | |
| 	char *name;
 | |
| 	cset s;
 | |
| 	short k,n;
 | |
| {
 | |
| 	short i;
 | |
| 
 | |
| 	printf("%s(%d) =	{",name,k);
 | |
| 	for (i = 1; i <= n; i++) {
 | |
| 		if (Cis_elem(i,s)) {
 | |
| 			printf("%d ",i);
 | |
| 		}
 | |
| 	}
 | |
| 	printf ("}\n");
 | |
| }
 | |
| 
 | |
| pr_blocks(p)
 | |
| 	proc_p p;
 | |
| {
 | |
| 	bblock_p b;
 | |
| 	short n;
 | |
| 
 | |
| 	for (b = p->p_start; b != 0; b = b->b_next) {
 | |
| 		printf ("\n");
 | |
| 		n = b->b_id;
 | |
| 		pr_set("GEN",n,GEN(b),nrdefs);
 | |
| 		pr_set("KILL",n,KILL(b),nrdefs);
 | |
| 		pr_set("IN ",n,IN(b),nrdefs);
 | |
| 		pr_set("OUT",n,OUT(b),nrdefs);
 | |
| 		pr_set("CHGVARS",n,CHGVARS(b),nrvars);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| pr_copies()
 | |
| {
 | |
| 	short i;
 | |
| 
 | |
| 	printf("\nCOPY TABLE\n\n");
 | |
| 	for (i = 1; i <= nrdefs; i++) {
 | |
| 		if (def_to_copynr[i] != 0) {
 | |
| 			printf("%d	%d\n",i,def_to_copynr[i]);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| pr_cblocks(p)
 | |
| 	proc_p p;
 | |
| {
 | |
| 	bblock_p b;
 | |
| 	short n;
 | |
| 
 | |
| 	for (b = p->p_start; b != 0; b = b->b_next) {
 | |
| 		printf ("\n");
 | |
| 		n = b->b_id;
 | |
| 		pr_set("CGEN",n,C_GEN(b),nrcopies);
 | |
| 		pr_set("CKILL",n,C_KILL(b),nrcopies);
 | |
| 		pr_set("CIN ",n,C_IN(b),nrcopies);
 | |
| 		pr_set("COUT",n,C_OUT(b),nrcopies);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*********** END TRACING ********/
 | |
| 
 | |
| #endif
 | |
| 
 | |
| STATIC ud_analysis(p)
 | |
| 	proc_p p;
 | |
| {
 | |
| 	/* Perform use-definition analysis on procedure p */
 | |
| 
 | |
| 	make_localtab(p);  /* See for which local we'll keep ud-info */
 | |
| #ifdef TRACE
 | |
| 	pr_localtab();
 | |
| #endif
 | |
| 	nrvars = nrglobals + nrlocals;
 | |
| 	make_defs(p);  /* Make a table of all useful definitions in p */
 | |
| #ifdef TRACE
 | |
| 	pr_defs();
 | |
| #endif
 | |
| 	nrdefs = nrexpldefs + nrvars; /* number of definitions */
 | |
| 	gen_sets(p); /* compute GEN(b), for every basic block b */
 | |
| 	kill_sets(p); /* compute KILL(b), for every basic block b */
 | |
| 	solve_equations(p); /* solve data flow equations for p */
 | |
| #ifdef TRACE
 | |
| 	pr_blocks(p);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| STATIC clean_maps()
 | |
| {
 | |
| 	local_p *p;
 | |
| 	cset *v;
 | |
| 
 | |
| 	oldmap(defs,nrexpldefs);
 | |
| 	for (p = &locals[1]; p <= &locals[nrlocals]; p++) {
 | |
| 		oldlocal(*p);
 | |
| 	}
 | |
| 	oldmap(locals,nrlocals);
 | |
| 	for (v = &vardefs[1]; v <= &vardefs[nrvars]; v++) {
 | |
| 		Cdeleteset(*v);
 | |
| 	}
 | |
| 	oldmap(vardefs,nrvars);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| STATIC bool try_optim(l,b)
 | |
| 	line_p l;
 | |
| 	bblock_p b;
 | |
| {
 | |
| 	/* Try copy propagation and constant propagation */
 | |
| 
 | |
| 	line_p def;
 | |
| 	offset val;
 | |
| 	short defnr;
 | |
| 
 | |
| 
 | |
| 	if (is_use(l) && (def = unique_def(l,b,&defnr)) != (line_p) 0) {
 | |
| 		if (is_copy(def)) {
 | |
| 			if (value_retained(def,defnr,l,b) &&
 | |
| 			    fold_is_desirable(l,PREV(def))) {
 | |
| 				fold_var(l,PREV(def),b);
 | |
| 				OUTVERBOSE("vp:variable folded in proc %d",
 | |
| 					    curproc->p_id,0);
 | |
| 				Svariable++;
 | |
| 				return TRUE;
 | |
| 			}
 | |
| 		} else {
 | |
| 			if (value_known(def,&val)) {
 | |
| 				fold_const(l,b,val);
 | |
| 				OUTVERBOSE("vp:value folded in proc %d",
 | |
| 				   curproc->p_id,0);
 | |
| 				Svalue++;
 | |
| 				return TRUE;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| value_propagation(p)
 | |
| 	proc_p p;
 | |
| {
 | |
| 	/* Apply value propagation to procedure p */
 | |
| 
 | |
| 	bool	changes;
 | |
| 	bblock_p b;
 | |
| 	line_p	l, next;
 | |
| 
 | |
| 	changes = TRUE;
 | |
| 	/* If a statement like A := B is folded to A := constant,
 | |
| 	 * new opportunities for constant folding may arise,
 | |
| 	 * e.g. the value of A might be statically known too now.
 | |
| 	 */
 | |
| 
 | |
| 	 while (changes) {
 | |
| 		changes = FALSE;
 | |
| 		for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
 | |
| 			for (l = b->b_start; l != (line_p) 0; l = next) {
 | |
| 				next = l->l_next;
 | |
| 				if (try_optim(l,b)) {
 | |
| 					changes = TRUE;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	oldmap(copies,nrcopies);
 | |
| 	oldtable(def_to_copynr,nrdefs);
 | |
| }
 | |
| 
 | |
| 
 | |
| STATIC ud_extend(p)
 | |
| 	proc_p p;
 | |
| {
 | |
| 	/* Allocate extended data structures for Use Definition analysis */
 | |
| 
 | |
| 	register bblock_p b;
 | |
| 
 | |
| 	for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
 | |
| 		b->b_extend = newudbx();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| STATIC ud_cleanup(p)
 | |
| 	proc_p p;
 | |
| {
 | |
| 	/* Deallocate extended data structures for Use Definition analysis */
 | |
| 
 | |
| 	register bblock_p b;
 | |
| 
 | |
| 	for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
 | |
| 		Cdeleteset(GEN(b));
 | |
| 		Cdeleteset(IN(b));
 | |
| 		Cdeleteset(C_GEN(b));
 | |
| 		Cdeleteset(C_KILL(b));
 | |
| 		Cdeleteset(C_IN(b));
 | |
| 		Cdeleteset(C_OUT(b));
 | |
| 		Cdeleteset(CHGVARS(b));
 | |
| 		oldudbx(b->b_extend);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| ud_optimize(p)
 | |
| 	proc_p p;
 | |
| {
 | |
| 	if (IS_ENTERED_WITH_GTO(p)) return;
 | |
| 	ud_extend(p);
 | |
| 	locals = (local_p *) 0;
 | |
| 	vardefs = (cset *) 0;
 | |
| 	defs = (line_p *) 0;
 | |
| 	ud_analysis(p);
 | |
| 	copy_analysis(p);
 | |
| #ifdef TRACE
 | |
| 	pr_copies();
 | |
| 	pr_cblocks(p);
 | |
| #endif
 | |
| 	value_propagation(p);
 | |
| 	ud_cleanup(p);
 | |
| 	clean_maps();
 | |
| }
 | |
| 
 | |
| main(argc,argv)
 | |
| 	int argc;
 | |
| 	char *argv[];
 | |
| {
 | |
| 	go(argc,argv,init_globals,ud_optimize,ud_machinit,no_action);
 | |
| 	report("values folded",Svalue);
 | |
| 	report("variables folded",Svariable);
 | |
| 	exit(0);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 |