562 lines
11 KiB
C
562 lines
11 KiB
C
/*
|
|
* (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 <stdio.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 "../../../h/em_spec.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);
|
|
}
|
|
}
|
|
|
|
|
|
ud_optimize(p)
|
|
proc_p p;
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
|