summaryrefslogtreecommitdiff
path: root/src/iconc/ccode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/iconc/ccode.c')
-rw-r--r--src/iconc/ccode.c4954
1 files changed, 4954 insertions, 0 deletions
diff --git a/src/iconc/ccode.c b/src/iconc/ccode.c
new file mode 100644
index 0000000..108cd15
--- /dev/null
+++ b/src/iconc/ccode.c
@@ -0,0 +1,4954 @@
+/*
+ * ccode.c - routines to produce internal representation of C code.
+ */
+#include "../h/gsupport.h"
+#include "../h/lexdef.h"
+#include "ctrans.h"
+#include "cglobals.h"
+#include "csym.h"
+#include "ccode.h"
+#include "ctree.h"
+#include "ctoken.h"
+#include "cproto.h"
+
+#ifdef OptimizeLit
+
+#define NO_LIMIT 0
+#define LIMITED 1
+#define LIMITED_TO_INT 2
+#define NO_TOUCH 3
+
+struct lit_tbl {
+ int modified;
+ int index;
+ int safe;
+ struct code *initial;
+ struct code *end;
+ struct val_loc *vloc;
+ struct centry *csym;
+ struct lit_tbl *prev;
+ struct lit_tbl *next;
+};
+#endif /* OptimizeLit */
+
+/*
+ * Prototypes for static functions.
+ */
+static struct c_fnc *alc_fnc (void);
+static struct tmplftm *alc_lftm (int num, union field *args);
+static int alc_tmp (int n, struct tmplftm *lifetm_ary);
+
+#ifdef OptimizePoll
+ static int analyze_poll (void);
+ static void remove_poll (void);
+#endif /* OptimizePoll */
+
+#ifdef OptimizeLit
+ static int instr (const char *str, int chr);
+ static void invalidate (struct val_loc *val,struct code *end,int code);
+ static void analyze_literals (struct code *start, struct code *top, int lvl);
+ static int eval_code (struct code *cd, struct lit_tbl *cur);
+ static void propagate_literals (void);
+ static void free_tbl (void);
+ static struct lit_tbl *alc_tbl (void);
+ static void tbl_add (truct lit_tbl *add);
+#endif /* OptimizeLit */
+
+static struct code *asgn_null (struct val_loc *loc1);
+static struct val_loc *bound (struct node *n, struct val_loc *rslt,
+ int catch_fail);
+static struct code *check_var (struct val_loc *d, struct code *lbl);
+static void deref_cd (struct val_loc *src, struct val_loc *dest);
+static void deref_ret (struct val_loc *src, struct val_loc *dest,
+ int subtypes);
+static void endlife (int kind, int indx, int old, nodeptr n);
+static struct val_loc *field_ref(struct node *n, struct val_loc *rslt);
+static struct val_loc *gen_act (nodeptr n, struct val_loc *rslt);
+static struct val_loc *gen_apply(struct node *n, struct val_loc *rslt);
+static struct val_loc *gen_args (struct node *n, int frst_arg, int nargs);
+static struct val_loc *gen_case (struct node *n, struct val_loc *rslt);
+static struct val_loc *gen_creat(struct node *n, struct val_loc *rslt);
+static struct val_loc *gen_lim (struct node *n, struct val_loc *rslt);
+static struct val_loc *gen_scan (struct node *n, struct val_loc *rslt);
+static struct val_loc *gencode (struct node *n, struct val_loc *rslt);
+static struct val_loc *genretval(struct node *n, struct node *expr,
+ struct val_loc *dest);
+static struct val_loc *inv_prc (nodeptr n, struct val_loc *rslt);
+static struct val_loc *inv_op (nodeptr n, struct val_loc *rslt);
+static nodeptr max_lftm (nodeptr n1, nodeptr n2);
+static void mk_callop (char *oper_nm, int ret_flag,
+ struct val_loc *arg1rslt, int nargs,
+ struct val_loc *rslt, int optim);
+static struct code *mk_cpyval (struct val_loc *loc1, struct val_loc *loc2);
+static struct code *new_call (void);
+static char *oper_name (struct implement *impl);
+static void restr_env (struct val_loc *sub_sav, struct val_loc *pos_sav);
+static void save_env (struct val_loc *sub_sav, struct val_loc *pos_sav);
+static void setloc (nodeptr n);
+static struct val_loc *tmp_loc (int n);
+static struct val_loc *var_ref (struct lentry *sym);
+static struct val_loc *vararg_sz(int n);
+
+#define FrstArg 2
+
+/*
+ * Information that must be passed between a loop and its next and break
+ * expressions.
+ */
+struct loop_info {
+ struct code *next_lbl; /* where to branch for a next expression */
+ struct code *end_loop; /* label at end of loop */
+ struct code *on_failure; /* where to go if the loop fails */
+ struct scan_info *scan_info; /* scanning environment upon entering loop */
+ struct val_loc *rslt; /* place to put result of loop */
+ struct c_fnc *succ_cont; /* the success continuation for the loop */
+ struct loop_info *prev; /* link to info for outer loop */
+ };
+
+/*
+ * The allocation status of a temporary variable can either be "in use",
+ * "not allocated", or reserved for use at a code position (indicated
+ * by a specific negative number).
+ */
+#define InUse 1
+#define NotAlc 0
+
+/*
+ * tmplftm is used to precompute lifetime information for use in allocating
+ * temporary variables.
+ */
+struct tmplftm {
+ int cur_status;
+ nodeptr lifetime;
+ };
+
+/*
+ * Places where &subject and &pos are saved during string scanning. "outer"
+ * values are saved when the scanning expression is executed. "inner"
+ * values are saved when the scanning expression suspends.
+ */
+struct scan_info {
+ struct val_loc *outer_sub;
+ struct val_loc *outer_pos;
+ struct val_loc *inner_sub;
+ struct val_loc *inner_pos;
+ struct scan_info *next;
+ };
+
+struct scan_info scan_base = {NULL, 0, NULL, 0, NULL};
+struct scan_info *nxt_scan = &scan_base;
+
+struct val_loc ignore; /* no values, just something to point at */
+static struct val_loc proc_rslt; /* result location for procedure */
+
+int *tmp_status = NULL; /* allocation status of temp descriptor vars */
+int *itmp_status = NULL; /* allocation status of temp C int vars*/
+int *dtmp_status = NULL; /* allocation status of temp C double vars */
+int *sbuf_status = NULL; /* allocation of string buffers */
+int *cbuf_status = NULL; /* allocation of cset buffers */
+int num_tmp; /* number of temp descriptors actually used */
+int num_itmp; /* number of temp C ints actually used */
+int num_dtmp; /* number of temp C doubles actually used */
+int num_sbuf; /* number of string buffers actually used */
+int num_cbuf; /* number of cset buffers actually used */
+int status_sz = 20; /* current size of tmp_status array */
+int istatus_sz = 20; /* current size of itmp_status array */
+int dstatus_sz = 20; /* current size of dtmp_status array */
+int sstatus_sz = 20; /* current size of sbuf_status array */
+int cstatus_sz = 20; /* current size of cbuf_status array */
+struct freetmp *freetmp_pool = NULL;
+
+static char frm_prfx[PrfxSz + 1];/* prefix for procedure frame */
+static char *lastfiln; /* last file name set in code */
+static int lastline; /* last line number set in code */
+
+#ifdef OptimizePoll
+static struct code *lastpoll;
+#endif /* OptimizePoll */
+
+#ifdef OptimizeLit
+static struct lit_tbl *tbl = NULL;
+static struct lit_tbl *free_lit_tbl = NULL;
+#endif /* OptimizeLit */
+
+static struct c_fnc *fnc_lst; /* list of C functions implementing proc */
+static struct c_fnc **flst_end; /* pointer to null pointer at end of fnc_lst */
+struct c_fnc *cur_fnc; /* C function currently being built */
+static int create_lvl = 0; /* co-expression create level */
+
+struct pentry *cur_proc; /* procedure currently being translated */
+
+struct code *on_failure; /* place to go on failure */
+
+static struct code *p_ret_lbl; /* label for procedure return */
+static struct code *p_fail_lbl; /* label for procedure fail */
+struct code *bound_sig; /* bounding signal for current procedure */
+
+/*
+ * statically declared "signals".
+ */
+struct code resume;
+struct code contin;
+struct code fallthru;
+struct code next_fail;
+
+int lbl_seq_num = 0; /* next label sequence number */
+
+#ifdef OptimizeLit
+static void print_tbl(struct lit_tbl *start) {
+ struct lit_tbl *ptr;
+
+ for (ptr=start; ptr != NULL ;ptr=ptr->next) {
+ printf("mod (%2d) strchr (%2d) ",ptr->modified,ptr->index);
+ if (ptr->csym != NULL) {
+ printf("image (%13s) ",ptr->csym->image);
+ }
+ if (ptr->vloc != NULL) {
+ printf("val (%6d) type (%d)",ptr->vloc->u.tmp,ptr->vloc->loc_type);
+ }
+ if (ptr->end == NULL)
+ printf(" END IS NULL");
+ printf("\n");
+ }
+}
+
+
+static void free_tbl() {
+/*
+ struct lit_tbl *ptr, *next;
+*/
+ free_lit_tbl = tbl;
+ tbl = NULL;
+/*
+ ptr = tbl;
+ while (ptr != NULL) {
+ next = ptr->next;
+ free(ptr);
+ ptr = next;
+ }
+ tbl = NULL;
+*/
+}
+
+
+static struct lit_tbl *alc_tbl() {
+ struct lit_tbl *new;
+ static int cnt=0;
+
+
+ if (free_lit_tbl != NULL) {
+ new = free_lit_tbl;
+ free_lit_tbl = new->next;
+ }
+ else
+ new = (struct lit_tbl *)alloc(sizeof(struct lit_tbl));
+ new->modified = NO_LIMIT;
+ new->index = -1;
+ new->safe = 1;
+ new->initial = NULL;
+ new->end = NULL;
+ new->vloc = NULL;
+ new->csym = NULL;
+ new->prev = NULL;
+ new->next = NULL;
+ return new;
+}
+#endif /* OptimizeLit */
+
+/*
+ * proccode - generate code for a procedure.
+ */
+void proccode(proc)
+struct pentry *proc;
+ {
+ struct c_fnc *fnc;
+ struct code *cd;
+ struct code *cd1;
+ struct code *lbl;
+ nodeptr n;
+ nodeptr failer;
+ int gen;
+ int i;
+#ifdef OptimizeLit
+ struct code *procstart;
+#endif /* OptimizeLit */
+
+ /*
+ * Initialize arrays used for allocating temporary variables.
+ */
+ if (tmp_status == NULL)
+ tmp_status = (int *)alloc((unsigned int)(status_sz * sizeof(int)));
+ if (itmp_status == NULL)
+ itmp_status = (int *)alloc((unsigned int)(istatus_sz * sizeof(int)));
+ if (dtmp_status == NULL)
+ dtmp_status = (int *)alloc((unsigned int)(dstatus_sz * sizeof(int)));
+ if (sbuf_status == NULL)
+ sbuf_status = (int *)alloc((unsigned int)(sstatus_sz * sizeof(int)));
+ if (cbuf_status == NULL)
+ cbuf_status = (int *)alloc((unsigned int)(cstatus_sz * sizeof(int)));
+ for (i = 0; i < status_sz; ++i)
+ tmp_status[i] = NotAlloc;
+ for (i = 0; i < istatus_sz; ++i)
+ itmp_status[i] = NotAlloc;
+ for (i = 0; i < dstatus_sz; ++i)
+ dtmp_status[i] = NotAlloc;
+ for (i = 0; i < sstatus_sz; ++i)
+ sbuf_status[i] = NotAlloc;
+ for (i = 0; i < cstatus_sz; ++i)
+ cbuf_status[i] = NotAlloc;
+ num_tmp = 0;
+ num_itmp = 0;
+ num_dtmp = 0;
+ num_sbuf = 0;
+ num_cbuf = 0;
+
+ /*
+ * Initialize standard signals.
+ */
+ resume.cd_id = C_Resume;
+ contin.cd_id = C_Continue;
+ fallthru.cd_id = C_FallThru;
+
+ /*
+ * Initialize procedure result and the transcan locations.
+ */
+ proc_rslt.loc_type = V_PRslt;
+ proc_rslt.mod_access = M_None;
+ ignore.loc_type = V_Ignore;
+ ignore.mod_access = M_None;
+
+ cur_proc = proc; /* current procedure */
+ lastfiln = NULL; /* file name */
+ lastline = 0; /* line number */
+
+#ifdef OptimizePoll
+ lastpoll = NULL;
+#endif /* OptimizePoll */
+
+ /*
+ * Procedure frame prefix is the procedure prefix.
+ */
+ for (i = 0; i < PrfxSz; ++i)
+ frm_prfx[i] = cur_proc->prefix[i];
+ frm_prfx[PrfxSz] = '\0';
+
+ /*
+ * Initialize the continuation list and allocate the outer function for
+ * this procedure.
+ */
+ fnc_lst = NULL;
+ flst_end = &fnc_lst;
+ cur_fnc = alc_fnc();
+
+#ifdef OptimizeLit
+ procstart = cur_fnc->cursor;
+#endif /* OptimizeLit */
+
+ /*
+ * If the procedure is not used anywhere don't generate code for it.
+ * This can happen when using libraries containing several procedures,
+ * but not all are needed. However, if there is a block for the
+ * procedure, we need at least a dummy function.
+ */
+ if (!cur_proc->reachable) {
+ if (!(glookup(cur_proc->name)->flag & F_SmplInv))
+ outerfnc(fnc_lst);
+ return;
+ }
+
+ /*
+ * Allocate labels for the code for procedure failure, procedure return,
+ * and allocate the bounding signal for this procedure (at this point
+ * signals and labels are not distinguished).
+ */
+ p_fail_lbl = alc_lbl("proc fail", 0);
+ p_ret_lbl = alc_lbl("proc return", 0);
+ bound_sig = alc_lbl("bound", 0);
+
+ n = proc->tree;
+ setloc(n);
+ if (Type(Tree1(n)) != N_Empty) {
+ /*
+ * initial clause.
+ */
+ Tree1(n)->lifetime = NULL;
+ liveness(Tree1(n), NULL, &failer, &gen);
+ if (tfatals > 0)
+ return;
+ lbl = alc_lbl("end initial", 0);
+ cd_add(lbl);
+ cur_fnc->cursor = lbl->prev; /* code goes before label */
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(1);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "!first_time";
+ cd->Cond = cd1;
+ cd->ThenStmt = mk_goto(lbl);
+ cd_add(cd);
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "first_time = 0;";
+ cd_add(cd);
+ bound(Tree1(n), &ignore, 1);
+ cur_fnc->cursor = lbl;
+ }
+ Tree2(n)->lifetime = NULL;
+ liveness(Tree2(n), NULL, &failer, &gen);
+ if (tfatals > 0)
+ return;
+ bound(Tree2(n), &ignore, 1);
+
+ /*
+ * Place code to perform procedure failure and return and the
+ * end of the outer function.
+ */
+ setloc(Tree3(n));
+ cd_add(p_fail_lbl);
+ cd = NewCode(0);
+ cd->cd_id = C_PFail;
+ cd_add(cd);
+ cd_add(p_ret_lbl);
+ cd = NewCode(0);
+ cd->cd_id = C_PRet;
+ cd_add(cd);
+
+ /*
+ * Fix up signal handling code and perform peephole optimizations.
+ */
+ fix_fncs(fnc_lst);
+
+#ifdef OptimizeLit
+ analyze_literals(procstart, NULL, 0);
+ propagate_literals();
+#endif /* OptimizeLit */
+
+ /*
+ * The outer function is the first one on the list. It has the
+ * procedure interface; the others are just continuations.
+ */
+ outerfnc(fnc_lst);
+ for (fnc = fnc_lst->next; fnc != NULL; fnc = fnc->next)
+ if (fnc->ref_cnt > 0)
+ prt_fnc(fnc);
+#ifdef OptimizeLit
+ free_tbl();
+#endif /* OptimizeLit */
+}
+
+/*
+ * gencode - generate code for a syntax tree.
+ */
+static struct val_loc *gencode(n, rslt)
+struct node *n;
+struct val_loc *rslt;
+ {
+ struct code *cd;
+ struct code *cd1;
+ struct code *fail_sav;
+ struct code *lbl1;
+ struct code *lbl2;
+ struct code *cursor_sav;
+ struct c_fnc *fnc_sav;
+ struct c_fnc *fnc;
+ struct implement *impl;
+ struct implement *impl1;
+ struct val_loc *r1[3];
+ struct val_loc *r2[2];
+ struct val_loc *frst_arg;
+ struct lentry *single;
+ struct freetmp *freetmp;
+ struct freetmp *ft;
+ struct tmplftm *lifetm_ary;
+ char *sbuf;
+ int i;
+ int tmp_indx;
+ int nargs;
+ static struct loop_info *loop_info = NULL;
+ struct loop_info *li_sav;
+
+ switch (n->n_type) {
+ case N_Activat:
+ rslt = gen_act(n, rslt);
+ break;
+
+ case N_Alt:
+ rslt = chk_alc(rslt, n->lifetime); /* insure a result location */
+
+ fail_sav = on_failure;
+ fnc_sav = cur_fnc;
+
+ /*
+ * If the first alternative fails, execution must go to the
+ * "alt" label.
+ */
+ lbl1 = alc_lbl("alt", 0);
+ on_failure = lbl1;
+
+ cd_add(lbl1);
+ cur_fnc->cursor = lbl1->prev; /* 1st alternative goes before label */
+ gencode(Tree0(n), rslt);
+
+ /*
+ * Each alternative must call the same success continuation.
+ */
+ fnc = alc_fnc();
+ callc_add(fnc);
+
+ cur_fnc = fnc_sav; /* return to the context of the label */
+ cur_fnc->cursor = lbl1; /* 2nd alternative goes after label */
+ on_failure = fail_sav; /* on failure, alternation fails */
+ gencode(Tree1(n), rslt);
+ callc_add(fnc); /* call continuation */
+
+ /*
+ * Code following the alternation goes in the continuation. If
+ * the code fails, the continuation returns the resume signal.
+ */
+ cur_fnc = fnc;
+ on_failure = &resume;
+ break;
+
+ case N_Apply:
+ rslt = gen_apply(n, rslt);
+ break;
+
+ case N_Augop:
+ impl = Impl0(n); /* assignment */
+ impl1 = Impl1(n); /* the operation */
+ if (impl == NULL || impl1 == NULL) {
+ rslt = &ignore; /* make sure code generation can continue */
+ break;
+ }
+
+ /*
+ * allocate an argument list for the operation.
+ */
+ lifetm_ary = alc_lftm(2, &n->n_field[2]);
+ tmp_indx = alc_tmp(2, lifetm_ary);
+ r1[0] = tmp_loc(tmp_indx);
+ r1[1] = tmp_loc(tmp_indx + 1);
+
+ gencode(Tree2(n), r1[0]); /* first argument */
+
+ /*
+ * allocate an argument list for the assignment and copy the
+ * value of the first argument into it.
+ */
+ lifetm_ary[0].cur_status = InUse;
+ lifetm_ary[1].cur_status = n->postn;
+ lifetm_ary[1].lifetime = n->intrnl_lftm;
+ tmp_indx = alc_tmp(2, lifetm_ary);
+ r2[0] = tmp_loc(tmp_indx++);
+ cd_add(mk_cpyval(r2[0], r1[0]));
+ r2[1] = tmp_loc(tmp_indx);
+
+ gencode(Tree3(n), r1[1]); /* second argument */
+
+ /*
+ * Produce code for the operation.
+ */
+ setloc(n);
+ implproto(impl1);
+ mk_callop(oper_name(impl1), impl1->ret_flag, r1[0], 2, r2[1], 0);
+
+ /*
+ * Produce code for the assignment.
+ */
+ implproto(impl);
+ if (impl->ret_flag & (DoesRet | DoesSusp))
+ rslt = chk_alc(rslt, n->lifetime);
+ mk_callop(oper_name(impl), impl->ret_flag, r2[0], 2, rslt, 0);
+
+ free((char *)lifetm_ary);
+ break;
+
+ case N_Bar: {
+ struct val_loc *fail_flg;
+
+ /*
+ * Allocate an integer variable to keep track of whether the
+ * repeated alternation should fail when execution reaches
+ * the top of its loop, and generate code to initialize the
+ * variable to 0.
+ */
+ fail_flg = itmp_loc(alc_itmp(n->intrnl_lftm));
+ cd = alc_ary(2);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = fail_flg;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = " = 0;";
+ cd_add(cd);
+
+ /*
+ * Code at the top of the repeated alternation loop checks
+ * the failure flag.
+ */
+ lbl1 = alc_lbl("rep alt", 0);
+ cd_add(lbl1);
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(1);
+ cd1->ElemTyp(0) = A_ValLoc;
+ cd1->ValLoc(0) = fail_flg;
+ cd->Cond = cd1;
+ cd->ThenStmt = sig_cd(on_failure, cur_fnc);
+ cd_add(cd);
+
+ /*
+ * If the expression fails without producing a value, the
+ * repeated alternation must fail.
+ */
+ cd = alc_ary(2);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = fail_flg;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = " = 1;";
+ cd_add(cd);
+
+ /*
+ * Generate code for the repeated expression. If it produces
+ * a value before before backtracking occurs, the loop is
+ * repeated as indicated by the value of the failure flag.
+ */
+ on_failure = lbl1;
+ rslt = gencode(Tree0(n), rslt);
+ cd = alc_ary(2);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = fail_flg;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = " = 0;";
+ cd_add(cd);
+ }
+ break;
+
+ case N_Break:
+ if (loop_info == NULL) {
+ nfatal(n, "invalid context for a break expression", NULL);
+ rslt = &ignore;
+ break;
+ }
+
+ /*
+ * If the break is in a different string scanning context from the
+ * loop itself, generate code to restore the scanning environment.
+ */
+ if (nxt_scan != loop_info->scan_info)
+ restr_env(loop_info->scan_info->outer_sub,
+ loop_info->scan_info->outer_pos);
+
+
+ if (Tree0(n)->n_type == N_Empty && loop_info->rslt == &ignore) {
+ /*
+ * The break has no associated expression and the loop needs
+ * no value, so just branch out of the loop.
+ */
+ cd_add(sig_cd(loop_info->end_loop, cur_fnc));
+ }
+ else {
+ /*
+ * The code for the expression associated with the break is
+ * actually placed at the end of the loop. Go there and
+ * add a label to branch to.
+ */
+ cursor_sav = cur_fnc->cursor;
+ fnc_sav = cur_fnc;
+ fail_sav = on_failure;
+ cur_fnc = loop_info->end_loop->Container;
+ cur_fnc->cursor = loop_info->end_loop->prev;
+ on_failure = loop_info->on_failure;
+ lbl1 = alc_lbl("break", 0);
+ cd_add(lbl1);
+
+ /*
+ * Make sure a result location has been allocated for the
+ * loop, restore the loop information for the next outer
+ * loop, generate code for the break expression, then
+ * restore the loop information for this loop.
+ */
+ loop_info->rslt = chk_alc(loop_info->rslt, Tree0(n)->lifetime);
+ li_sav = loop_info;
+ loop_info = loop_info->prev;
+ gencode(Tree0(n), li_sav->rslt);
+ loop_info = li_sav;
+
+ /*
+ * If this or another break expression suspends so we cannot
+ * just branch to the end of the loop, all breaks must
+ * call a common continuation.
+ */
+ if (cur_fnc->cursor->next != loop_info->end_loop &&
+ loop_info->succ_cont == NULL)
+ loop_info->succ_cont = alc_fnc();
+ if (loop_info->succ_cont == NULL)
+ cd_add(mk_goto(loop_info->end_loop)); /* go to end of loop */
+ else
+ callc_add(loop_info->succ_cont); /* call continuation */
+
+ /*
+ * Return to the location of the break and generate a branch to
+ * the code for its associated expression.
+ */
+ cur_fnc = fnc_sav;
+ cur_fnc->cursor = cursor_sav;
+ on_failure = fail_sav;
+ cd_add(sig_cd(lbl1, cur_fnc));
+ }
+ rslt = &ignore; /* shouldn't be used but must be something valid */
+ break;
+
+ case N_Case:
+ rslt = gen_case(n, rslt);
+ break;
+
+ case N_Create:
+ rslt = gen_creat(n, rslt);
+ break;
+
+ case N_Cset:
+ case N_Int:
+ case N_Real:
+ case N_Str:
+ cd = NewCode(2);
+ cd->cd_id = C_Lit;
+ rslt = chk_alc(rslt, n->lifetime);
+ cd->Rslt = rslt;
+ cd->Literal = CSym0(n);
+ cd_add(cd);
+ break;
+
+ case N_Empty:
+ /*
+ * Assume null value is needed.
+ */
+ if (rslt == &ignore)
+ break;
+ rslt = chk_alc(rslt, n->lifetime);
+ cd_add(asgn_null(rslt));
+ break;
+
+ case N_Field:
+ rslt = field_ref(n, rslt);
+ break;
+
+ case N_Id:
+ /*
+ * If the variable reference is not going to be used, don't bother
+ * building it.
+ */
+ if (rslt == &ignore)
+ break;
+ cd = NewCode(2);
+ cd->cd_id = C_NamedVar;
+ rslt = chk_alc(rslt, n->lifetime);
+ cd->Rslt = rslt;
+ cd->NamedVar = LSym0(n);
+ cd_add(cd);
+ break;
+
+ case N_If:
+ if (Type(Tree2(n)) == N_Empty) {
+ /*
+ * if-then. Control clause is bounded, but otherwise trivial.
+ */
+ bound(Tree0(n), &ignore, 0); /* control clause */
+ rslt = gencode(Tree1(n), rslt); /* then clause */
+ }
+ else {
+ /*
+ * if-then-else. Establish an "else" label as the failure
+ * label of the bounded control clause.
+ */
+ fail_sav = on_failure;
+ fnc_sav = cur_fnc;
+ lbl1 = alc_lbl("else", 0);
+ on_failure = lbl1;
+
+ bound(Tree0(n), &ignore, 0); /* control clause */
+
+ cd_add(lbl1);
+ cur_fnc->cursor = lbl1->prev; /* then clause goes before else lbl */
+ on_failure = fail_sav;
+ rslt = chk_alc(rslt, n->lifetime);
+ gencode(Tree1(n), rslt); /* then clause */
+
+ /*
+ * If the then clause is not a generator, execution can
+ * just go to the end of the if-then-else expression. If it
+ * is a generator, the continuation for the expression must be
+ * in a separate function.
+ */
+ if (cur_fnc->cursor->next == lbl1) {
+ fnc = NULL;
+ lbl2 = alc_lbl("end if", 0);
+ cd_add(mk_goto(lbl2));
+ cur_fnc->cursor = lbl1;
+ cd_add(lbl2);
+ }
+ else {
+ lbl2 = NULL;
+ fnc = alc_fnc();
+ callc_add(fnc);
+ cur_fnc = fnc_sav;
+ }
+
+ cur_fnc->cursor = lbl1; /* else clause goes after label */
+ on_failure = fail_sav;
+ gencode(Tree2(n), rslt); /* else clause */
+
+ /*
+ * If the else clause is not a generator, execution is at
+ * the end of the if-then-else expression, but the if clause
+ * may have forced the continuation to be in a separate function.
+ * If the else clause is a generator, it forces the continuation
+ * to be in a separate function.
+ */
+ if (fnc == NULL) {
+ if (cur_fnc->cursor->next == lbl2)
+ cur_fnc->cursor = lbl2;
+ else {
+ fnc = alc_fnc();
+ callc_add(fnc);
+ /*
+ * The then clause is not a generator, so it has branched
+ * to lbl2. We must add a call to the continuation there.
+ */
+ cur_fnc = fnc_sav;
+ cur_fnc->cursor = lbl2;
+ on_failure = fail_sav;
+ callc_add(fnc);
+ }
+ }
+ else
+ callc_add(fnc);
+
+ if (fnc != NULL) {
+ /*
+ * We produced a continuation for the if-then-else, so code
+ * generation must proceed in it.
+ */
+ cur_fnc = fnc;
+ on_failure = &resume;
+ }
+ }
+ break;
+
+ case N_Invok:
+ /*
+ * General invocation.
+ */
+ nargs = Val0(n);
+ if (Tree1(n)->n_type == N_Empty) {
+ /*
+ * Mutual evaluation.
+ */
+ for (i = 2; i <= nargs; ++i)
+ gencode(n->n_field[i].n_ptr, &ignore); /* arg i - 1 */
+ rslt = chk_alc(rslt, n->lifetime);
+ gencode(n->n_field[nargs + 1].n_ptr, rslt); /* last argument */
+ }
+ else {
+ ++nargs; /* consider the procedure an argument to invoke() */
+ frst_arg = gen_args(n, 1, nargs);
+ setloc(n);
+ /*
+ * Assume this operation uses its result location as a work
+ * area. Give it a location that is tended, where the value
+ * is retained as long as the operation can be resumed.
+ */
+ if (rslt == &ignore)
+ rslt = NULL; /* force allocation of temporary */
+ rslt = chk_alc(rslt, max_lftm(n->lifetime, n->intrnl_lftm));
+ mk_callop( "invoke", DoesRet | DoesFail | DoesSusp, frst_arg, nargs,
+ rslt, 0);
+ }
+ break;
+
+ case N_InvOp:
+ rslt = inv_op(n, rslt);
+ break;
+
+ case N_InvProc:
+ rslt = inv_prc(n, rslt);
+ break;
+
+ case N_InvRec: {
+ /*
+ * Directly invoke a record constructor.
+ */
+ struct rentry *rec;
+
+ nargs = Val0(n); /* number of arguments */
+ frst_arg = gen_args(n, 2, nargs);
+ setloc(n);
+ rec = Rec1(n);
+
+ rslt = chk_alc(rslt, n->lifetime);
+
+ /*
+ * If error conversion can occur then the record constructor may
+ * fail and we must check the signal.
+ */
+ if (err_conv) {
+ sbuf = (char *)alloc((unsigned int)(strlen(rec->name) +
+ strlen("signal = R_") + PrfxSz + 1));
+ sprintf(sbuf, "signal = R%s_%s(", rec->prefix, rec->name);
+ }
+ else {
+ sbuf = (char *)alloc((unsigned int)(strlen(rec->name) + PrfxSz +4));
+ sprintf(sbuf, "R%s_%s(", rec->prefix, rec->name);
+ }
+ cd = alc_ary(9);
+ cd->ElemTyp(0) = A_Str; /* constructor name */
+ cd->Str(0) = sbuf;
+ cd->ElemTyp(1) = A_Intgr; /* number of arguments */
+ cd->Intgr(1) = nargs;
+ cd->ElemTyp(2) = A_Str; /* , */
+ cd->Str(2) = ", ";
+ if (frst_arg == NULL) { /* location of first argument */
+ cd->ElemTyp(3) = A_Str;
+ cd->Str(3) = "NULL";
+ cd->ElemTyp(4) = A_Str;
+ cd->Str(4) = "";
+ }
+ else {
+ cd->ElemTyp(3) = A_Str;
+ cd->Str(3) = "&";
+ cd->ElemTyp(4) = A_ValLoc;
+ cd->ValLoc(4) = frst_arg;
+ }
+ cd->ElemTyp(5) = A_Str; /* , */
+ cd->Str(5) = ", ";
+ cd->ElemTyp(6) = A_Str; /* location of result */
+ cd->Str(6) = "&";
+ cd->ElemTyp(7) = A_ValLoc;
+ cd->ValLoc(7) = rslt;
+ cd->ElemTyp(8) = A_Str;
+ cd->Str(8) = ");";
+ cd_add(cd);
+ if (err_conv) {
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(1);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "signal == A_Resume";
+ cd->Cond = cd1;
+ cd->ThenStmt = sig_cd(on_failure, cur_fnc);
+ cd_add(cd);
+ }
+ }
+ break;
+
+ case N_Limit:
+ rslt = gen_lim(n, rslt);
+ break;
+
+ case N_Loop: {
+ struct loop_info li;
+
+ /*
+ * Set up loop information for use by break and next expressions.
+ */
+ li.end_loop = alc_lbl("end loop", 0);
+ cd_add(li.end_loop);
+ cur_fnc->cursor = li.end_loop->prev; /* loop goes before label */
+ li.rslt = rslt;
+ li.on_failure = on_failure;
+ li.scan_info = nxt_scan;
+ li.succ_cont = NULL;
+ li.prev = loop_info;
+ loop_info = &li;
+
+ switch ((int)Val0(Tree0(n))) {
+ case EVERY:
+ /*
+ * "next" in the control clause just fails.
+ */
+ li.next_lbl = &next_fail;
+ gencode(Tree1(n), &ignore); /* control clause */
+ /*
+ * "next" in the do clause transfers control to the
+ * statement at the end of the loop that resumes the
+ * control clause.
+ */
+ li.next_lbl = alc_lbl("next", 0);
+ bound(Tree2(n), &ignore, 1); /* do clause */
+ cd_add(li.next_lbl);
+ cd_add(sig_cd(on_failure, cur_fnc)); /* resume control clause */
+ break;
+
+ case REPEAT:
+ li.next_lbl = alc_lbl("repeat", 0);
+ cd_add(li.next_lbl);
+ bound(Tree1(n), &ignore, 1);
+ cd_add(mk_goto(li.next_lbl));
+ break;
+
+ case SUSPEND: /* suspension expression */
+ if (create_lvl > 0) {
+ nfatal(n, "invalid context for suspend", NULL);
+ return &ignore;
+ }
+ /*
+ * "next" in the control clause just fails. The result
+ * of the control clause goes in the procedure return
+ * location.
+ */
+ li.next_lbl = &next_fail;
+ genretval(n, Tree1(n), &proc_rslt);
+
+ /*
+ * If necessary, swap scanning environments before suspending.
+ * if there is no success continuation, just return.
+ */
+ if (nxt_scan != &scan_base) {
+ save_env(scan_base.inner_sub, scan_base.inner_pos);
+ restr_env(scan_base.outer_sub, scan_base.outer_pos);
+ }
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(2);
+ cd1->ElemTyp(0) = A_ProcCont;
+ cd1->ElemTyp(1) = A_Str;
+ cd1->Str(1) = " == NULL";
+ cd->Cond = cd1;
+ cd->ThenStmt = sig_cd(p_ret_lbl, cur_fnc);
+ cd_add(cd);
+ cd = NewCode(0);
+ cd->cd_id = C_PSusp;
+ cd_add(cd);
+ cur_fnc->flag |= CF_ForeignSig;
+
+ /*
+ * Force updating file name and line number, and if needed,
+ * switch scanning environments before resuming.
+ */
+ lastfiln = NULL;
+ lastline = 0;
+ if (nxt_scan != &scan_base) {
+ save_env(scan_base.outer_sub, scan_base.outer_pos);
+ restr_env(scan_base.inner_sub, scan_base.inner_pos);
+ }
+
+ /*
+ * "next" in the do clause transfers control to the
+ * statement at the end of the loop that resumes the
+ * control clause.
+ */
+ li.next_lbl = alc_lbl("next", 0);
+ bound(Tree2(n), &ignore, 1); /* do clause */
+ cd_add(li.next_lbl);
+ cd_add(sig_cd(on_failure, cur_fnc));
+ break;
+
+ case WHILE:
+ li.next_lbl = alc_lbl("while", 0);
+ cd_add(li.next_lbl);
+ /*
+ * The control clause and do clause are both bounded expressions,
+ * but only the do clause establishes a new failure label.
+ */
+ bound(Tree1(n), &ignore, 0); /* control clause */
+ bound(Tree2(n), &ignore, 1); /* do clause */
+ cd_add(mk_goto(li.next_lbl));
+ break;
+
+ case UNTIL:
+ fail_sav = on_failure;
+ li.next_lbl = alc_lbl("until", 0);
+ cd_add(li.next_lbl);
+
+ /*
+ * If the control clause fails, execution continues in
+ * the loop.
+ */
+ if (Type(Tree2(n)) == N_Empty)
+ on_failure = li.next_lbl;
+ else {
+ lbl2 = alc_lbl("do", 0);
+ on_failure = lbl2;
+ cd_add(lbl2);
+ cur_fnc->cursor = lbl2->prev; /* control before label */
+ }
+ bound(Tree1(n), &ignore, 0); /* control clause */
+
+ /*
+ * If the control clause succeeds, the loop fails.
+ */
+ cd_add(sig_cd(fail_sav, cur_fnc));
+
+ if (Type(Tree2(n)) != N_Empty) {
+ /*
+ * Do clause goes after the label and the loop repeats.
+ */
+ cur_fnc->cursor = lbl2;
+ bound(Tree2(n), &ignore, 1); /* do clause */
+ cd_add(mk_goto(li.next_lbl));
+ }
+ break;
+ }
+
+ /*
+ * Go to the end of the loop and see if the loop's success continuation
+ * is in a separate function.
+ */
+ cur_fnc = li.end_loop->Container;
+ cur_fnc->cursor = li.end_loop;
+ if (li.succ_cont != NULL) {
+ callc_add(li.succ_cont);
+ cur_fnc = li.succ_cont;
+ on_failure = &resume;
+ }
+ if (li.rslt == NULL)
+ rslt = &ignore; /* shouldn't be used but must be something valid */
+ else
+ rslt = li.rslt;
+ loop_info = li.prev;
+ break;
+ }
+
+ case N_Next:
+ /*
+ * In some contexts "next" just fails. In other contexts it
+ * transfers control to a label, in which case it may have
+ * to restore a scanning environment.
+ */
+ if (loop_info == NULL)
+ nfatal(n, "invalid context for a next expression", NULL);
+ else if (loop_info->next_lbl == &next_fail)
+ cd_add(sig_cd(on_failure, cur_fnc));
+ else {
+ if (nxt_scan != loop_info->scan_info)
+ restr_env(loop_info->scan_info->outer_sub,
+ loop_info->scan_info->outer_pos);
+ cd_add(sig_cd(loop_info->next_lbl, cur_fnc));
+ }
+ rslt = &ignore; /* shouldn't be used but must be something valid */
+ break;
+
+ case N_Not:
+ lbl1 = alc_lbl("not", 0);
+ fail_sav = on_failure;
+ on_failure = lbl1;
+ cd_add(lbl1);
+ cur_fnc->cursor = lbl1->prev; /* code goes before label */
+ bound(Tree0(n), &ignore, 0);
+ on_failure = fail_sav;
+ cd_add(sig_cd(on_failure, cur_fnc)); /* convert success to failure */
+ cur_fnc->cursor = lbl1; /* convert failure to null */
+ if (rslt != &ignore) {
+ rslt = chk_alc(rslt, n->lifetime);
+ cd_add(asgn_null(rslt));
+ }
+ break;
+
+ case N_Ret:
+ if (create_lvl > 0) {
+ nfatal(n, "invalid context for return or fail", NULL);
+ return &ignore;
+ }
+ if (Val0(Tree0(n)) == RETURN) {
+ /*
+ * Set up the failure action of the return expression to do a
+ * procedure fail.
+ */
+ if (nxt_scan != &scan_base) {
+ /*
+ * we must switch scanning environments if the expression fails.
+ */
+ lbl1 = alc_lbl("return fail", 0);
+ cd_add(lbl1);
+ restr_env(scan_base.outer_sub, scan_base.outer_pos);
+ cd_add(sig_cd(p_fail_lbl, cur_fnc));
+ cur_fnc->cursor = lbl1->prev; /* code goes before label */
+ on_failure = lbl1;
+ }
+ else
+ on_failure = p_fail_lbl;
+
+ /*
+ * Produce code to place return value in procedure result location.
+ */
+ genretval(n, Tree1(n), &proc_rslt);
+
+ /*
+ * See if a scanning environment must be restored and
+ * transfer control to the procedure return code.
+ */
+ if (nxt_scan != &scan_base)
+ restr_env(scan_base.outer_sub, scan_base.outer_pos);
+ cd_add(sig_cd(p_ret_lbl, cur_fnc));
+ }
+ else {
+ /*
+ * fail. See if a scanning environment must be restored and
+ * transfer control to the procedure failure code.
+ */
+ if (nxt_scan != &scan_base)
+ restr_env(scan_base.outer_sub, scan_base.outer_pos);
+ cd_add(sig_cd(p_fail_lbl, cur_fnc));
+ }
+ rslt = &ignore; /* shouldn't be used but must be something valid */
+ break;
+
+ case N_Scan:
+ rslt = gen_scan(n, rslt);
+ break;
+
+ case N_Sect:
+ /*
+ * x[i+:j] or x[i-:j] (x[i:j] handled as ordinary operator)
+ */
+ impl1 = Impl0(n); /* sectioning */
+ if (impl1 == NULL) {
+ rslt = &ignore; /* make sure code generation can continue */
+ break;
+ }
+ implproto(impl1);
+
+ impl = Impl1(n); /* plus or minus */
+ /*
+ * Allocate work area of temporary variables for sectioning.
+ */
+ lifetm_ary = alc_lftm(3, NULL);
+ lifetm_ary[0].cur_status = Tree2(n)->postn;
+ lifetm_ary[0].lifetime = n->intrnl_lftm;
+ lifetm_ary[1].cur_status = Tree3(n)->postn;
+ lifetm_ary[1].lifetime = n->intrnl_lftm;
+ lifetm_ary[2].cur_status = n->postn;
+ lifetm_ary[2].lifetime = n->intrnl_lftm;
+ tmp_indx = alc_tmp(3, lifetm_ary);
+ for (i = 0; i < 3; ++i)
+ r1[i] = tmp_loc(tmp_indx++);
+ gencode(Tree2(n), r1[0]); /* generate code to compute x */
+ gencode(Tree3(n), r1[1]); /* generate code compute i */
+
+ /*
+ * Allocate work area of temporary variables for arithmetic.
+ */
+ lifetm_ary[0].cur_status = InUse;
+ lifetm_ary[0].lifetime = Tree3(n)->lifetime;
+ lifetm_ary[1].cur_status = Tree4(n)->postn;
+ lifetm_ary[1].lifetime = Tree4(n)->lifetime;
+ tmp_indx = alc_tmp(2, lifetm_ary);
+ for (i = 0; i < 2; ++i)
+ r2[i] = tmp_loc(tmp_indx++);
+ cd_add(mk_cpyval(r2[0], r1[1])); /* generate code to copy i */
+ gencode(Tree4(n), r2[1]); /* generate code to compute j */
+
+ /*
+ * generate code for i op j.
+ */
+ setloc(n);
+ implproto(impl);
+ mk_callop(oper_name(impl), impl->ret_flag, r2[0], 2, r1[2], 0);
+
+ /*
+ * generate code for x[i : (i op j)]
+ */
+ rslt = chk_alc(rslt, n->lifetime);
+ mk_callop(oper_name(impl1),impl1->ret_flag,r1[0],3,rslt,0);
+ free((char *)lifetm_ary);
+ break;
+
+ case N_Slist:
+ bound(Tree0(n), &ignore, 1);
+ rslt = gencode(Tree1(n), rslt);
+ break;
+
+ case N_SmplAsgn: {
+ struct val_loc *var, *val;
+
+ /*
+ * Optimized assignment to a named variable. Use information
+ * from type inferencing to determine if the right-hand-side
+ * is a variable.
+ */
+ var = var_ref(LSym0(Tree2(n)));
+ if (HasVar(varsubtyp(Tree3(n)->type, &single)))
+ Val0(n) = AsgnDeref;
+ if (single != NULL) {
+ /*
+ * Right-hand-side results in a named variable. Compute
+ * the expression but don't bother saving the result, we
+ * know what it is. Assignment just copies value from
+ * one variable to the other.
+ */
+ gencode(Tree3(n), &ignore);
+ val = var_ref(single);
+ cd_add(mk_cpyval(var, val));
+ }
+ else switch (Val0(n)) {
+ case AsgnDirect:
+ /*
+ * It is safe to compute the result directly into the variable.
+ */
+ gencode(Tree3(n), var);
+ break;
+ case AsgnCopy:
+ /*
+ * The result is not a variable reference, but it is not
+ * safe to compute it into the variable, we must use a
+ * temporary variable.
+ */
+ val = gencode(Tree3(n), NULL);
+ cd_add(mk_cpyval(var, val));
+ break;
+ case AsgnDeref:
+ /*
+ * We must dereference the result into the variable.
+ */
+ val = gencode(Tree3(n), NULL);
+ deref_cd(val, var);
+ break;
+ }
+
+ /*
+ * If the assignment has to produce a result, construct the
+ * variable reference.
+ */
+ if (rslt != &ignore)
+ rslt = gencode(Tree2(n), rslt);
+ }
+ break;
+
+ case N_SmplAug: {
+ /*
+ * Optimized augmented assignment to a named variable.
+ */
+ struct val_loc *var, *val;
+
+ impl = Impl1(n); /* the operation */
+ if (impl == NULL) {
+ rslt = &ignore; /* make sure code generation can continue */
+ break;
+ }
+
+ implproto(impl); /* insure prototype for operation */
+
+ /*
+ * Generate code to compute the arguments for the operation.
+ */
+ frst_arg = gen_args(n, 2, 2);
+ setloc(n);
+
+ /*
+ * Use information from type inferencing to determine if the
+ * operation produces a variable.
+ */
+ if (HasVar(varsubtyp(Typ4(n), &single)))
+ Val0(n) = AsgnDeref;
+ var = var_ref(LSym0(Tree2(n)));
+ if (single != NULL) {
+ /*
+ * The operation results in a named variable. Call the operation
+ * but don't bother saving the result, we know what it is.
+ * Assignment just copies value from one variable to the other.
+ */
+ mk_callop(oper_name(impl), impl->ret_flag, frst_arg, 2,
+ &ignore, 0);
+ val = var_ref(single);
+ cd_add(mk_cpyval(var, val));
+ }
+ else switch (Val0(n)) {
+ case AsgnDirect:
+ /*
+ * It is safe to compute the result directly into the variable.
+ */
+ mk_callop(oper_name(impl), impl->ret_flag, frst_arg, 2,
+ var, 0);
+ break;
+ case AsgnCopy:
+ /*
+ * The result is not a variable reference, but it is not
+ * safe to compute it into the variable, we must use a
+ * temporary variable.
+ */
+ val = chk_alc(NULL, n);
+ mk_callop(oper_name(impl), impl->ret_flag, frst_arg, 2, val, 0);
+ cd_add(mk_cpyval(var, val));
+ break;
+ case AsgnDeref:
+ /*
+ * We must dereference the result into the variable.
+ */
+ val = chk_alc(NULL, n);
+ mk_callop(oper_name(impl), impl->ret_flag, frst_arg, 2, val, 0);
+ deref_cd(val, var);
+ break;
+ }
+
+ /*
+ * If the assignment has to produce a result, construct the
+ * variable reference.
+ */
+ if (rslt != &ignore)
+ rslt = gencode(Tree2(n), rslt);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "compiler error: node type %d unknown\n", n->n_type);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Free any temporaries whose lifetime ends at this node.
+ */
+ freetmp = n->freetmp;
+ while (freetmp != NULL) {
+ switch (freetmp->kind) {
+ case DescTmp:
+ tmp_status[freetmp->indx] = freetmp->old;
+ break;
+ case CIntTmp:
+ itmp_status[freetmp->indx] = freetmp->old;
+ break;
+ case CDblTmp:
+ dtmp_status[freetmp->indx] = freetmp->old;
+ break;
+ case SBuf:
+ sbuf_status[freetmp->indx] = freetmp->old;
+ break;
+ case CBuf:
+ cbuf_status[freetmp->indx] = freetmp->old;
+ break;
+ }
+ ft = freetmp->next;
+ freetmp->next = freetmp_pool;
+ freetmp_pool = freetmp;
+ freetmp = ft;
+ }
+ return rslt;
+ }
+
+/*
+ * chk_alc - make sure a result location has been allocated. If it is
+ * a temporary variable, indicate that it is now in use.
+ */
+struct val_loc *chk_alc(rslt, lifetime)
+struct val_loc *rslt;
+nodeptr lifetime;
+ {
+ struct tmplftm tmplftm;
+
+ if (rslt == NULL) {
+ if (lifetime == NULL)
+ rslt = &ignore;
+ else {
+ tmplftm.cur_status = InUse;
+ tmplftm.lifetime = lifetime;
+ rslt = tmp_loc(alc_tmp(1, &tmplftm));
+ }
+ }
+ else if (rslt->loc_type == V_Temp)
+ tmp_status[rslt->u.tmp] = InUse;
+ return rslt;
+ }
+
+/*
+ * mk_goto - make a code structure for goto label
+ */
+struct code *mk_goto(label)
+struct code *label;
+ {
+ register struct code *cd;
+
+ cd = NewCode(1); /* # fields == # fields of C_RetSig & C_Break */
+ cd->cd_id = C_Goto;
+ cd->next = NULL;
+ cd->prev = NULL;
+ cd->Lbl = label;
+ ++label->RefCnt;
+ return cd;
+ }
+
+/*
+ * mk_cpyval - make code to copy a value from one location to another.
+ */
+static struct code *mk_cpyval(loc1, loc2)
+struct val_loc *loc1;
+struct val_loc *loc2;
+ {
+ struct code *cd;
+
+ cd = alc_ary(4);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = loc1;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = " = ";
+ cd->ElemTyp(2) = A_ValLoc;
+ cd->ValLoc(2) = loc2;
+ cd->ElemTyp(3) = A_Str;
+ cd->Str(3) = ";";
+ return cd;
+ }
+
+/*
+ * asgn_null - make code to assign the null value to a location.
+ */
+static struct code *asgn_null(loc1)
+struct val_loc *loc1;
+ {
+ struct code *cd;
+
+ cd = alc_ary(2);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = loc1;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = " = nulldesc;";
+ return cd;
+ }
+
+/*
+ * oper_name - create the name for the most general implementation of an Icon
+ * operation.
+ */
+static char *oper_name(impl)
+struct implement *impl;
+ {
+ char *sbuf;
+
+ sbuf = (char *)alloc((unsigned int)(strlen(impl->name) + 5));
+ sprintf(sbuf, "%c%c%c_%s", impl->oper_typ, impl->prefix[0], impl->prefix[1],
+ impl->name);
+ return sbuf;
+ }
+
+/*
+ * gen_args - generate code to evaluate an argument list.
+ */
+static struct val_loc *gen_args(n, frst_arg, nargs)
+struct node *n;
+int frst_arg;
+int nargs;
+ {
+ struct tmplftm *lifetm_ary;
+ int i;
+ int tmp_indx;
+
+ if (nargs == 0)
+ return NULL;
+
+ lifetm_ary = alc_lftm(nargs, &n->n_field[frst_arg]);
+ tmp_indx = alc_tmp(nargs, lifetm_ary);
+ for (i = 0; i < nargs; ++i)
+ gencode(n->n_field[frst_arg + i].n_ptr, tmp_loc(tmp_indx + i));
+ free((char *)lifetm_ary);
+ return tmp_loc(tmp_indx);
+ }
+
+/*
+ * gen_case - generate code for a case expression.
+ */
+static struct val_loc *gen_case(n, rslt)
+struct node *n;
+struct val_loc *rslt;
+ {
+ struct node *control;
+ struct node *cases;
+ struct node *deflt;
+ struct node *clause;
+ struct val_loc *r1;
+ struct val_loc *r2;
+ struct val_loc *r3;
+ struct code *cd;
+ struct code *cd1;
+ struct code *fail_sav;
+ struct code *skp_lbl;
+ struct code *cd_lbl;
+ struct code *end_lbl;
+ struct c_fnc *fnc_sav;
+ struct c_fnc *succ_cont = NULL;
+
+ control = Tree0(n);
+ cases = Tree1(n);
+ deflt = Tree2(n);
+
+ /*
+ * The control clause is bounded.
+ */
+ r1 = chk_alc(NULL, n);
+ bound(control, r1, 0);
+
+ /*
+ * Remember the context in which the case expression occurs and
+ * establish a label at the end of the expression.
+ */
+ fail_sav = on_failure;
+ fnc_sav = cur_fnc;
+ end_lbl = alc_lbl("end case", 0);
+ cd_add(end_lbl);
+ cur_fnc->cursor = end_lbl->prev; /* generate code before the end label */
+
+ /*
+ * All cases share the result location of the case expression.
+ */
+ rslt = chk_alc(rslt, n->lifetime);
+ r2 = chk_alc(NULL, n); /* for result of selection clause */
+ r3 = chk_alc(NULL, n); /* for dereferenced result of control clause */
+
+ while (cases != NULL) {
+ /*
+ * See if we are at the end of the case clause list.
+ */
+ if (cases->n_type == N_Ccls) {
+ clause = cases;
+ cases = NULL;
+ }
+ else {
+ clause = Tree1(cases);
+ cases = Tree0(cases);
+ }
+
+ /*
+ * If the evaluation of the selection code or the comparison of
+ * its value to the control clause fail, execution will proceed
+ * to the "skip clause" label and on to the next case.
+ */
+ skp_lbl = alc_lbl("skip clause", 0);
+ on_failure = skp_lbl;
+ cd_add(skp_lbl);
+ cur_fnc->cursor = skp_lbl->prev; /* generate code before end label */
+
+ /*
+ * Bound the selection code for this clause.
+ */
+ cd_lbl = alc_lbl("selected code", Bounding);
+ cd_add(cd_lbl);
+ cur_fnc->cursor = cd_lbl->prev;
+ gencode(Tree0(clause), r2);
+
+ /*
+ * Dereference the results of the control clause and the selection
+ * clause and compare them.
+ */
+ setloc(clause);
+ deref_cd(r1, r3);
+ deref_cd(r2, r2);
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(5);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "!equiv(&";
+ cd1->ElemTyp(1) = A_ValLoc;
+ cd1->ValLoc(1) = r3;
+ cd->Cond = cd1;
+ cd1->ElemTyp(2) = A_Str;
+ cd1->Str(2) = ", &";
+ cd1->ElemTyp(3) = A_ValLoc;
+ cd1->ValLoc(3) = r2;
+ cd1->ElemTyp(4) = A_Str;
+ cd1->Str(4) = ")";
+ cd->ThenStmt = sig_cd(on_failure, cur_fnc);
+ cd_add(cd);
+ cd_add(sig_cd(cd_lbl, cur_fnc)); /* transfer control to bounding label */
+
+ /*
+ * Generate code for the body of this clause after the bounding label.
+ */
+ cur_fnc = fnc_sav;
+ cur_fnc->cursor = cd_lbl;
+ on_failure = fail_sav;
+ gencode(Tree1(clause), rslt);
+
+ /*
+ * If this clause is a generator, call the success continuation
+ * for the case expression, otherwise branch to the end of the
+ * expression.
+ */
+ if (cur_fnc->cursor->next != skp_lbl) {
+ if (succ_cont == NULL)
+ succ_cont = alc_fnc(); /* allocate a continuation function */
+ callc_add(succ_cont);
+ cur_fnc = fnc_sav;
+ }
+ else
+ cd_add(mk_goto(end_lbl));
+
+ /*
+ * The code for the next clause goes after the "skip" label of
+ * this clause.
+ */
+ cur_fnc->cursor = skp_lbl;
+ }
+
+ if (deflt == NULL)
+ cd_add(sig_cd(fail_sav, cur_fnc)); /* default action is failure */
+ else {
+ /*
+ * There is an explicit default action.
+ */
+ on_failure = fail_sav;
+ gencode(deflt, rslt);
+ if (cur_fnc->cursor->next != end_lbl) {
+ if (succ_cont == NULL)
+ succ_cont = alc_fnc();
+ callc_add(succ_cont);
+ cur_fnc = fnc_sav;
+ }
+ }
+ cur_fnc->cursor = end_lbl;
+
+ /*
+ * If some clauses are generators but others have transferred control
+ * to here, we must call the success continuation of the case
+ * expression and generate subsequent code there.
+ */
+ if (succ_cont != NULL) {
+ on_failure = fail_sav;
+ callc_add(succ_cont);
+ cur_fnc = succ_cont;
+ on_failure = &resume;
+ }
+ return rslt;
+ }
+
+/*
+ * gen_creat - generate code to create a co-expression.
+ */
+static struct val_loc *gen_creat(n, rslt)
+struct node *n;
+struct val_loc *rslt;
+ {
+ struct code *cd;
+ struct code *fail_sav;
+ struct code *fail_lbl;
+ struct c_fnc *fnc_sav;
+ struct c_fnc *fnc;
+ struct val_loc *co_rslt;
+ struct freetmp *ft;
+ char sav_prfx[PrfxSz];
+ int *tmp_sv;
+ int *itmp_sv;
+ int *dtmp_sv;
+ int *sbuf_sv;
+ int *cbuf_sv;
+ int ntmp_sv;
+ int nitmp_sv;
+ int ndtmp_sv;
+ int nsbuf_sv;
+ int ncbuf_sv;
+ int stat_sz_sv;
+ int istat_sz_sv;
+ int dstat_sz_sv;
+ int sstat_sz_sv;
+ int cstat_sz_sv;
+ int i;
+
+
+ rslt = chk_alc(rslt, n->lifetime);
+
+ fail_sav = on_failure;
+ fnc_sav = cur_fnc;
+ for (i = 0; i < PrfxSz; ++i)
+ sav_prfx[i] = frm_prfx[i];
+
+ /*
+ * Temporary variables are allocated independently for the co-expression.
+ */
+ tmp_sv = tmp_status;
+ itmp_sv = itmp_status;
+ dtmp_sv = dtmp_status;
+ sbuf_sv = sbuf_status;
+ cbuf_sv = cbuf_status;
+ stat_sz_sv = status_sz;
+ istat_sz_sv = istatus_sz;
+ dstat_sz_sv = dstatus_sz;
+ sstat_sz_sv = sstatus_sz;
+ cstat_sz_sv = cstatus_sz;
+ ntmp_sv = num_tmp;
+ nitmp_sv = num_itmp;
+ ndtmp_sv = num_dtmp;
+ nsbuf_sv = num_sbuf;
+ ncbuf_sv = num_cbuf;
+ tmp_status = (int *)alloc((unsigned int)(status_sz * sizeof(int)));
+ itmp_status = (int *)alloc((unsigned int)(istatus_sz * sizeof(int)));
+ dtmp_status = (int *)alloc((unsigned int)(dstatus_sz * sizeof(int)));
+ sbuf_status = (int *)alloc((unsigned int)(sstatus_sz * sizeof(int)));
+ cbuf_status = (int *)alloc((unsigned int)(cstatus_sz * sizeof(int)));
+ for (i = 0; i < status_sz; ++i)
+ tmp_status[i] = NotAlloc;
+ for (i = 0; i < istatus_sz; ++i)
+ itmp_status[i] = NotAlloc;
+ for (i = 0; i < dstatus_sz; ++i)
+ dtmp_status[i] = NotAlloc;
+ for (i = 0; i < sstatus_sz; ++i)
+ sbuf_status[i] = NotAlloc;
+ for (i = 0; i < cstatus_sz; ++i)
+ cbuf_status[i] = NotAlloc;
+ num_tmp = 0;
+ num_itmp = 0;
+ num_dtmp = 0;
+ num_sbuf = 0;
+ num_cbuf = 0;
+
+ /*
+ * Put code for co-expression in separate function. We will need a new
+ * type of procedure frame which contains copies of local variables,
+ * copies of arguments, and temporaries for use by the co-expression.
+ */
+ fnc = alc_fnc();
+ fnc->ref_cnt = 1;
+ fnc->flag |= CF_Coexpr;
+ ChkPrefix(fnc->prefix);
+ for (i = 0; i < PrfxSz; ++i)
+ frm_prfx[i] = fnc->frm_prfx[i] = fnc->prefix[i];
+ cur_fnc = fnc;
+
+ /*
+ * Set up a co-expression failure label followed by a context switch
+ * and a branch back to the failure label.
+ */
+ fail_lbl = alc_lbl("co_fail", 0);
+ cd_add(fail_lbl);
+ lastline = 0; /* force setting line number so tracing matches interp */
+ setloc(n);
+ cd = alc_ary(2);
+ cd->ElemTyp(0) = A_Str;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(0) = "co_chng(popact((struct b_coexpr *)BlkLoc(k_current)),";
+ cd->Str(1) = "NULL, NULL, A_Cofail, 1);";
+ cd_add(cd);
+ cd_add(mk_goto(fail_lbl));
+ cur_fnc->cursor = fail_lbl->prev; /* code goes before failure label */
+ on_failure = fail_lbl;
+
+ /*
+ * Generate code for the co-expression body, using the same
+ * dereferencing rules as for procedure return.
+ */
+ lastfiln = ""; /* force setting of file name and line number */
+ lastline = 0;
+ setloc(n);
+ ++create_lvl;
+ co_rslt = genretval(n, Tree0(n), NULL);
+ --create_lvl;
+
+ /*
+ * If the co-expression might produce a result, generate a co-expression
+ * context switch.
+ */
+ if (co_rslt != NULL) {
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "++BlkLoc(k_current)->coexpr.size;";
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "co_chng(popact((struct b_coexpr *)BlkLoc(k_current)), &";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = co_rslt;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ", NULL, A_Coret, 1);";
+ cd_add(cd);
+ cd_add(sig_cd(on_failure, cur_fnc)); /* if reactivated, resume expr */
+ }
+
+ /*
+ * Output the new frame definition.
+ */
+ prt_frame(frm_prfx, cur_proc->tnd_loc + num_tmp + Abs(cur_proc->nargs),
+ num_itmp, num_dtmp, num_sbuf, num_cbuf);
+
+ /*
+ * Now return to original function and produce code to create the
+ * co-expression.
+ */
+ cur_fnc = fnc_sav;
+ for (i = 0; i < PrfxSz; ++i)
+ frm_prfx[i] = sav_prfx[i];
+ on_failure = fail_sav;
+
+ lastfiln = ""; /* force setting of file name and line number */
+ lastline = 0;
+ setloc(n);
+ cd = NewCode(5);
+ cd->cd_id = C_Create;
+ cd->Rslt = rslt;
+ cd->Cont = fnc;
+ cd->NTemps = num_tmp;
+ cd->WrkSize = num_itmp;
+ cd->NextCreat = cur_fnc->creatlst;
+ cur_fnc->creatlst = cd;
+ cd_add(cd);
+
+ /*
+ * Restore arrays for temporary variable allocation.
+ */
+ free((char *)tmp_status);
+ free((char *)itmp_status);
+ free((char *)dtmp_status);
+ free((char *)sbuf_status);
+ free((char *)cbuf_status);
+ tmp_status = tmp_sv;
+ itmp_status = itmp_sv;
+ dtmp_status = dtmp_sv;
+ sbuf_status = sbuf_sv;
+ cbuf_status = cbuf_sv;
+ status_sz = stat_sz_sv;
+ istatus_sz = istat_sz_sv;
+ dstatus_sz = dstat_sz_sv;
+ sstatus_sz = sstat_sz_sv;
+ cstatus_sz = cstat_sz_sv;
+ num_tmp = ntmp_sv;
+ num_itmp = nitmp_sv;
+ num_dtmp = ndtmp_sv;
+ num_sbuf = nsbuf_sv;
+ num_cbuf = ncbuf_sv;
+
+ /*
+ * Temporary variables that exist to the end of the co-expression
+ * have no meaning in the surrounding code and must not be
+ * deallocated there.
+ */
+ while (n->freetmp != NULL) {
+ ft = n->freetmp->next;
+ n->freetmp->next = freetmp_pool;
+ freetmp_pool = n->freetmp;
+ n->freetmp = ft;
+ }
+
+ return rslt;
+ }
+
+/*
+ * gen_lim - generate code for limitation.
+ */
+static struct val_loc *gen_lim(n, rslt)
+struct node *n;
+struct val_loc *rslt;
+ {
+ struct node *expr;
+ struct node *limit;
+ struct val_loc *lim_desc;
+ struct code *cd;
+ struct code *cd1;
+ struct code *lbl;
+ struct code *fail_sav;
+ struct c_fnc *fnc_sav;
+ struct c_fnc *succ_cont;
+ struct val_loc *lim_int;
+ struct lentry *single;
+ int deref;
+
+ expr = Tree0(n);
+ limit = Tree1(n);
+
+ /*
+ * Generate code to compute the limitation value and dereference it.
+ */
+ deref = HasVar(varsubtyp(limit->type, &single));
+ if (single != NULL) {
+ /*
+ * Limitation is in a named variable. Use value directly from
+ * the variable rather than saving the result of the expression.
+ */
+ gencode(limit, &ignore);
+ lim_desc = var_ref(single);
+ }
+ else {
+ lim_desc = gencode(limit, NULL);
+ if (deref)
+ deref_cd(lim_desc, lim_desc);
+ }
+
+ setloc(n);
+ fail_sav = on_failure;
+
+ /*
+ * Try to convert the limitation value into an integer.
+ */
+ lim_int = itmp_loc(alc_itmp(n->intrnl_lftm));
+ cur_symtyps = n->symtyps;
+ if (largeints || (eval_is(int_typ, 0) & MaybeFalse)) {
+ /*
+ * Must call the conversion routine.
+ */
+ lbl = alc_lbl("limit is int", 0);
+ cd_add(lbl);
+ cur_fnc->cursor = lbl->prev; /* conversion goes before label */
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(5);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "cnv_c_int(&";
+ cd1->ElemTyp(1) = A_ValLoc;
+ cd1->ValLoc(1) = lim_desc;
+ cd1->ElemTyp(2) = A_Str;
+ cd1->Str(2) = ", &";
+ cd1->ElemTyp(3) = A_ValLoc;
+ cd1->ValLoc(3) = lim_int;
+ cd1->ElemTyp(4) = A_Str;
+ cd1->Str(4) = ")";
+ cd->Cond = cd1;
+ cd->ThenStmt = mk_goto(lbl);
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "err_msg(101, &";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = lim_desc;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ");";
+ cd_add(cd);
+ if (err_conv)
+ cd_add(sig_cd(on_failure, cur_fnc));
+ cur_fnc->cursor = lbl;
+ }
+ else {
+ /*
+ * The C integer is in the vword.
+ */
+ cd = alc_ary(4);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = lim_int;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = " = IntVal(";
+ cd->ElemTyp(2) = A_ValLoc;
+ cd->ValLoc(2) = lim_desc;
+ cd->ElemTyp(3) = A_Str;
+ cd->Str(3) = ");";
+ cd_add(cd);
+ }
+
+ /*
+ * Make sure the limitation value is positive.
+ */
+ lbl = alc_lbl("limit positive", 0);
+ cd_add(lbl);
+ cur_fnc->cursor = lbl->prev; /* code goes before label */
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(2);
+ cd1->ElemTyp(0) = A_ValLoc;
+ cd1->ValLoc(0) = lim_int;
+ cd1->ElemTyp(1) = A_Str;
+ cd1->Str(1) = " >= 0";
+ cd->Cond = cd1;
+ cd->ThenStmt = mk_goto(lbl);
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "err_msg(205, &";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = lim_desc;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ");";
+ cd_add(cd);
+ if (err_conv)
+ cd_add(sig_cd(on_failure, cur_fnc));
+ cur_fnc->cursor = lbl;
+
+ /*
+ * If the limitation value is 0, fail immediately.
+ */
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(2);
+ cd1->ElemTyp(0) = A_ValLoc;
+ cd1->ValLoc(0) = lim_int;
+ cd1->ElemTyp(1) = A_Str;
+ cd1->Str(1) = " == 0";
+ cd->Cond = cd1;
+ cd->ThenStmt = sig_cd(on_failure, cur_fnc);
+ cd_add(cd);
+
+ /*
+ * Establish where to go when limit has been reached.
+ */
+ fnc_sav = cur_fnc;
+ lbl = alc_lbl("limit", 0);
+ cd_add(lbl);
+ cur_fnc->cursor = lbl->prev; /* limited expression goes before label */
+
+ /*
+ * Generate code for limited expression and to check the limit value.
+ */
+ rslt = gencode(expr, rslt);
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(3);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "--";
+ cd1->ElemTyp(1) = A_ValLoc;
+ cd1->ValLoc(1) = lim_int;
+ cd1->ElemTyp(2) = A_Str;
+ cd1->Str(2) = " == 0";
+ cd->Cond = cd1;
+ cd->ThenStmt = sig_cd(lbl, cur_fnc);
+ cd_add(cd);
+
+ /*
+ * Call the success continuation both here and after the limitation
+ * label.
+ */
+ succ_cont = alc_fnc();
+ callc_add(succ_cont);
+ cur_fnc = fnc_sav;
+ cur_fnc->cursor = lbl;
+ on_failure = fail_sav;
+ callc_add(succ_cont);
+ cur_fnc = succ_cont;
+ on_failure = &resume;
+
+ return rslt;
+ }
+
+/*
+ * gen_apply - generate code for the apply operator, !.
+ */
+static struct val_loc *gen_apply(n, rslt)
+struct node *n;
+struct val_loc *rslt;
+ {
+ struct val_loc *callee;
+ struct val_loc *lst;
+ struct code *arg_lst;
+ struct code *on_ret;
+ struct c_fnc *fnc;
+
+ /*
+ * Generate code to compute the two operands.
+ */
+ callee = gencode(Tree0(n), NULL);
+ lst = gencode(Tree1(n), NULL);
+ rslt = chk_alc(rslt, n->lifetime);
+ setloc(n);
+
+ /*
+ * Construct argument list for apply().
+ */
+ arg_lst = alc_ary(6);
+ arg_lst->ElemTyp(0) = A_Str;
+ arg_lst->Str(0) = "&";
+ arg_lst->ElemTyp(1) = A_ValLoc;
+ arg_lst->ValLoc(1) = callee;
+ arg_lst->ElemTyp(2) = A_Str;
+ arg_lst->Str(2) = ", &";
+ arg_lst->ElemTyp(3) = A_ValLoc;
+ arg_lst->ValLoc(3) = lst;
+ arg_lst->ElemTyp(4) = A_Str;
+ arg_lst->Str(4) = ", &";
+ arg_lst->ElemTyp(5) = A_ValLoc;
+ arg_lst->ValLoc(5) = rslt;
+
+ /*
+ * Generate code to call apply(). Assume the operation can suspend and
+ * allocate a continuation. If it returns a "continue" signal,
+ * just break out of the signal handling code and fall into a call
+ * to the continuation.
+ */
+ on_ret = NewCode(1); /* #fields for C_Break == #fields for C_Goto */
+ on_ret->cd_id = C_Break;
+ on_ret->next = NULL;
+ on_ret->prev = NULL;
+ fnc = alc_fnc(); /* success continuation */
+ callo_add("apply", DoesFail | DoesRet | DoesSusp, fnc, 1, arg_lst, on_ret);
+ callc_add(fnc);
+ cur_fnc = fnc; /* subsequent code goes in the continuation */
+ on_failure = &resume;
+
+ return rslt;
+ }
+
+
+/*
+ * gen_scan - generate code for string scanning.
+ */
+static struct val_loc *gen_scan(n, rslt)
+nodeptr n;
+struct val_loc *rslt;
+ {
+ struct node *op;
+ struct node *subj;
+ struct node *body;
+ struct scan_info *scanp;
+ struct val_loc *asgn_var;
+ struct val_loc *new_subj;
+ struct val_loc *scan_rslt;
+ struct tmplftm *lifetm_ary;
+ struct lentry *subj_single;
+ struct lentry *body_single;
+ struct code *cd;
+ struct code *cd1;
+ struct code *lbl;
+ struct implement *impl;
+ int subj_deref;
+ int body_deref;
+ int op_tok;
+ int tmp_indx;
+
+ op = Tree0(n); /* operator node '?' or '?:=' */
+ subj = Tree1(n); /* subject expression */
+ body = Tree2(n); /* scanning expression */
+ op_tok = optab[Val0(op)].tok.t_type;
+
+ /*
+ * The location of the save areas for scanning environments is stored
+ * in list so they can be accessed by expressions that transfer
+ * control out of string scanning. Get the next list element and
+ * allocate the save areas in the procedure frame.
+ */
+ scanp = nxt_scan;
+ if (nxt_scan->next == NULL)
+ nxt_scan->next = NewStruct(scan_info);
+ nxt_scan = nxt_scan->next;
+ scanp->outer_sub = chk_alc(NULL, n->intrnl_lftm);
+ scanp->outer_pos = itmp_loc(alc_itmp(n->intrnl_lftm));
+ scanp->inner_sub = chk_alc(NULL, n->intrnl_lftm);
+ scanp->inner_pos = itmp_loc(alc_itmp(n->intrnl_lftm));
+
+ subj_deref = HasVar(varsubtyp(subj->type, &subj_single));
+ if (subj_single != NULL) {
+ /*
+ * The subject value is in a named variable. Use value directly from
+ * the variable rather than saving the result of the expression.
+ */
+ gencode(subj, &ignore);
+ new_subj = var_ref(subj_single);
+
+ if (op_tok == AUGQMARK) {
+ body_deref = HasVar(varsubtyp(body->type, &body_single));
+ if (body_single != NULL)
+ scan_rslt = &ignore; /* we know where the value will be */
+ else
+ scan_rslt = chk_alc(NULL, n->intrnl_lftm);
+ }
+ else
+ scan_rslt = rslt; /* result of 2nd operand is result of scanning */
+ }
+ else if (op_tok == AUGQMARK) {
+ /*
+ * Augmented string scanning using general assignment. The operands
+ * must be in consecutive locations.
+ */
+ lifetm_ary = alc_lftm(2, &n->n_field[1]);
+ tmp_indx = alc_tmp(2, lifetm_ary);
+ asgn_var = tmp_loc(tmp_indx++);
+ scan_rslt = tmp_loc(tmp_indx);
+ free((char *)lifetm_ary);
+
+ gencode(subj, asgn_var);
+ new_subj = chk_alc(NULL, n->intrnl_lftm);
+ deref_cd(asgn_var, new_subj);
+ }
+ else {
+ new_subj = gencode(subj, NULL);
+ if (subj_deref)
+ deref_cd(new_subj, new_subj);
+ scan_rslt = rslt; /* result of 2nd operand is result of scanning */
+ }
+
+ /*
+ * Produce code to save the old scanning environment.
+ */
+ setloc(op);
+ save_env(scanp->outer_sub, scanp->outer_pos);
+
+ /*
+ * Produce code to handle failure of the body of string scanning.
+ */
+ lbl = alc_lbl("scan fail", 0);
+ cd_add(lbl);
+ restr_env(scanp->outer_sub, scanp->outer_pos);
+ cd_add(sig_cd(on_failure, cur_fnc)); /* fail */
+ cur_fnc->cursor = lbl->prev; /* body goes before label */
+ on_failure = lbl;
+
+ /*
+ * If necessary, try to convert the subject to a string. Note that if
+ * error conversion occurs, backtracking will restore old subject.
+ */
+ cur_symtyps = n->symtyps;
+ if (eval_is(str_typ, 0) & MaybeFalse) {
+ lbl = alc_lbl("&subject is string", 0);
+ cd_add(lbl);
+ cur_fnc->cursor = lbl->prev; /* code goes before label */
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(3);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "cnv_str(&";
+ cd1->ElemTyp(1) = A_ValLoc;
+ cd1->ValLoc(1) = new_subj;
+ cd1->ElemTyp(2) = A_Str;
+ cd1->Str(2) = ", &k_subject)";
+ cd->Cond = cd1;
+ cd->ThenStmt = mk_goto(lbl);
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "err_msg(103, &";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = new_subj;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ");";
+ cd_add(cd);
+ if (err_conv)
+ cd_add(sig_cd(on_failure, cur_fnc));
+ cur_fnc->cursor = lbl;
+ }
+ else {
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "k_subject = ";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = new_subj;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ";";
+ cd_add(cd);
+ }
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "k_pos = 1;";
+ cd_add(cd);
+
+ scan_rslt = gencode(body, scan_rslt);
+
+ setloc(op);
+ if (op_tok == AUGQMARK) {
+ /*
+ * '?:=' - perform assignment.
+ */
+ if (subj_single != NULL) {
+ /*
+ * Assignment to a named variable.
+ */
+ if (body_single != NULL)
+ cd_add(mk_cpyval(new_subj, var_ref(body_single)));
+ else if (body_deref)
+ deref_cd(scan_rslt, new_subj);
+ else
+ cd_add(mk_cpyval(new_subj, scan_rslt));
+ }
+ else {
+ /*
+ * Use general assignment.
+ */
+ impl = optab[asgn_loc].binary;
+ if (impl == NULL) {
+ nfatal(op, "assignment not implemented", NULL);
+ rslt = &ignore; /* make sure code generation can continue */
+ }
+ else {
+ implproto(impl);
+ rslt = chk_alc(rslt, n->lifetime);
+ mk_callop(oper_name(impl), impl->ret_flag, asgn_var, 2, rslt,0);
+ }
+ }
+ }
+ else {
+ /*
+ * '?'
+ */
+ rslt = scan_rslt;
+ }
+
+ /*
+ * Produce code restore subject and pos when the body of the
+ * scanning expression succeeds. The new subject and pos must
+ * be saved in case of resumption.
+ */
+ save_env(scanp->inner_sub, scanp->inner_pos);
+ restr_env(scanp->outer_sub, scanp->outer_pos);
+
+ /*
+ * Produce code to handle resumption of string scanning.
+ */
+ lbl = alc_lbl("scan resume", 0);
+ cd_add(lbl);
+ save_env(scanp->outer_sub, scanp->outer_pos);
+ restr_env(scanp->inner_sub, scanp->inner_pos);
+ cd_add(sig_cd(on_failure, cur_fnc)); /* fail */
+ cur_fnc->cursor = lbl->prev; /* success continuation goes before label */
+ on_failure = lbl;
+
+ nxt_scan = scanp;
+ return rslt;
+ }
+
+/*
+ * gen_act - generate code for co-expression activation.
+ */
+static struct val_loc *gen_act(n, rslt)
+nodeptr n;
+struct val_loc *rslt;
+ {
+ struct node *op;
+ struct node *transmit;
+ struct node *coexpr;
+ struct tmplftm *lifetm_ary;
+ struct val_loc *trans_loc;
+ struct val_loc *coexpr_loc;
+ struct val_loc *asgn1;
+ struct val_loc *asgn2;
+ struct val_loc *act_rslt;
+ struct lentry *c_single;
+ struct code *cd;
+ struct code *cd1;
+ struct code *lbl;
+ struct implement *impl;
+ int c_deref;
+ int op_tok;
+ int tmp_indx;
+
+ op = Tree0(n); /* operator node for '@' or '@:=' */
+ transmit = Tree1(n); /* expression for value to transmit */
+ coexpr = Tree2(n); /* expression for co-expression */
+ op_tok = optab[Val0(op)].tok.t_type;
+
+ /*
+ * Produce code for the value to be transmitted.
+ */
+ if (op_tok == AUGAT) {
+ /*
+ * Augmented activation. This is seldom used so don't try too
+ * hard to optimize it. Allocate contiguous temporaries for
+ * the operands to the assignment.
+ */
+ lifetm_ary = alc_lftm(2, &n->n_field[1]);
+ tmp_indx = alc_tmp(2, lifetm_ary);
+ asgn1 = tmp_loc(tmp_indx++);
+ asgn2 = tmp_loc(tmp_indx);
+ free((char *)lifetm_ary);
+
+ /*
+ * Generate code to produce the left-hand-side of the assignment.
+ * This is also the transmitted value. Activation may need a
+ * dereferenced value, so this must be in a different location.
+ */
+ gencode(transmit, asgn1);
+ trans_loc = chk_alc(NULL, n->intrnl_lftm);
+ setloc(op);
+ deref_ret(asgn1, trans_loc, varsubtyp(transmit->type, NULL));
+ }
+ else
+ trans_loc = genretval(op, transmit, NULL); /* ordinary activation */
+
+ /*
+ * Determine if the value to be activated needs dereferencing, and
+ * see if it can only come from a single named variable.
+ */
+ c_deref = HasVar(varsubtyp(coexpr->type, &c_single));
+ if (c_single == NULL) {
+ /*
+ * The value is something other than a single named variable.
+ */
+ coexpr_loc = gencode(coexpr, NULL);
+ if (c_deref)
+ deref_cd(coexpr_loc, coexpr_loc);
+ }
+ else {
+ /*
+ * The value is in a named variable. Use it directly from the
+ * variable rather than saving the result of the expression.
+ */
+ gencode(coexpr, &ignore);
+ coexpr_loc = var_ref(c_single);
+ }
+
+ /*
+ * Make sure the value to be activated is a co-expression. Perform
+ * run-time checking if necessary.
+ */
+ cur_symtyps = n->symtyps;
+ if (eval_is(coexp_typ, 1) & MaybeFalse) {
+ lbl = alc_lbl("is co-expression", 0);
+ cd_add(lbl);
+ cur_fnc->cursor = lbl->prev; /* code goes before label */
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(3);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "(";
+ cd1->ElemTyp(1) = A_ValLoc;
+ cd1->ValLoc(1) = coexpr_loc;
+ cd1->ElemTyp(2) = A_Str;
+ cd1->Str(2) = ").dword == D_Coexpr";
+ cd->Cond = cd1;
+ cd->ThenStmt = mk_goto(lbl);
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "err_msg(118, &(";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = coexpr_loc;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = "));";
+ cd_add(cd);
+ if (err_conv)
+ cd_add(sig_cd(on_failure, cur_fnc));
+ cur_fnc->cursor = lbl;
+ }
+
+ /*
+ * Make sure a result location has been allocated. For ordinary
+ * activation, this is where activate() puts its result. For
+ * augmented activation, this is where assignment puts its result.
+ */
+ rslt = chk_alc(rslt, n->lifetime);
+ if (op_tok == AUGAT)
+ act_rslt = asgn2;
+ else
+ act_rslt = rslt;
+
+ /*
+ * Generate code to call activate().
+ */
+ setloc(n);
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(7);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "activate(&";
+ cd1->ElemTyp(1) = A_ValLoc;
+ cd1->ValLoc(1) = trans_loc;
+ cd1->ElemTyp(2) = A_Str;
+ cd1->Str(2) = ", (struct b_coexpr *)BlkLoc(";
+ cd1->ElemTyp(3) = A_ValLoc;
+ cd1->ValLoc(3) = coexpr_loc;
+ cd1->ElemTyp(4) = A_Str;
+ cd1->Str(4) = "), &";
+ cd1->ElemTyp(5) = A_ValLoc;
+ cd1->ValLoc(5) = act_rslt;
+ cd1->ElemTyp(6) = A_Str;
+ cd1->Str(6) = ") == A_Resume";
+ cd->Cond = cd1;
+ cd->ThenStmt = sig_cd(on_failure, cur_fnc);
+ cd_add(cd);
+
+ /*
+ * For augmented activation, generate code to call assignment.
+ */
+ if (op_tok == AUGAT) {
+ impl = optab[asgn_loc].binary;
+ if (impl == NULL) {
+ nfatal(op, "assignment not implemented", NULL);
+ rslt = &ignore; /* make sure code generation can continue */
+ }
+ else {
+ implproto(impl);
+ mk_callop(oper_name(impl), impl->ret_flag, asgn1, 2, rslt, 0);
+ }
+ }
+
+ return rslt;
+ }
+
+/*
+ * save_env - generate code to save scanning environment.
+ */
+static void save_env(sub_sav, pos_sav)
+struct val_loc *sub_sav;
+struct val_loc *pos_sav;
+ {
+ struct code *cd;
+
+ cd = alc_ary(2);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = sub_sav;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = " = k_subject;";
+ cd_add(cd);
+ cd = alc_ary(2);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = pos_sav;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = " = k_pos;";
+ cd_add(cd);
+ }
+
+/*
+ * restr_env - generate code to restore scanning environment.
+ */
+static void restr_env(sub_sav, pos_sav)
+struct val_loc *sub_sav;
+struct val_loc *pos_sav;
+ {
+ struct code *cd;
+
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "k_subject = ";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = sub_sav;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ";";
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "k_pos = ";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = pos_sav;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ";";
+ cd_add(cd);
+ }
+
+/*
+ * mk_callop - produce the code to directly call an operation.
+ */
+static void mk_callop(oper_nm, ret_flag, arg1rslt, nargs, rslt, optim)
+char *oper_nm;
+int ret_flag;
+struct val_loc *arg1rslt;
+int nargs;
+struct val_loc *rslt;
+int optim;
+ {
+ struct code *arg_lst;
+ struct code *on_ret;
+ struct c_fnc *fnc;
+ int n;
+ int need_cont;
+
+ /*
+ * If this operation can return an "continue" signal, we will need
+ * a break statement in the signal switch to handle it.
+ */
+ if (ret_flag & DoesRet) {
+ on_ret = NewCode(1); /* #fields == #fields C_Goto */
+ on_ret->cd_id = C_Break;
+ on_ret->next = NULL;
+ on_ret->prev = NULL;
+ }
+ else
+ on_ret = NULL;
+
+ /*
+ * Construct argument list for the C function implementing the
+ * operation. First compute the size of the code array for the
+ * argument list; this varies if we are using an optimized calling
+ * interface.
+ */
+ if (optim) {
+ n = 0;
+ if (arg1rslt != NULL)
+ n += 2;
+ if (ret_flag & (DoesRet | DoesSusp)) {
+ if (n > 0)
+ ++n;
+ n += 2;
+ }
+ }
+ else
+ n = 7;
+ if (n == 0)
+ arg_lst = NULL;
+ else {
+ arg_lst = alc_ary(n);
+ n = 0;
+ if (!optim) {
+ arg_lst->ElemTyp(n) = A_Intgr; /* number of arguments */
+ arg_lst->Intgr(n) = nargs;
+ ++n;
+ arg_lst->ElemTyp(n) = A_Str; /* , */
+ arg_lst->Str(n) = ", ";
+ ++n;
+ }
+ if (arg1rslt == NULL) { /* location of first argument */
+ if (!optim) {
+ arg_lst->ElemTyp(n) = A_Str;
+ arg_lst->Str(n) = "NULL";
+ ++n;
+ arg_lst->ElemTyp(n) = A_Str;
+ arg_lst->Str(n) = ""; /* nothing, but must fill slot */
+ ++n;
+ }
+ }
+ else {
+ arg_lst->ElemTyp(n) = A_Str;
+ arg_lst->Str(n) = "&";
+ ++n;
+ arg_lst->ElemTyp(n) = A_ValLoc;
+ arg_lst->ValLoc(n) = arg1rslt;
+ ++n;
+ }
+ if (!optim || ret_flag & (DoesRet | DoesSusp)) {
+ if (n > 0) {
+ arg_lst->ElemTyp(n) = A_Str; /* , */
+ arg_lst->Str(n) = ", ";
+ ++n;
+ }
+ arg_lst->ElemTyp(n) = A_Str; /* location of result */
+ arg_lst->Str(n) = "&";
+ ++n;
+ arg_lst->ElemTyp(n) = A_ValLoc;
+ arg_lst->ValLoc(n) = rslt;
+ }
+ }
+
+ /*
+ * Generate code to call the operation and handle returned signals.
+ */
+ if (ret_flag & DoesSusp) {
+ /*
+ * The operation suspends, so call it with a continuation, then
+ * proceed to generate code in the continuation.
+ */
+ fnc = alc_fnc();
+ callo_add(oper_nm, ret_flag, fnc, 1, arg_lst, on_ret);
+ if (ret_flag & DoesRet)
+ callc_add(fnc);
+ cur_fnc = fnc;
+ on_failure = &resume;
+ }
+ else {
+ /*
+ * No continuation is needed, but if standard calling conventions
+ * are used, a NULL continuation argument is required.
+ */
+ if (optim)
+ need_cont = 0;
+ else
+ need_cont = 1;
+ callo_add(oper_nm, ret_flag, NULL, need_cont, arg_lst, on_ret);
+ }
+}
+
+/*
+ * genretval - generate code for the expression in a return/suspend or
+ * for the expression for the value to be transmitted in a co-expression
+ * context switch.
+ */
+static struct val_loc *genretval(n, expr, dest)
+struct node *n;
+struct node *expr;
+struct val_loc *dest;
+ {
+ int subtypes;
+ struct lentry *single;
+ struct val_loc *val;
+
+ subtypes = varsubtyp(expr->type, &single);
+
+ /*
+ * If we have a single local or argument, we don't need to construct
+ * a variable reference; we need the value and we know where it is.
+ */
+ if (single != NULL && (subtypes & (HasLcl | HasPrm))) {
+ gencode(expr, &ignore);
+ val = var_ref(single);
+ if (dest == NULL)
+ dest = val;
+ else
+ cd_add(mk_cpyval(dest, val));
+ }
+ else {
+ dest = gencode(expr, dest);
+ setloc(n);
+ deref_ret(dest, dest, subtypes);
+ }
+
+ return dest;
+ }
+
+/*
+ * deref_ret - produced dereferencing code for values returned from
+ * procedures or transmitted to co-expressions.
+ */
+static void deref_ret(src, dest, subtypes)
+struct val_loc *src;
+struct val_loc *dest;
+int subtypes;
+ {
+ struct code *cd;
+ struct code *lbl;
+
+ if (src == NULL)
+ return; /* no value to dereference */
+
+ /*
+ * If there may be values that do not need dereferencing, insure that the
+ * values are in the destination and make it the source of dereferencing.
+ */
+ if ((subtypes & (HasVal | HasGlb)) && (src != dest)) {
+ cd_add(mk_cpyval(dest, src));
+ src = dest;
+ }
+
+ if (subtypes & (HasLcl | HasPrm)) {
+ /*
+ * Some values may need to be dereferenced.
+ */
+ lbl = NULL;
+ if (subtypes & HasVal) {
+ /*
+ * We may have a non-variable and must check at run time.
+ */
+ lbl = check_var(dest, NULL);
+ }
+
+ if (subtypes & HasGlb) {
+ /*
+ * Make sure we don't dereference any globals, use retderef().
+ */
+ if (subtypes & HasLcl) {
+ /*
+ * We must dereference any locals.
+ */
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "retderef(&";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = dest;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) =
+ ", (word *)pfp->tend.d, (word *)(pfp->tend.d + pfp->tend.num));";
+ cd_add(cd);
+ /*
+ * We may now have a value. We must check at run-time and skip
+ * any attempt to dereference an argument.
+ */
+ lbl = check_var(dest, lbl);
+ }
+
+ if (subtypes & HasPrm) {
+ /*
+ * We must dereference any arguments.
+ */
+ cd = alc_ary(5);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "retderef(&";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = dest;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ", (word *)glbl_argp, (word *)(glbl_argp + ";
+ cd->ElemTyp(3) = A_Intgr;
+ cd->Intgr(3) = Abs(cur_proc->nargs);
+ cd->ElemTyp(4) = A_Str;
+ cd->Str(4) = "));";
+ cd_add(cd);
+ }
+ }
+ else /* No globals */
+ deref_cd(src, dest);
+
+ if (lbl != NULL)
+ cur_fnc->cursor = lbl; /* continue after label */
+ }
+ }
+
+/*
+ * check_var - generate code to make sure a descriptor contains a variable
+ * reference. If no label is given to jump to for a non-variable, allocate
+ * one and generate code before it.
+ */
+static struct code *check_var(d, lbl)
+struct val_loc *d;
+struct code *lbl;
+ {
+ struct code *cd, *cd1;
+
+ if (lbl == NULL) {
+ lbl = alc_lbl("not variable", 0);
+ cd_add(lbl);
+ cur_fnc->cursor = lbl->prev; /* code goes before label */
+ }
+
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(3);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "!Var(";
+ cd1->ElemTyp(1) = A_ValLoc;
+ cd1->ValLoc(1) = d;
+ cd1->ElemTyp(2) = A_Str;
+ cd1->Str(2) = ")";
+ cd->Cond = cd1;
+ cd->ThenStmt = mk_goto(lbl);
+ cd_add(cd);
+
+ return lbl;
+ }
+
+/*
+ * field_ref - generate code for a field reference.
+ */
+static struct val_loc *field_ref(n, rslt)
+struct node *n;
+struct val_loc *rslt;
+ {
+ struct node *rec;
+ struct node *fld;
+ struct fentry *fp;
+ struct par_rec *rp;
+ struct val_loc *rec_loc;
+ struct code *cd;
+ struct code *cd1;
+ struct code *lbl;
+ struct lentry *single;
+ int deref;
+ int num_offsets;
+ int offset;
+ int bad_recs;
+
+ rec = Tree0(n);
+ fld = Tree1(n);
+
+ /*
+ * Generate code to compute the record value and dereference it.
+ */
+ deref = HasVar(varsubtyp(rec->type, &single));
+ if (single != NULL) {
+ /*
+ * The record is in a named variable. Use value directly from
+ * the variable rather than saving the result of the expression.
+ */
+ gencode(rec, &ignore);
+ rec_loc = var_ref(single);
+ }
+ else {
+ rec_loc = gencode(rec, NULL);
+ if (deref)
+ deref_cd(rec_loc, rec_loc);
+ }
+
+ setloc(fld);
+
+ /*
+ * Make sure the operand is a record.
+ */
+ cur_symtyps = n->symtyps;
+ if (eval_is(rec_typ, 0) & MaybeFalse) {
+ lbl = alc_lbl("is record", 0);
+ cd_add(lbl);
+ cur_fnc->cursor = lbl->prev; /* code goes before label */
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(3);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "(";
+ cd1->ElemTyp(1) = A_ValLoc;
+ cd1->ValLoc(1) = rec_loc;
+ cd1->ElemTyp(2) = A_Str;
+ cd1->Str(2) = ").dword == D_Record";
+ cd->Cond = cd1;
+ cd->ThenStmt = mk_goto(lbl);
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "err_msg(107, &";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = rec_loc;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ");";
+ cd_add(cd);
+ if (err_conv)
+ cd_add(sig_cd(on_failure, cur_fnc));
+ cur_fnc->cursor = lbl;
+ }
+
+ rslt = chk_alc(rslt, n->lifetime);
+
+ /*
+ * Find the list of records containing this field.
+ */
+ if ((fp = flookup(Str0(fld))) == NULL) {
+ nfatal(n, "invalid field", Str0(fld));
+ return rslt;
+ }
+
+ /*
+ * Generate code for declarations and to get the record block pointer.
+ */
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "{";
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "struct b_record *r_rp = (struct b_record *) BlkLoc(";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = rec_loc;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ");";
+ cd_add(cd);
+ if (err_conv) {
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "int r_must_fail = 0;";
+ cd_add(cd);
+ }
+
+ /*
+ * Determine which records are in the record type.
+ */
+ mark_recs(fp, cur_symtyps->types[0], &num_offsets, &offset, &bad_recs);
+
+ /*
+ * Generate code to insure that the field belongs to the record
+ * and to index into the record block.
+ */
+ if (num_offsets == 1 && !bad_recs) {
+ /*
+ * We already know the offset of the field.
+ */
+ cd = alc_ary(4);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = rslt;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = ".dword = D_Var + ((word *)&r_rp->fields[";
+ cd->ElemTyp(2) = A_Intgr;
+ cd->Intgr(2) = offset;
+ cd->ElemTyp(3) = A_Str;
+ cd->Str(3) = "] - (word *)r_rp);";
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "VarLoc(";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = rslt;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ") = (dptr)r_rp;";
+ cd_add(cd);
+ for (rp = fp->rlist; rp != NULL; rp = rp->next)
+ rp->mark = 0;
+ }
+ else {
+ /*
+ * The field appears in several records. generate code to determine
+ * which one it is.
+ */
+
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "dptr r_dp;";
+ cd_add(cd);
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "switch (r_rp->recdesc->proc.recnum) {";
+ cd_add(cd);
+
+ rp = fp->rlist;
+ while (rp != NULL) {
+ offset = rp->offset;
+ while (rp != NULL && rp->offset == offset) {
+ if (rp->mark) {
+ rp->mark = 0;
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = " case ";
+ cd->ElemTyp(1) = A_Intgr;
+ cd->Intgr(1) = rp->rec->rec_num;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ":";
+ cd_add(cd);
+ }
+ rp = rp->next;
+ }
+
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = " r_dp = &r_rp->fields[";
+ cd->ElemTyp(1) = A_Intgr;
+ cd->Intgr(1) = offset;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = "];";
+ cd_add(cd);
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = " break;";
+ cd_add(cd);
+ }
+
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = " default:";
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = " err_msg(207, &";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = rec_loc;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ");";
+ cd_add(cd);
+ if (err_conv) {
+ /*
+ * The peephole analyzer doesn't know how to handle a goto or return
+ * in a switch statement, so just set a flag here.
+ */
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = " r_must_fail = 1;";
+ cd_add(cd);
+ }
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = " }";
+ cd_add(cd);
+ if (err_conv) {
+ /*
+ * Now that we are out of the switch statement, see if the flag
+ * was set to indicate error conversion.
+ */
+ cd = NewCode(2);
+ cd->cd_id = C_If;
+ cd1 = alc_ary(1);
+ cd1->ElemTyp(0) = A_Str;
+ cd1->Str(0) = "r_must_fail";
+ cd->Cond = cd1;
+ cd->ThenStmt = sig_cd(on_failure, cur_fnc);
+ cd_add(cd);
+ }
+ cd = alc_ary(2);
+ cd->ElemTyp(0) = A_ValLoc;
+ cd->ValLoc(0) = rslt;
+ cd->ElemTyp(1) = A_Str;
+ cd->Str(1) = ".dword = D_Var + ((word *)r_dp - (word *)r_rp);";
+ cd_add(cd);
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "VarLoc(";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = rslt;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ") = (dptr)r_rp;";
+ cd_add(cd);
+ }
+
+ cd = alc_ary(1);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "}";
+ cd_add(cd);
+ return rslt;
+ }
+
+/*
+ * bound - bound the code for the given sub-tree. If catch_fail is true,
+ * direct failure to the bounding label.
+ */
+static struct val_loc *bound(n, rslt, catch_fail)
+struct node *n;
+struct val_loc *rslt;
+int catch_fail;
+ {
+ struct code *lbl1;
+ struct code *fail_sav;
+ struct c_fnc *fnc_sav;
+
+ fnc_sav = cur_fnc;
+ fail_sav = on_failure;
+
+ lbl1 = alc_lbl("bound", Bounding);
+ cd_add(lbl1);
+ cur_fnc->cursor = lbl1->prev; /* code goes before label */
+ if (catch_fail)
+ on_failure = lbl1;
+
+ rslt = gencode(n, rslt);
+
+ cd_add(sig_cd(lbl1, cur_fnc)); /* transfer control to bounding label */
+ cur_fnc = fnc_sav;
+ cur_fnc->cursor = lbl1;
+
+ on_failure = fail_sav;
+ return rslt;
+ }
+
+/*
+ * cd_add - add a code struct at the cursor in the current function.
+ */
+void cd_add(cd)
+struct code *cd;
+ {
+ register struct code *cursor;
+
+ cursor = cur_fnc->cursor;
+
+ cd->next = cursor->next;
+ cd->prev = cursor;
+ if (cursor->next != NULL)
+ cursor->next->prev = cd;
+ cursor->next = cd;
+ cur_fnc->cursor = cd;
+ }
+
+/*
+ * sig_cd - convert a signal/label into a goto or return signal in
+ * the context of the given function.
+ */
+struct code *sig_cd(sig, fnc)
+struct code *sig;
+struct c_fnc *fnc;
+ {
+ struct code *cd;
+
+ if (sig->cd_id == C_Label && sig->Container == fnc)
+ return mk_goto(sig);
+ else {
+ cd = NewCode(1); /* # fields <= # fields of C_Goto */
+ cd->cd_id = C_RetSig;
+ cd->next = NULL;
+ cd->prev = NULL;
+ cd->SigRef = add_sig(sig, fnc);
+ return cd;
+ }
+ }
+
+/*
+ * add_sig - add signal to list of signals returned by function.
+ */
+struct sig_lst *add_sig(sig, fnc)
+struct code *sig;
+struct c_fnc *fnc;
+ {
+ struct sig_lst *sl;
+
+ for (sl = fnc->sig_lst; sl != NULL && sl->sig != sig; sl = sl->next)
+ ;
+ if (sl == NULL) {
+ sl = NewStruct(sig_lst);
+ sl->sig = sig;
+ sl->ref_cnt = 1;
+ sl->next = fnc->sig_lst;
+ fnc->sig_lst = sl;
+ }
+ else
+ ++sl->ref_cnt;
+ return sl;
+ }
+
+/*
+ * callc_add - add code to call a continuation. Note the action to be
+ * taken if the continuation returns resumption. The actual list
+ * signals returned and actions to take will be figured out after
+ * the continuation has been optimized.
+ */
+void callc_add(cont)
+struct c_fnc *cont;
+ {
+ struct code *cd;
+
+ cd = new_call();
+ cd->OperName = NULL;
+ cd->Cont = cont;
+ cd->ArgLst = NULL;
+ cd->ContFail = on_failure;
+ cd->SigActs = NULL;
+ ++cont->ref_cnt;
+ }
+
+/*
+ * callo_add - add code to call an operation.
+ */
+void callo_add(oper_nm, ret_flag, cont, need_cont, arglist, on_ret)
+char *oper_nm;
+int ret_flag;
+struct c_fnc *cont;
+int need_cont;
+struct code *arglist;
+struct code *on_ret;
+ {
+ struct code *cd;
+ struct code *cd1;
+
+ cd = new_call();
+ cd->OperName = oper_nm;
+ cd->Cont = cont;
+ if (need_cont)
+ cd->Flags = NeedCont;
+ cd->ArgLst = arglist;
+ cd->ContFail = NULL; /* operation handles failure from the continuation */
+ /*
+ * Decide how to handle the signals produced by the operation. (Those
+ * produced by the continuation will be examined after the continuation
+ * is optimized.)
+ */
+ cd->SigActs = NULL;
+ if (MightFail(ret_flag))
+ cd->SigActs = new_sgact(&resume, sig_cd(on_failure,cur_fnc), cd->SigActs);
+ if (ret_flag & DoesRet)
+ cd->SigActs = new_sgact(&contin, on_ret, cd->SigActs);
+ if (ret_flag & DoesFThru) {
+ cd1 = NewCode(1); /* #fields == #fields C_Goto */
+ cd1->cd_id = C_Break;
+ cd1->next = NULL;
+ cd1->prev = NULL;
+ cd->SigActs = new_sgact(&fallthru, cd1, cd->SigActs);
+ }
+ if (cont != NULL)
+ ++cont->ref_cnt; /* increment reference count */
+}
+
+/*
+ * Create a call, add it to the code for the current function, and
+ * add it to the list of calls from the current function.
+ */
+static struct code *new_call()
+ {
+ struct code *cd;
+
+ cd = NewCode(7);
+ cd->cd_id = C_CallSig;
+ cd_add(cd);
+ cd->Flags = 0;
+ cd->NextCall = cur_fnc->call_lst;
+ cur_fnc->call_lst = cd;
+ return cd;
+ }
+
+/*
+ * sig_act - create a new binding of an action to a signal.
+ */
+struct sig_act *new_sgact(sig, cd, next)
+struct code *sig;
+struct code *cd;
+struct sig_act *next;
+ {
+ struct sig_act *sa;
+
+ sa = NewStruct(sig_act);
+ sa->sig = sig;
+ sa->cd = cd;
+ sa->shar_act = NULL;
+ sa->next = next;
+ return sa;
+ }
+
+
+#ifdef OptimizeLit
+static int instr(const char *str, int chr) {
+ int i, found, go;
+
+ found = 0; go = 1;
+ for(i=0; ((str[i] != '\0') && go) ;i++) {
+ if (str[i] == chr) {
+ go = 0;
+ found = 1;
+ if ((str[i+1] != '\0') && (chr == '='))
+ if (str[i+1] == '=')
+ found = 0;
+ if ((chr == '=') && (i > 0)) {
+ if (str[i-1] == '>')
+ found = 0;
+ else if (str[i-1] == '<')
+ found = 0;
+ else if (str[i-1] == '!')
+ found = 0;
+ }
+ }
+ }
+ return found;
+}
+
+static void tbl_add(struct lit_tbl *add) {
+ struct lit_tbl *ins;
+ static struct lit_tbl *ptr = NULL;
+ int go = 1;
+
+ if (tbl == NULL) {
+ tbl = add;
+ ptr = add;
+ }
+ else {
+ ins = ptr;
+ while ((ins != NULL) && go) {
+ if (add->index != ins->index)
+ ins = ins->prev;
+ else
+ go = 0;
+ }
+ if (ins != NULL) {
+ if (ins->end == NULL)
+ ins->end = add->initial;
+ }
+ ptr->next = add;
+ add->prev = ptr;
+ ptr = add;
+ }
+}
+
+
+static void invalidate(struct val_loc *val, struct code *end, int code) {
+ struct lit_tbl *ptr, *back;
+ int index, go = 1;
+
+ if (val == NULL)
+ return;
+ if (val->loc_type == V_NamedVar) {
+ index = val->u.nvar->val.index;
+ return;
+ }
+ else if (val->loc_type == V_Temp)
+ index = val->u.tmp + cur_proc->tnd_loc;
+ else
+ return;
+ if (tbl == NULL)
+ return;
+ back = tbl;
+ while (back->next != NULL)
+ back = back->next;
+ go = 1;
+ for(ptr=back; ((ptr != NULL) && go) ; ptr=ptr->prev) {
+ if ((ptr->index == index) && (ptr->modified != NO_TOUCH)) {
+ ptr->modified = code;
+ if ((code != LIMITED_TO_INT) && (ptr->safe)) {
+ ptr->end = end;
+ ptr->safe = 0;
+ }
+ go = 0;
+ }
+ else if ((ptr->index == index) && (ptr->modified == NO_TOUCH)) {
+ if ((code != LIMITED_TO_INT) && (ptr->safe)) {
+ ptr->end = end;
+ ptr->safe = 0;
+ }
+ go = 0;
+ }
+ else if (ptr->index == index)
+ go = 0;
+ }
+}
+
+
+static int eval_code(struct code *cd, struct lit_tbl *cur) {
+ struct code *tmp;
+ struct lit_tbl *tmp_tbl;
+ int i, j;
+ char *str;
+
+ for (i=0; cd->ElemTyp(i) != A_End ;i++) {
+ switch(cd->ElemTyp(i)) {
+ case A_ValLoc:
+ if (cd->ValLoc(i)->mod_access != M_CInt)
+ break;
+ if ((cd->ValLoc(i)->u.tmp + cur_proc->tnd_loc) == cur->index) {
+ switch (cd->ValLoc(i)->loc_type) {
+ case V_Temp:
+ if (cur->csym->flag == F_StrLit) {
+#if 0
+ cd->ElemTyp(i) = A_Str;
+ str = (char *)alloc(strlen(cur->csym->image)+8);
+ sprintf(str, "\"%s\"/*Z*/", cur->csym->image);
+ cd->Str(i) = str;
+#endif
+ }
+ else if (cur->csym->flag == F_IntLit) {
+ cd->ElemTyp(i) = A_Str;
+ cd->Str(i) = cur->csym->image;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case A_Ary:
+ for(tmp=cd->Array(i); tmp != NULL ;tmp=tmp->next)
+ eval_code(tmp, cur);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void propagate_literals() {
+ struct lit_tbl *ptr;
+ struct code *cd, *arg;
+ int ret;
+
+ for(ptr=tbl; ptr != NULL ;ptr=ptr->next) {
+ if (ptr->modified != NO_TOUCH) {
+ for(cd=ptr->initial; cd != ptr->end ;cd=cd->next) {
+ switch (cd->cd_id) {
+ case C_If:
+ for(arg=cd->Cond; arg != NULL ;arg=arg->next)
+ ret = eval_code(arg, ptr);
+ /*
+ * Again, don't take the 'then' portion.
+ * It might lead to infinite loops.
+ * for(arg=cd->ThenStmt; arg != NULL ;arg=arg->next)
+ * ret = eval_code(arg, ptr);
+ */
+ break;
+ case C_CdAry:
+ ret = eval_code(cd, ptr);
+ break;
+ case C_CallSig:
+ for(arg=cd->ArgLst; arg != NULL ;arg=arg->next)
+ ret = eval_code(arg, ptr);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * analyze_literals - analyzes the generated code to replace
+ * complex record dereferences with C
+ * literals.
+ */
+static void analyze_literals(struct code *start, struct code *top, int lvl) {
+ struct code *ptr, *tmp, *not_null;
+ struct lit_tbl *new_tbl;
+ struct lbl_tbl *new_lbl;
+ struct val_loc *prev = NULL;
+ int i, inc=0, addr=0, assgn=0, equal = 0;
+
+ for (ptr = start; ptr != NULL ; ptr = ptr->next) {
+ if (!lvl)
+ not_null = ptr;
+ else
+ not_null = top;
+ switch (ptr->cd_id) {
+ case C_NamedVar:
+ break;
+ case C_CallSig:
+ analyze_literals(ptr->ArgLst, not_null, lvl+1);
+ break;
+ case C_Goto:
+ break;
+ case C_Label:
+ break;
+ case C_Lit:
+ new_tbl = alc_tbl();
+ new_tbl->initial = ptr;
+ new_tbl->vloc = ptr->Rslt;
+ new_tbl->csym = ptr->Literal;
+ switch (ptr->Rslt->loc_type) {
+ case V_NamedVar:
+ new_tbl->index = ptr->Rslt->u.nvar->val.index;
+ tbl_add(new_tbl);
+ break;
+ case V_Temp:
+ new_tbl->index = ptr->Rslt->u.tmp + cur_proc->tnd_loc;
+ tbl_add(new_tbl);
+ break;
+ default:
+ new_tbl->index = -1;
+ free(new_tbl);
+ break;
+ }
+ break;
+ case C_If:
+ analyze_literals(ptr->Cond, not_null, lvl+1);
+ /*
+ * Don't analyze the 'then' portion such as in:
+ * analyze_literals(ptr->ThenStmt, not_null, lvl+1);
+ * Apparently, all the intermediate code does is maintain
+ * a pointer to where the flow of execution jumps to in
+ * case the 'then' is taken. These are all goto statments
+ * and can result in infinite loops of analyzation.
+ */
+ break;
+ case C_CdAry:
+ for(i=0; ptr->ElemTyp(i) != A_End ;i++) {
+ switch(ptr->ElemTyp(i)) {
+ case A_Str:
+ if (ptr->Str(i) != NULL) {
+ if ( (strstr(ptr->Str(i), "-=")) ||
+ (strstr(ptr->Str(i), "+=")) ||
+ (strstr(ptr->Str(i), "*=")) ||
+ (strstr(ptr->Str(i), "/=")) )
+ invalidate(prev, not_null, NO_TOUCH);
+ else if (instr(ptr->Str(i), '=')) {
+ invalidate(prev, not_null, LIMITED);
+ assgn = 1;
+ }
+ else if ( (strstr(ptr->Str(i), "++")) ||
+ (strstr(ptr->Str(i), "--")) )
+ inc = 1;
+ else if (instr(ptr->Str(i), '&'))
+ addr = 1;
+ else if (strstr(ptr->Str(i), "=="))
+ equal = 1;
+ }
+ break;
+ case A_ValLoc:
+ if (inc) {
+ invalidate(ptr->ValLoc(i), not_null, NO_TOUCH);
+ inc = 0;
+ }
+ if (addr) {
+ invalidate(ptr->ValLoc(i), not_null, LIMITED);
+ addr = 0;
+ }
+ if ((assgn) && (ptr->ValLoc(i)->mod_access == M_None)) {
+ invalidate(ptr->ValLoc(i), not_null, LIMITED);
+ assgn = 0;
+ }
+ else if (assgn)
+ assgn = 0;
+ if (equal) {
+ invalidate(ptr->ValLoc(i), not_null, LIMITED_TO_INT);
+ equal = 0;
+ }
+ prev = ptr->ValLoc(i);
+ break;
+ case A_Intgr:
+ break;
+ case A_SBuf:
+ break;
+ case A_Ary:
+ for(tmp=ptr->Array(i); tmp != NULL ;tmp=tmp->next)
+ analyze_literals(tmp, not_null, lvl+1);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+#endif /* OptimizeLit */
+
+/*
+ * analyze_poll - analyzes the internal C code representation from
+ * the position of the last Poll() function call to
+ * the current position in the code.
+ * Returns a 0 if the last Poll() function should not
+ * be removed.
+ */
+#ifdef OptimizePoll
+static int analyze_poll(void) {
+ struct code *cursor, *ptr;
+ int cont = 1;
+
+ ptr = lastpoll;
+ if (ptr == NULL)
+ return 0;
+ cursor = cur_fnc->cursor;
+ while ((cursor != ptr) && (ptr != NULL) && (cont)) {
+ switch (ptr->cd_id) {
+ case C_Null :
+ case C_NamedVar :
+ case C_Label :
+ case C_Lit :
+ case C_Resume :
+ case C_Continue :
+ case C_FallThru :
+ case C_PFail :
+ case C_Goto :
+ case C_Create :
+ case C_If :
+ case C_SrcLoc :
+ case C_CdAry :
+ break;
+ case C_CallSig :
+ case C_RetSig :
+ case C_LBrack :
+ case C_RBrack :
+ case C_PRet :
+ case C_PSusp :
+ case C_Break :
+ cont = 0;
+ break;
+ }
+ ptr = ptr->next;
+ }
+ return cont;
+}
+
+/*
+ * remove_poll - removes the ccode structure that represents the last
+ * call to the "Poll()" function by simply changing the code ID to
+ * C_Null code.
+ */
+static void remove_poll(void) {
+
+ if (lastpoll == NULL)
+ return;
+ lastpoll->cd_id = C_Null;
+}
+#endif /* OptimizePoll */
+
+/*
+ * setloc produces code to set the file name and line number to the
+ * source location of node n. Code is only produced if the corresponding
+ * value has changed since the last time setloc was called.
+ */
+static void setloc(n)
+nodeptr n;
+ {
+ struct code *cd;
+ static int count=0;
+
+ if (n == NULL || File(n) == NULL || Line(n) == 0)
+ return;
+
+ if (File(n) != lastfiln || Line(n) != lastline) {
+#ifdef OptimizePoll
+ if (analyze_poll())
+ remove_poll();
+ cd = alc_ary(1);
+ lastpoll = cd;
+#else /* OptimizePoll */
+ cd = alc_ary(1);
+#endif /* OptimizePoll */
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "Poll();";
+ cd_add(cd);
+
+ if (line_info) {
+ cd = NewCode(2);
+ cd->cd_id = C_SrcLoc;
+
+ if (File(n) == lastfiln)
+ cd->FileName = NULL;
+ else {
+ lastfiln = File(n);
+ cd->FileName = lastfiln;
+ }
+
+ if (Line(n) == lastline)
+ cd->LineNum = 0;
+ else {
+ lastline = Line(n);
+ cd->LineNum = lastline;
+ }
+
+ cd_add(cd);
+ }
+ }
+ }
+
+/*
+ * alc_ary - create an array for a sequence of code fragments.
+ */
+struct code *alc_ary(n)
+int n;
+ {
+ struct code *cd;
+ static cnt=1;
+
+ cd = NewCode(2 * n + 1);
+ cd->cd_id = C_CdAry;
+ cd->next = NULL;
+ cd->prev = NULL;
+ cd->ElemTyp(n) = A_End;
+ return cd;
+ }
+
+
+/*
+ * alc_lbl - create a label.
+ */
+struct code *alc_lbl(desc, flag)
+char *desc;
+int flag;
+ {
+ register struct code *cd;
+
+ cd = NewCode(5);
+ cd->cd_id = C_Label;
+ cd->next = NULL;
+ cd->prev = NULL;
+ cd->Container = cur_fnc; /* function containing label */
+ cd->SeqNum = 0; /* sequence number is allocated later */
+ cd->Desc = desc; /* identifying comment */
+ cd->RefCnt = 0; /* reference count */
+ cd->LabFlg = flag;
+ return cd;
+ }
+
+/*
+ * alc_fnc - allocate a function structure;
+ */
+static struct c_fnc *alc_fnc()
+ {
+ register struct c_fnc *cf;
+ int i;
+
+ cf = NewStruct(c_fnc);
+ cf->prefix[0] = '\0'; /* prefix is allocated later */
+ cf->prefix[PrfxSz] = '\0'; /* terminate prefix for printing */
+ cf->flag = 0;
+ for (i = 0; i < PrfxSz; ++i)
+ cf->frm_prfx[i] = frm_prfx[i]; /* note procedure frame prefix */
+ cf->frm_prfx[PrfxSz] = '\0'; /* terminate prefix for printing */
+ cf->cd.cd_id = C_Null; /* base of code sequence in function */
+ cf->cd.next = NULL;
+ cf->cursor = &cf->cd; /* current place to insert code */
+ cf->call_lst = NULL; /* functions called by this function */
+ cf->creatlst = NULL; /* creates within this function */
+ cf->sig_lst = NULL; /* signals returned by this function */
+ cf->ref_cnt = 0;
+ cf->next = NULL;
+ *flst_end = cf; /* link entry onto global list */
+ flst_end = &(cf->next);
+ return cf;
+ }
+
+/*
+ * tmp_loc - allocate a value location structure for nth temporary descriptor
+ * variable in procedure frame.
+ */
+static struct val_loc *tmp_loc(n)
+int n;
+ {
+ register struct val_loc *r;
+
+ r = NewStruct(val_loc);
+ r->loc_type = V_Temp;
+ r->mod_access = M_None;
+ r->u.tmp = n;
+ return r;
+ }
+
+/*
+ * itmp_loc - allocate a value location structure for nth temporary integer
+ * variable in procedure frame.
+ */
+struct val_loc *itmp_loc(n)
+int n;
+ {
+ register struct val_loc *r;
+
+ r = NewStruct(val_loc);
+ r->loc_type = V_ITemp;
+ r->mod_access = M_None;
+ r->u.tmp = n;
+ return r;
+ }
+
+/*
+ * dtmp_loc - allocate a value location structure for nth temporary double
+ * variable in procedure frame.
+ */
+struct val_loc *dtmp_loc(n)
+int n;
+ {
+ register struct val_loc *r;
+
+ r = NewStruct(val_loc);
+ r->loc_type = V_DTemp;
+ r->mod_access = M_None;
+ r->u.tmp = n;
+ return r;
+ }
+
+/*
+ * vararg_sz - allocate a value location structure that refers to the size
+ * of the variable part of an argument list.
+ */
+static struct val_loc *vararg_sz(n)
+int n;
+ {
+ register struct val_loc *r;
+
+ r = NewStruct(val_loc);
+ r->loc_type = V_Const;
+ r->mod_access = M_None;
+ r->u.int_const = n;
+ return r;
+ }
+
+/*
+ * cvar_loc - allocate a value location structure for a C variable.
+ */
+struct val_loc *cvar_loc(name)
+char *name;
+ {
+ register struct val_loc *r;
+
+ r = NewStruct(val_loc);
+ r->loc_type = V_CVar;
+ r->mod_access = M_None;
+ r->u.name = name;
+ return r;
+ }
+
+/*
+ * var_ref - allocate a value location structure for an Icon named variable.
+ */
+static struct val_loc *var_ref(sym)
+struct lentry *sym;
+ {
+ struct val_loc *loc;
+
+ loc = NewStruct(val_loc);
+ loc->loc_type = V_NamedVar;
+ loc->mod_access = M_None;
+ loc->u.nvar = sym;
+ return loc;
+ }
+
+/*
+ * deref_cd - generate code to dereference a descriptor.
+ */
+static void deref_cd(src, dest)
+struct val_loc *src;
+struct val_loc *dest;
+ {
+ struct code *cd;
+
+ cd = alc_ary(5);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "deref(&";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = src;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ", &";
+ cd->ElemTyp(3) = A_ValLoc;
+ cd->ValLoc(3) = dest;
+ cd->ElemTyp(4) = A_Str;
+ cd->Str(4) = ");";
+ cd_add(cd);
+ }
+
+/*
+ * inv_op - directly invoke a run-time operation, in-lining it if possible.
+ */
+static struct val_loc *inv_op(n, rslt)
+nodeptr n;
+struct val_loc *rslt;
+ {
+ struct implement *impl;
+ struct code *scont_strt;
+ struct code *scont_fail;
+ struct c_fnc *fnc;
+ struct val_loc *frst_arg;
+ struct val_loc *arg_rslt;
+ struct val_loc *r;
+ struct val_loc **varg_rslt;
+ struct op_symentry *symtab;
+ struct lentry **single;
+ struct tmplftm *lifetm_ary;
+ nodeptr rslt_lftm;
+ char *sbuf;
+ int *maybe_var;
+ int may_mod;
+ int nsyms;
+ int nargs;
+ int nparms;
+ int cont_loc;
+ int flag;
+ int refs;
+ int var_args;
+ int n_varargs;
+ int arg_loc;
+ int dcl_var;
+ int i;
+ int j;
+ int v;
+
+ nargs = Val0(n);
+ impl = Impl1(n);
+ if (impl == NULL) {
+ /*
+ * We have already printed an error, just make sure we can
+ * continue.
+ */
+ return &ignore;
+ }
+
+ /*
+ * If this operation uses its result location as a work area, it must
+ * be given a tended result location and the value must be retained
+ * as long as the operation can be resumed.
+ */
+ rslt_lftm = n->lifetime;
+ if (impl->use_rslt) {
+ rslt_lftm = max_lftm(rslt_lftm, n->intrnl_lftm);
+ if (rslt == &ignore)
+ rslt = NULL; /* force allocation of temporary */
+ }
+
+ /*
+ * Determine if this operation takes a variable number of arguments
+ * and determine the size of the variable part of the arg list.
+ */
+ nparms = impl->nargs;
+ if (nparms > 0 && impl->arg_flgs[nparms - 1] & VarPrm) {
+ var_args = 1;
+ n_varargs = nargs - nparms + 1;
+ if (n_varargs < 0)
+ n_varargs = 0;
+ }
+ else {
+ var_args = 0;
+ n_varargs = 0;
+ }
+
+ /*
+ * Construct a symbol table (implemented as an array) for the operation.
+ * The symbol table includes parameters, and both the tended and
+ * ordinary variables from the RTL declare statement.
+ */
+ nsyms = (n->symtyps == NULL ? 0 : n->symtyps->nsyms);
+ if (var_args)
+ ++nsyms;
+ nsyms += impl->ntnds + impl->nvars;
+ if (nsyms > 0)
+ symtab = (struct op_symentry *)alloc((unsigned int)(nsyms *
+ sizeof(struct op_symentry)));
+ else
+ symtab = NULL;
+ for (i = 0; i < nsyms; ++i) {
+ symtab[i].n_refs = 0; /* number of non-modifying references */
+ symtab[i].n_mods = 0; /* number of modifying references */
+ symtab[i].n_rets = 0; /* number of times returned directly */
+ symtab[i].var_safe = 0; /* Icon variable arg can be passed directly */
+ symtab[i].adjust = 0; /* adjustments needed to "dereference" */
+ symtab[i].itmp_indx = -1; /* loc after "in-place" convert to C integer */
+ symtab[i].dtmp_indx = -1; /* loc after "in-place" convert to C double */
+ symtab[i].loc = NULL; /* location as a descriptor */
+ }
+
+ /*
+ * If in-lining has not been disabled or the operation is a keyword,
+ * check to see if it can reasonably be in-lined and gather information
+ * needed to in-line it.
+ */
+ if ((allow_inline || impl->oper_typ == 'K') &&
+ do_inlin(impl, n, &cont_loc, symtab, n_varargs)) {
+ /*
+ * In-line the operation.
+ */
+
+ if (impl->ret_flag & DoesRet || impl->ret_flag & DoesSusp)
+ rslt = chk_alc(rslt, rslt_lftm); /* operation produces a result */
+
+ /*
+ * Allocate arrays to hold information from type inferencing about
+ * whether arguments are variables. This is used to optimize
+ * dereferencing.
+ */
+ if (nargs > 0) {
+ maybe_var = (int *)alloc((unsigned int)(nargs * sizeof(int)));
+ single = (struct lentry **)alloc((unsigned int)(nargs *
+ sizeof(struct lentry *)));
+ }
+
+ if (var_args)
+ --nparms; /* don't deal with varargs parameter yet. */
+
+ /*
+ * Match arguments with parameters and generate code for the
+ * arguments. The type of code generated depends on the kinds
+ * of dereferencing optimizations that are possible, though
+ * in general, dereferencing must wait until all arguments are
+ * computed. Because there may be both dereferenced and undereferenced
+ * parameters for an argument, the symbol table index does not always
+ * match the argument index.
+ */
+ i = 0; /* symbol table index */
+ for (j = 0; j < nparms && j < nargs; ++j) {
+ /*
+ * Use information from type inferencing to determine if the
+ * argument might me a variable and whether it is a single
+ * known named variable.
+ */
+ maybe_var[j] = HasVar(varsubtyp(n->n_field[FrstArg + j].n_ptr->type,
+ &(single[j])));
+
+ /*
+ * Determine how many times the argument is referenced. If we
+ * optimize away return statements because we don't need the
+ * result, those references don't count. Take into account
+ * that there may be both dereferenced and undereferenced
+ * parameters for this argument.
+ */
+ if (rslt == &ignore)
+ symtab[i].n_refs -= symtab[i].n_rets;
+ refs = symtab[i].n_refs + symtab[i].n_mods;
+ flag = impl->arg_flgs[j] & (RtParm | DrfPrm);
+ if (flag == (RtParm | DrfPrm))
+ refs += symtab[i + 1].n_refs + symtab[i + 1].n_mods;
+ if (refs == 0) {
+ /*
+ * Indicate that we don't need the argument value (we must
+ * still perform the computation in case it has side effects).
+ */
+ arg_rslt = &ignore;
+ symtab[i].adjust = AdjNone;
+ }
+ else {
+ /*
+ * Decide whether the result location for the argument can be
+ * used directly as the parameter.
+ */
+ if (flag == (RtParm | DrfPrm) && symtab[i].n_refs +
+ symtab[i].n_mods == 0) {
+ /*
+ * We have both dereferenced and undereferenced parameters,
+ * but don't use the undereferenced one so ignore it.
+ */
+ symtab[i].adjust = AdjNone;
+ ++i;
+ flag = DrfPrm;
+ }
+ if (flag == DrfPrm && single[j] != NULL) {
+ /*
+ * We need only a dereferenced value, but know what variable
+ * it is in. We don't need the computed argument value, we will
+ * get it directly from the variable. If it is safe to do
+ * so, we will pass a pointer to the variable as the argument
+ * to the operation.
+ */
+ arg_rslt = &ignore;
+ symtab[i].loc = var_ref(single[j]);
+ if (symtab[i].var_safe)
+ symtab[i].adjust = AdjNone;
+ else
+ symtab[i].adjust = AdjCpy;
+ }
+ else {
+ /*
+ * Determine if the argument descriptor is modified by the
+ * operation; dereferencing a variable is a modification.
+ */
+ may_mod = (symtab[i].n_mods != 0);
+ if (flag == DrfPrm)
+ may_mod |= maybe_var[j];
+ if (n->n_field[FrstArg + j].n_ptr->reuse && may_mod) {
+ /*
+ * The parameter may be reused without recomputing
+ * the argument and the value may be modified. The
+ * argument result location and the parameter location
+ * must be separate so the parameter is reloaded upon
+ * each invocation.
+ */
+ arg_rslt = chk_alc(NULL,
+ n->n_field[FrstArg + j].n_ptr->lifetime);
+ if (flag == DrfPrm && maybe_var[j])
+ symtab[i].adjust = AdjNDrf; /* var: must dereference */
+ else
+ symtab[i].adjust = AdjCpy; /* value only: just copy */
+ }
+ else {
+ /*
+ * Argument result location will act as parameter location.
+ * Its lifetime must be as long as both that of the
+ * the argument and the parameter (operation internal
+ * lifetime).
+ */
+ arg_rslt = chk_alc(NULL, max_lftm(n->intrnl_lftm,
+ n->n_field[FrstArg + j].n_ptr->lifetime));
+ if (flag == DrfPrm && maybe_var[j])
+ symtab[i].adjust = AdjDrf; /* var: must dereference */
+ else
+ symtab[i].adjust = AdjNone;
+ }
+ symtab[i].loc = arg_rslt;
+ }
+ }
+
+ /*
+ * Generate the code for the argument.
+ */
+ gencode(n->n_field[FrstArg + j].n_ptr, arg_rslt);
+
+ if (flag == (RtParm | DrfPrm)) {
+ /*
+ * We have computed the value for the undereferenced parameter,
+ * decide how to get the dereferenced value.
+ */
+ ++i;
+ if (symtab[i].n_refs + symtab[i].n_mods == 0)
+ symtab[i].adjust = AdjNone; /* not needed, ignore */
+ else {
+ if (single[j] != NULL) {
+ /*
+ * The value is in a specific Icon variable, get it from
+ * there. If is is safe to pass the variable directly
+ * to the operation, do so.
+ */
+ symtab[i].loc = var_ref(single[j]);
+ if (symtab[i].var_safe)
+ symtab[i].adjust = AdjNone;
+ else
+ symtab[i].adjust = AdjCpy;
+ }
+ else {
+ /*
+ * If there might be a variable reference, note that it
+ * must be dereferenced. Otherwise decide whether the
+ * argument location can be used for both the dereferenced
+ * and undereferenced parameter.
+ */
+ symtab[i].loc = arg_rslt;
+ if (maybe_var[j])
+ symtab[i].adjust = AdjNDrf;
+ else if (symtab[i - 1].n_mods + symtab[i].n_mods == 0)
+ symtab[i].adjust = AdjNone;
+ else
+ symtab[i].adjust = AdjCpy;
+ }
+ }
+ }
+ ++i;
+ }
+
+ /*
+ * Fill out parameter list with null values.
+ */
+ while (j < nparms) {
+ int k, kn;
+ kn = 0;
+ if (impl->arg_flgs[j] & RtParm)
+ ++kn;
+ if (impl->arg_flgs[j] & DrfPrm)
+ ++kn;
+ for (k = 0; k < kn; ++k) {
+ if (symtab[i].n_refs + symtab[i].n_mods > 0) {
+ arg_rslt = chk_alc(NULL, n->intrnl_lftm);
+ cd_add(asgn_null(arg_rslt));
+ symtab[i].loc = arg_rslt;
+ }
+ symtab[i].adjust = AdjNone;
+ ++i;
+ }
+ ++j;
+ }
+
+ if (var_args) {
+ /*
+ * Compute variable part of argument list.
+ */
+ ++nparms; /* add varargs parameter back into parameter list */
+
+ /*
+ * The variable part of the parameter list must be in contiguous
+ * descriptors. Create location and lifetime arrays for use in
+ * allocating the descriptors.
+ */
+ if (n_varargs > 0) {
+ varg_rslt = (struct val_loc **)alloc((unsigned int)(n_varargs *
+ sizeof(struct val_loc *)));
+ lifetm_ary = alc_lftm(n_varargs, NULL);
+ }
+
+ flag = impl->arg_flgs[j] & (RtParm | DrfPrm);
+
+ /*
+ * Compute the lifetime of the elements of the varargs parameter array.
+ */
+ for (v = 0; v < n_varargs; ++v) {
+ /*
+ * Use information from type inferencing to determine if the
+ * argument might me a variable and whether it is a single
+ * known named variable.
+ */
+ maybe_var[j + v] = HasVar(varsubtyp(
+ n->n_field[FrstArg+j+v].n_ptr->type, &(single[j + v])));
+
+ /*
+ * Determine if the elements of the vararg parameter array
+ * might be modified. If it is a variable, dereferencing
+ * modifies it.
+ */
+ may_mod = (symtab[j].n_mods != 0);
+ if (flag == DrfPrm)
+ may_mod |= maybe_var[j + v];
+
+ if ((flag == DrfPrm && single[j + v] != NULL) ||
+ (n->n_field[FrstArg + j + v].n_ptr->reuse && may_mod)) {
+ /*
+ * The argument value is only placed in the vararg parameter
+ * array during "dereferencing". So the lifetime of the array
+ * element is the lifetime of the parameter and the element
+ * is not used until dereferencing.
+ */
+ lifetm_ary[v].lifetime = n->intrnl_lftm;
+ lifetm_ary[v].cur_status = n->postn;
+ }
+ else {
+ /*
+ * The argument is computed into the vararg parameter array.
+ * The lifetime of the array element encompasses both
+ * the lifetime of the argument and the parameter. The
+ * element is used as soon as the argument is computed.
+ */
+ lifetm_ary[v].lifetime = max_lftm(n->intrnl_lftm,
+ n->n_field[FrstArg+j+v].n_ptr->lifetime);
+ lifetm_ary[v].cur_status = n->n_field[FrstArg+j+v].n_ptr->postn;
+ }
+ }
+
+ /*
+ * Allocate (reserve) the array of temporary variables for the
+ * vararg list.
+ */
+ if (n_varargs > 0) {
+ arg_loc = alc_tmp(n_varargs, lifetm_ary);
+ free((char *)lifetm_ary);
+ }
+
+ /*
+ * Generate code to compute arguments.
+ */
+ for (v = 0; v < n_varargs; ++v) {
+ may_mod = (symtab[j].n_mods != 0);
+ if (flag == DrfPrm)
+ may_mod |= maybe_var[j + v];
+ if (flag == DrfPrm && single[j + v] != NULL) {
+ /*
+ * We need a dereferenced value and it is in a known place: a
+ * named variable; don't bother saving the result of the
+ * argument computation.
+ */
+ r = &ignore;
+ }
+ else if (n->n_field[FrstArg + j + v].n_ptr->reuse && may_mod) {
+ /*
+ * The argument can be reused without being recomputed and
+ * the parameter may be modified, so we cannot safely
+ * compute the argument into the vararg parameter array; we
+ * must compute it elsewhere and copy (dereference) it at the
+ * beginning of the operation. Let gencode allocate an argument
+ * result location.
+ */
+ r = NULL;
+ }
+ else {
+ /*
+ * We can compute the argument directly into the vararg
+ * parameter array.
+ */
+ r = tmp_loc(arg_loc + v);
+ }
+ varg_rslt[v] = gencode(n->n_field[FrstArg + j + v].n_ptr, r);
+ }
+
+ setloc(n);
+ /*
+ * Dereference or copy argument values that are not already in vararg
+ * parameter list. Preceding arguments are dereferenced later, but
+ * it is okay if dereferencing is out-of-order.
+ */
+ for (v = 0; v < n_varargs; ++v) {
+ if (flag == DrfPrm && single[j + v] != NULL) {
+ /*
+ * Copy the value from the known named variable into the
+ * parameter list.
+ */
+ varg_rslt[v] = var_ref(single[j + v]);
+ cd_add(mk_cpyval(tmp_loc(arg_loc + v), varg_rslt[v]));
+ }
+ else if (flag == DrfPrm && maybe_var[j + v]) {
+ /*
+ * Dereference the argument into the parameter list.
+ */
+ deref_cd(varg_rslt[v], tmp_loc(arg_loc + v));
+ }
+ else if (arg_loc + v != varg_rslt[v]->u.tmp) {
+ /*
+ * The argument is a dereferenced value, but is not yet
+ * in the parameter list; copy it there.
+ */
+ cd_add(mk_cpyval(tmp_loc(arg_loc + v), varg_rslt[v]));
+ }
+ tmp_status[arg_loc + v] = InUse; /* parameter location in use */
+ }
+
+ /*
+ * The vararg parameter gets the address of the first element
+ * in the variable part of the argument list and the size
+ * parameter gets the number of elements in the list.
+ */
+ if (n_varargs > 0) {
+ free((char *)varg_rslt);
+ symtab[i].loc = tmp_loc(arg_loc);
+ }
+ else
+ symtab[i].loc = chk_alc(NULL, n); /* dummy arg location */
+ symtab[i].loc->mod_access = M_Addr;
+ ++i;
+ symtab[i].loc = vararg_sz(n_varargs);
+ ++i;
+ }
+ else {
+ /*
+ * Compute extra arguments, but discard the results.
+ */
+ while (j < nargs) {
+ gencode(n->n_field[FrstArg + j].n_ptr, &ignore);
+ ++j;
+ }
+ }
+
+ if (nargs > 0) {
+ free((char *)maybe_var);
+ free((char *)single);
+ }
+
+ /*
+ * If execution does not continue through the parameter evaluation,
+ * don't try to generate in-line code. A lack of parameter types
+ * will cause problems with some in-line type conversions.
+ */
+ if (!past_prms(n))
+ return rslt;
+
+ setloc(n);
+
+ dcl_var = i;
+
+ /*
+ * Perform any needed copying or dereferencing.
+ */
+ for (i = 0; i < nsyms; ++i) {
+ switch (symtab[i].adjust) {
+ case AdjNDrf:
+ /*
+ * Dereference into a new temporary which is used as the
+ * parameter.
+ */
+ arg_rslt = chk_alc(NULL, n->intrnl_lftm);
+ deref_cd(symtab[i].loc, arg_rslt);
+ symtab[i].loc = arg_rslt;
+ break;
+ case AdjDrf:
+ /*
+ * Dereference in place.
+ */
+ deref_cd(symtab[i].loc, symtab[i].loc);
+ break;
+ case AdjCpy:
+ /*
+ * Copy into a new temporary which is used as the
+ * parameter.
+ */
+ arg_rslt = chk_alc(NULL, n->intrnl_lftm);
+ cd_add(mk_cpyval(arg_rslt, symtab[i].loc));
+ symtab[i].loc = arg_rslt;
+ break;
+ case AdjNone:
+ break; /* nothing need be done */
+ }
+ }
+
+ switch (cont_loc) {
+ case SepFnc:
+ /*
+ * success continuation must be in a separate function.
+ */
+ fnc = alc_fnc();
+ sbuf = (char *)alloc((unsigned int)(strlen(impl->name) + 5));
+ sprintf(sbuf, "end %s", impl->name);
+ scont_strt = alc_lbl(sbuf, 0);
+ cd_add(scont_strt);
+ cur_fnc->cursor = scont_strt->prev; /* put oper before label */
+ gen_inlin(impl->in_line, rslt, &scont_strt, NULL, fnc, impl,
+ nsyms, symtab, n, dcl_var, n_varargs);
+ cur_fnc->cursor = scont_strt;
+ callc_add(fnc);
+ cur_fnc = fnc;
+ on_failure = &resume;
+ break;
+ case SContIL:
+ /*
+ * one suspend an no return: success continuation is put in-line.
+ */
+ gen_inlin(impl->in_line, rslt, &scont_strt, &scont_fail, NULL, impl,
+ nsyms, symtab, n, dcl_var, n_varargs);
+ cur_fnc->cursor = scont_strt;
+ on_failure = scont_fail;
+ break;
+ case EndOper:
+ /*
+ * no suspends: success continuation goes at end of operation.
+ */
+
+ sbuf = (char *)alloc((unsigned int)(strlen(impl->name) + 5));
+ sprintf(sbuf, "end %s", impl->name);
+ scont_strt = alc_lbl(sbuf, 0);
+ cd_add(scont_strt);
+ cur_fnc->cursor = scont_strt->prev; /* put operation before lbl */
+ gen_inlin(impl->in_line, rslt, &scont_strt, NULL, NULL, impl,
+ nsyms, symtab, n, dcl_var, n_varargs);
+ cur_fnc->cursor = scont_strt;
+ break;
+ }
+ }
+ else {
+ /*
+ * Do not in-line operation.
+ */
+ implproto(impl);
+ frst_arg = gen_args(n, 2, nargs);
+ setloc(n);
+ if (impl->ret_flag & (DoesRet | DoesSusp))
+ rslt = chk_alc(rslt, rslt_lftm);
+ mk_callop(oper_name(impl), impl->ret_flag, frst_arg, nargs, rslt,
+ 0);
+ }
+ if (symtab != NULL)
+ free((char *)symtab);
+ return rslt;
+ }
+
+/*
+ * max_lftm - given two lifetimes (in the form of nodes) return the
+ * maximum one.
+ */
+static nodeptr max_lftm(n1, n2)
+nodeptr n1;
+nodeptr n2;
+ {
+ if (n1 == NULL)
+ return n2;
+ else if (n2 == NULL)
+ return n1;
+ else if (n1->postn > n2->postn)
+ return n1;
+ else
+ return n2;
+ }
+
+/*
+ * inv_prc - directly invoke a procedure.
+ */
+static struct val_loc *inv_prc(n, rslt)
+nodeptr n;
+struct val_loc *rslt;
+ {
+ struct pentry *proc;
+ struct val_loc *r;
+ struct val_loc *arg1rslt;
+ struct val_loc *var_part;
+ int *must_deref;
+ struct lentry **single;
+ struct val_loc **arg_rslt;
+ struct code *cd;
+ struct tmplftm *lifetm_ary;
+ char *sbuf;
+ int nargs;
+ int nparms;
+ int i, j;
+ int arg_loc;
+ int var_sz;
+ int var_loc;
+
+ /*
+ * This procedure is implemented without argument list adjustment or
+ * dereferencing, so they must be done before the call.
+ */
+ nargs = Val0(n); /* number of arguments */
+ proc = Proc1(n);
+ nparms = Abs(proc->nargs);
+
+ if (nparms > 0) {
+ must_deref = (int *)alloc((unsigned int)(nparms * sizeof(int)));
+ single = (struct lentry **)alloc((unsigned int)(nparms *
+ sizeof(struct lentry *)));
+ arg_rslt = (struct val_loc **)alloc((unsigned int)(nparms *
+ sizeof(struct val_loc *)));
+ }
+
+ /*
+ * Allocate a work area of temporaries to use as argument list. If
+ * an argument can be reused without being recomputed, it must not
+ * be computed directly into the work area. It will be copied or
+ * dereferenced into the work area when execution reaches the
+ * operation. If an argument is a single named variable, it can
+ * be dereferenced directly into the argument location. These
+ * conditions affect when the temporary will receive a value.
+ */
+ if (nparms > 0)
+ lifetm_ary = alc_lftm(nparms, NULL);
+ for (i = 0; i < nparms; ++i)
+ lifetm_ary[i].lifetime = n->intrnl_lftm;
+ for (i = 0; i < nparms && i < nargs; ++i) {
+ must_deref[i] = HasVar(varsubtyp(n->n_field[FrstArg + i].n_ptr->type,
+ &(single[i])));
+ if (single[i] != NULL || n->n_field[FrstArg + i].n_ptr->reuse)
+ lifetm_ary[i].cur_status = n->postn;
+ else
+ lifetm_ary[i].cur_status = n->n_field[FrstArg + i].n_ptr->postn;
+ }
+ while (i < nparms) {
+ lifetm_ary[i].cur_status = n->postn; /* arg list extension */
+ ++i;
+ }
+ if (proc->nargs < 0)
+ lifetm_ary[nparms - 1].cur_status = n->postn; /* variable part */
+
+ if (nparms > 0) {
+ arg_loc = alc_tmp(nparms, lifetm_ary);
+ free((char *)lifetm_ary);
+ }
+ if (proc->nargs < 0)
+ --nparms; /* treat variable part specially */
+ for (i = 0; i < nparms && i < nargs; ++i) {
+ if (single[i] != NULL)
+ r = &ignore; /* we know where the dereferenced value is */
+ else if (n->n_field[FrstArg + i].n_ptr->reuse)
+ r = NULL; /* let gencode allocate a new temporary */
+ else
+ r = tmp_loc(arg_loc + i);
+ arg_rslt[i] = gencode(n->n_field[FrstArg + i].n_ptr, r);
+ }
+
+ /*
+ * If necessary, fill out argument list with nulls.
+ */
+ while (i < nparms) {
+ cd_add(asgn_null(tmp_loc(arg_loc + i)));
+ tmp_status[arg_loc + i] = InUse;
+ ++i;
+ }
+
+ if (proc->nargs < 0) {
+ /*
+ * handle variable part of list.
+ */
+ var_sz = nargs - nparms;
+
+ if (var_sz > 0) {
+ lifetm_ary = alc_lftm(var_sz, &n->n_field[FrstArg + nparms]);
+ var_loc = alc_tmp(var_sz, lifetm_ary);
+ free((char *)lifetm_ary);
+ for (j = 0; j < var_sz; ++j) {
+ gencode(n->n_field[FrstArg + nparms + j].n_ptr,
+ tmp_loc(var_loc + j));
+ }
+ }
+ }
+ else {
+ /*
+ * If there are extra arguments, compute them, but discard the
+ * results.
+ */
+ while (i < nargs) {
+ gencode(n->n_field[FrstArg + i].n_ptr, &ignore);
+ ++i;
+ }
+ }
+
+ setloc(n);
+ /*
+ * Dereference or copy argument values that are not already in argument
+ * list as dereferenced values.
+ */
+ for (i = 0; i < nparms && i < nargs; ++i) {
+ if (must_deref[i]) {
+ if (single[i] == NULL) {
+ deref_cd(arg_rslt[i], tmp_loc(arg_loc + i));
+ }
+ else {
+ arg_rslt[i] = var_ref(single[i]);
+ cd_add(mk_cpyval(tmp_loc(arg_loc + i), arg_rslt[i]));
+ }
+ }
+ else if (n->n_field[FrstArg + i].n_ptr->reuse)
+ cd_add(mk_cpyval(tmp_loc(arg_loc + i), arg_rslt[i]));
+ tmp_status[arg_loc + i] = InUse;
+ }
+
+ if (proc->nargs < 0) {
+ var_part = tmp_loc(arg_loc + nparms);
+ tmp_status[arg_loc + nparms] = InUse;
+ if (var_sz <= 0) {
+ cd = alc_ary(3);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "varargs(NULL, 0, &";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = var_part;
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ");";
+ }
+ else {
+ cd = alc_ary(7);
+ cd->ElemTyp(0) = A_Str;
+ cd->Str(0) = "varargs(&";
+ cd->ElemTyp(1) = A_ValLoc;
+ cd->ValLoc(1) = tmp_loc(var_loc);
+ cd->ElemTyp(2) = A_Str;
+ cd->Str(2) = ", ";
+ cd->ElemTyp(3) = A_Intgr;
+ cd->Intgr(3) = var_sz;
+ cd->ElemTyp(4) = A_Str;
+ cd->Str(4) = ", &";
+ cd->ElemTyp(5) = A_ValLoc;
+ cd->ValLoc(5) = var_part;
+ cd->ElemTyp(6) = A_Str;
+ cd->Str(6) = ");";
+ }
+ cd_add(cd);
+ ++nparms; /* include variable part in call */
+ }
+
+ if (nparms > 0) {
+ free((char *)must_deref);
+ free((char *)single);
+ free((char *)arg_rslt);
+ }
+
+ sbuf = (char *)alloc((unsigned int)(strlen(proc->name) + PrfxSz + 3));
+ sprintf(sbuf, "P%s_%s", proc->prefix, proc->name);
+ if (nparms > 0)
+ arg1rslt = tmp_loc(arg_loc);
+ else
+ arg1rslt = NULL;
+ if (proc->ret_flag & (DoesRet | DoesSusp))
+ rslt = chk_alc(rslt, n->lifetime);
+ mk_callop(sbuf, proc->ret_flag, arg1rslt, nargs, rslt, 1);
+ return rslt;
+ }
+
+/*
+ * endlife - link a temporary variable onto the list to be freed when
+ * execution reaches a node.
+ */
+static void endlife(kind, indx, old, n)
+int kind;
+int indx;
+int old;
+nodeptr n;
+ {
+ struct freetmp *freetmp;
+
+ if ((freetmp = freetmp_pool) == NULL)
+ freetmp = NewStruct(freetmp);
+ else
+ freetmp_pool = freetmp_pool->next;
+ freetmp->kind = kind;
+ freetmp->indx = indx;
+ freetmp->old = old;
+ freetmp->next = n->freetmp;
+ n->freetmp = freetmp;
+ }
+
+/*
+ * alc_tmp - allocate a block of temporary variables with the given lifetimes.
+ */
+static int alc_tmp(num, lifetm_ary)
+int num;
+struct tmplftm *lifetm_ary;
+ {
+ int i, j, k;
+ register int status;
+ int *new_status;
+ int new_size;
+
+ i = 0;
+ for (;;) {
+ if (i + num > status_sz) {
+ /*
+ * The status array is too small, expand it.
+ */
+ new_size = status_sz + Max(num, status_sz);
+ new_status = (int *)alloc((unsigned int)(new_size * sizeof(int)));
+ k = 0;
+ while (k < status_sz) {
+ new_status[k] = tmp_status[k];
+ ++k;
+ }
+ while (k < new_size) {
+ new_status[k] = NotAlloc;
+ ++k;
+ }
+ free((char *)tmp_status);
+ tmp_status = new_status;
+ status_sz = new_size;
+ }
+ for (j = 0; j < num; ++j) {
+ status = tmp_status[i + j];
+ if (status != NotAlloc &&
+ (status == InUse || status <= lifetm_ary[j].lifetime->postn))
+ break;
+ }
+ /*
+ * Did we find a block of temporaries that we can use?
+ */
+ if (j == num) {
+ while (--j >= 0) {
+ endlife(DescTmp, i + j, tmp_status[i + j], lifetm_ary[j].lifetime);
+ tmp_status[i + j] = lifetm_ary[j].cur_status;
+ }
+ if (i + num > num_tmp)
+ num_tmp = i + num;
+ return i;
+ }
+ ++i;
+ }
+ }
+
+/*
+ * alc_lftm - allocate an array of lifetime information for an argument
+ * list.
+ */
+static struct tmplftm *alc_lftm(num, args)
+int num;
+union field *args;
+ {
+ struct tmplftm *lifetm_ary;
+ int i;
+
+ lifetm_ary = (struct tmplftm *)alloc((unsigned int)(num *
+ sizeof(struct tmplftm)));
+ if (args != NULL)
+ for (i = 0; i < num; ++i) {
+ lifetm_ary[i].cur_status = args[i].n_ptr->postn; /* reserved for arg */
+ lifetm_ary[i].lifetime = args[i].n_ptr->lifetime;
+ }
+ return lifetm_ary;
+ }
+
+/*
+ * alc_itmp - allocate a temporary C integer variable.
+ */
+int alc_itmp(lifetime)
+nodeptr lifetime;
+ {
+ int i, j;
+ int new_size;
+
+ i = 0;
+ while (i < istatus_sz && itmp_status[i] == InUse)
+ ++i;
+ if (i >= istatus_sz) {
+ /*
+ * The status array is too small, expand it.
+ */
+ free((char *)itmp_status);
+ new_size = istatus_sz * 2;
+ itmp_status = (int *)alloc((unsigned int)(new_size * sizeof(int)));
+ j = 0;
+ while (j < istatus_sz)
+ itmp_status[j++] = InUse;
+ while (j < new_size)
+ itmp_status[j++] = NotAlloc;
+ istatus_sz = new_size;
+ }
+ endlife(CIntTmp, i, NotAlloc, lifetime);
+ itmp_status[i] = InUse;
+ if (num_itmp < i + 1)
+ num_itmp = i + 1;
+ return i;
+ }
+
+/*
+ * alc_dtmp - allocate a temporary C integer variable.
+ */
+int alc_dtmp(lifetime)
+nodeptr lifetime;
+ {
+ int i, j;
+ int new_size;
+
+ i = 0;
+ while (i < dstatus_sz && dtmp_status[i] == InUse)
+ ++i;
+ if (i >= dstatus_sz) {
+ /*
+ * The status array is too small, expand it.
+ */
+ free((char *)dtmp_status);
+ new_size = dstatus_sz * 2;
+ dtmp_status = (int *)alloc((unsigned int)(new_size * sizeof(int)));
+ j = 0;
+ while (j < dstatus_sz)
+ dtmp_status[j++] = InUse;
+ while (j < new_size)
+ dtmp_status[j++] = NotAlloc;
+ dstatus_sz = new_size;
+ }
+ endlife(CDblTmp, i, NotAlloc, lifetime);
+ dtmp_status[i] = InUse;
+ if (num_dtmp < i + 1)
+ num_dtmp = i + 1;
+ return i;
+ }
+
+/*
+ * alc_sbufs - allocate a block of string buffers with the given lifetime.
+ */
+int alc_sbufs(num, lifetime)
+int num;
+nodeptr lifetime;
+ {
+ int i, j, k;
+ int *new_status;
+ int new_size;
+
+ i = 0;
+ for (;;) {
+ if (i + num > sstatus_sz) {
+ /*
+ * The status array is too small, expand it.
+ */
+ new_size = sstatus_sz + Max(num, sstatus_sz);
+ new_status = (int *)alloc((unsigned int)(new_size * sizeof(int)));
+ k = 0;
+ while (k < sstatus_sz) {
+ new_status[k] = sbuf_status[k];
+ ++k;
+ }
+ while (k < new_size) {
+ new_status[k] = NotAlloc;
+ ++k;
+ }
+ free((char *)sbuf_status);
+ sbuf_status = new_status;
+ sstatus_sz = new_size;
+ }
+ for (j = 0; j < num && sbuf_status[i + j] == NotAlloc; ++j)
+ ;
+ /*
+ * Did we find a block of buffers that we can use?
+ */
+ if (j == num) {
+ while (--j >= 0) {
+ endlife(SBuf, i + j, sbuf_status[i + j], lifetime);
+ sbuf_status[i + j] = InUse;
+ }
+ if (i + num > num_sbuf)
+ num_sbuf = i + num;
+ return i;
+ }
+ ++i;
+ }
+ }
+
+/*
+ * alc_cbufs - allocate a block of cset buffers with the given lifetime.
+ */
+int alc_cbufs(num, lifetime)
+int num;
+nodeptr lifetime;
+ {
+ int i, j, k;
+ int *new_status;
+ int new_size;
+
+ i = 0;
+ for (;;) {
+ if (i + num > cstatus_sz) {
+ /*
+ * The status array is too small, expand it.
+ */
+ new_size = cstatus_sz + Max(num, cstatus_sz);
+ new_status = (int *)alloc((unsigned int)(new_size * sizeof(int)));
+ k = 0;
+ while (k < cstatus_sz) {
+ new_status[k] = cbuf_status[k];
+ ++k;
+ }
+ while (k < new_size) {
+ new_status[k] = NotAlloc;
+ ++k;
+ }
+ free((char *)cbuf_status);
+ cbuf_status = new_status;
+ cstatus_sz = new_size;
+ }
+ for (j = 0; j < num && cbuf_status[i + j] == NotAlloc; ++j)
+ ;
+ /*
+ * Did we find a block of buffers that we can use?
+ */
+ if (j == num) {
+ while (--j >= 0) {
+ endlife(CBuf, i + j, cbuf_status[i + j], lifetime);
+ cbuf_status[i + j] = InUse;
+ }
+ if (i + num > num_cbuf)
+ num_cbuf = i + num;
+ return i;
+ }
+ ++i;
+ }
+ }