17bc9cdef7
Most warnings are for functions implicitly returning int. Change most
of these functions to return void. (Traditional K&R C had no void
type, but C89 has it.)
Add prototypes to most function declarations in headers. This is
easy, because ego declares most of its extern functions, and the
comments listed most parameters. There were a few outdated or missing
declarations, and a few .c files that failed to include an .h with the
declarations.
Add prototypes to a few function definitions in .c files. Most
functions still have traditional K&R definitions. Most STATIC
functions still don't have prototypes, because they have no earlier
declaration where I would have added the prototype.
Change some prototypes in util/ego/share/alloc.h. Functions newmap()
and oldmap() handle an array of pointers to something; change the
array's type from `short **` to `void **`. Callers use casts to go
between `void **` and the correct type, like `line_p *`. Function
oldtable() takes a `short *`, not a `short **`; I added the wrong type
in 5bbbaf4
.
Make a few other changes to silence warnings. There are a few places
where clang wants extra parentheses in the code.
Edit util/ego/ra/build.lua to add the missing dependency on ra*.h; I
needed this to prevent crashes from ra.
567 lines
11 KiB
C
567 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 <string.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/utils.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 void ud_machinit(void *vp)
|
|
{
|
|
FILE *f = vp;
|
|
char s[100];
|
|
|
|
for (;;) {
|
|
while(getc(f) != '\n');
|
|
fscanf(f,"%99s",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 void 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 void 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 void 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;
|
|
|
|
fprintf(stderr,"LOCAL-TABLE (%d)\n\n",nrlocals);
|
|
for (i = 1; i <= nrlocals; i++) {
|
|
lc = locals[i];
|
|
fprintf(stderr,"LOCAL %d\n",i);
|
|
fprintf(stderr,"\toffset= %ld\n",lc->lc_off);
|
|
fprintf(stderr,"\tsize= %d\n",lc->lc_size);
|
|
fprintf(stderr,"\tflags= %d\n",lc->lc_flags);
|
|
}
|
|
}
|
|
|
|
pr_globals()
|
|
{
|
|
dblock_p d;
|
|
obj_p obj;
|
|
|
|
fprintf(stderr,"GLOBALS (%d)\n\n",nrglobals);
|
|
fprintf(stderr,"ID\tGLOBNR\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) {
|
|
fprintf(stderr,"%d\t%d\n",
|
|
obj->o_id,obj->o_globnr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern char em_mnem[];
|
|
|
|
pr_defs()
|
|
{
|
|
short i;
|
|
line_p l;
|
|
|
|
fprintf(stderr,"DEF TABLE\n\n");
|
|
for (i = 1; i <= nrexpldefs; i++) {
|
|
l = defs[i];
|
|
fprintf(stderr,"%d\t%s ",EXPL_TO_DEFNR(i),
|
|
&em_mnem[(INSTR(l)-sp_fmnem)*4]);
|
|
switch(TYPE(l)) {
|
|
case OPSHORT:
|
|
fprintf(stderr,"%d\n",SHORT(l));
|
|
break;
|
|
case OPOFFSET:
|
|
fprintf(stderr,"%ld\n",OFFSET(l));
|
|
break;
|
|
case OPOBJECT:
|
|
fprintf(stderr,"%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;
|
|
|
|
fprintf(stderr,"%s(%d) =\t{",name,k);
|
|
for (i = 1; i <= n; i++) {
|
|
if (Cis_elem(i,s)) {
|
|
fprintf(stderr,"%d ",i);
|
|
}
|
|
}
|
|
fprintf(stderr,"}\n");
|
|
}
|
|
|
|
pr_blocks(p)
|
|
proc_p p;
|
|
{
|
|
bblock_p b;
|
|
short n;
|
|
|
|
for (b = p->p_start; b != 0; b = b->b_next) {
|
|
fprintf(stderr,"\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;
|
|
|
|
fprintf(stderr,"\nCOPY TABLE\n\n");
|
|
for (i = 1; i <= nrdefs; i++) {
|
|
if (def_to_copynr[i] != 0) {
|
|
fprintf(stderr,"%d\t%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) {
|
|
fprintf(stderr,"\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 void 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 void clean_maps()
|
|
{
|
|
local_p *p;
|
|
cset *v;
|
|
|
|
oldmap((void **) defs,nrexpldefs);
|
|
for (p = &locals[1]; p <= &locals[nrlocals]; p++) {
|
|
oldlocal(*p);
|
|
}
|
|
oldmap((void **) locals,nrlocals);
|
|
for (v = &vardefs[1]; v <= &vardefs[nrvars]; v++) {
|
|
Cdeleteset(*v);
|
|
}
|
|
oldmap((void **) 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;
|
|
}
|
|
|
|
|
|
|
|
STATIC void 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((void **) copies,nrcopies);
|
|
oldtable(def_to_copynr,nrdefs);
|
|
}
|
|
|
|
|
|
STATIC void 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 void 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(void *vp)
|
|
{
|
|
proc_p p = vp;
|
|
|
|
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();
|
|
}
|
|
|
|
int 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);
|
|
}
|
|
|
|
|
|
|