ack/util/ego/lv/lv.c
1987-03-09 19:15:41 +00:00

593 lines
12 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".
*/
/* L I V E V A R I A B L E S A N A L Y S I S */
#include <stdio.h>
#include "../share/types.h"
#include "lv.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/alloc.h"
#include "../share/map.h"
#include "../share/get.h"
#include "../share/put.h"
#include "../share/aux.h"
#include "../share/init_glob.h"
#include "../share/locals.h"
#include "../share/go.h"
#include "../../../h/em_mnem.h"
#include "../../../h/em_pseu.h"
#include "../../../h/em_spec.h"
#include "../share/parser.h"
#define newlvbx() (bext_p) newstruct(bext_lv)
#define oldlvbx(x) oldstruct(bext_lv,x)
/* TEMPORARY: should be put in ../../../h/em_mes.h: */
#define ms_liv 9
#define ms_ded 10
short nrglobals;
short nrvars;
STATIC int Slv;
STATIC bool mesgflag = FALSE; /* Suppress generation of live/dead info */
STATIC clean_up()
{
local_p *p;
for (p = &locals[1]; p <= &locals[nrlocals]; p++) {
oldlocal(*p);
}
oldmap(locals,nrlocals);
}
STATIC bool is_dir_use(l)
line_p l;
{
/* See if l is a direct use of some variable
* (i.e. not through a pointer). A LIL is a
* direct use of some pointer variable
* (and an indirect use of some other variable).
* A SIL is also a direct use.
* A LOI, however, is not an direct use of a variable.
* An an increment/decrement instruction is regarded
* as a use here, and not as a definition, as the
* variable is first used and than defined.
*/
switch(INSTR(l)) {
case op_dee:
case op_del:
case op_ine:
case op_inl:
case op_lde:
case op_ldl:
case op_lil:
case op_loe:
case op_lol:
case op_sil:
return TRUE;
default:
return FALSE;
}
/* NOTREACHED */
}
STATIC bool is_indir_use(l)
line_p l;
{
/* See if instruction l uses some variable(s) indirectly,
* i.e. through a pointer or via a procedure call.
*/
switch(INSTR(l)) {
case op_blm:
case op_bls:
case op_cai:
case op_cal:
case op_lar:
case op_ldf:
case op_lil:
case op_lof:
case op_loi:
case op_los:
case op_mon:
return TRUE;
default:
return FALSE;
}
/* NOTREACHED */
}
STATIC bool is_def(l)
line_p l;
{
/* See if l does a direct definition */
switch(INSTR(l)) {
case op_sde:
case op_sdl:
case op_ste:
case op_stl:
case op_zre:
case op_zrl:
return TRUE;
default:
return FALSE;
}
/* NOTREACHED */
}
STATIC def_use(p)
proc_p p;
{
/* Compute DEF(b) and USE(b), for every basic block b
* of procedure p. DEF(b) contains the variables that
* are certain to be defined (assigned) in b
* before being used. USE(b) contains the variables
* that may be used in b, before being defined.
* (Note that uncertainty arises in the presence of
* pointers and procedure calls).
* We compute these sets, by scanning the text of
* the basic block from beginning till end.
*/
register bblock_p b;
register line_p l;
short v;
bool found;
cset all_ind_uses;
all_ind_uses = Cempty_set(nrvars);
for (v = 1; v < nrlocals; v++) {
if (!IS_REGVAR(locals[v])) {
Cadd(LOC_TO_VARNR(v),&all_ind_uses);
}
}
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
USE(b) = Cempty_set(nrvars);
DEF(b) = Cempty_set(nrvars);
for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
if (is_def(l)) {
/* An direct definition (i.e. not
* through a pointer).
*/
var_nr(l,&v,&found);
if (found && !Cis_elem(v,USE(b))) {
/* We do maintain live-dead info
* for this variable, and it was
* not used earlier in b.
*/
Cadd(v, &DEF(b));
}
} else {
if (is_dir_use(l)) {
var_nr(l,&v,&found);
if (found && !Cis_elem(v,DEF(b))) {
Cadd(v, &USE(b));
}
}
if (is_indir_use(l)) {
/* Add variable that may be used
* by l to USE(b).
*/
Cjoin(all_ind_uses,&USE(b));
}
}
}
}
Cdeleteset(all_ind_uses);
}
STATIC unite_ins(bbset,setp)
lset bbset;
cset *setp;
{
/* Take the union of L_IN(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(L_IN((bblock_p) Lelem(i)), setp);
}
}
STATIC solve_lv(p)
proc_p p;
{
/* Solve the data flow equations for Live Variables,
* for procedure p. These equations are:
* (1) IN[b] = OUT[b] - DEF[b] + USE[b]
* (2) OUT(b) = IN(s1) + ... + IN(sn) ;
* where SUCC(b) = {s1, ... , sn}
*/
register bblock_p b;
cset newout = Cempty_set(nrvars);
bool change = TRUE;
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
L_IN(b) = Cempty_set(nrvars);
Ccopy_set(USE(b), &L_IN(b));
L_OUT(b) = Cempty_set(nrvars);
}
while (change) {
change = FALSE;
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
unite_ins(b->b_succ,&newout);
if (!Cequal(newout,L_OUT(b))) {
change = TRUE;
Ccopy_set(newout, &L_OUT(b));
Ccopy_set(newout, &L_IN(b));
Csubtract(DEF(b), &L_IN(b));
Cjoin(USE(b), &L_IN(b));
}
}
}
Cdeleteset(newout);
}
STATIC live_variables_analysis(p)
proc_p p;
{
make_localtab(p);
nrvars = nrglobals + nrlocals;
def_use(p);
solve_lv(p);
}
STATIC init_live_dead(b)
bblock_p b;
{
/* For every register variable, see if it is
* live or dead at the end of b.
*/
register short v;
local_p loc;
for (v = 1; v <= nrlocals; v++) {
loc = locals[v];
if (IS_REGVAR(loc) && Cis_elem(LOC_TO_VARNR(v),L_OUT(b))) {
LIVE(loc);
} else {
DEAD(loc);
}
}
}
STATIC line_p make_mesg(mesg,loc)
short mesg;
local_p loc;
{
/* Create a line for a message stating that
* local variable loc is live/dead. This message
* looks like: "mes ms_liv,off,size" or
* "mes ms_ded,off,size".
*/
line_p l = newline(OPLIST);
register arg_p ap;
l->l_instr = ps_mes;
ap = ARG(l) = newarg(ARGOFF);
ap->a_a.a_offset = mesg;
ap = ap->a_next = newarg(ARGOFF);
ap->a_a.a_offset = loc->lc_off;
ap = ap->a_next = newarg(ARGOFF);
ap->a_a.a_offset = loc->lc_size;
return l;
}
STATIC block_entry(b,prev)
bblock_p b,prev;
{
short v,vn;
local_p loc;
bool was_live, is_live;
/* Generate a live/dead message for every register variable that
* was live at the end of prev, but dead at the beginning of b,
* or v.v. If prev = 0 (i.e. begin of procedure), parameters were
* live, normal local variables were dead.
*/
for (v = 1; v <= nrlocals; v++) {
loc = locals[v];
vn = LOC_TO_VARNR(v);
if (prev == (bblock_p) 0) {
was_live = loc->lc_off >= 0;
} else {
was_live = Cis_elem(vn,L_OUT(prev));
}
is_live = Cis_elem(vn,L_IN(b));
if (was_live != is_live) {
app_block(make_mesg((is_live?ms_liv:ms_ded),loc),b);
}
}
}
STATIC app_block(l,b)
line_p l;
bblock_p b;
{
line_p x = b->b_start;
if (x != (line_p) 0 && INSTR(x) == ps_pro) {
/* start of procedure; append after pro pseudo ! */
if ((l->l_next = x->l_next) != (line_p) 0) {
PREV(l->l_next) = l;
}
x->l_next = l;
PREV(l) = x;
} else {
if ((l->l_next = x) != (line_p) 0) {
PREV(l->l_next) = l;
}
b->b_start = l;
PREV(l) = (line_p) 0;
}
}
STATIC definition(l,useless_out,v_out,mesgflag)
line_p l;
bool *useless_out;
short *v_out;
bool mesgflag;
{
/* Process a definition. If the defined (register-) variable
* is live after 'l', then create a live-message and put
* it after 'l'.
*/
short v;
bool found;
local_p loc;
*useless_out = FALSE;
var_nr(l,&v,&found);
if (found && IS_LOCAL(v)) {
*v_out = v;
loc = locals[TO_LOCAL(v)];
if (IS_REGVAR(loc)) {
if (IS_LIVE(loc)) {
if (!mesgflag) {
appnd_line(make_mesg(ms_liv,loc), l);
}
DEAD(loc);
} else {
*useless_out = TRUE;
}
}
}
}
STATIC use(l,mesgflag)
line_p l;
bool mesgflag;
{
/* Process a use. If the defined (register-) variable
* is dead after 'l', then create a dead-message and put
* it after 'l'.
*/
short v;
bool found;
local_p loc;
var_nr(l,&v,&found);
if (found && IS_LOCAL(v)) {
loc = locals[TO_LOCAL(v)];
if (IS_REGVAR(loc) && IS_DEAD(loc)) {
if (!mesgflag) {
appnd_line(make_mesg(ms_ded,loc), l);
}
LIVE(loc);
}
}
}
STATIC nothing() { } /* No action to be undertaken at level 0 of parser */
STATIC rem_code(l1,l2,b)
line_p l1,l2;
bblock_p b;
{
line_p l,x,y;
x = PREV(l1);
y = l2->l_next;
for (l = l1; l != l2; l = l->l_next) {
oldline(l);
}
if (x == (line_p) 0) {
b->b_start = y;
} else {
x->l_next = y;
}
if (y != (line_p) 0) {
PREV(y) = x;
}
}
#define SIZE(v) ((offset) locals[TO_LOCAL(v)]->lc_size)
lv_mesg(p,mesgflag)
proc_p p;
bool mesgflag;
{
/* Create live/dead messages for every possible register
* variable of p. A dead-message is put after a "use" of
* such a variable, if the variable becomes dead just
* after the use (i.e. this was its last use).
* A live message is put after a "definition" of such
* a variable, if the variable becomes live just
* after the definition (which will usually be the case).
* We traverse every basic block b of p from the last
* instruction of b backwards to the beginning of b.
* Initially, all variables that are dead at the end
* of b are marked dead. All others are marked live.
* If we come accross a definition of a variable X that
* was marked live, we put a live-message after the
* definition and mark X dead.
* If we come accross a use of a variable X that
* was marked dead, we put a dead-message after the
* use and mark X live.
* So at any point, the mark of X tells whether X is
* live or dead immediately before (!) that point.
* We also generate a message at the start of a basic block
* for every variable that was live at the end of the (textually)
* previous block, but dead at the entry of this block, or v.v.
* On the fly, useless assignments are removed.
*/
register bblock_p b;
register line_p l;
line_p lnp, prev;
bblock_p prevb = (bblock_p) 0;
short v;
bool useless;
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
block_entry(b,prevb); /* generate message at head of block */
prevb = b;
if (!mesgflag) {
init_live_dead(b);
}
for (l = last_instr(b); l != (line_p) 0; l = prev) {
/* traverse backwards! */
prev = PREV(l);
if (is_def(l)) {
definition(l,&useless,&v,mesgflag);
if (useless && /* assignment to dead var. */
parse(prev,SIZE(v),&lnp,0,nothing)) {
/* The code "VAR := expression" can
* be removed. 'l' is the "STL VAR",
* lnp is the beginning of the EM code
* for the expression.
*/
prev = PREV(lnp);
rem_code(lnp,l,b);
OUTVERBOSE("useless assignment ,proc %d,local %d", curproc->p_id,
(int) locals[TO_LOCAL(v)]->lc_off);
Slv++;
}
} else {
if (is_dir_use(l)) {
use(l,mesgflag);
}
}
}
}
}
STATIC lv_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 = newlvbx();
}
}
STATIC lv_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(USE(b));
Cdeleteset(DEF(b));
Cdeleteset(L_IN(b));
Cdeleteset(L_OUT(b));
oldlvbx(b->b_extend);
}
}
lv_flags(p)
char *p;
{
switch(*p) {
case 'N':
mesgflag = TRUE;
break;
}
}
lv_optimize(p)
proc_p p;
{
locals = (local_p *) 0;
lv_extend(p);
live_variables_analysis(p);
lv_mesg(p,mesgflag);
/* generate live-dead messages for regvars */
lv_cleanup(p);
clean_up();
}
main(argc,argv)
int argc;
char *argv[];
{
go(argc,argv,init_globals,lv_optimize,no_action,lv_flags);
report("useless assignments deleted",Slv);
exit(0);
}