diff options
Diffstat (limited to 'src/iconc/fixcode.c')
-rw-r--r-- | src/iconc/fixcode.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/src/iconc/fixcode.c b/src/iconc/fixcode.c new file mode 100644 index 0000000..b8c06e0 --- /dev/null +++ b/src/iconc/fixcode.c @@ -0,0 +1,372 @@ +/* + * 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; + } + |