Initial revision
This commit is contained in:
334
util/ego/cf/cf.c
Normal file
334
util/ego/cf/cf.c
Normal file
@@ -0,0 +1,334 @@
|
||||
/* C O N T R O L F L O W
|
||||
*
|
||||
* M A I N R O U T I N E
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../share/types.h"
|
||||
#include "../share/debug.h"
|
||||
#include "../share/map.h"
|
||||
#include "../share/files.h"
|
||||
#include "../share/global.h"
|
||||
#include "../share/alloc.h"
|
||||
#include "../share/lset.h"
|
||||
#include "../share/cset.h"
|
||||
#include "../share/get.h"
|
||||
#include "../share/put.h"
|
||||
#include "../../../h/em_mnem.h"
|
||||
#include "cf.h"
|
||||
#include "cf_succ.h"
|
||||
#include "cf_idom.h"
|
||||
#include "cf_loop.h"
|
||||
|
||||
|
||||
STATIC cset lpi_set; /* set of procedures used in LPI instruction */
|
||||
STATIC cset cai_set; /* set of all procedures doing a CAI */
|
||||
|
||||
STATIC interproc_analysis(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Interprocedural analysis of a procedure p determines:
|
||||
* - all procedures called by p (the 'call graph')
|
||||
* - the set of objects changed by p (directly)
|
||||
* - whether p does a load-indirect (loi,lof etc.)
|
||||
* - whether p does a store-indirect (sti, stf etc.)
|
||||
* The changed/used variables information will be
|
||||
* transitively closed, i.e. if P calls Q and Q changes
|
||||
* a variable X, the P changes X too.
|
||||
* (The same applies for used variables and for use/store
|
||||
* indirect).
|
||||
* The transitive closure will be computed by main
|
||||
* after all procedures have been processed.
|
||||
*/
|
||||
|
||||
bblock_p b;
|
||||
line_p lnp;
|
||||
bool inloop;
|
||||
|
||||
/* Allocate memory for structs and sets */
|
||||
|
||||
p->p_use = newuse();
|
||||
p->p_change = newchange();
|
||||
p->p_change->c_ext = Cempty_set(olength);
|
||||
p->p_calling = Cempty_set(plength);
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
inloop = (Lnrelems(b->b_loops) > 0);
|
||||
for (lnp = b->b_start; lnp != (line_p) 0; lnp = lnp->l_next) {
|
||||
/* for all instructions of p do */
|
||||
switch(INSTR(lnp)) {
|
||||
case op_cal:
|
||||
Cadd(PROC(lnp)->p_id, &p->p_calling);
|
||||
/* add called proc to p_calling */
|
||||
if (inloop) {
|
||||
CALLED_IN_LOOP(PROC(lnp));
|
||||
}
|
||||
break;
|
||||
case op_cai:
|
||||
Cadd(p->p_id,&cai_set);
|
||||
break;
|
||||
case op_lpi:
|
||||
Cadd(PROC(lnp)->p_id, &lpi_set);
|
||||
/* All procedures that have their names used
|
||||
* in an lpi instruction, may be called via
|
||||
* a cai instruction.
|
||||
*/
|
||||
PROC(lnp)->p_flags1 |= PF_LPI;
|
||||
break;
|
||||
case op_ste:
|
||||
case op_sde:
|
||||
case op_ine:
|
||||
case op_dee:
|
||||
case op_zre:
|
||||
Cadd(OBJ(lnp)->o_id, &p->p_change->c_ext);
|
||||
/* Add changed object to c_ext */
|
||||
break;
|
||||
case op_lil:
|
||||
case op_lof:
|
||||
case op_loi:
|
||||
case op_los:
|
||||
case op_lar:
|
||||
p->p_use->u_flags |= UF_INDIR;
|
||||
/* p does a load-indirect */
|
||||
break;
|
||||
case op_sil:
|
||||
case op_stf:
|
||||
case op_sti:
|
||||
case op_sts:
|
||||
case op_sar:
|
||||
p->p_change->c_flags |= CF_INDIR;
|
||||
/* p does a store-indirect */
|
||||
break;
|
||||
case op_blm:
|
||||
case op_bls:
|
||||
p->p_use->u_flags |= UF_INDIR;
|
||||
p->p_change->c_flags |= CF_INDIR;
|
||||
/* p does both */
|
||||
break;
|
||||
case op_mon:
|
||||
printf("mon not yet implemented\n");
|
||||
break;
|
||||
case op_lxl:
|
||||
case op_lxa:
|
||||
curproc->p_flags1 |= PF_ENVIRON;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
STATIC cf_cleanproc(p)
|
||||
proc_p p;
|
||||
{
|
||||
/* Remove the extended data structures of p */
|
||||
|
||||
register bblock_p b;
|
||||
register Lindex pi;
|
||||
loop_p lp;
|
||||
|
||||
for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
|
||||
oldcfbx(b->b_extend);
|
||||
}
|
||||
for (pi = Lfirst(p->p_loops); pi != (Lindex) 0; pi = Lnext(pi,
|
||||
p->p_loops)) {
|
||||
lp = (loop_p) Lelem(pi);
|
||||
oldcflpx(lp->lp_extend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define CHANGE_INDIR(ch) ((ch->c_flags & CF_INDIR) != 0)
|
||||
#define USE_INDIR(us) ((us->u_flags & UF_INDIR) != 0)
|
||||
#define CALLS_UNKNOWN(p) (p->p_flags1 & (byte) PF_CALUNKNOWN)
|
||||
#define BODY_KNOWN(p) (p->p_flags1 & (byte) PF_BODYSEEN)
|
||||
#define ENVIRON(p) (p->p_flags1 & (byte) PF_ENVIRON)
|
||||
|
||||
|
||||
STATIC bool add_info(q,p)
|
||||
proc_p q,p;
|
||||
{
|
||||
/* Determine the consequences for used/changed variables info
|
||||
* of the fact that p calls q. If e.g. q changes a variable X
|
||||
* then p changes this variable too. This routine is an
|
||||
* auxiliary routine of the transitive closure process.
|
||||
* The returned value indicates if there was any change in
|
||||
* the information of p.
|
||||
*/
|
||||
|
||||
change_p chp, chq;
|
||||
use_p usp, usq;
|
||||
bool diff = FALSE;
|
||||
|
||||
chp = p->p_change;
|
||||
chq = q->p_change;
|
||||
usp = p->p_use;
|
||||
usq = q->p_use;
|
||||
|
||||
if (!BODY_KNOWN(q)) {
|
||||
/* q is a procedure of which the body is not available
|
||||
* as EM text.
|
||||
*/
|
||||
if (CALLS_UNKNOWN(p)) {
|
||||
return FALSE;
|
||||
/* p already called an unknown procedure */
|
||||
} else {
|
||||
p->p_flags1 |= PF_CALUNKNOWN;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
if (CALLS_UNKNOWN(q)) {
|
||||
/* q calls a procedure of which the body is not available
|
||||
* as EM text.
|
||||
*/
|
||||
if (!CALLS_UNKNOWN(p)) {
|
||||
p->p_flags1 |= PF_CALUNKNOWN;
|
||||
diff = TRUE;
|
||||
}
|
||||
}
|
||||
if (IS_CALLED_IN_LOOP(p) && !IS_CALLED_IN_LOOP(q)) {
|
||||
CALLED_IN_LOOP(q);
|
||||
diff = TRUE;
|
||||
}
|
||||
if (!Cis_subset(chq->c_ext, chp->c_ext)) {
|
||||
/* q changes global variables (objects) that
|
||||
* p did not (yet) change. Add all variables
|
||||
* changed by q to the c_ext set of p.
|
||||
*/
|
||||
Cjoin(chq->c_ext, &chp->c_ext);
|
||||
diff = TRUE;
|
||||
}
|
||||
if (CHANGE_INDIR(chq) && !CHANGE_INDIR(chp)) {
|
||||
/* q does a change-indirect (sil etc.)
|
||||
* and p did not (yet).
|
||||
*/
|
||||
chp->c_flags |= CF_INDIR;
|
||||
diff = TRUE;
|
||||
}
|
||||
if (USE_INDIR(usq) && !USE_INDIR(usp)) {
|
||||
/* q does a use-indirect (lil etc.)
|
||||
* and p dis not (yet).
|
||||
*/
|
||||
usp->u_flags |= UF_INDIR;
|
||||
diff = TRUE;
|
||||
}
|
||||
if (ENVIRON(q) && !ENVIRON(p)) {
|
||||
/* q uses or changes local variables in its
|
||||
* environment while p does not (yet).
|
||||
*/
|
||||
p->p_flags1 |= PF_ENVIRON;
|
||||
diff = TRUE;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
STATIC trans_clos(head)
|
||||
proc_p head;
|
||||
{
|
||||
/* Compute the transitive closure of the used/changed
|
||||
* variable information.
|
||||
*/
|
||||
|
||||
register proc_p p,q;
|
||||
Cindex i;
|
||||
bool changes = TRUE;
|
||||
|
||||
while(changes) {
|
||||
changes = FALSE;
|
||||
for (p = head; p != (proc_p) 0; p = p->p_next) {
|
||||
if (!BODY_KNOWN(p)) continue;
|
||||
for (i = Cfirst(p->p_calling); i != (Cindex) 0;
|
||||
i = Cnext(i,p->p_calling)) {
|
||||
q = pmap[Celem(i)];
|
||||
if (add_info(q,p)) {
|
||||
changes = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
indir_calls()
|
||||
{
|
||||
Cindex i;
|
||||
proc_p p;
|
||||
|
||||
for (i = Cfirst(cai_set); i != (Cindex) 0; i = Cnext(i,cai_set)) {
|
||||
p = pmap[Celem(i)]; /* p does a CAI */
|
||||
Cjoin(lpi_set, &p->p_calling);
|
||||
}
|
||||
Cdeleteset(lpi_set);
|
||||
Cdeleteset(cai_set);
|
||||
}
|
||||
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
FILE *f, *f2, *gf2; /* The EM input, EM output, basic block output */
|
||||
bblock_p g;
|
||||
short n, kind;
|
||||
line_p l;
|
||||
|
||||
linecount = 0;
|
||||
fproc = getptable(pname); /* proc table */
|
||||
fdblock = getdtable(dname); /* data block table */
|
||||
lpi_set = Cempty_set(plength);
|
||||
cai_set = Cempty_set(plength);
|
||||
if ((f = fopen(lname,"r")) == NULL) {
|
||||
error("cannot open %s", lname);
|
||||
}
|
||||
if ((f2 = fopen(lname2,"w")) == NULL) {
|
||||
error("cannot open %s", lname2);
|
||||
}
|
||||
if ((gf2 = fopen(bname2,"w")) == NULL) {
|
||||
error("cannot open %s",bname2);
|
||||
}
|
||||
while (getbblocks(f,&kind,&n,&g,&l)) {
|
||||
/* read EM text of one unit and
|
||||
* (if it is a procedure)
|
||||
* partition it into n basic blocks.
|
||||
*/
|
||||
if (kind == LDATA) {
|
||||
putunit(LDATA,(proc_p) 0,l,gf2,f2);
|
||||
} else {
|
||||
curproc->p_start = g;
|
||||
/* The global variable curproc points to the
|
||||
* current procedure. It is set by getbblocks
|
||||
*/
|
||||
control_flow(g); /* compute pred and succ */
|
||||
dominators(g,n); /* compute immediate dominators */
|
||||
loop_detection(curproc); /* compute loops */
|
||||
interproc_analysis(curproc);
|
||||
/* Interprocedural analysis */
|
||||
cf_cleanproc(curproc);
|
||||
putunit(LTEXT,curproc,(line_p) 0,gf2,f2);
|
||||
/* output control flow graph + text */
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
fclose(f2);
|
||||
fclose(gf2);
|
||||
indir_calls();
|
||||
trans_clos(fproc);
|
||||
/* Compute transitive closure of used/changed
|
||||
* variables information for every procedure.
|
||||
*/
|
||||
if ((f = fopen(dname2,"w")) == NULL) {
|
||||
error("cannot open %s",dname2);
|
||||
}
|
||||
putdtable(fdblock,f);
|
||||
if ((f = fopen(pname2,"w")) == NULL) {
|
||||
error("cannot open %s",pname2);
|
||||
}
|
||||
putptable(fproc,f,TRUE);
|
||||
exit(0);
|
||||
}
|
||||
Reference in New Issue
Block a user