home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
historic
/
v941.tgz
/
icon.v941src.tar
/
icon.v941src
/
src
/
iconc
/
fixcode.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-07-29
|
11KB
|
373 lines
/*
* fixcode.c - routines to "fix code" by determining what signals are returned
* by continuations and what must be done when they are. Also perform
* optional control flow optimizations.
*/
#include "../h/gsupport.h"
#include "ctrans.h"
#include "cglobals.h"
#include "ccode.h"
#include "ctree.h"
#include "csym.h"
#include "cproto.h"
/*
* Prototypes for static functions.
*/
static struct code *ck_unneed (struct code *cd, struct code *lbl);
static void clps_brch (struct code *branch);
static void dec_refs (struct code *cd);
static void rm_unrch (struct code *cd);
/*
* fix_fncs - go through the generated C functions, determine how calls
* handle signals, in-line trivial functions where possible, remove
* goto's which immediately precede their labels, and remove unreachable
* code.
*/
void fix_fncs(fnc)
struct c_fnc *fnc;
{
struct code *cd, *cd1;
struct code *contbody;
struct sig_act *sa;
struct sig_lst *sl;
struct code *call;
struct code *create;
struct code *ret_sig;
struct code *sig;
struct c_fnc *calledcont;
int no_break;
int collapse;
/*
* Fix any called functions and decide how the calls handle the
* returned signals.
*/
fnc->flag |= CF_Mark;
for (call = fnc->call_lst; call != NULL; call = call->NextCall) {
calledcont = call->Cont;
if (calledcont != NULL) {
if (!(calledcont->flag & CF_Mark))
fix_fncs(calledcont);
if (calledcont->flag & CF_ForeignSig) {
call->Flags |= ForeignSig;
fnc->flag |= CF_ForeignSig;
}
}
/*
* Try to collapse call chains of continuations.
*/
if (opt_cntrl && calledcont != NULL) {
contbody = calledcont->cd.next;
if (call->OperName == NULL && contbody->cd_id == C_RetSig) {
/*
* A direct call of a continuation which consists of just a
* return. Replace call with code to handle the returned signal.
*/
ret_sig = contbody->SigRef->sig;
if (ret_sig == &resume)
cd1 = sig_cd(call->ContFail, fnc);
else
cd1 = sig_cd(ret_sig, fnc);
cd1->prev = call->prev;
cd1->prev->next = cd1;
cd1->next = call->next;
if (cd1->next != NULL)
cd1->next->prev = cd1;
--calledcont->ref_cnt;
continue; /* move on to next call */
}
else if (contbody->cd_id == C_CallSig && contbody->next == NULL) {
/*
* The called continuation contains only a call.
*/
if (call->OperName == NULL) {
/*
* We call the continuation directly, so we can in-line it.
* We must replace signal returns with appropriate actions.
*/
if (--calledcont->ref_cnt != 0 && contbody->Cont != NULL)
++contbody->Cont->ref_cnt;
call->OperName = contbody->OperName;
call->ArgLst = contbody->ArgLst;
call->Cont = contbody->Cont;
call->Flags = contbody->Flags;
for (sa = contbody->SigActs; sa != NULL; sa = sa->next) {
ret_sig = sa->cd->SigRef->sig;
if (ret_sig == &resume)
cd1 = sig_cd(call->ContFail, fnc);
else
cd1 = sig_cd(ret_sig, fnc);
call->SigActs = new_sgact(sa->sig, cd1, call->SigActs);
}
continue; /* move on to next call */
}
else if (contbody->OperName == NULL) {
/*
* The continuation simply calls another continuation. We can
* eliminate the intermediate continuation as long as we can
* move signal conversions to the other side of the operation.
* The operation only intercepts resume signals.
*/
collapse = 1;
for (sa = contbody->SigActs; sa != NULL; sa = sa->next) {
ret_sig = sa->cd->SigRef->sig;
if (sa->sig != ret_sig && (sa->sig == &resume ||
ret_sig == &resume))
collapse = 0;
}
if (collapse) {
if (--calledcont->ref_cnt != 0 && contbody->Cont != NULL)
++contbody->Cont->ref_cnt;
call->Cont = contbody->Cont;
for (sa = contbody->SigActs; sa != NULL; sa = sa->next) {
ret_sig = sa->cd->SigRef->sig;
if (ret_sig != &resume)
call->SigActs = new_sgact(sa->sig, sig_cd(ret_sig, fnc),
call->SigActs);
}
continue; /* move on to next call */
}
}
}
}
/*
* We didn't do any optimizations. We must still figure out
* out how to handle signals returned by the continuation.
*/
if (calledcont != NULL) {
for (sl = calledcont->sig_lst; sl != NULL; sl = sl->next) {
if (sl->ref_cnt > 0) {
sig = sl->sig;
/*
* If an operation is being called, it handles failure from the
* continuation.
*/
if (sig != &resume || call->OperName == NULL) {
if (sig == &resume)
cd1 = sig_cd(call->ContFail, fnc);
else
cd1 = sig_cd(sig, fnc);
call->SigActs = new_sgact(sig, cd1, call->SigActs);
}
}
}
}
}
/*
* fix up the signal handling in the functions implementing co-expressions.
*/
for (create = fnc->creatlst; create != NULL; create = create->NextCreat)
fix_fncs(create->Cont);
if (!opt_cntrl)
return; /* control flow optimizations disabled. */
/*
* Collapse branch chains and remove unreachable code.
*/
for (cd = &(fnc->cd); cd != NULL; cd = cd->next) {
switch (cd->cd_id) {
case C_CallSig:
no_break = 1;
for (sa = cd->SigActs; sa != NULL; sa = sa->next) {
if (sa->cd->cd_id == C_Break) {
switch (cd->next->cd_id) {
case C_Goto:
sa->cd->cd_id = cd->next->cd_id;
sa->cd->Lbl = cd->next->Lbl;
++sa->cd->Lbl->RefCnt;
break;
case C_RetSig:
sa->cd->cd_id = cd->next->cd_id;
sa->cd->SigRef= cd->next->SigRef;
++sa->cd->SigRef->ref_cnt;
break;
default:
no_break = 0;
}
}
if (sa->cd->cd_id == C_Goto)
clps_brch(sa->cd);
}
if (no_break)
rm_unrch(cd);
/*
* Try converting gotos into breaks.
*/
for (sa = cd->SigActs; sa != NULL; sa = sa->next)
if (sa->cd->cd_id == C_Goto) {
cd1 = cd->next;
while (cd1 != NULL && (cd1->cd_id == C_Label ||
cd1->cd_id == C_RBrack)) {
if (cd1 == sa->cd->Lbl) {
sa->cd->cd_id = C_Break;
--cd1->RefCnt;
break;
}
cd1 = cd1->next;
}
}
break;
case C_Goto:
clps_brch(cd);
rm_unrch(cd);
if (cd->cd_id == C_Goto)
ck_unneed(cd, cd->Lbl);
break;
case C_If:
if (cd->ThenStmt->cd_id == C_Goto) {
clps_brch(cd->ThenStmt);
if (cd->ThenStmt->cd_id == C_Goto)
ck_unneed(cd, cd->ThenStmt->Lbl);
}
break;
case C_PFail:
case C_PRet:
case C_RetSig:
rm_unrch(cd);
break;
}
}
/*
* If this function only contains a return, indicate that we can
* call a shared signal returning function instead of it. This is
* a special case of "common subROUTINE elimination".
*/
if (fnc->cd.next->cd_id == C_RetSig)
fnc->flag |= CF_SigOnly;
}
/*
* clps_brch - collapse branch chains.
*/
static void clps_brch(branch)
struct code *branch;
{
struct code *cd;
int save_id;
cd = branch->Lbl->next;
while (cd->cd_id == C_Label)
cd = cd->next;
/*
* Avoid infinite recursion on empty infinite loops.
*/
save_id = branch->cd_id;
branch->cd_id = 0;
if (cd->cd_id == C_Goto)
clps_brch(cd);
branch->cd_id = save_id;
switch (cd->cd_id) {
case C_Goto:
--branch->Lbl->RefCnt;
++cd->Lbl->RefCnt;
branch->Lbl = cd->Lbl;
break;
case C_RetSig:
/*
* This optimization requires that C_Goto have as many fields
* as C_RetSig.
*/
--branch->Lbl->RefCnt;
++cd->SigRef->ref_cnt;
branch->cd_id = C_RetSig;
branch->SigRef = cd->SigRef;
break;
}
}
/*
* rm_unrch - any code after the given point up to the next label is
* unreachable. Remove it.
*/
static void rm_unrch(cd)
struct code *cd;
{
struct code *cd1;
for (cd1 = cd->next; cd1 != NULL && cd1->cd_id != C_LBrack &&
(cd1->cd_id != C_Label || cd1->RefCnt == 0); cd1 = cd1->next) {
if (cd1->cd_id == C_RBrack) {
/*
* Continue deleting past a '}', but don't delete the '}' itself.
*/
cd->next = cd1;
cd1->prev = cd;
cd = cd1;
}
else
dec_refs(cd1);
}
cd->next = cd1;
if (cd1 != NULL)
cd1->prev = cd;
}
/*
* dec_refs - decrement reference counts for things this code references.
*/
static void dec_refs(cd)
struct code *cd;
{
struct sig_act *sa;
if (cd == NULL)
return;
switch (cd->cd_id) {
case C_Goto:
--cd->Lbl->RefCnt;
return;
case C_RetSig:
--cd->SigRef->ref_cnt;
return;
case C_CallSig:
if (cd->Cont != NULL)
--cd->Cont->ref_cnt;
for (sa = cd->SigActs; sa != NULL; sa = sa->next)
dec_refs(sa->cd);
return;
case C_If:
dec_refs(cd->ThenStmt);
return;
case C_Create:
--cd->Cont->ref_cnt;
return;
}
}
/*
* ck_unneed - if there is nothing between a goto and its label, except
* perhaps other labels or '}', it is useless, so remove it.
*/
static struct code *ck_unneed(cd, lbl)
struct code *cd;
struct code *lbl;
{
struct code *cd1;
cd1 = cd->next;
while (cd1 != NULL && (cd1->cd_id == C_Label || cd1->cd_id == C_RBrack)) {
if (cd1 == lbl) {
cd = cd->prev;
cd->next = cd->next->next;
cd->next->prev = cd;
--lbl->RefCnt;
break;
}
cd1 = cd1->next;
}
return cd;
}