Initial revision
This commit is contained in:
21
util/ego/ud/ud.h
Normal file
21
util/ego/ud/ud.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* U S E - D E F I N I T I O N A N A L Y S I S
|
||||
*
|
||||
* U D . H
|
||||
*/
|
||||
|
||||
#define GEN(b) (b)->b_extend->bx_ud.bx_gen
|
||||
#define KILL(b) (b)->b_extend->bx_ud.bx_kill
|
||||
#define IN(b) (b)->b_extend->bx_ud.bx_in
|
||||
#define OUT(b) (b)->b_extend->bx_ud.bx_out
|
||||
#define C_GEN(b) (b)->b_extend->bx_ud.bx_cgen
|
||||
#define C_KILL(b) (b)->b_extend->bx_ud.bx_ckill
|
||||
#define C_IN(b) (b)->b_extend->bx_ud.bx_cin
|
||||
#define C_OUT(b) (b)->b_extend->bx_ud.bx_cout
|
||||
#define CHGVARS(b) (b)->b_extend->bx_ud.bx_chgvars
|
||||
|
||||
extern short nrglobals; /* number of global variables for which
|
||||
* ud-info is maintained.
|
||||
*/
|
||||
extern short nrvars; /* total number of variables (global + local)
|
||||
* for which ud-info is maintained.
|
||||
*/
|
||||
55
util/ego/ud/ud_aux.c
Normal file
55
util/ego/ud/ud_aux.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/* C O P Y P R O P A G A T I O N
|
||||
*
|
||||
* A U X I L I A R Y R O U T I N E S
|
||||
*/
|
||||
|
||||
|
||||
#include "../share/types.h"
|
||||
#include "../ud/ud.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/cset.h"
|
||||
#include "../share/def.h"
|
||||
#include "../share/locals.h"
|
||||
#include "../share/aux.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../../../h/em_spec.h"
|
||||
#include "../ud/ud_defs.h"
|
||||
|
||||
repl_line(old,new,b)
|
||||
line_p old,new;
|
||||
bblock_p b;
|
||||
{
|
||||
/* Replace 'old' by 'new' */
|
||||
|
||||
if (PREV(old) == (line_p) 0) {
|
||||
b->b_start = new;
|
||||
} else {
|
||||
PREV(old)->l_next = new;
|
||||
}
|
||||
PREV(new) = PREV(old);
|
||||
if ((new->l_next = old->l_next) != (line_p) 0) {
|
||||
PREV(new->l_next) = new;
|
||||
}
|
||||
oldline(old);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool same_var(use,def)
|
||||
line_p use,def;
|
||||
{
|
||||
/* 'use' is an instruction that uses a variable
|
||||
* for which we maintain ud-info (e.g. a LOL).
|
||||
* See if 'def' references the same variable.
|
||||
*/
|
||||
|
||||
if (TYPE(use) == OPOBJECT) {
|
||||
return TYPE(def) == OPOBJECT && OBJ(use) == OBJ(def);
|
||||
} else {
|
||||
return TYPE(def) != OPOBJECT && off_set(use) == off_set(def);
|
||||
}
|
||||
}
|
||||
17
util/ego/ud/ud_aux.h
Normal file
17
util/ego/ud/ud_aux.h
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
/* C O P Y P R O P A G A T I O N
|
||||
*
|
||||
* A U X I L I A R Y R O U T I N E S
|
||||
*/
|
||||
|
||||
|
||||
extern repl_line(); /* (line_p old,new; bblock_p b)
|
||||
* Replace EM instruction 'old' by a
|
||||
* copy of 'new'. Update doubly-linked
|
||||
* list.
|
||||
*/
|
||||
extern bool same_var(); /* (line_p use,def)
|
||||
* 'use' is an instruction that uses a variable
|
||||
* for which we maintain ud-info (e.g. a LOL).
|
||||
* See if 'def' references the same variable.
|
||||
*/
|
||||
246
util/ego/ud/ud_const.c
Normal file
246
util/ego/ud/ud_const.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/* C O N S T A N T P R O P A G A T I O N */
|
||||
|
||||
#include "../share/types.h"
|
||||
#include "../ud/ud.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/cset.h"
|
||||
#include "../share/def.h"
|
||||
#include "../share/aux.h"
|
||||
#include "../share/locals.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../../../h/em_spec.h"
|
||||
#include "../ud/ud_defs.h"
|
||||
#include "ud_const.h"
|
||||
#include "ud_aux.h"
|
||||
|
||||
|
||||
#define CHANGE_INDIR(p) (p->p_change->c_flags & CF_INDIR)
|
||||
#define IS_REG(v) (locals[TO_LOCAL(v)]->lc_flags & LCF_REG)
|
||||
#define BODY_KNOWN(p) (p->p_flags1 & (byte) PF_BODYSEEN)
|
||||
#define CALLS_UNKNOWN(p) (p->p_flags1 & (byte) PF_CALUNKNOWN)
|
||||
|
||||
|
||||
bool is_use(l)
|
||||
line_p l;
|
||||
{
|
||||
/* See if 'l' is a use of a variable */
|
||||
|
||||
switch(INSTR(l)) {
|
||||
case op_lde:
|
||||
case op_ldl:
|
||||
case op_loe:
|
||||
case op_lol:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool value_known(def,val_out)
|
||||
line_p def;
|
||||
offset *val_out;
|
||||
{
|
||||
/* See if the value stored by definition 'def'
|
||||
* is known statically (i.e. is a constant).
|
||||
*/
|
||||
|
||||
short sz1, sz2;
|
||||
offset v;
|
||||
line_p l;
|
||||
|
||||
sz1 = ws;
|
||||
switch(INSTR(def)) {
|
||||
case op_inl:
|
||||
case op_ine:
|
||||
case op_del:
|
||||
case op_dee:
|
||||
return FALSE;
|
||||
case op_zrl:
|
||||
case op_zre:
|
||||
v = (offset) 0;
|
||||
break;
|
||||
case op_sdl:
|
||||
case op_sde:
|
||||
sz1 += ws;
|
||||
/* fall through ... */
|
||||
case op_stl:
|
||||
case op_ste:
|
||||
l = PREV(def);
|
||||
if (l == (line_p) 0) return FALSE;
|
||||
sz2 = ws;
|
||||
switch(INSTR(l)) {
|
||||
case op_zer:
|
||||
if (SHORT(l) >= sz1) {
|
||||
v = (offset) 0;
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
case op_ldc:
|
||||
sz2 += ws;
|
||||
/* fall through ...*/
|
||||
case op_loc:
|
||||
if (sz1 == sz2) {
|
||||
v = off_set(l);
|
||||
break;
|
||||
}
|
||||
/* fall through ... */
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
*val_out = v;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool affected(use,v,l)
|
||||
line_p use,l;
|
||||
short v;
|
||||
{
|
||||
/* See if the variable referenced by 'use' may be
|
||||
* changed by instruction l, which is either a cal, cai or
|
||||
* an indirect assignment.
|
||||
*/
|
||||
|
||||
if (INSTR(l) == op_cal &&
|
||||
TYPE(use) == OPOBJECT &&
|
||||
BODY_KNOWN(PROC(l)) &&
|
||||
!CALLS_UNKNOWN(PROC(l)) &&
|
||||
!CHANGE_INDIR(PROC(l))) {
|
||||
return Cis_elem(OBJ(use)->o_id,PROC(l)->p_change->c_ext);
|
||||
}
|
||||
return TYPE(use) == OPOBJECT || !IS_REG(v);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
STATIC search_backwards(use,v,found,def)
|
||||
line_p use, *def;
|
||||
short v;
|
||||
bool *found;
|
||||
{
|
||||
/* Search backwards in the current basic block,
|
||||
* starting at 'use', trying to find a definition
|
||||
* of the variable referenced by 'use', whose variable
|
||||
* number is v. If the definition found is an
|
||||
* implicit one, return 0 as def.
|
||||
*/
|
||||
|
||||
register line_p l;
|
||||
|
||||
for (l = PREV(use); l != (line_p) 0; l = PREV(l)) {
|
||||
if (does_expl_def(l) && same_var(use,l)) {
|
||||
*found = TRUE;
|
||||
*def = l;
|
||||
return;
|
||||
}
|
||||
if (does_impl_def(l) && affected(use,v,l)) {
|
||||
*found = TRUE;
|
||||
*def = (line_p) 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*found = FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
STATIC short outer_def(vdefs,in)
|
||||
cset vdefs, in;
|
||||
{
|
||||
/* See if there is a unique definition of variable
|
||||
* v reaching the beginning of block b.
|
||||
* 'vdefs' is vardefs[v], 'in' is IN(b).
|
||||
*/
|
||||
|
||||
short n,defnr = 0;
|
||||
Cindex i;
|
||||
|
||||
for (i = Cfirst(vdefs); i != (Cindex) 0; i = Cnext(i,vdefs)) {
|
||||
n = Celem(i);
|
||||
if (Cis_elem(EXPL_TO_DEFNR(n),in)) {
|
||||
if (defnr != 0) return 0;
|
||||
/* If there was already a def., there's no unique one */
|
||||
defnr = n;
|
||||
}
|
||||
}
|
||||
return defnr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
line_p unique_def(use,b,defnr_out)
|
||||
line_p use;
|
||||
bblock_p b;
|
||||
short *defnr_out;
|
||||
{
|
||||
/* See if there is one unique explicit definition
|
||||
* of the variable used by 'use', that reaches 'use'.
|
||||
*/
|
||||
|
||||
short v;
|
||||
bool found;
|
||||
line_p def = (line_p) 0;
|
||||
|
||||
*defnr_out = 0;
|
||||
var_nr(use,&v,&found);
|
||||
if (found) {
|
||||
/* We do maintain ud-info for this variable.
|
||||
* See if there is a previous explicit definition
|
||||
* in the current basic block.
|
||||
*/
|
||||
search_backwards(use,v,&found,&def);
|
||||
if (!found && !Cis_elem(IMPLICIT_DEF(v),IN(b))) {
|
||||
/* See if there is a unique explicit definition
|
||||
* outside the current block, reaching the
|
||||
* beginning of the current block.
|
||||
*/
|
||||
*defnr_out = outer_def(vardefs[v],IN(b));
|
||||
def = (*defnr_out == 0 ? (line_p) 0 : defs[*defnr_out]);
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
|
||||
fold_const(l,b,val)
|
||||
line_p l;
|
||||
bblock_p b;
|
||||
offset val;
|
||||
{
|
||||
/* Perform the substitutions required for constant folding */
|
||||
|
||||
line_p n;
|
||||
|
||||
n = int_line(val);
|
||||
switch(INSTR(l)) {
|
||||
case op_lol:
|
||||
case op_loe:
|
||||
n->l_instr = op_loc;
|
||||
break;
|
||||
case op_ldl:
|
||||
case op_lde:
|
||||
n->l_instr = op_ldc;
|
||||
break;
|
||||
default:
|
||||
assert (FALSE);
|
||||
}
|
||||
repl_line(l,n,b);
|
||||
}
|
||||
24
util/ego/ud/ud_const.h
Normal file
24
util/ego/ud/ud_const.h
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
/* C O N S T A N T P R O P A G A T I O N */
|
||||
|
||||
extern line_p unique_def(); /* ( line_p use; bblock_p b; short *defnr_out;)
|
||||
* See if there is a unique explicit definition
|
||||
* of the variable used by 'use' that
|
||||
* reaches 'use'.
|
||||
*/
|
||||
extern bool value_known(); /* (line_p def; offset *val_out)
|
||||
* See if the value stored by definition 'def'
|
||||
* is known statically (i.e. is a constant).
|
||||
*/
|
||||
extern fold_const(); /* (line_p l; bblock_p b; offset val)
|
||||
* Perform the substitutions required for
|
||||
* constant folding.
|
||||
*/
|
||||
extern bool is_use(); /* (line_p l)
|
||||
* See if 'l' is a use of a variable.
|
||||
*/
|
||||
extern bool affected(); /* (line_p use,l; short v)
|
||||
* See if the variable referenced by 'use' may
|
||||
* be changed by instruction l, which is
|
||||
* either a cal, cai or an indirect assignment.
|
||||
*/
|
||||
390
util/ego/ud/ud_copy.c
Normal file
390
util/ego/ud/ud_copy.c
Normal file
@@ -0,0 +1,390 @@
|
||||
/* C O P Y P R O P A G A T I O N */
|
||||
|
||||
#include "../share/types.h"
|
||||
#include "../ud/ud.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/cset.h"
|
||||
#include "../share/def.h"
|
||||
#include "../share/aux.h"
|
||||
#include "../share/locals.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "../../../h/em_pseu.h"
|
||||
#include "../../../h/em_spec.h"
|
||||
#include "../ud/ud_defs.h"
|
||||
#include "ud_copy.h"
|
||||
#include "ud_const.h"
|
||||
#include "ud_aux.h"
|
||||
|
||||
|
||||
|
||||
line_p *copies; /* table of copies; every entry points to the
|
||||
* store-instruction.
|
||||
*/
|
||||
short *def_to_copynr; /* table that maps a 'definition'-number to a
|
||||
* 'copy' number.
|
||||
*/
|
||||
short nrcopies; /* number of copies in the current procedure
|
||||
* (length of copies-table)
|
||||
*/
|
||||
|
||||
#define COPY_NR(c) def_to_copynr[c]
|
||||
#define CHANGED(v,b) (Cis_elem(v,CHGVARS(b)) || Cis_elem(IMPLICIT_DEF(v),GEN(b)))
|
||||
|
||||
|
||||
#define COUNT 0
|
||||
#define MAP 1
|
||||
|
||||
STATIC traverse_defs(p,action)
|
||||
proc_p p;
|
||||
int action;
|
||||
{
|
||||
bblock_p b;
|
||||
line_p l;
|
||||
bool found;
|
||||
short defcnt,v,cnt;
|
||||
|
||||
defcnt = 1;
|
||||
if (action == COUNT) {
|
||||
nrcopies = 0;
|
||||
} else {
|
||||
copies = (line_p *) newmap(nrcopies);
|
||||
def_to_copynr = newtable(nrdefs);
|
||||
cnt = 1;
|
||||
}
|
||||
if (defcnt > nrdefs) return;
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
|
||||
if (defs[defcnt] == l) {
|
||||
if (is_copy(l)) {
|
||||
var_nr(PREV(l),&v,&found);
|
||||
if (found) {
|
||||
if (action == COUNT) {
|
||||
nrcopies++;
|
||||
} else {
|
||||
copies[cnt] = l;
|
||||
def_to_copynr[defcnt] =
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (++defcnt > nrdefs) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC make_copytab(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Make a table of all copies appearing in procedure p.
|
||||
* We first count how many there are, because we
|
||||
* have to allocate a dynamic array of the correct size.
|
||||
*/
|
||||
|
||||
traverse_defs(p,COUNT);
|
||||
traverse_defs(p,MAP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool is_changed(varl,start,stop)
|
||||
line_p varl, start, stop;
|
||||
{
|
||||
/* See if the variable used by instruction varl
|
||||
* is changed anywhere between 'start' and 'stop'
|
||||
*/
|
||||
|
||||
register line_p l;
|
||||
short v;
|
||||
bool found;
|
||||
|
||||
var_nr(varl,&v,&found);
|
||||
if (!found) {
|
||||
return TRUE; /* We don't maintain ud-info for this variable */
|
||||
}
|
||||
for (l = start; l != (line_p) 0 && l != stop; l = l->l_next) {
|
||||
if (does_expl_def(l) && same_var(varl,l)) return TRUE;
|
||||
if (does_impl_def(l) && affected(varl,v,l)) return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC gen_kill_copies(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Compute C_GEN and C_KILL for every basic block
|
||||
* of p.
|
||||
*/
|
||||
|
||||
register line_p l;
|
||||
register bblock_p b,n;
|
||||
short v;
|
||||
bool found;
|
||||
short copycnt = 1, defcnt = 1;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
C_GEN(b) = Cempty_set(nrcopies);
|
||||
C_KILL(b) = Cempty_set(nrcopies);
|
||||
}
|
||||
if (nrcopies == 0) return;
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
|
||||
if (copies[copycnt] == l) {
|
||||
var_nr(PREV(l),&v,&found);
|
||||
assert(found);
|
||||
for (n = p->p_start; n != (bblock_p) 0;
|
||||
n = n->b_next) {
|
||||
if (n != b && CHANGED(v,n) &&
|
||||
Cis_elem(EXPL_TO_DEFNR(defcnt),IN(n))) {
|
||||
Cadd(copycnt,&C_KILL(n));
|
||||
}
|
||||
}
|
||||
if (is_changed(PREV(l),l,(line_p) 0)) {
|
||||
Cadd(copycnt,&C_KILL(b));
|
||||
} else {
|
||||
Cadd(copycnt,&C_GEN(b));
|
||||
}
|
||||
if (++copycnt > nrcopies) return;
|
||||
}
|
||||
if (defs[defcnt] == l) defcnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC intersect_outs(bbset,setp,full_set)
|
||||
lset bbset;
|
||||
cset *setp,full_set;
|
||||
{
|
||||
/* Take the intersection of C_OUT(b), for all b in bbset,
|
||||
* and put the result in setp.
|
||||
*/
|
||||
|
||||
Lindex i;
|
||||
|
||||
Ccopy_set(full_set,setp);
|
||||
for (i = Lfirst(bbset); i != (Lindex) 0; i = Lnext(i,bbset)) {
|
||||
Cintersect(C_OUT((bblock_p) Lelem(i)), setp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC init_cin(p,full_set)
|
||||
proc_p p;
|
||||
cset full_set;
|
||||
{
|
||||
/* Initialize C_IN(b) and C_OUT(b), for every basic block b.
|
||||
* C_IN of the root of the CFG (i.e. the procedure entry block)
|
||||
* will contain every copy, as it trivially holds that for
|
||||
* every copy "s: A := B" there is no assignment to B on any
|
||||
* path from s to the beginning of the root (because PRED(root)=empty).
|
||||
* C_IN and C_OUT of the root will never be changed.
|
||||
* For all remaining blocks b, C_IN(b) is initialized to the set of
|
||||
* all copies, and C_OUT is set to all copies but those killed in b.
|
||||
*/
|
||||
|
||||
bblock_p b;
|
||||
bblock_p root = p->p_start;
|
||||
|
||||
C_IN(root) = Cempty_set(nrcopies);
|
||||
Ccopy_set(full_set,&C_IN(root)); /* full_set is the set of all copies */
|
||||
/* C_OUT(root) = {all copies} - C_KILL(root) + C_GEN(root) */
|
||||
C_OUT(root) = Cempty_set(nrcopies);
|
||||
Ccopy_set(full_set,&C_OUT(root));
|
||||
Csubtract(C_KILL(root),&C_OUT(root));
|
||||
Cjoin(C_GEN(root),&C_OUT(root));
|
||||
for (b = root->b_next; b != (bblock_p) 0; b = b->b_next) {
|
||||
C_IN(b) = Cempty_set(nrcopies);
|
||||
Ccopy_set(full_set,&C_IN(b));
|
||||
C_OUT(b) = Cempty_set(nrcopies);
|
||||
Ccopy_set(full_set,&C_OUT(b));
|
||||
Csubtract(C_KILL(b),&C_OUT(b));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC solve_cin(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Solve the data flow equations for reaching
|
||||
* definitions of procedure p.
|
||||
* These equations are:
|
||||
* (1) C_OUT(b) = C_IN(b) - C_KILL(b) + C_GEN(b)
|
||||
* (2) C_IN(b) = C_OUT(p1) * .. * C_OUT(pn)
|
||||
* (3) C_IN(root) = {all copies} ;
|
||||
* where PRED(b) = {p1, .. , pn}
|
||||
* and '*' denotes set intersection.
|
||||
* We use the iterative algorithm of Aho&Ullman to
|
||||
* solve the equations.
|
||||
*/
|
||||
|
||||
register bblock_p b;
|
||||
bool change;
|
||||
cset newin,full_set;
|
||||
short n;
|
||||
|
||||
/* initializations */
|
||||
full_set = Cempty_set(nrcopies);
|
||||
for (n = 1; n <= nrcopies; n++) {
|
||||
Cadd(n,&full_set);
|
||||
}
|
||||
newin = Cempty_set(nrcopies);
|
||||
init_cin(p,full_set);
|
||||
change = TRUE;
|
||||
/* main loop */
|
||||
while (change) {
|
||||
change = FALSE;
|
||||
for (b = p->p_start->b_next; b != (bblock_p) 0; b = b->b_next) {
|
||||
intersect_outs(b->b_pred, &newin,full_set);
|
||||
/* newin = C_OUT(p1) * .. * C_OUT(pn) */
|
||||
if (!Cequal(newin,C_IN(b))) {
|
||||
change = TRUE;
|
||||
Ccopy_set(newin, &C_IN(b));
|
||||
Ccopy_set(C_IN(b), &C_OUT(b));
|
||||
Csubtract(C_KILL(b), &C_OUT(b));
|
||||
Cjoin(C_GEN(b), &C_OUT(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
Cdeleteset(newin);
|
||||
Cdeleteset(full_set);
|
||||
}
|
||||
|
||||
|
||||
|
||||
copy_analysis(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Determine which copies procedure p has. Compute C_IN(b),
|
||||
* for every basic block b.
|
||||
*/
|
||||
|
||||
make_copytab(p); /* Make a table of all copies */
|
||||
gen_kill_copies(p); /* Compute C_GEN(b) and C_KILL(b), for every b */
|
||||
solve_cin(p); /* Solve equations for C_IN(b) */
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool is_copy(def)
|
||||
line_p def;
|
||||
{
|
||||
/* See if the definition def is also a 'copy', i.e. an
|
||||
* statement of the form 'A := B' (or, in EM terminology:
|
||||
* a sequence 'Load Variable; Store Variable').
|
||||
*/
|
||||
|
||||
|
||||
line_p lhs;
|
||||
int instr;
|
||||
|
||||
lhs = PREV(def);
|
||||
if (lhs == (line_p) 0) return FALSE;
|
||||
instr = INSTR(def);
|
||||
switch(INSTR(lhs)) {
|
||||
case op_lol:
|
||||
case op_loe:
|
||||
return instr == op_stl || instr == op_ste;
|
||||
case op_ldl:
|
||||
case op_lde:
|
||||
return instr == op_sdl || instr == op_sde;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
|
||||
fold_var(old,new,b)
|
||||
line_p old, new;
|
||||
bblock_p b;
|
||||
{
|
||||
/* The variable referenced by the EM instruction 'old'
|
||||
* must be replaced by the variable referenced by 'new'.
|
||||
*/
|
||||
|
||||
line_p l;
|
||||
|
||||
/* DEBUGGING:
|
||||
local_p loc;
|
||||
short nr;
|
||||
bool ok;
|
||||
if (TYPE(old) == OPOBJECT) {
|
||||
printf("global var.");
|
||||
} else {
|
||||
printf("local var. with off. %D",off_set(old));
|
||||
find_local(off_set(old),&nr,&ok);
|
||||
assert(ok);
|
||||
loc = locals[nr];
|
||||
printf(",score %D",loc->lc_score);
|
||||
}
|
||||
printf(" replaced by ");
|
||||
if (TYPE(new) == OPOBJECT) {
|
||||
printf("global var.");
|
||||
} else {
|
||||
printf("local var. with off. %D",off_set(new));
|
||||
find_local(off_set(new),&nr,&ok);
|
||||
assert(ok);
|
||||
loc = locals[nr];
|
||||
printf(",score %D",loc->lc_score);
|
||||
}
|
||||
printf("\n");
|
||||
END DEBUG */
|
||||
l = old;
|
||||
if (TYPE(l) != TYPE(new)) {
|
||||
l = newline(TYPE(new));
|
||||
l->l_instr = INSTR(new);
|
||||
repl_line(old,l,b);
|
||||
}
|
||||
switch(TYPE(new)) {
|
||||
case OPOBJECT:
|
||||
OBJ(l) = OBJ(new);
|
||||
break;
|
||||
case OPSHORT:
|
||||
SHORT(l) = SHORT(new);
|
||||
break;
|
||||
case OPOFFSET:
|
||||
OFFSET(l) = OFFSET(new);
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool value_retained(copy,defnr,use,b)
|
||||
line_p copy,use;
|
||||
short defnr;
|
||||
bblock_p b;
|
||||
{
|
||||
/* See if the right hand side variable of the
|
||||
* copy still has the same value at 'use'.
|
||||
* If the copy and the use are in the same
|
||||
* basic block (defnr = 0), search from the
|
||||
* copy to the use, to see if the rhs variable
|
||||
* is changed. If the copy is in another block,
|
||||
* defnr is the definition-number of the copy.
|
||||
* Search from the beginning of the block to
|
||||
* the use, to see if the rhs is changed; if not,
|
||||
* check that the copy is in C_IN(b).
|
||||
*/
|
||||
|
||||
line_p rhs, start;
|
||||
|
||||
rhs = PREV(copy);
|
||||
start = (defnr == 0 ? copy : b->b_start);
|
||||
return !is_changed(rhs,start,use) &&
|
||||
(defnr == 0 || Cis_elem(COPY_NR(defnr), C_IN(b)));
|
||||
}
|
||||
41
util/ego/ud/ud_copy.h
Normal file
41
util/ego/ud/ud_copy.h
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
/* C O P Y P R O P A G A T I O N */
|
||||
|
||||
extern line_p *copies; /* table of copies; every entry points to the
|
||||
* store-instruction.
|
||||
*/
|
||||
extern short *def_to_copynr; /* Table that maps a 'definition'-number to a
|
||||
* 'copy' number.
|
||||
*/
|
||||
extern short nrcopies; /* number of copies in the current procedure
|
||||
* (length of copies-table)
|
||||
*/
|
||||
|
||||
extern copy_analysis(); /* (proc_p p)
|
||||
* Determine which copies procedure p has.
|
||||
* Compute C_IN(b), for every basic block b.
|
||||
*/
|
||||
extern bool is_copy(); /* (line_p def)
|
||||
* See if the definition def is also a 'copy',
|
||||
* i.e. an statement of the form
|
||||
* 'A := B' (or, in EM terminology:
|
||||
* a sequence 'Load Variable; Store Variable').
|
||||
*/
|
||||
extern fold_var(); /* (line_p old,new; bblock_p b)
|
||||
* The variable referenced by the
|
||||
* EM instruction 'old' must be replaced
|
||||
* by the variable referenced by 'new'.
|
||||
*/
|
||||
extern bool value_retained(); /* (line_p copy; short defnr; line_p use;
|
||||
* bblock_p b)
|
||||
* See if the right hand side variable of the
|
||||
* copy still has the same value at 'use'.
|
||||
* If the copy and the use are in the same
|
||||
* basic block (defnr = 0), search from the
|
||||
* copy to the use, to see if the rhs variable
|
||||
* is changed. If the copy is in another block,
|
||||
* defnr is the definition-number of the copy.
|
||||
* Search from the beginning of the block to
|
||||
* the use, to see if the rhs is changed;
|
||||
* if not, check that the copy is in C_IN(b).
|
||||
*/
|
||||
378
util/ego/ud/ud_defs.c
Normal file
378
util/ego/ud/ud_defs.c
Normal file
@@ -0,0 +1,378 @@
|
||||
|
||||
/* U S E - D E F I N I T I O N A N A L Y S I S
|
||||
*
|
||||
* U D _ D E F S . C
|
||||
*/
|
||||
|
||||
#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/map.h"
|
||||
#include "../share/locals.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "ud_defs.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/aux.h"
|
||||
|
||||
#define BODY_KNOWN(p) (p->p_flags1 & (byte) PF_BODYSEEN)
|
||||
#define CHANGE_INDIR(p) (p->p_change->c_flags & CF_INDIR)
|
||||
|
||||
short nrdefs; /* total number of definitions */
|
||||
short nrexpldefs; /* number of explicit definitions */
|
||||
line_p *defs;
|
||||
cset *vardefs;
|
||||
|
||||
STATIC cset all_globl_defs, all_indir_defs;
|
||||
/* auxiliary sets, used by gen_sets */
|
||||
|
||||
|
||||
bool does_expl_def(l)
|
||||
line_p l;
|
||||
{
|
||||
/* See if instruction l does an explicit definition */
|
||||
|
||||
switch(INSTR(l)) {
|
||||
case op_stl:
|
||||
case op_sdl:
|
||||
case op_ste:
|
||||
case op_sde:
|
||||
case op_inl:
|
||||
case op_del:
|
||||
case op_ine:
|
||||
case op_dee:
|
||||
case op_zrl:
|
||||
case op_zre:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool does_impl_def(l)
|
||||
line_p l;
|
||||
{
|
||||
/* See if instruction l does an implicit definition */
|
||||
|
||||
switch(INSTR(l)) {
|
||||
case op_cal:
|
||||
case op_cai:
|
||||
case op_sil:
|
||||
case op_stf:
|
||||
case op_sti:
|
||||
case op_sts:
|
||||
case op_sdf:
|
||||
case op_sar:
|
||||
case op_blm:
|
||||
case op_bls:
|
||||
case op_zrf:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
make_defs(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Make a map of all explicit definitions
|
||||
* occurring in p.
|
||||
* Determine the set of explicit definitions
|
||||
* of variable v (i.e. vardefs[v]), for all
|
||||
* v from 1 to nrvars.
|
||||
* For every basic block b, compute CHGVARS(b),
|
||||
* i.e. the set of variables changed in b by an
|
||||
* explicit definition.
|
||||
*/
|
||||
|
||||
register bblock_p b;
|
||||
register line_p l;
|
||||
short v, i, cnt = 0;
|
||||
bool found;
|
||||
|
||||
/* first count the number of definitions */
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
for (l = b->b_start; l != (line_p) 0 ; l = l->l_next) {
|
||||
if (does_expl_def(l)) {
|
||||
var_nr(l,&v,&found);
|
||||
if (!found) continue; /* no ud for this var */
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
nrexpldefs = cnt;
|
||||
/* now allocate the defs table and the vardefs table*/
|
||||
defs = (line_p *) newmap(nrexpldefs);
|
||||
vardefs = (cset *) newmap(nrvars);
|
||||
for (i = 1; i <= nrvars; i++) {
|
||||
vardefs[i] = Cempty_set(nrexpldefs);
|
||||
}
|
||||
cnt = 1;
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
CHGVARS(b) =Cempty_set(nrvars);
|
||||
for (l = b->b_start; l != (line_p) 0 ; l = l->l_next) {
|
||||
if (does_expl_def(l)) {
|
||||
var_nr(l,&v,&found);
|
||||
if (!found) continue;
|
||||
assert (v <= nrvars);
|
||||
Cadd(v,&CHGVARS(b));
|
||||
defs[cnt] = l;
|
||||
Cadd(cnt,&vardefs[v]);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC init_gen(nrdefs)
|
||||
short nrdefs;
|
||||
{
|
||||
/* Initializing routine of gen_sets. Compute the set
|
||||
* of all implicit definitions to global variables
|
||||
* (all_globl_defs) and the set of all implicit
|
||||
* definition generated by an indirect assignment
|
||||
* through a pointer (all_indir_defs).
|
||||
*/
|
||||
|
||||
short v;
|
||||
|
||||
all_globl_defs = Cempty_set(nrdefs);
|
||||
all_indir_defs = Cempty_set(nrdefs);
|
||||
for (v = 1; v <= nrglobals; v++) {
|
||||
Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)), &all_globl_defs);
|
||||
Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)), &all_indir_defs);
|
||||
}
|
||||
for (v = 1; v <= nrlocals; v++) {
|
||||
if (!IS_REGVAR(locals[v])) {
|
||||
Cadd(IMPLICIT_DEF(LOC_TO_VARNR(v)), &all_indir_defs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC clean_gen()
|
||||
{
|
||||
Cdeleteset(all_globl_defs);
|
||||
Cdeleteset(all_indir_defs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC bool same_target(l,defnr)
|
||||
line_p l;
|
||||
short defnr;
|
||||
{
|
||||
/* See if l defines the same variable as def */
|
||||
|
||||
line_p def;
|
||||
short v;
|
||||
|
||||
if (IS_IMPL_DEF(defnr)) {
|
||||
/* An implicitly generated definition */
|
||||
v = IMPL_VAR(TO_IMPLICIT(defnr));
|
||||
if (IS_GLOBAL(v)) {
|
||||
return TYPE(l) == OPOBJECT &&
|
||||
OBJ(l)->o_globnr == TO_GLOBAL(v);
|
||||
} else {
|
||||
return TYPE(l) != OPOBJECT &&
|
||||
locals[TO_LOCAL(v)]->lc_off == off_set(l);
|
||||
}
|
||||
}
|
||||
/* explicit definition */
|
||||
def = defs[TO_EXPLICIT(defnr)];
|
||||
if (TYPE(l) == OPOBJECT) {
|
||||
return TYPE(def) == OPOBJECT && OBJ(def) == OBJ(l);
|
||||
} else {
|
||||
return TYPE(def) != OPOBJECT && off_set(def) == off_set(l);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC rem_prev_defs(l,gen_p)
|
||||
line_p l;
|
||||
cset *gen_p;
|
||||
{
|
||||
/* Remove all definitions in gen that define the
|
||||
* same variable as l.
|
||||
*/
|
||||
|
||||
cset gen;
|
||||
Cindex i,next;
|
||||
|
||||
gen = *gen_p;
|
||||
for (i = Cfirst(gen); i != (Cindex) 0; i = next) {
|
||||
next = Cnext(i,gen);
|
||||
if (same_target(l,Celem(i))) {
|
||||
Cremove(Celem(i),gen_p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
STATIC impl_globl_defs(p,gen_p)
|
||||
proc_p p;
|
||||
cset *gen_p;
|
||||
{
|
||||
/* Add all definitions of global variables
|
||||
* that are generated implicitly by a call
|
||||
* to p to the set gen_p.
|
||||
*/
|
||||
|
||||
Cindex i;
|
||||
short v;
|
||||
cset ext = p->p_change->c_ext;
|
||||
|
||||
for (i = Cfirst(ext); i != (Cindex) 0; i = Cnext(i,ext)) {
|
||||
if (( v = omap[Celem(i)]->o_globnr) != (short) 0) {
|
||||
/* the global variable v, for which we do
|
||||
* maintain ud-info is changed by p, so a
|
||||
* definition of v is generated implicitly.
|
||||
*/
|
||||
Cadd(IMPLICIT_DEF(GLOB_TO_VARNR(v)),gen_p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC impl_gen_defs(l,gen_p)
|
||||
line_p l;
|
||||
cset *gen_p;
|
||||
{
|
||||
/* Add all definitions generated implicitly by instruction l
|
||||
* to gen_p. l may be a call or some kind of indirect
|
||||
* assignment.
|
||||
*/
|
||||
|
||||
proc_p p;
|
||||
|
||||
switch(INSTR(l)) {
|
||||
case op_cal:
|
||||
p = PROC(l);
|
||||
if (BODY_KNOWN(p)) {
|
||||
impl_globl_defs(p,gen_p);
|
||||
if (!CHANGE_INDIR(p)) return;
|
||||
break;
|
||||
}
|
||||
/* else fall through ... */
|
||||
case op_cai:
|
||||
/* Indirect subroutine call or call to
|
||||
* a subroutine whose body is not available.
|
||||
* Assume worst case; all global
|
||||
* variables are changed and
|
||||
* the called proc. does a store-
|
||||
* indirect.
|
||||
*/
|
||||
Cjoin(all_globl_defs,gen_p);
|
||||
break;
|
||||
/* default: indir. assignment */
|
||||
}
|
||||
Cjoin(all_indir_defs,gen_p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gen_sets(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Compute for every basic block b of p the
|
||||
* set GEN(b) of definitions in b (explicit as
|
||||
* well as implicit) that reach the end of b.
|
||||
*/
|
||||
|
||||
register bblock_p b;
|
||||
register line_p l;
|
||||
short defnr = 1;
|
||||
|
||||
init_gen(nrdefs); /* compute all_globl_defs and all_indir_defs */
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
GEN(b) = Cempty_set(nrdefs);
|
||||
for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
|
||||
if (does_impl_def(l)) {
|
||||
impl_gen_defs(l,&GEN(b));
|
||||
/* add definitions implicitly
|
||||
* generated by subroutine call
|
||||
* or indir. pointer assignment.
|
||||
*/
|
||||
} else {
|
||||
if (does_expl_def(l)) {
|
||||
if (defnr <= nrdefs && defs[defnr] == l) {
|
||||
rem_prev_defs(l,&GEN(b));
|
||||
/* previous defs. of same var
|
||||
* don't reach the end of b.
|
||||
*/
|
||||
Cadd(EXPL_TO_DEFNR(defnr),&GEN(b));
|
||||
defnr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clean_gen(); /* clean up */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
STATIC killed_defs(v,b)
|
||||
short v;
|
||||
bblock_p b;
|
||||
{
|
||||
/* Put all definitions of v occurring outside b
|
||||
* in KILL(b). In fact, we also put explicit
|
||||
* definitions occurring in b, but not reaching the
|
||||
* end of b, in KILL(b). This causes no harm.
|
||||
*/
|
||||
|
||||
Cindex i;
|
||||
short d;
|
||||
|
||||
for (i = Cfirst(vardefs[v]); i != (Cindex) 0; i = Cnext(i,vardefs[v])) {
|
||||
d = Celem(i); /* d is an explicit definition of v */
|
||||
if (!Cis_elem(EXPL_TO_DEFNR(d),GEN(b))) {
|
||||
Cadd(EXPL_TO_DEFNR(d),&KILL(b));
|
||||
}
|
||||
}
|
||||
/* Also add implicit definition of v to KILL(b) */
|
||||
Cadd(IMPLICIT_DEF(v),&KILL(b));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
kill_sets(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* For every basic block b of p compute the set
|
||||
* KILL(b) of definitions outside b that define
|
||||
* variables redefined by b.
|
||||
* KILL(b) contains explicit as well as implicit
|
||||
* definitions.
|
||||
*/
|
||||
|
||||
register bblock_p b;
|
||||
Cindex i;
|
||||
short v;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
KILL(b) = Cempty_set(nrdefs);
|
||||
for (i = Cfirst(CHGVARS(b)); i != (Cindex) 0;
|
||||
i = Cnext(i,CHGVARS(b))) {
|
||||
v = Celem(i); /* v is a variable changed in b */
|
||||
killed_defs(v,b);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
util/ego/ud/ud_defs.h
Normal file
51
util/ego/ud/ud_defs.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* U S E - D E F I N I T I O N A N A L Y S I S
|
||||
*
|
||||
* U D _ D E F S . H
|
||||
*/
|
||||
|
||||
extern short nrdefs; /* total number of definitions */
|
||||
extern short nrexpldefs; /* number of explicit definitions */
|
||||
extern line_p *defs; /* map of explicit definitions */
|
||||
extern cset *vardefs; /* set of explicit defs. of all variables */
|
||||
|
||||
extern make_defs(); /* (proc_p p)
|
||||
* Compute defs[], vardefs[]
|
||||
* and CHGVARS(b) (for every b).
|
||||
*/
|
||||
extern gen_sets(); /* (proc_p p)
|
||||
* Compute GEN(b) (for every b).
|
||||
*/
|
||||
extern kill_sets(); /* (proc_p p)
|
||||
*Compute KILL(b) (for every b).
|
||||
*/
|
||||
extern bool does_expl_def(); /* (line_p l)
|
||||
* See if instruction l does an explicit
|
||||
* definition (e.g. a STL).
|
||||
*/
|
||||
extern bool does_impl_def(); /* (line_p l)
|
||||
* See if instruction l does an implicit
|
||||
* definition (e.g. a CAL).
|
||||
*/
|
||||
|
||||
|
||||
/* Two kinds of definitions exist:
|
||||
* - an explicit definition is an assignment to a single
|
||||
* variable (e.g. a STL, STE, INE).
|
||||
* - an implicit definition is an assignment to a variable
|
||||
* performed via a subroutine call or an
|
||||
* indirect assignment (through a pointer).
|
||||
* Every explicit definition has an 'explicit definition number',
|
||||
* which is its index in the 'defs' table.
|
||||
* Every implicit definition has an 'implicit definition number',
|
||||
* which is the 'variable number' of the changed variable.
|
||||
* Every such definition also has a 'definition number'.
|
||||
* Conversions exist between these numbers.
|
||||
*/
|
||||
|
||||
#define TO_EXPLICIT(defnr) (defnr - nrvars)
|
||||
#define TO_IMPLICIT(defnr) (defnr)
|
||||
#define EXPL_TO_DEFNR(explnr) (explnr + nrvars)
|
||||
#define IMPL_TO_DEFNR(implnr) (implnr)
|
||||
#define IMPLICIT_DEF(v) (v)
|
||||
#define IMPL_VAR(defnr) (defnr)
|
||||
#define IS_IMPL_DEF(defnr) (defnr <= nrvars)
|
||||
18
util/ego/ud/ud_locals.h
Normal file
18
util/ego/ud/ud_locals.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* U S E - D E F I N I T I O N A N A L Y S I S
|
||||
*
|
||||
* U D _ L O C A L S . H
|
||||
*/
|
||||
|
||||
extern local_p *locals; /* table of locals, index is local-number */
|
||||
extern short nrlocals; /* number of locals for which we keep ud-info */
|
||||
|
||||
extern make_localtab(); /* (proc_p p)
|
||||
* Analyse the text of procedure p to determine
|
||||
* which local variable p has. Make a table of
|
||||
* these variables ('locals') and count them
|
||||
* ('nrlocals'). Also collect register messages.
|
||||
*/
|
||||
extern var_nr(); /* (line_p l; short *nr_out;bool *found_out)
|
||||
* Compute the 'variable number' of the
|
||||
* variable referenced by EM instruction l.
|
||||
*/
|
||||
Reference in New Issue
Block a user