/* * rttilc.c - routines to construct pieces of C code to put in the data base * as in-line code. * * In-line C code is represented internally as a linked list of structures. * The information contained in each structure depends on the type of code * being represented. Some structures contain other fragments of C code. * Code that does not require special processing is stored as strings. These * strings are accumulated in a buffer until it is full or code that cannot * be represented as a string must be produced. At that point, the string * in placed in a structure and put on the list. */ #include "rtt.h" #ifndef Rttx /* * prototypes for static functions. */ static void add_ptr (struct node *dcltor); static void alloc_ilc (int il_c_type); static void flush_str (void); static void ilc_chnl (struct token *t); static void ilc_cnv (struct node *cnv_typ, struct node *src, struct node *dflt, struct node *dest); static void ilc_cgoto (int neg, struct node *cond, word lbl); static void ilc_goto (word lbl); static void ilc_lbl (word lbl); static void ilc_ret (struct token *t, int ilc_typ, struct node *n); static void ilc_str (char *s); static void ilc_tok (struct token *t); static void ilc_var (struct sym_entry *sym, int just_desc, int may_mod); static void ilc_walk (struct node *n, int may_mod, int const_cast); static void init_ilc (void); static void insrt_str (void); static void new_ilc (int il_c_type); static struct il_c *sep_ilc (char *s1,struct node *n,char *s2); #define SBufSz 256 static char sbuf[SBufSz]; /* buffer for constructing fragments of code */ static int nxt_char; /* next position in sbuf */ static struct token *line_ref; /* "recent" token for comparing line number */ static struct il_c ilc_base; /* base for list of in-line C code */ static struct il_c *ilc_cur; /* current end of list of in-line C code */ static int insert_nl; /* flag: new-line should be inserted in code */ static word cont_lbl = 0; /* destination label for C continue statement */ static word brk_lbl = 0; /* destination label for C break statement */ /* * inlin_c - Create a self-contained piece of in-line C code from a syntax * sub-tree. */ struct il_c *inlin_c(n, may_mod) struct node *n; int may_mod; { init_ilc(); /* initialize code list and string buffer */ ilc_walk(n, may_mod, 0); /* translate the syntax sub-tree */ flush_str(); /* flush string buffer to code list */ return ilc_base.next; } /* * simpl_dcl - produce a simple declaration both in the output file and as * in-line C code. */ struct il_c *simpl_dcl(tqual, addr_of, sym) char *tqual; int addr_of; struct sym_entry *sym; { init_ilc(); /* initialize code list and string buffer */ prt_str(tqual, 0); ilc_str(tqual); if (addr_of) { prt_str("*", 0); ilc_str("*"); } prt_str(sym->image, 0); ilc_str(sym->image); prt_str(";", 0); ForceNl(); flush_str(); /* flush string buffer to code list */ return ilc_base.next; } /* * parm_dcl - produce the declaration for a parameter to a body function. * Print it in the output file and proceduce in-line C code for it. */ struct il_c *parm_dcl(addr_of, sym) int addr_of; struct sym_entry *sym; { init_ilc(); /* initialize code list and string buffer */ /* * Produce type-qualifier list, but without non-type information. */ just_type(sym->u.declare_var.tqual, 0, 1); prt_str(" ", 0); ilc_str(" "); /* * If the caller requested another level of indirection on the * declaration add it. */ if (addr_of) add_ptr(sym->u.declare_var.dcltor); else { c_walk(sym->u.declare_var.dcltor, 0, 0); ilc_walk(sym->u.declare_var.dcltor, 0, 0); } prt_str(";", 0); ForceNl(); flush_str(); /* flush string buffer to code list */ return ilc_base.next; } /* * add_ptr - add another level of indirection to a declarator. Print it in * the output file and proceduce in-line C code. */ static void add_ptr(dcltor) struct node *dcltor; { while (dcltor->nd_id == ConCatNd) { c_walk(dcltor->u[0].child, IndentInc, 0); ilc_walk(dcltor->u[0].child, 0, 0); dcltor = dcltor->u[1].child; } switch (dcltor->nd_id) { case PrimryNd: /* * We have reached the name, add a level of indirection. */ prt_str("(*", IndentInc); ilc_str("(*"); prt_str(dcltor->tok->image, IndentInc); ilc_str(dcltor->tok->image); prt_str(")", IndentInc); ilc_str(")"); break; case PrefxNd: /* * (...) */ prt_str("(", IndentInc); ilc_str("("); add_ptr(dcltor->u[0].child); prt_str(")", IndentInc); ilc_str(")"); break; case BinryNd: if (dcltor->tok->tok_id == ')') { /* * Function declaration. */ add_ptr(dcltor->u[0].child); prt_str("(", IndentInc); ilc_str("("); c_walk(dcltor->u[1].child, IndentInc, 0); ilc_walk(dcltor->u[1].child, 0, 0); prt_str(")", IndentInc); ilc_str(")"); } else { /* * Array. */ add_ptr(dcltor->u[0].child); prt_str("[", IndentInc); ilc_str("["); c_walk(dcltor->u[1].child, IndentInc, 0); ilc_walk(dcltor->u[1].child, 0, 0); prt_str("]", IndentInc); ilc_str("]"); } } } /* * bdy_prm - produce the code that must be be supplied as the argument * to the call of a body function. */ struct il_c *bdy_prm(addr_of, just_desc, sym, may_mod) int addr_of; int just_desc; struct sym_entry *sym; int may_mod; { init_ilc(); /* initialize code list and string buffer */ if (addr_of) ilc_str("&("); /* call-by-reference parameter */ ilc_var(sym, just_desc, may_mod); /* variable to pass as argument */ if (addr_of) ilc_str(")"); flush_str(); /* flush string buffer to code list */ return ilc_base.next; } /* * ilc_dcl - produce in-line code for a C declaration. */ struct il_c *ilc_dcl(tqual, dcltor, init) struct node *tqual; struct node *dcltor; struct node *init; { init_ilc(); /* initialize code list and string buffer */ ilc_walk(tqual, 0, 0); ilc_str(" "); ilc_walk(dcltor, 0, 0); if (init != NULL) { ilc_str(" = "); ilc_walk(init, 0, 0); } ilc_str(";"); flush_str(); /* flush string buffer to code list */ return ilc_base.next; } /* * init_ilc - initialize the code list by pointing to ilc_base. Initialize * the string buffer. */ static void init_ilc() { nxt_char = 0; line_ref = NULL; insert_nl = 0; ilc_base.il_c_type = 0; ilc_base.next = NULL; ilc_cur = &ilc_base; } /* * - ilc_chnl - check for new-line. */ static void ilc_chnl(t) struct token *t; { /* * See if this is a reasonable place to put a newline. */ if (t->flag & LineChk) { if (line_ref != NULL && (t->fname != line_ref->fname || t->line != line_ref->line)) insert_nl = 1; line_ref = t; } } /* * ilc_tok - convert a token to its string representation, quoting it * if it is a string or character literal. */ static void ilc_tok(t) struct token *t; { char *s; ilc_chnl(t); s = t->image; switch (t->tok_id) { case StrLit: ilc_str("\""); ilc_str(s); ilc_str("\""); break; case LStrLit: ilc_str("L\""); ilc_str(s); ilc_str("\""); break; case CharConst: ilc_str("'"); ilc_str(s); ilc_str("'"); break; case LCharConst: ilc_str("L'"); ilc_str(s); ilc_str("'"); break; default: ilc_str(s); } } /* * ilc_str - append a string to the string buffer. */ static void ilc_str(s) char *s; { /* * see if a new-line is needed before the string */ if (insert_nl && (nxt_char == 0 || sbuf[nxt_char - 1] != '\n')) { insert_nl = 0; ilc_str("\n"); } /* * Put the string in the buffer. If the buffer is full, flush it * to an element in the in-line code list. */ while (*s != '\0') { if (nxt_char >= SBufSz - 1) insrt_str(); sbuf[nxt_char++] = *s++; } } /* * insrt_str - insert the string in the buffer into the list of in-line * code. */ static void insrt_str() { alloc_ilc(ILC_Str); sbuf[nxt_char] = '\0'; ilc_cur->s = salloc(sbuf); nxt_char = 0; } /* * flush_str - if the string buffer is not empty, flush it to the list * of in-line code. */ static void flush_str() { if (insert_nl) ilc_str(""); if (nxt_char != 0) insrt_str(); } /* * new_ilc - create a new element for the list of in-line C code. This * is called for non-string elements. If necessary it flushes the * string buffer to another element first. */ static void new_ilc(il_c_type) int il_c_type; { flush_str(); alloc_ilc(il_c_type); } /* * alloc_ilc - allocate a new element for the list of in-line C code * and add it to the list. */ static void alloc_ilc(il_c_type) int il_c_type; { int i; ilc_cur->next = NewStruct(il_c); ilc_cur = ilc_cur->next; ilc_cur->next = NULL; ilc_cur->il_c_type = il_c_type; for (i = 0; i < 3; ++i) ilc_cur->code[i] = NULL; ilc_cur->n = 0; ilc_cur->s = NULL; } /* * sep_ilc - translate the syntax tree, n, (possibly surrounding it by * strings) into a sub-list of in-line C code, remove the sub-list from * the main list, and return it. */ static struct il_c *sep_ilc(s1, n, s2) char *s1; struct node *n; char *s2; { struct il_c *ilc; ilc = ilc_cur; /* remember the starting point in the main list */ if (s1 != NULL) ilc_str(s1); ilc_walk(n, 0, 0); if (s2 != NULL) ilc_str(s2); flush_str(); /* * Reset the main list to its condition upon entry, and return the sublist * created from s1, n, and s2. */ ilc_cur = ilc; ilc = ilc_cur->next; ilc_cur->next = NULL; return ilc; } /* * ilc_var - create in-line C code for a variable in the symbol table. */ static void ilc_var(sym, just_desc, may_mod) struct sym_entry *sym; int just_desc; int may_mod; { if (sym->il_indx >= 0) { /* * This symbol will be in symbol table iconc builds from the * data base entry. iconc needs to know if this is a modifying * reference so it can perform optimizations. This is indicated by * may_mod. Some variables are implemented as the vword of a * descriptor. Sometime the entire descriptor must be accessed. * This is indicated by just_desc. */ if (may_mod) { new_ilc(ILC_Mod); if (sym->id_type & DrfPrm) sym->u.param_info.parm_mod |= 1; } else new_ilc(ILC_Ref); ilc_cur->n = sym->il_indx; if (just_desc) ilc_cur->s = "d"; } else switch (sym->id_type) { case TndDesc: /* * variable declared: tended struct descrip ... */ new_ilc(ILC_Tend); ilc_cur->n = sym->t_indx; /* index into tended variables */ break; case TndStr: /* * variable declared: tended char *... */ new_ilc(ILC_Tend); ilc_cur->n = sym->t_indx; /* index into tended variables */ ilc_str(".vword.sptr"); /* get string pointer from vword union */ break; case TndBlk: /* * If blk_name field is null, this variable was declared: * tended union block *... * otherwise it was declared: * tended struct *... */ if (sym->u.tnd_var.blk_name != NULL) { /* * Cast the "union block *" from the vword to the correct * struct pointer. This cast can be used as an r-value or * an l-value. */ ilc_str("(*(struct "); ilc_str(sym->u.tnd_var.blk_name); ilc_str("**)&"); } new_ilc(ILC_Tend); ilc_cur->n = sym->t_indx; /* index into tended variables */ ilc_str(".vword.bptr"); /* get block pointer from vword union */ if (sym->u.tnd_var.blk_name != NULL) ilc_str(")"); break; case RsltLoc: /* * This is the special variable for the result of the operation. * iconc needs to know if this is a modifying reference so it * can perform optimizations. */ if (may_mod) new_ilc(ILC_Mod); else new_ilc(ILC_Ref); ilc_cur->n = RsltIndx; break; default: /* * This is a variable with an ordinary declaration. Access it by * its identifier. */ ilc_str(sym->image); } } /* * ilc_walk - walk the syntax tree for C code producing a list of "in-line" * code. This function needs to know if the code is in a modifying context, * such as the left-hand-side of an assignment. */ static void ilc_walk(n, may_mod, const_cast) struct node *n; int may_mod; int const_cast; { struct token *t; struct node *n1; struct node *n2; struct sym_entry *sym; word cont_sav; word brk_sav; word l1, l2; int typcd; if (n == NULL) return; t = n->tok; switch (n->nd_id) { case PrimryNd: /* * Primary expressions consisting of a single token. */ switch (t->tok_id) { case Fail: /* * fail statement. Note that this operaion can fail, output * the corresponding "in-line" code, and make sure we have * seen an abstract clause of some kind. */ cur_impl->ret_flag |= DoesFail; insert_nl = 1; new_ilc(ILC_Fail); insert_nl = 1; line_ref = NULL; chkabsret(t, SomeType); break; case Errorfail: /* * errorfail statement. Note that this operaion can do error * conversion and output the corresponding "in-line" code. */ cur_impl->ret_flag |= DoesEFail; insert_nl = 1; new_ilc(ILC_EFail); insert_nl = 1; line_ref = NULL; break; case Break: /* * iconc can only handle gotos for transfer of control in * in-line code. A break label has been established for * the current loop; transform the "break" into a goto. */ ilc_goto(brk_lbl); break; case Continue: /* * iconc can only handle gotos for transfer of control in * in-line code. A continue label has been established for * the current loop; transform the "continue" into a goto. */ ilc_goto(cont_lbl); break; default: /* * No special processing is needed for this primary * expression, just output the image of the token. */ ilc_tok(t); } break; case PrefxNd: /* * Expressions with one operand that are introduced by a token. * Note, "default :" does not appear here because switch * statements are not allowed in in-line code. */ switch (t->tok_id) { case Sizeof: /* * sizeof(...) */ ilc_tok(t); ilc_str("("); ilc_walk(n->u[0].child, 0, 0); ilc_str(")"); break; case '{': /* * initializer: { ... } */ ilc_tok(t); ilc_walk(n->u[0].child, 0, 0); ilc_str("}"); break; case Goto: /* * goto