Mercurial > repo
diff interps/c-intercal/src/feh2.c @ 996:859f9b4339e6
<Gregor> tar xf egobot.tar.xz
author | HackBot |
---|---|
date | Sun, 09 Dec 2012 19:30:08 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interps/c-intercal/src/feh2.c Sun Dec 09 19:30:08 2012 +0000 @@ -0,0 +1,2777 @@ +/**************************************************************************** + +Name + feh2.c -- code-generator back-end for ick parser + +DESCRIPTION + This module provides storage manglement and code degeneration + for the INTERCAL compiler. Optimizations (formerly in this file) + were split into dekludge.c. + +LICENSE TERMS + Copyright (C) 1996 Eric S. Raymond + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +****************************************************************************/ +/*LINTLIBRARY */ +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "sizes.h" +#include "ick.h" +#include "parser.h" +#include "fiddle.h" +#include "ick_lose.h" +#include "feh.h" + +/* AIS: Destaticed for dekludge.c */ +int emitlineno; /* line number for errors encountered during emit */ + +/*@-exportlocal@*/ /* the parser uses this */ +int mark112 = 0; /* AIS: Mark the ick_next generated tuple for W112 */ +/*@=exportlocal@*/ + +/* AIS: From perpet.c */ +extern int pickcompile; +extern int ick_clcsemantics; + +/************************************************************************* + * + * Node allocation functions. + * + * Nodes are used to represent expression trees. The emit() function + * deallocates them. + * + **************************************************************************/ + +/*@partial@*/ node *newnode(void) +/* allocate and zero out a new expression node */ +{ + node* temp; + temp=calloc(sizeof(node), 1); + if(!temp) ick_lose(IE345, 0, (const char*) NULL); + return temp; +} + +/*@partial@*/ node *cons(int type, /*@null@*/ /*@keep@*/ node *car, /*@null@*/ /*@keep@*/ node *cdr) +{ + node *np = newnode(); + + np->opcode = type; + np->lval = car; + np->rval = cdr; + + return(np); +} + +/************************************************************************* + * + * Variable-name mapping + * + * This permits us to optimize use of variable storage at runtime + * + **************************************************************************/ + +unsigned long intern(int type, unsigned long index) +{ + atom *x; + + /* AIS: Allow use of a modifiable constant 0 or >65535. */ + if ((index < 1LU || index > 65535LU) && type!=MESH) + ick_lose(IE200, iyylineno, (const char *)NULL); + + /*@-branchstate@*/ + if (!oblist) + { + /* initialize oblist and obdex */ + oblist = malloc(ALLOC_CHUNK * sizeof(atom)); + if (!oblist) + ick_lose(IE345, iyylineno, (const char *)NULL); + obdex = oblist; + obcount = ALLOC_CHUNK; + } + else + { + /* if it's already on the oblist, return its intindex */ + for (x = oblist; x < obdex; x++) + if (x->type == type && x->extindex == index) + return(x->intindex); + } + /*@=branchstate@*/ + assert(oblist != NULL); + + /* else we must intern a new symbol */ + /* AIS: Splint doesn't understand what's going on here at all. Disabling + the warnings; I've checked this and think it's correct. */ + /*@-usedef@*/ /*@-usereleased@*/ /*@-branchstate@*/ + if (obdex >= oblist + obcount) + { + obcount += ALLOC_CHUNK; + x = realloc(oblist, obcount * sizeof(atom)); + if (!x) + ick_lose(IE333, iyylineno, (const char *)NULL); + obdex = x + (obdex - oblist); + oblist = x; + } + /*@=branchstate@*/ /*@=usereleased@*/ /*@=usedef@*/ + obdex->type = type; + obdex->extindex = index; + obdex->memloc = 0; /* AIS: not placed in memory yet */ + if (type == ick_ONESPOT) + obdex->intindex = (unsigned)nonespots++; + if (type == ick_TWOSPOT) + obdex->intindex = (unsigned)ntwospots++; + if (type == ick_TAIL) + obdex->intindex = (unsigned)ntails++; + if (type == ick_HYBRID) + obdex->intindex = (unsigned)nhybrids++; + if (type == MESH) /* AIS: count meshes too */ + obdex->intindex = (unsigned)nmeshes++; + ++obdex; + + /*@-usedef@*/ + return(obdex[-1].intindex); + /*@=usedef@*/ +} + +/************************************************************************* + * + * This function insures a label is valid. + * + **************************************************************************/ + +/* AIS: I haven't modified this function, but I have repurposed it without + changing the code; this function must not now have side effects (apart from + an error exit), because some labels are initialised in the preprocessor + without causing this. */ +void checklabel(int label) +{ + if (label < 1 || label > 65535) + ick_lose(IE197, iyylineno, (const char *)NULL); +} + +/************************************************************************* + * + * AIS: Search for the indexth COME_FROM sucking in the given tuple. + * Return an int representing the COME_FROM's tn-tuples+1, or -1. + * index is based at 1, not 0 as is usual for C. + * + ***************************************************************************/ + +int comefromsearch(tuple* tn, unsigned int index) +{ + tuple* tp; + for (tp = tuples; tp < tuples + ick_lineno; tp++) + { + if((tp->type == COME_FROM || tp->type == NEXTFROMLABEL) + && tp->u.target == (unsigned)(tn-tuples+1)) + index--; + if(!index) return tp-tuples+1; + } + return -1; +} + +/************************************************************************* + * + * Tuple allocation functions. + * + **************************************************************************/ + +void treset(void) +{ + tuplecount = 0; + if (tuples) + { + /* AIS: Splint doesn't understand lazy allocation, which is why it + thinks I'm treating an unqualified as an only (I am, but a lazy + list doesn't fit any of Splint's storage classes); also, I am + completely destroying the tuples, because any nodes in them ought + to have been deallocated in prexpr. */ + /*@-unqualifiedtrans@*/ /*@-compdestroy@*/ + free(tuples); + tuples = NULL; + /*@=unqualifiedtrans@*/ /*@=compdestroy@*/ + } + nmeshes = nonespots = ntwospots = ntails = nhybrids = 0; + obdex = oblist; + ick_lineno = 0; + /* AIS: It's easier to mark tuples as 'always allocated', because + it usually is, and just supress the warnings. Maybe the 'proper' + way to do it would be to assert that tuples was non-null everywhere, + but again this is just problems with Splint not understanding how + lazy allocation works. So I tell Splint that it's allocated everywhere + and just supress the warnings it produces when it isn't. */ + /*@-globstate@*/ +} +/*@=globstate@*/ + +/*@out@*/ /*@dependent@*/ tuple *newtuple(void) +/* allocate and zero out a new expression tuple */ +{ + /* Patch by Joris Huizer: must leave at least 1 tuple empty */ + if (ick_lineno >= tuplecount - 1 || tuples == NULL) + { + tuplecount += ALLOC_CHUNK; + if (tuples) + tuples = realloc(tuples, tuplecount * sizeof(tuple)); + else + tuples = malloc(tuplecount * sizeof(tuple)); + if (!tuples) + ick_lose(IE666, iyylineno, (const char *)NULL); + memset(tuples + ick_lineno, 0, (tuplecount - ick_lineno) * sizeof(tuple)); + } + if(mark112) tuples[ick_lineno].warn112 = 1; mark112 = 0; /* AIS */ + /* Yes, tuples is strictly speaking 'partial' at this point, but it's going + to be filled in later, and isn't marked as partial due to it not being + partial through most of the code, and you can't write out on a global. + So instead I'm just suppressing the warning, because it doesn't lead to + a problem long-term. */ + /*@-compdef@*/ + return(tuples + ick_lineno++); + /*@=compdef@*/ +} + +void tupleswap(int distback1, int distback2) +{ + tuple temp; + memcpy(&temp, &tuples[ick_lineno-distback1], sizeof(tuple)); + memcpy(&tuples[ick_lineno-distback1], &tuples[ick_lineno-distback2], sizeof(tuple)); + memcpy(&tuples[ick_lineno-distback2], &temp, sizeof(tuple)); + /* Splint doesn't understand memcpy, and so falsely things this is a memory leak. */ + /*@-compdestroy@*/ +} +/*@=compdestroy@*/ + +void ppinit(int tuplecount) +{ + while(tuplecount) + { + /* 0 is an impossible exechance; make sure it's set for tuple elements. */ + if(!tuples[ick_lineno-tuplecount].exechance) + tuples[ick_lineno-tuplecount].exechance=100; + /* The onceagainflag also needs to be set. */ + tuples[ick_lineno-tuplecount].onceagainflag=onceagain_NORMAL; + tuplecount--; + } +} + +/************************************************************************* + * + * The typecaster + * + * The theory here is that we associate a type with each node in order to + * know what widths of unary-logical operator to use. + * + **************************************************************************/ + +void typecast(node *np) +{ + /* recurse so we typecast each node after all its subnodes */ + if (np == (node *)NULL) + return; + else if (np->lval != (node *)NULL) + typecast(np->lval); + if (np->rval != (node *)NULL) + typecast(np->rval); + + /* + * This is an entire set of type-deducing machinery right here. + */ + /*@-nullderef@*/ /* AIS: because the opcode defines whether lval or rval are nonnull */ + if (np->opcode == MESH || np->opcode == ick_ONESPOT || np->opcode == ick_TAIL) + np->width = 16; + else if (np->opcode == ick_TWOSPOT || np->opcode == ick_HYBRID + || np->opcode == MINGLE || np->opcode == MESH32 + || np->opcode == UNKNOWNOP /* AIS */) + np->width = 32; + else if (np->opcode == AND || np->opcode == OR || np->opcode == XOR || + np->opcode == FIN || + (np->opcode >= WHIRL && np->opcode <= WHIRL5)) + np->width = np->rval->width; + else if (np->opcode == SELECT) + np->width = np->rval->width; /* n-bit select has an n-bit result */ + else if (np->opcode == INTERSECTION) /* AIS */ + np->width = (np->rval ? + np->lval ? np->rval->width == 16 ? np->lval->width : 32 : + np->rval->width : np->lval ? np->lval->width : 32); + else if (np->opcode == BADCHAR) /* AIS */ + np->width = 16; + else if (np->opcode == SUB) + np->width = np->lval->width; /* type of the array */ + else if (np->opcode == SLAT || np->opcode == BACKSLAT) + np->width = np->lval->width; /* AIS: \ and / return their left arg */ + /*@=nullderef@*/ +} + +/************************************************************************* + * + * The codechecker + * + * This checks for nasties like mismatched types in assignments that + * can be detected at compile time -- also for errors that could cause + * the compilation of the generated C to fail, like generated gotos to + * nonexistent labels or duplicate labels. + * + * AIS: codecheck has another important job, that of filling in information + * about COME FROM suckpoints and ABSTAIN/REINSTATE command numbers + * into the tuples. + * + **************************************************************************/ + +void codecheck(void) +{ + tuple *tp, *up; + int notpast1900; /* AIS */ + + /* check for assignment type mismatches */ + /* This check can't be done at compile time---RTFM. [LHH] */ +/* + for (tp = tuples; tp < tuples + ick_lineno; tp++) + if (tp->type == GETS) + if (tp->u.node->lval->width == 16 && tp->u.node->rval->width == 32) + ick_lose(IE275, tp - tuples + 1, (const char *)NULL); +*/ + + /* check for duplicate labels */ + for (tp = tuples; tp < tuples + ick_lineno; tp++) + if (tp->label) + for (up = tuples; up < tuples + ick_lineno; up++) + if (tp != up && tp->label == up->label) + ick_lose(IE182, tp - tuples + 1, (const char *)NULL); + + /* + * Check that every NEXT, ABSTAIN, REINSTATE and COME_FROM actually has a + * legitimate target label. + */ + notpast1900 = ick_TRUE; + for (tp = tuples; tp < tuples + ick_lineno; tp++) + { + if (tp->label == 1900) notpast1900 = ick_FALSE; /* AIS */ + if (tp->type == NEXT + || tp->type == ABSTAIN || tp->type == REINSTATE + || tp->type == COME_FROM || tp->type == FROM + || tp->type == NEXTFROMLABEL) /* AIS: added FROM, NEXTFROMLABEL. */ + { + ick_bool foundit = ick_FALSE; + + if (tp->u.target >= 1900 && tp->u.target <= 1998) + { + /* AIS: This program uses syslib.i's random number feature... or are + we in syslib already? */ + if(notpast1900) coopt = 0; + } + + if (tp->u.target > 65535 && !tp->preproc) /* AIS */ + ick_lose(IE197, tp - tuples + 1, (const char*) NULL); + + for (up = tuples; up < tuples + ick_lineno; up++) + if (tp->u.target == up->label) + { + foundit = ick_TRUE; + break; + } + + if (!foundit) + { + /* AIS: Added the pickcompile check. Syslib has to be optimized + for PICs, so syslib.i isn't imported and so none of the lables + in it will appear in the program. Also added the useickec + check, as that's another legitimate way for a NEXT to target + a nonexistent line label */ + if (tp->type == NEXT && !useickec && + (!pickcompile||tp->u.target<1000||tp->u.target>1999)) + ick_lose(IE129, tp - tuples + 1, (const char *)NULL); + else if (tp->type == NEXT) /* AIS */ + {tp->nexttarget=0; continue;} + else if (useickec) /* AIS */ + continue; + /* AIS: NEXTFROMLABEL's basically identical to COME_FROM */ + else if (tp->type == COME_FROM || tp->type == NEXTFROMLABEL) + ick_lose(IE444, tp - tuples + 1, (const char *)NULL); + else + ick_lose(IE139, tp - tuples + 1, (const char *)NULL); + } + /* tell the other tuple if it is a COME FROM target */ + /* AIS: NEXTFROMLABEL again */ + else if (tp->type == COME_FROM || tp->type == NEXTFROMLABEL) + { + if (up->ncomefrom && !multithread) /* AIS: multithread check */ + ick_lose(IE555, iyylineno, (const char *)NULL); + else + up->ncomefrom++; /* AIS: to handle multiple COME FROMs */ + } + /* this substitutes line numbers for label numbers + AIS: COME FROM now uses this too. This changes the logic + slightly so that an !foundit condition would fall through, + but as long as ick_lose doesn't return, it's not a problem. + (I removed the else before the if.) */ + if (tp->type != NEXT) + { + /* AIS: added this useickec condition. */ + if(!useickec || (tp->type!=NEXTFROMLABEL && tp->type!=COME_FROM)) + tp->u.target = (unsigned)(up - tuples + 1); + } + else /* AIS */ + { + tp->nexttarget = (unsigned)(up - tuples + 1); + up->nextable = ick_TRUE; + } + } + } +} + +/* AIS: Added the third argument to prexpr and prvar. It specifies + whether the node should be freed or not. I added the third + argument in all calls of prexpr/prvar. This protoype has been moved + up through the file so it can be used earlier. Destaticed so it can + be referenced by dekludge.c. */ +void prexpr(node *np, FILE *fp, int freenode); +/************************************************************************* + * + * Code degeneration + * + * The theory behind this crock is that we've been handed a pointer to + * a tuple representing a single INTERCAL statement, possibly with an + * expression tree hanging off it and twisting slowly, slowly in the wind. + * + * Our mission, should we choose to accept it, is to emit C code which, + * when linked to the INTERCAL run-time support, will do something + * resembling the right thing. + * + **************************************************************************/ + +/* + * If the order of statement-token defines in parser.y ever changes, + * this will need to be reordered. + */ +/*@observer@*/ const char *enablersm1[MAXTYPES+1] = +{ + "UNKNOWN", /* AIS: so comments can be ABSTAINED/REINSTATED */ + "GETS", + "RESIZE", + "NEXT", + "GO_AHEAD", /* AIS: Added for backtracking */ + "GO_BACK", /* AIS: Added for backtracking */ + "FORGET", + "RESUME", + "STASH", + "RETRIEVE", + "IGNORE", + "REMEMBER", + "ABSTAIN", + "REINSTATE", + "DISABLE", + "ENABLE", + "MANYFROM", /* AIS: Added ABSTAIN expr FROM gerunds */ + "GIVE_UP", + "READ_OUT", + "WRITE_IN", + "PIN", + "COME_FROM", + "NEXTFROMLABEL", /* AIS */ + "NEXTFROMEXPR", /* AIS */ + "NEXTFROMGERUND", /* AIS */ + "COMPUCOME", /* AIS: Added COMPUCOME */ + "GERUCOME", /* AIS: This is COME FROM gerunds */ + "PREPROC", /* AIS: Nonexistent statement */ + "WHILE", /* AIS: statement WHILE statement */ + "TRY_AGAIN", /* AIS: Added TRY AGAIN */ + "CREATE", /* AIS */ + "COMPUCREATE", /* AIS */ + "FROM", /* AIS: ABSTAIN expr FROM LABEL */ +}; +const char** enablers = enablersm1+1; + +const assoc vartypes[] = +{ + { ick_ONESPOT, "ick_ONESPOT" }, + { ick_TWOSPOT, "ick_TWOSPOT" }, + { ick_TAIL, "ick_TAIL" }, + { ick_HYBRID, "ick_HYBRID" }, + { 0, (const char *)NULL } +}; + +static const assoc forgetbits[] = +{ + { ick_ONESPOT, "ick_oneforget" }, + { ick_TWOSPOT, "ick_twoforget" }, + { ick_TAIL, "ick_tailforget" }, + { ick_HYBRID, "ick_hyforget" }, + { 0, (const char *)NULL } +}; + +/* AIS: Destatic. This is now needed in perpet.c. */ +const assoc varstores[] = +{ + { ick_ONESPOT, "ick_onespots" }, + { ick_TWOSPOT, "ick_twospots" }, + { ick_TAIL, "ick_tails" }, + { ick_HYBRID, "ick_hybrids" }, + { 0, (const char *)NULL } +}; + +/* AIS: A demangled version */ +static const assoc varstoresdem[] = +{ + { ick_ONESPOT, "onespots" }, + { ick_TWOSPOT, "twospots" }, + { ick_TAIL, "tails" }, + { ick_HYBRID, "hybrids" }, + { 0, (const char *)NULL } +}; + +static const assoc typedefs[] = +{ + { ick_ONESPOT, "ick_type16" }, + { ick_TWOSPOT, "ick_type32" }, + { ick_TAIL, "ick_type16" }, + { ick_HYBRID, "ick_type32" }, + { 0, (const char *)NULL } +}; + +/*@observer@*/ const char *nameof(int value, const assoc table[]) +/* return string corresponding to value in table */ +{ + const assoc *ap; + + for (ap = table; ap->name; ap++) + if (ap->value == value) + return(ap->name); + return((const char *)NULL); +} + +/* AIS: Code for printing explanations (mixed C/INTERCAL code that lets + the user know what the meaning of an expression is). This is paraphrased + from the prexpr/prvar code lower down. It's passed to yuk so that the + explain ('e') command works. It's also included in the degenerated C + code when the option -c is used, so the person looking at the code can + debug both the INTERCAL and ick itself more effectively, and used by + -h to produce its optimizer-debug output, and used to produce the variable + numbers used in ick_createdata. */ + +unsigned long varextern(unsigned long intern, int vartype) +{ + atom *x; + if(!oblist) ick_lose(IE778, emitlineno, (const char*) NULL); + for (x = oblist; x < obdex; x++) + if (x->type == vartype && (unsigned long)x->intindex == intern) + return(x->extindex); + if(vartype==MESH) return 0; /* the mesh wasn't used after all */ + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ return 0; /*@=unreachable@*/ +} + +static void explvar(node* np, FILE* fp) +{ + node *sp; + switch(np->opcode) + { + case ick_ONESPOT: + (void) fprintf(fp, ".%lu", varextern(np->constant,ick_ONESPOT)); + break; + case ick_TWOSPOT: + (void) fprintf(fp, ":%lu", varextern(np->constant,ick_TWOSPOT)); + break; + case ick_TAIL: + (void) fprintf(fp, ",%lu", varextern(np->constant,ick_TAIL)); + break; + case ick_HYBRID: + (void) fprintf(fp, ";%lu", varextern(np->constant,ick_HYBRID)); + break; + case SUB: + (void) fprintf(fp, "("); + explvar(np->lval, fp); + (void) fprintf(fp, " SUB "); + for (sp = np->rval ; sp ; sp = sp->rval) + explexpr(sp->lval, fp); + (void) fprintf(fp, ")"); + break; + default: + ick_lose(IE778, emitlineno, (const char*) NULL); + } +} + +/* unlike prexpr, this doesn't free its operands */ +void explexpr(node* np, FILE* fp) +{ + if(!np) return; + switch (np->opcode) + { + case MINGLE: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " $ "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case SELECT: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " ~ "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case UNKNOWNOP: + (void) fprintf(fp, "("); + explexpr(np->rval->lval, fp); + if(np->lval->constant < 256) + (void) fprintf(fp, " %c ", (char)np->lval->constant); + else + (void) fprintf(fp, " %c^H%c ", + (char)(np->lval->constant / 256), + (char)(np->lval->constant % 256)); + explexpr(np->rval->rval, fp); + (void) fprintf(fp, ")"); + break; + + case SLAT: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " / "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case BACKSLAT: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " \\ "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case AND: + (void) fprintf(fp, "(& "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case OR: + (void) fprintf(fp, "(V "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case XOR: + (void) fprintf(fp, "(? "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case FIN: + if (ick_Base < 3) + ick_lose(IE997, emitlineno, (const char *)NULL); + (void) fprintf(fp, "(^ "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case WHIRL: + case WHIRL2: + case WHIRL3: + case WHIRL4: + case WHIRL5: + if (np->opcode - WHIRL + 3 > ick_Base) + ick_lose(IE997, emitlineno, (const char *)NULL); + if(np->opcode == WHIRL) + (void) fprintf(fp, "(@ "); + else + (void) fprintf(fp, "(%d@ ", np->opcode - WHIRL + 1); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case MESH: + if(variableconstants) /* AIS */ + (void) fprintf(fp, "meshes[%lu]", np->constant); + else + (void) fprintf(fp, "0x%lx", np->constant); + break; + + case MESH32: + (void) fprintf(fp, "0x%lx", np->constant); + break; + + case ick_ONESPOT: + case ick_TWOSPOT: + case ick_TAIL: + case ick_HYBRID: + case SUB: + explvar(np, fp); + break; + + /* cases from here down are generated by the optimizer */ + case C_AND: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " & "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_OR: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " | "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_XOR: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " ^ "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_NOT: + (void) fprintf(fp, "(~ "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_NOTEQUAL: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " != "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_A: + (void) fprintf(fp, "a"); + break; + + case C_RSHIFTBY: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " >> "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_LOGICALNOT: + (void) fprintf(fp, "(! "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_LSHIFTBY: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " << "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_PLUS: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " + "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_MINUS: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " - "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_TIMES: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " * "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_DIVIDEBY: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " / "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_MODULUS: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " %% "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_GREATER: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " > "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_LESS: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " < "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_ISEQUAL: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " == "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_LOGICALAND: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " && "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case C_LOGICALOR: + (void) fprintf(fp, "("); + explexpr(np->lval, fp); + (void) fprintf(fp, " || "); + explexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case INTERSECTION: + explexpr(np->lval, fp); + (void) fprintf(fp, " + "); + explexpr(np->rval, fp); + break; + + case GETS: + case RESIZE: + explexpr(np->lval, fp); + (void) fprintf(fp, " <- "); + explexpr(np->rval, fp); + break; + + case BY: + explexpr(np->lval, fp); + (void) fprintf(fp, " BY "); + explexpr(np->rval, fp); + break; + + default: + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + } +} + +/* AIS: Added the third argument to prexpr and prvar. It specifies + whether the node should be freed or not. I added the third + argument in all calls of prexpr/prvar. */ + +/* AIS: I moved prexpr's prototype higher in the file. + Destaticed so the optimizer can access it. */ + +static void prvar(node *np, FILE *fp, int freenode) +/* print out args to pass to storage manager for reference */ +{ + node *sp; + int dim; + + switch (np->opcode) + { + case ick_ONESPOT: + (void) fprintf(fp, "ick_onespots[%lu]", np->constant); + break; + + case ick_TWOSPOT: + (void) fprintf(fp, "ick_twospots[%lu]", np->constant); + break; + + case ick_TAIL: + (void) fprintf(fp, "ick_TAIL, &ick_tails[%lu]", np->constant); + break; + + case ick_HYBRID: + (void) fprintf(fp, "ick_HYBRID, &ick_hybrids[%lu]", np->constant); + break; + + case SUB: + { + (void) fprintf(fp, "ick_aref("); + prvar(np->lval, fp, freenode); + + dim = 0; + for (sp = np->rval ; sp ; sp = sp->rval) + dim++; + (void) fprintf(fp, ", %d", dim); + + for (sp = np->rval ; sp ; sp = sp->rval) { + (void) fprintf(fp, ", "); + prexpr(sp->lval, fp, freenode); + } + (void) fprintf(fp, ")"); + } + break; + default: /* Added by AIS */ + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + } +} + +static void ooprvar(node *np, FILE *fp, int freenode) +/* AIS: Print out the overloaded version */ +{ + node *sp; + int dim; + + switch (np->opcode) + { + case ick_ONESPOT: + (void) fprintf(fp, "ick_oo_onespots[%lu]", np->constant); + break; + + case ick_TWOSPOT: + (void) fprintf(fp, "ick_oo_twospots[%lu]", np->constant); + break; + + case ick_TAIL: + case ick_HYBRID: + /* This should never be reached */ + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + + case SUB: + { + (void) fprintf(fp, "ick_aref("); + prvar(np->lval, fp, freenode); + + dim = 0; + for (sp = np->rval ; sp ; sp = sp->rval) + dim++; + (void) fprintf(fp, ", %d", dim); + + for (sp = np->rval ; sp ; sp = sp->rval) { + (void) fprintf(fp, ", "); + prexpr(sp->lval, fp, freenode); + } + (void) fprintf(fp, ")"); + } + break; + default: + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + } +} + +/* AIS: Give us a mesh with value x */ +static unsigned long meshval(unsigned long x) +{ + if(variableconstants) + return intern(MESH, x); + else + return x; +} + +/* AIS: This is the reverse of prexpr, in a way. + It degenerates an expression that causes *np to become equal to *target. + If this is impossible at any point, it degenerates code that causes + error 277 (and itself causes error 278 if the situation is inevitable). + As for the annotations; there quite possibly are memory allocation mistakes + here, but just about every line is a false positive (because we're operating + at the subobject level in terms of copy/free/allocate) for Splint, and so + disabling the warnings doesn't make the output any less useful. (When there + are so many false positives, disabling the true positives doesn't make them + any harder to find by eye. */ +/*@-temptrans@*/ /*@-kepttrans@*/ /*@-compdestroy@*/ /*@-branchstate@*/ +static void revprexpr(node *np, FILE *fp, node *target) +{ + node* temp; + switch (np->opcode) + { + case MINGLE: + /* We can use select to determine what np->lval and np->rval have + to become equal to, as long as we're in base 2. */ + if(ick_Base!=2) + { + fprintf(fp, " ick_lose(IE277, ick_lineno, (const char*) NULL);\n"); + ick_lwarn(W278, emitlineno, (const char*) NULL); + break; + } + temp=cons(MESH,0,0); + temp->constant=meshval(0xAAAAAAAALU); + temp->width=32; + temp=cons(SELECT,target,temp); + temp->width=target->width; + revprexpr(np->lval, fp, temp); + free(temp->rval); + free(temp); + temp=cons(MESH,0,0); + temp->constant=meshval(0x55555555LU); + temp->width=32; + temp=cons(SELECT,target,temp); + temp->width=target->width; + revprexpr(np->rval, fp, temp); + free(temp->rval); + free(temp); + break; + + case SELECT: + /* Set the left of the select to the target, and the right + to 0xffffffff or 0xffff. This only works in base 2. */ + if(ick_Base!=2) + { + fprintf(fp, " ick_lose(IE277, ick_lineno, (const char*) NULL);\n"); + ick_lwarn(W278, emitlineno, (const char*) NULL); + break; + } + temp=cons(MESH,0,0); + temp->constant=meshval(target->width==32?0xFFFFFFFFLU:0xFFFFLU); + temp->width=target->width; + revprexpr(np->lval, fp, target); + revprexpr(np->rval, fp, temp); + free(temp); + break; + + case UNKNOWNOP: /* don't be silly */ + fprintf(fp, " ick_lose(IE277, ick_lineno, (const char*) NULL);\n"); + ick_lwarn(W278, emitlineno, (const char*) NULL); + break; + + case BACKSLAT: + /* Unimplemented. This isn't even in the parser yet, so it's a + ick_mystery how we got here. */ + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + + case SLAT: + /* We need to set the true value of the LHS... */ + + /* Copied and modified from the GETS code */ + if(!pickcompile) + { + (void) fprintf(fp," (void) ick_assign((char*)&"); + prvar(np->lval, fp, 0); + (void) fprintf(fp,", %s", nameof(np->lval->opcode, vartypes)); + (void) fprintf(fp,", %s[%lu], ", nameof(np->lval->opcode, forgetbits), + np->lval->constant); + prexpr(target, fp, 0); + (void) fprintf(fp,"); \n"); + } + else /* AIS: Added this case for the simpler PIC assignment rules */ + { + (void) fprintf(fp,"\t""if(ignore%s%lu) ", + nameof(np->lval->opcode,varstores), + np->lval->constant); + prexpr(np->lval, fp, 0); + (void) fprintf(fp, " = "); + prexpr(target, fp, 0); + (void) fprintf(fp, "; \n"); + } + + /* ... and we need to cause overloading to happen. This is a copy + of part of the code for SLAT, modified to work in this context. */ + ooprvar(np->lval, fp, 0); + /* Do something highly non-portable with pointers that should work + anyway. Each pointer needs to be given a unique code; so we use + the hex representation of np casted to an unsigned long. + Technically speaking, np->rval could be casted to anything; but + all implementations I've ever seen cast unique pointers to unique + numbers, which is good enough for our purposes. */ + (void) fprintf(fp, ".get=ick_og%lx;\n ", (unsigned long)np->rval); + ooprvar(np->lval, fp, 0); + (void) fprintf(fp, ".set=ick_os%lx;\n", (unsigned long)np->rval); + break; + + case AND: + case OR: + case XOR: + case FIN: + case WHIRL: + case WHIRL2: + case WHIRL3: + case WHIRL4: + case WHIRL5: + temp=cons(np->opcode-AND+REV_AND,0,target); + temp->width=temp->rval->width=np->width; + revprexpr(np->rval, fp, temp); + free(temp); + break; + + case MESH: + if(!variableconstants) + { + /* Can't set a mesh in this case */ + fprintf(fp, " ick_lose(IE277, ick_lineno, (const char*) NULL);\n"); + ick_lwarn(W278, emitlineno, (const char*) NULL); + break; + } + (void) fprintf(fp," meshes[%lu] = ",np->constant); + prexpr(target, fp, 0); + (void) fprintf(fp,";\n"); + break; + + case ick_ONESPOT: + case ick_TWOSPOT: + case ick_TAIL: + case ick_HYBRID: + case SUB: + /* Copy the code for the GETS statement; this is almost the same + thing. Modified because we're assigning target to np, not + np->lval to np->rval, and to not free(). */ + if(opoverused&& + (np->opcode==ick_ONESPOT||np->opcode==ick_TWOSPOT)) /* AIS */ + { + (void) fprintf(fp," "); + ooprvar(np, fp, 0); + (void) fprintf(fp,".set("); + prexpr(target, fp, 0); + (void) fprintf(fp,",os%dspot%lu);\n", + (np->opcode==ick_TWOSPOT)+1, np->constant); + } + else if(!pickcompile) + { + node* sp; + + if (np->opcode != SUB) { + sp = np; + (void) fprintf(fp," (void) ick_assign((char*)&"); + } + else { + sp = np->lval; + (void) fprintf(fp," (void) ick_assign("); + } + prvar(np, fp, 0); + (void) fprintf(fp,", %s", nameof(sp->opcode, vartypes)); + (void) fprintf(fp,", %s[%lu], ", nameof(sp->opcode, forgetbits), + sp->constant); + prexpr(target, fp, 0); + (void) fprintf(fp,");\n"); + } + else /* AIS: Added this case for the simpler PIC assignment rules */ + { + (void) fprintf(fp," if(ignore%s%lu) ", + nameof(np->opcode,varstores), + np->constant); + prexpr(np, fp, 0); + (void) fprintf(fp, " = "); + prexpr(target, fp, 0); + (void) fprintf(fp, ";\n"); + } + break; + + /* cases from here down are generated by the optimizer, and so + should never come up here and are errors. The exception is + C_A, which should only ever appear in a target expression, + so is also an error. */ + case MESH32: + case C_AND: + case C_OR: + case C_XOR: + case C_NOT: + case C_NOTEQUAL: + case C_A: + case C_RSHIFTBY: + case C_LOGICALNOT: + case C_LSHIFTBY: + case C_PLUS: + case C_MINUS: + case C_TIMES: + case C_DIVIDEBY: + case C_MODULUS: + case C_GREATER: + case C_LESS: + case C_ISEQUAL: + case C_LOGICALAND: + case C_LOGICALOR: + case GETS: /* should never come up */ + default: + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + } +} +/*@=temptrans@*/ /*@=kepttrans@*/ /*@=compdestroy@*/ /*@=branchstate@*/ + +/*@observer@*/ static char* E000string; /* AIS */ + +static int prunknownstr(node*, FILE*); /* AIS */ + +/* AIS: Destaticed */ +/* Splint doesn't understand the concept of a function which might or might + not free its argument. That's a pity, because its checking would come in + useful here, but as it is we have to annotate memory checking off for this + function. */ +/*@-temptrans@*/ /*@-onlytrans@*/ /*@-compdestroy@*/ /*@-branchstate@*/ +void prexpr(node *np, FILE *fp, int freenode) +/* print out C-function equivalent of an expression */ +{ + int tempint; + switch (np->opcode) + { + case MINGLE: + (void) fprintf(fp, "ick_mingle("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, ", "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case SELECT: + (void) fprintf(fp, "ick_iselect("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, ", "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case UNKNOWNOP: /* AIS */ + if(!useickec || !createsused) + { + /* CREATEd operators require -ea */ + (void) fprintf(fp, "(ick_lose(IE000, ick_lineno, \"%s\"),0)", + E000string); + break; + } + /* We need to do the same as UNKNOWN statements, but as an expression. + This is achieved with the helper function ick_dounop in ick_ec.c. */ + (void) fprintf(fp, "ick_dounop(\""); + prunknownstr(np->lval, fp); + if(freenode) free(np->lval); + (void) fprintf(fp, "\", "); + prexpr(np->rval->lval, fp, 0); + (void) fprintf(fp, ", "); + prexpr(np->rval->rval, fp, 0); + (void) fprintf(fp, + ", ick_lineno, %luUL, %luUL, %luUL" + ", ick_og%lx, ick_og%lx, og2spot%lu" + ", ick_os%lx, ick_os%lx, os2spot%lu, \"%s\")", + intern(ick_TWOSPOT, 1601), + intern(ick_TWOSPOT, 1602), + intern(ick_TWOSPOT, 1603), + (unsigned long) np->rval->lval, + (unsigned long) np->rval->rval, + intern(ick_TWOSPOT, 1603), + (unsigned long) np->rval->lval, + (unsigned long) np->rval->rval, + intern(ick_TWOSPOT, 1603), + E000string); + if(freenode) free(np->rval); + break; + + case BACKSLAT: /* AIS */ + /* Unimplemented. This isn't even in the parser yet, so it's a + ick_mystery how we got here. */ + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + + case SLAT: /* AIS */ + (void) fprintf(fp,"(("); + ooprvar(np->lval, fp, 0); + /* Do something highly non-portable with pointers that should work + anyway. Each pointer needs to be given a unique code; so we use + the hex representation of np casted to an unsigned long. + Technically speaking, np->rval could be casted to anything; but + all implementations I've ever seen cast unique pointers to unique + numbers, which is good enough for our purposes. */ + (void) fprintf(fp, ".get=ick_og%lx),(", (unsigned long)np->rval); + ooprvar(np->lval, fp, 0); + (void) fprintf(fp, ".set=ick_os%lx),(", (unsigned long)np->rval); + prvar(np->lval, fp, freenode); + /* np->rval will be freed later, when its expression is printed */ + (void) fprintf(fp, "))"); + return; /* mustn't be freed */ + + case AND: + (void) fprintf(fp, "ick_and%d(", np->width); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case OR: + (void) fprintf(fp, "ick_or%d(", np->width); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case XOR: + (void) fprintf(fp, "ick_xor%d(", np->width); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case FIN: + if (ick_Base < 3) + ick_lose(IE997, emitlineno, (const char *)NULL); + (void) fprintf(fp, "ick_fin%d(", np->width); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case WHIRL: + case WHIRL2: + case WHIRL3: + case WHIRL4: + case WHIRL5: + if (np->opcode - WHIRL + 3 > ick_Base) + ick_lose(IE997, emitlineno, (const char *)NULL); + (void) fprintf(fp, "ick_whirl%d(%d, ", np->width, np->opcode - WHIRL + 1); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + /* AIS: Reversed operations */ + case REV_AND: + (void) fprintf(fp, "ick_rev_and%d(", np->width); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case REV_OR: + (void) fprintf(fp, "ick_rev_or%d(", np->width); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case REV_XOR: + (void) fprintf(fp, "ick_rev_xor%d(", np->width); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case REV_FIN: + if (ick_Base < 3) + ick_lose(IE997, emitlineno, (const char *)NULL); + (void) fprintf(fp, "rev_fin%d(", np->width); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case REV_WHIRL: + case REV_WHIRL2: + case REV_WHIRL3: + case REV_WHIRL4: + case REV_WHIRL5: + if (np->opcode - WHIRL + 3 > ick_Base) + ick_lose(IE997, emitlineno, (const char *)NULL); + (void) fprintf(fp, + "rev_whirl%d(%d, ", np->width, np->opcode - WHIRL + 1); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case MESH: + if(variableconstants) /* AIS */ + (void) fprintf(fp, "meshes[%lu]", np->constant); + else + (void) fprintf(fp, "0x%lx", np->constant); + break; + + case MESH32: + (void) fprintf(fp, "0x%lx", np->constant); + break; + + case ick_ONESPOT: + case ick_TWOSPOT: + case ick_TAIL: + case ick_HYBRID: + if(!opoverused||np->opcode==ick_TAIL||np->opcode==ick_HYBRID) + prvar(np, fp, freenode); + else /* AIS */ + { + ooprvar(np, fp, freenode); + fprintf(fp, ".get("); + prvar(np, fp, freenode); + fprintf(fp,")"); + } + break; + + case SUB: + (void) fprintf(fp, "*(%s*)", nameof(np->lval->opcode, typedefs)); + prvar(np, fp, freenode); + break; + + /* cases from here down are generated by the optimizer */ + case C_AND: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " & "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_OR: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " | "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_XOR: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " ^ "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_NOT: + (void) fprintf(fp, "(~"); + tempint=np->rval->width; /* AIS */ + prexpr(np->rval, fp, freenode); + if (tempint == ick_Small_digits) + (void) fprintf(fp, " & ick_Max_small)"); + else + (void) fprintf(fp, " & ick_Max_large)"); + break; + + /* AIS: I added the rest of the cases */ + case C_NOTEQUAL: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " != "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_A: + (void) fprintf(fp, "a"); + break; + + case C_RSHIFTBY: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " >> "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_LOGICALNOT: + (void) fprintf(fp, "(!"); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_LSHIFTBY: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " << "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_PLUS: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " + "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_MINUS: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " - "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_TIMES: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " * "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_DIVIDEBY: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " / "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_MODULUS: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " %% "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_GREATER: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " > "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_LESS: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " < "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_ISEQUAL: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " == "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_LOGICALAND: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " && "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case C_LOGICALOR: + (void) fprintf(fp, "("); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " || "); + prexpr(np->rval, fp, freenode); + (void) fprintf(fp, ")"); + break; + + case GETS: /* AIS: this is used only if freenode == 0 */ + if(freenode) ick_lose(IE778, emitlineno, (const char*) NULL); + prexpr(np->lval, fp, freenode); + (void) fprintf(fp, " = "); + prexpr(np->rval, fp, freenode); + break; + + default: /* Added by AIS */ + if(!freenode) break; /* Be less careful when not freeing, because + this is used by -hH to print out its + intermediate optimization stages */ + ick_lose(IE778, emitlineno, (const char*) NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + } + + if(freenode) (void) free(np); +} +/*@=temptrans@*/ /*@=onlytrans@*/ /*@=compdestroy@*/ /*@=branchstate@*/ + +/* By AIS: Helper function for prunknown */ +static int prunknownstr(node *np, FILE* fp) +{ + int i; + switch(np->opcode) + { + case INTERSECTION: + i=prunknownstr(np->lval, fp); + i+=prunknownstr(np->rval, fp); + return i; + case BADCHAR: + if (np->constant > 256) + (void) fprintf(fp, "o%xx%x", + (unsigned int)(np->constant / 256), + (unsigned int)(np->constant % 256)); + else + (void) fprintf(fp, "u%x", (unsigned int)np->constant); + return 2; + case US_ID: (void) fputc((char)np->constant, fp); return 0; + case US_ELEM: (void) fputc(';', fp); return 1; + case US_SCALAR: (void) fputc('.', fp); return 1; + case US_ARRVAR: (void) fputc(',', fp); return 1; + case US_EXPR: (void) fputc('~', fp); return 1; + default: ick_lose(IE778, emitlineno, (const char*) NULL); + } + /*@-unreachable@*/ return 0; /*@=unreachable@*/ +} + +/* By AIS: Helper function for prunknown */ +static void prunknowncreatedata(node *np, FILE* fp) +{ + unsigned long ve; + switch(np->opcode) + { + case INTERSECTION: + prunknowncreatedata(np->lval, fp); + prunknowncreatedata(np->rval, fp); + return; + case US_ID: return; /* nothing to do */ + case US_SCALAR: + ve=varextern(np->rval->constant,np->rval->opcode); + fprintf(fp,"\t\t{%d,0,%lu,",np->rval->width,ve); + break; + case US_ARRVAR: /* an array doesn't itself have a value */ + ve=varextern(np->rval->constant,np->rval->opcode); + fprintf(fp,"\t\t{%d,1,%lu,{ick_ieg277,ick_ies277},0},\n", + np->rval->width,ve); + return; + case US_ELEM: /* these two cases are actually treated the same way, */ + case US_EXPR: /* because expressions can be assigned to */ + fprintf(fp,"\t\t{%d,0,0,",np->rval->width); + break; + default: ick_lose(IE778, emitlineno, (const char*) NULL); + } + if(createsused) + fprintf(fp,"{ick_og%lx,ick_os%lx},", + (unsigned long)np->rval,(unsigned long)np->rval); + else + fprintf(fp,"{0,0},"); + prexpr(np->rval,fp,0); + fprintf(fp,"},\n"); +} + +/* This function by AIS. Print a check to see if a just-in-case compiled + statement actually has a meaning yet, or if we should error. */ +static void prunknown(node *np, FILE* fp) +{ + static long negcounter=-65538; + int i,j; + fprintf(fp,"\tif((ick_skipto=ick_jicmatch(\""); + i=prunknownstr(np, fp); + fprintf(fp, "\")))\n\t{\n\t ick_createdata icd[]={\n"); + prunknowncreatedata(np, fp); + fprintf(fp, "\t };\n"); + if(createsused) + { + j=i; + while(j--) + (void) fprintf(fp, "\t\t""ICKSTASH(ick_TWOSPOT, %lu, " + "ick_twospots, ick_oo_twospots);\n" + "\t\t""ick_oo_twospots[%lu]=icd[%d].accessors;\n", + intern(ick_TWOSPOT,1601+j), + intern(ick_TWOSPOT,1601+j),j); + } + if(useickec) + { + fprintf(fp,"\t\t""ick_global_createdata=icd;\n"); + fprintf(fp,"\t\t""ick_dogoto(ick_skipto,ick_lineno,1);\n"); + } + else + { + fprintf(fp,"\t\t""ick_pushnext(%ld); ick_skipto=-ick_skipto; goto top; " + "case %ld:;\n",negcounter,negcounter); + negcounter--; + } + if(createsused) + { + j=i; + while(j--) + (void) fprintf(fp, "\t\t""ICKRETRIEVE(ick_twospots, %lu, " + "ick_TWOSPOT, ick_twoforget, ick_oo_twospots);\n", + intern(ick_TWOSPOT,1601+j)); + + } + fprintf(fp, "\t} else\n"); +} + +/*@dependent@*/ static char *nice_text(char *texts[], int lines) +{ +#define MAXNICEBUF 512 + static char buf[MAXNICEBUF]; + char *cp, *text; + int i; + + if (lines < 1) + lines = 1; + for (cp = buf, i = 0 ; i < lines ; ++i) { + if (cp>buf+MAXNICEBUF-10) + { + (*cp++) = '.'; + (*cp++) = '.'; + (*cp++) = '.'; + *cp = '\0'; + return buf; + } + if (i) { + (*cp++) = '\\'; + (*cp++) = 'n'; + (*cp++) = '\\'; + (*cp++) = '\n'; + (*cp++) = '\t'; + } + for (text = texts[i] ; text != NULL && *text != '\0'; cp++, text++) { + if (cp>buf+MAXNICEBUF-10) + { + (*cp++) = '.'; + (*cp++) = '.'; + (*cp++) = '.'; + *cp = '\0'; + return buf; + } + if(*text == '"' || *text == '\\') { + (*cp++) = '\\'; + } + if(*text == 'K') /* AIS: break the string so that the ick_ec + preprocessor doesn't trigger on the string + ICKNUMBERPAIR */ + { + (*cp++) = '"'; + (*cp++) = '"'; + } + *cp = *text; + } + } + *cp = '\0'; + return buf; +} + +static void emit_guard(tuple *tn, FILE *fp) +/* emit execution guard for giiven tuple (note the unbalanced trailing {!) */ +{ + if(tn->maybe) /* AIS */ + { + if(!multithread) ick_lose(IE405, emitlineno, (const char *)NULL); + (void) fprintf(fp, " gonebackto = setjmp(btjb); choicepoint();\n"); + } + if(!flowoptimize || tn->abstainable) /* This condition by AIS */ + { + (void) fprintf(fp, " if ("); + if (tn->maybe) /* AIS */ + (void) fprintf(fp, "gonebackto == !("); + if (tn->exechance < 100) + (void) fprintf(fp, "ick_roll(%d) && ", tn->exechance); + if ((tn->type != NEXT && tn->type != GO_BACK && tn->type != COME_FROM + && /* AIS */ tn->type != NEXTFROMLABEL && tn->type != UNKNOWN) + || tn->onceagainflag == onceagain_NORMAL) + (void) fprintf(fp, "!ICKABSTAINED(%d))%s {\n", (int)(tn - tuples), + /* AIS */ tn->maybe?")":""); + else /* AIS: [NEXT, GO_BACK, COME_FROM] ONCE needs specially + handled abstentions */ + (void) fprintf(fp, "!ick_oldabstain)%s {\n", + /* AIS */ tn->maybe?")":""); + } + else + { /* AIS */ + if(tn->maybe) ick_lose(IE778, emitlineno, (const char*) NULL); + if(!tn->initabstain) + { + if(tn->type != COMPUCOME && tn->type != GERUCOME + && tn->type != NEXTFROMEXPR && tn->type != NEXTFROMGERUND) + (void) fprintf(fp, " {\n"); + else (void) fprintf(fp, " if(1) {\n"); /* COMPUCOME specifically needs + an if() so it can have + an else. */ + } + else (void) fprintf(fp, " if(0) {\n"); /* for exceptional cases like + DON'T COME FROM #1 where we + need a label or an else. */ + } +} + +void emittextlines(FILE *fp) +{ + int i=0; /* The first textline is line 1 */ + (void) fprintf(fp, "\"\",\n"); + while(++i<iyylineno) + { + (void) fprintf(fp, "\"%s\",\n",nice_text(textlines + i, 0)); + } + (void) fprintf(fp, "\"\"\n"); +} + +void emit(tuple *tn, FILE *fp) +/* emit code for the given tuple */ +{ + node *np, *sp; + int dim; + int generatecfjump=0; /* AIS */ + static int pasttryagain=0; /* AIS */ + extern int cdebug; + + /* grind out label and source dump */ + if (yydebug || cdebug || compile_only) + (void) fprintf(fp, " /* line %03d */\n", tn->ick_lineno); + if (tn->label) + { + if(!useickec) /* AIS */ + (void) fprintf(fp, "case -%u: ; L%u:\n", tn->label, tn->label); + else + /* AIS: start one of ick_ec's labeled blocks. */ + (void) fprintf(fp, "ick_labeledblock(%uU,{",tn->label); + } + if (yydebug || cdebug || compile_only) + { + (void) fprintf(fp, "\t""/* %s */", textlines[tn->ick_lineno]); + /* AIS: grind out an expression explanation */ + if (tn->type == GETS || tn->type == FORGET || tn->type == RESUME + || tn->type == FROM || tn->type == COMPUCOME + || tn->type == MANYFROM || tn->type == NEXTFROMEXPR) + { + (void) fprintf(fp, "\n\t/* Expression is "); + explexpr(tn->type == MANYFROM ? tn->u.node->lval : + tn->type == GETS ? tn->u.node->rval : tn->u.node, fp); + (void) fprintf(fp, " */"); + } + } + (void) fputc('\n', fp); + + /* set up the "next" lexical line number for error messages */ + if (tn->type == NEXT) { + tuple *up; + for (up = tuples; up < tuples + ick_lineno; up++) + if (tn->u.target == up->label) { + emitlineno = up->ick_lineno; + break; + } + } + else if (tn->ncomefrom) + { + /* AIS: For multithreading. Return the 1st if we're forking. */ + emitlineno = comefromsearch(tn,1); + if(emitlineno != -1) + emitlineno = tuples[emitlineno-1].ick_lineno; + } + else if (tn < tuples + ick_lineno - 1) + emitlineno = tn[1].ick_lineno; + else + emitlineno = iyylineno; + if(!pickcompile) /* AIS: PICs can't report errors, + so don't bother with ick_lineno */ + (void) fprintf(fp, " ick_lineno = %d;\n", emitlineno); + + /* AIS: figure out which line we're on, so E000 can be done correctly */ + if (tn < tuples + ick_lineno - 1) + dim = tn[1].ick_lineno - tn->ick_lineno; + else + dim = iyylineno - tn->ick_lineno; + if (tn->sharedline) + ++dim; + E000string=nice_text(textlines + tn->ick_lineno, dim); + + /* AIS: set weaving status if necessary */ + if(tn->setweave) + (void) fprintf(fp, " weaving = %d;\n", tn->setweave>0); + + /* AIS: print warnings on -l */ + if(ick_checkforbugs) + { + if(tn->warn112) ick_lwarn(W112, emitlineno, (const char*) NULL); + if(tn->warn128) ick_lwarn(W128, emitlineno, (const char*) NULL); + if(tn->warn534) ick_lwarn(W534, emitlineno, (const char*) NULL); + if(tn->warn018) ick_lwarn(W018, emitlineno, (const char*) NULL); + if(tn->warn016) ick_lwarn(W016, emitlineno, (const char*) NULL); + if(tn->warn276) ick_lwarn(W276, emitlineno, (const char*) NULL); + if(tn->warn239) ick_lwarn(W239, emitlineno, (const char*) NULL); + if(tn->warn622) ick_lwarn(W622, emitlineno, (const char*) NULL); + } + + /* AIS: emit debugging information */ + if (yukdebug||yukprofile) + { + (void) fprintf(fp, " YUK(%d,%d);\n", + (int)(tn-tuples),emitlineno); + } + + /* AIS: The +mystery option on degenerated code causes the code to + unexpectedly terminate after 4 billion commands are run, thus + preventing an infinite loop. Of course, it will enhance the fun + if we don't tell the user that. (This is necessary for the + constant-output optimizer to work properly.) */ + if(coopt) (void) fprintf(fp, " ick_MYSTERYLINE;\n"); + + /* AIS: If the tuple is ONCE/AGAIN flagged, we need a delayed-action + set of its abstention status to the AGAIN-flagged status. The + problem is that some statements, like COME FROM, need to set after + the command has finished, and some, like NEXT, need it before the + command has started. At the moment, only NEXT and GO_BACK have a + ONCE/AGAIN before it, rather than after (because neither of them + continue in the normal fashion). UNKNOWN is also handled this way, + because CREATEd statements can be NEXT-like but not COME FROM-like. */ + if ((tn->type == NEXT || tn->type == GO_BACK || tn->type == UNKNOWN) && + tn->onceagainflag != onceagain_NORMAL) + { + /* ONCE/AGAIN has already been swapped by perpet.c in the case + of a preabstained statement ('DO NOT'...). So if we currently have + a ONCE, it means that being abstained is the attractive state, + and if we currently have an AGAIN, it means that being + reinstated is the attractive state. Semantics with computed + ABSTAIN: Don't change the abstention count unless necessary, in + which case change it to 0 or 1. */ + fprintf(fp," ick_oldabstain = ICKABSTAINED(%d);\n", (int)(tn - tuples)); + fprintf(fp," ICKABSTAINED(%d) = %s;\n", + (int)(tn - tuples), + tn->onceagainflag==onceagain_ONCE ? "ick_oldabstain ? ick_oldabstain : 1" + : "0"); + /* This test-and-set must be atomic. As all statements are atomic anyway + in the current version of ick, that isn't a problem, but if anyone + wants to try using POSIX's multithreading features, the above two + lines need to be a critical section. */ + } + + /* AIS: in the case of COMPUCOME, we need an extra guard unless useickec. */ + if ((!useickec && (tn->type == COMPUCOME || tn->type == NEXTFROMEXPR)) + || tn->type == GERUCOME || tn->type == NEXTFROMGERUND) + { + fprintf(fp," if(0)\n {\n"); + fprintf(fp,"CCF%d:\n",compucomecount++); + if(tn->type == COMPUCOME || tn->type == NEXTFROMEXPR) + { + fprintf(fp," if(ick_skipto&&ick_skipto=="); + prexpr(tn->u.node, fp, 1); + } + else if(tn->type == GERUCOME || tn->type == NEXTFROMGERUND) + { + fprintf(fp," if("); + for (np = tn->u.node; np; np = np->rval) + { + if (np->constant == ABSTAIN) { + (void) fprintf(fp, +"linetype[truelineno] == %s || linetype[truelineno] == %s || " +"linetype[truelineno] == %s || linetype[truelineno] == %s || ", + enablers[np->constant-GETS], + enablers[np->constant-GETS+2], + enablers[FROM-GETS], enablers[MANYFROM-GETS]); + } else if (np->constant == REINSTATE) { + (void) fprintf(fp, +"linetype[truelineno] == %s || linetype[truelineno] == %s || ", + enablers[np->constant-GETS], + enablers[np->constant-GETS+2]); + } else if (np->constant == GETS) { + (void) fprintf(fp, +"linetype[truelineno] == %s || linetype[truelineno] == %s || ", + enablers[GETS-GETS], + enablers[RESIZE-GETS]); + } else if (np->constant == COME_FROM) { + (void) fprintf(fp, +"linetype[truelineno] == %s || linetype[truelineno] == %s || " +"linetype[truelineno] == %s || ", + enablers[COME_FROM-GETS], + enablers[COMPUCOME-GETS], + enablers[GERUCOME-GETS]); + } else if (np->constant == NEXTFROMLABEL) { + (void) fprintf(fp, +"linetype[truelineno] == %s || linetype[truelineno] == %s || " +"linetype[truelineno] == %s || ", + enablers[NEXTFROMLABEL-GETS], + enablers[NEXTFROMEXPR-GETS], + enablers[NEXTFROMGERUND-GETS]); + } else { + (void) fprintf(fp, "linetype[truelineno] == %s || ", + enablers[np->constant-GETS]); + } + } + fprintf(fp, "0"); + } + fprintf(fp,") {\n"); + } + + /* AIS: With this block placed here, you can't even have a comment + after a TRY AGAIN line. Move it below the next check if this seems + to be undesirable behaviour. */ + if(pasttryagain) /* AIS */ + { + ick_lose(IE993, emitlineno, (const char*)NULL); + } + + if(flowoptimize && tn->initabstain && !tn->abstainable + && tn->type != COMPUCOME && tn->type != COME_FROM && + tn->type != NEXT && tn->type != GERUCOME && + tn->type != NEXTFROMLABEL && tn->type != NEXTFROMEXPR && + tn->type != NEXTFROMGERUND) /* AIS */ + goto skipcomment; /* Look, a comment! We can completely skip all + degeneration of this statement (although with + -c, comments will appear in the degenerated + code in its place). The COMPUCOME condition is + because it is so weird. COME_FROM and NEXT are + exempted so labels are generated. */ + + /* emit conditional-execution prefixes */ + /* AIS: added the useickec condition */ + if ((tn->type != COME_FROM && tn->type != NEXTFROMLABEL) || useickec) + emit_guard(tn, fp); + + /* now emit the code for the statement body */ + switch(tn->type) + { + case GETS: + /* AIS: variableconstants means GETS has been generalised */ + if(variableconstants) + { + revprexpr(tn->u.node->lval, fp, tn->u.node->rval); + nodefree(tn->u.node); + break; + } + + /* Start of AIS optimization */ + np = tn->u.node; + if(np->lval->opcode == SUB) np = np->lval; + if(flowoptimize && ick_Base == 2 && !opoverused && !variableconstants && + (np->lval->opcode == ick_TWOSPOT + || np->lval->opcode == ick_HYBRID + || !(tn->u.node->rval->optdata & ~0xffffLU))) + { + atom* op; + int ignorable = 1; + assert(oblist != NULL); + for(op = oblist; op < obdex; op++) + { + if(op->type == np->lval->opcode && + (unsigned long)op->intindex == np->lval->constant) + { + ignorable &= op->ignorable; + } + } + if(!ignorable) + { /* Variable can't be ignored, and expression must be in range */ + (void) fprintf(fp,"\t"""); + prexpr(tn->u.node->lval, fp, 1); + (void) fprintf(fp, " = "); + prexpr(tn->u.node->rval, fp, 1); + (void) fprintf(fp, ";\n"); + break; + } + } + /* End of AIS optimization */ + if(opoverused&& + (tn->u.node->lval->opcode==ick_ONESPOT|| + tn->u.node->lval->opcode==ick_TWOSPOT)) /* AIS */ + { + (void) fprintf(fp,"\t"""); + ooprvar(tn->u.node->lval, fp, 1); + (void) fprintf(fp,".set("); + prexpr(tn->u.node->rval, fp, 1); + (void) fprintf(fp,",os%dspot%lu);\n", + (tn->u.node->lval->opcode==ick_TWOSPOT)+1, + tn->u.node->lval->constant); + } + else if(!pickcompile) + { + np = tn->u.node; + + if (np->lval->opcode != SUB) { + sp = np->lval; + (void) fprintf(fp,"\t""(void) ick_assign((char*)&"); + } + else { + sp = np->lval->lval; + (void) fprintf(fp,"\t""(void) ick_assign("); + } + prvar(np->lval, fp, 1); + (void) fprintf(fp,", %s", nameof(sp->opcode, vartypes)); + (void) fprintf(fp,", %s[%lu], ", nameof(sp->opcode, forgetbits), + sp->constant); + prexpr(np->rval, fp, 1); + (void) fprintf(fp,");\n"); + } + else /* AIS: Added this case for the simpler PIC assignment rules */ + { + (void) fprintf(fp,"\t""if(ignore%s%lu) ", + nameof(tn->u.node->lval->opcode,varstores), + tn->u.node->lval->constant); + prexpr(tn->u.node->lval, fp, 1); + (void) fprintf(fp, " = "); + prexpr(tn->u.node->rval, fp, 1); + (void) fprintf(fp, ";\n"); + } + break; + + case RESIZE: + if(pickcompile) ick_lose(IE256, emitlineno, (const char*) NULL); /* AIS */ + np = tn->u.node; + dim = 0; + for (sp = np->rval; sp; sp = sp->rval) + dim++; + (void) fprintf(fp, "\t""ick_resize("); + prvar(np->lval, fp, 1); + (void) fprintf(fp, ", %s[%lu]", nameof(np->lval->opcode, forgetbits), + np->lval->constant); + (void) fprintf(fp, ", %d", dim); + for (sp = np->rval; sp; sp = sp->rval) { + (void) fprintf(fp, ", (size_t)"); + prexpr(sp->lval, fp, 1); + } + (void) fprintf(fp, ");\n"); + break; + + case NEXT: + /* AIS: if using ickec, use its features for the next */ + if(useickec) + { + (void) fprintf(fp,"\t""ick_dogoto(%uU,ick_lineno,1);\n",tn->u.target); + break; + } + /* Start of AIS optimization */ + if(tn->u.target>=1000 && tn->u.target<=1999 && pickcompile) + { + /* optimize syslib call on a PIC */ + (void) fprintf(fp, "\t""syslibopt%u();\n", tn->u.target); + break; + } + if(tn->optversion) + { /* optimizef has checked that this is a valid optimization */ + (void) fprintf(fp, "\t""if(1 == "); + prexpr(tn->u.node, fp, 1); /* frees optimizef's nodecopy */ + /* AIS: Everything now in one giant switch(), with some very strange + constructs (including ;{;} as a null statement; this makes + degenerating the code slightly easier) */ + (void) fprintf(fp, ") {ick_pushnext(%d); ick_skipto=%uU; goto top;}} case %d:;{;\n", + (int)(tn - tuples + 1), tn->nexttarget, (int)(tn - tuples + 1)); + break; + } + /* End of AIS optimization */ + (void) fprintf(fp, /* same change as above (case rather than a label) */ + "\t""ick_pushnext(%d); goto L%u;} case %d:;{;\n", + (int)(tn - tuples + 1), tn->u.target, (int)(tn - tuples + 1)); + break; + + case GO_BACK: /* By AIS */ + if(!multithread) ick_lose(IE405, emitlineno, (const char*) NULL); + (void) fprintf(fp, "\t""choiceback();\n"); + break; + + case GO_AHEAD: /* By AIS */ + if(!multithread) ick_lose(IE405, emitlineno, (const char*) NULL); + (void) fprintf(fp, "\t""choiceahead();\n"); + break; + + case RESUME: + if(useickec) /* AIS */ + { + (void) fprintf(fp, "\t""ick_doresume("); + prexpr(tn->u.node, fp, 1); + (void) fprintf(fp, ", ick_lineno);\n"); + break; + } + (void) fprintf(fp, "\t""ick_skipto = ick_resume("); + prexpr(tn->u.node, fp, 1); + (void) fprintf(fp, "); goto top;\n"); + break; + + case FORGET: + if(useickec) /* AIS */ + { + (void) fprintf(fp, "\t""ick_forget("); + prexpr(tn->u.node, fp, 1); + (void) fprintf(fp, ");\n"); + break; + } + + (void) fprintf(fp, "\t""ick_popnext("); + prexpr(tn->u.node, fp, 1); + (void) fprintf(fp, ");\n"); + break; + + case STASH: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp, "\t""ICKSTASH(%s, %lu, %s, %s%s);\n", + nameof(np->opcode, vartypes), + np->constant, + nameof(np->opcode, varstores), + /* AIS */(opoverused&&(np->opcode==ick_ONESPOT|| + np->opcode==ick_TWOSPOT)? + "ick_oo_":"0"), + /* AIS */(opoverused&&(np->opcode==ick_ONESPOT|| + np->opcode==ick_TWOSPOT)? + nameof(np->opcode, varstoresdem):"0")); + break; + + case RETRIEVE: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp, "\t""ICKRETRIEVE(%s, %lu, %s, %s, %s%s);\n", + nameof(np->opcode, varstores), np->constant, + nameof(np->opcode, vartypes), + nameof(np->opcode, forgetbits), + /* AIS */(opoverused&&(np->opcode==ick_ONESPOT|| + np->opcode==ick_TWOSPOT)? + "ick_oo_":"0"), + /* AIS */(opoverused&&(np->opcode==ick_ONESPOT|| + np->opcode==ick_TWOSPOT)? + nameof(np->opcode, varstoresdem):"0")); + + break; + + case IGNORE: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp,"\t""ICKIGNORE(%s,%lu,%s) = ick_TRUE;\n", + nameof(np->opcode, forgetbits), + np->constant, + nameof(np->opcode, varstores)); + break; + + case REMEMBER: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp,"\t""ICKIGNORE(%s,%lu,%s) = ick_FALSE;\n", + nameof(np->opcode, forgetbits), + np->constant, + nameof(np->opcode, varstores)); + break; + + /* All abstention code has been edited by AIS to allow for the new + abstention rules */ + case ABSTAIN: + /* AIS: In CLC-INTERCAL, you can't abstain a GIVE UP line, so I copied + a modified version of Joris's REINSTATE patch here as well */ + if (!ick_clcsemantics || (tuples + tn->u.target - 1)->type != GIVE_UP) + { + if(!pickcompile) + (void) fprintf(fp, "\t""if(!ICKABSTAINED(%u)) ICKABSTAINED(%u) = 1;\n", tn->u.target - 1, tn->u.target-1); + else + (void) fprintf(fp, "ICKABSTAINED(%u) = 1;\n", tn->u.target-1); + } + else + (void) fprintf(fp, "\t""/* not abstaining from a GIVE UP line */\n");; + break; + + case FROM: + if(pickcompile) ick_lose(IE256, emitlineno, (const char*) NULL); + (void) fprintf(fp, "\t""ICKABSTAINED(%u)+=", tn->u.target-1); + tn->u.node->width = 32; + prexpr(tn->u.node,fp, 1); + (void) fprintf(fp, ";\n"); + break; + + case REINSTATE: + /* (Joris Huizer) ensure it is not an GIVE UP statement */ + if ((tuples + tn->u.target - 1)->type != GIVE_UP) + { + if(!pickcompile) + (void) fprintf(fp, "\t""if(ICKABSTAINED(%u)) ICKABSTAINED(%u)--;\n", tn->u.target - 1, tn->u.target-1); + else + (void) fprintf(fp, "\t""ICKABSTAINED(%u)=0;\n", tn->u.target - 1); + } + else + (void) fprintf(fp, "\t""/* not reinstating a GIVE UP line */\n"); + break; + + case ENABLE: + case DISABLE: + case MANYFROM: + /* AIS: This code has been rewritten to make use of the revlinetype array + (an optimisation that Joris Huizer came up with; however, I am not + using his code, but rewriting it, to make use of a single array and an + index to it, rather than one array for each command type, for + maintainability reasons. */ + if(pickcompile) ick_lose(IE256, emitlineno, (const char*) NULL); + (void) fprintf(fp,"\tint i;\n"); + np=tn->u.node; + if(tn->type==MANYFROM) + { + np=np->rval; + fprintf(fp,"\tint j = "); + prexpr(tn->u.node->lval, fp, 1); + fprintf(fp,";\n"); + } + for(; np; np = np->rval) + { + int npc = np->constant; + anothertype: + (void) fprintf(fp,"\n\tfor(i=revlineindex[%s];i<revlineindex[%s+1];i++)\n", + enablers[npc-GETS],enablers[npc-GETS]); + switch(tn->type) + { + case ENABLE: (void) fprintf(fp,"\t if (ick_abstained[revlinetype[i]])" + "\t\tick_abstained[revlinetype[i]]--;\n"); break; + case DISABLE: (void) fprintf(fp,"\t if(!ick_abstained[revlinetype[i]])" + "\t\tick_abstained[revlinetype[i]]=1;\n"); break; + case MANYFROM:(void) fprintf(fp,"\tick_abstained[revlinetype[i]]+=j;\n"); break; + default: ick_lose(IE778, emitlineno, (const char *)NULL); + } + switch(npc) + { + case ABSTAIN: npc=DISABLE; goto anothertype; + case DISABLE: npc=FROM; goto anothertype; + case FROM: npc=MANYFROM; goto anothertype; + case REINSTATE: npc=ENABLE; goto anothertype; + case COME_FROM: npc=COMPUCOME; goto anothertype; + case COMPUCOME: npc=GERUCOME; goto anothertype; + case NEXTFROMLABEL: npc=NEXTFROMEXPR; goto anothertype; + case NEXTFROMEXPR: npc=NEXTFROMGERUND; goto anothertype; + default: break; + } + } + break; + + case NEXTFROMEXPR: + case NEXTFROMGERUND: + case GERUCOME: + case COMPUCOME: /* By AIS. Note that this doesn't even have balanced + braces; it's designed to work with COMPUCOME's + crazy guarding arrangements */ + if(pickcompile) ick_lose(IE256, emitlineno, (const char*) NULL); /* AIS */ + if(useickec) /* use ick_ec's features for next from and come from*/ + { + if(tn->type == COMPUCOME) + { + fprintf(fp,"\t""ick_docomefromif("); + prexpr(tn->u.node, fp, 1); + fprintf(fp,",ick_lineno,({int i=0;"); + emit_guard(tn,fp); /* re-emit the guard */ + fprintf(fp,"i=1;};i;}));\n"); + break; + } + else if(tn->type == NEXTFROMEXPR) + { + fprintf(fp,"\t""ick_donextfromif("); + prexpr(tn->u.node, fp, 1); + fprintf(fp,",ick_lineno,({int i=0;"); + emit_guard(tn,fp); /* re-emit the guard */ + fprintf(fp,"i=1;};i;}));\n"); + break; + } + } + fprintf(fp,"\t""%s;} else goto CCF%d;\n", + multithread?"NEXTTHREAD":useprintflow? + "if(ick_printflow) fprintf(stderr,\"[%d]\",ick_lineno)":"", + compucomecount); + break; + + case GIVE_UP: /* AIS: Edited to allow for yuk */ + if(yukprofile||yukdebug) fprintf(fp, "\t""YUKTERM;\n"); + if(multithread) fprintf(fp, "\t""killthread();\n"); + else + { + if(nonespots||opoverused) + fprintf(fp,"\t""if(ick_onespots) free(ick_onespots);\n"); + if(ntwospots||opoverused) + fprintf(fp,"\t""if(ick_twospots) free(ick_twospots);\n"); + if(ntails) fprintf(fp,"\t""if(ick_tails) free(ick_tails);\n"); + if(nhybrids) fprintf(fp,"\t""if(ick_hybrids) free(ick_hybrids);\n"); + if(nonespots||opoverused) + fprintf(fp,"\t""if(ick_oneforget) free(ick_oneforget);\n"); + if(ntwospots||opoverused) + fprintf(fp,"\t""if(ick_twoforget) free(ick_twoforget);\n"); + if(ntails) fprintf(fp,"\t""if(ick_tailforget) free(ick_tailforget);\n"); + if(nhybrids) fprintf(fp,"\t""if(ick_hyforget) free(ick_hyforget);\n"); + if(opoverused) + { + fprintf(fp,"\t""if(ick_oo_onespots) free(ick_oo_onespots);\n"); + fprintf(fp,"\t""if(ick_oo_twospots) free(ick_oo_twospots);\n"); + } + fprintf(fp,"\t""if(ick_next) free(ick_next);\n"); + if(useickec) + fprintf(fp,"\t""if(ick_next_jmpbufs) free(ick_next_jmpbufs);\n"); + } + (void) fprintf(fp, "\t""exit(0);\n"); + break; + + case TRY_AGAIN: /* By AIS */ + (void) fprintf(fp, "\t""goto ick_restart;\n }\n"); + if(yukprofile||yukdebug) fprintf(fp, " if(yukloop) goto ick_restart;\n"); + if(yukprofile||yukdebug) fprintf(fp, " YUKTERM;\n"); + if(multithread) fprintf(fp, "\t""killthread();\n"); + else + { + if(nonespots||opoverused) + fprintf(fp,"\t""if(ick_onespots) free(ick_onespots);\n"); + if(ntwospots||opoverused) + fprintf(fp,"\t""if(ick_twospots) free(ick_twospots);\n"); + if(ntails) fprintf(fp,"\t""if(ick_tails) free(ick_tails);\n"); + if(nhybrids) fprintf(fp,"\t""if(ick_hybrids) free(ick_hybrids);\n"); + if(nonespots||opoverused) + fprintf(fp,"\t""if(ick_oneforget) free(ick_oneforget);\n"); + if(ntwospots||opoverused) + fprintf(fp,"\t""if(ick_twoforget) free(ick_twoforget);\n"); + if(ntails) fprintf(fp,"\t""if(ick_tailforget) free(ick_tailforget);\n"); + if(nhybrids) fprintf(fp,"\t""if(ick_hyforget) free(ick_hyforget);\n"); + if(opoverused) + { + fprintf(fp,"\t""if(ick_oo_onespots) free(ick_oo_onespots);\n"); + fprintf(fp,"\t""if(ick_oo_twospots) free(ick_oo_twospots);\n"); + } + } + (void) fprintf(fp, " {\n\treturn(0);\n"); + /* because if TRY AGAIN is the last line, falling off the end isn't an error */ + pasttryagain=1; /* flag an error if we try any more commands */ + break; + + case WRITE_IN: + if(pickcompile) ick_lose(IE256, emitlineno, (const char*) NULL); /* AIS */ + for (np = tn->u.node; np; np = np->rval) { + if (np->lval->opcode == ick_TAIL || np->lval->opcode == ick_HYBRID) { + (void) fprintf(fp,"\t""ick_binin("); + prvar(np->lval, fp, 1); + (void) fprintf(fp, ", %s[%lu]", + nameof(np->lval->opcode, forgetbits), + np->lval->constant); + (void) fprintf(fp,");\n"); + } + else { + if (np->lval->opcode != SUB) { + sp = np->lval; + (void) fprintf(fp,"\t""(void) ick_assign((char*)&"); + } + else { + sp = np->lval->lval; + (void) fprintf(fp,"\t""(void) ick_assign("); + } + prvar(np->lval, fp, 1); + (void) fprintf(fp,", %s", nameof(sp->opcode, vartypes)); + (void) fprintf(fp,", %s[%lu]", nameof(sp->opcode, forgetbits), + sp->constant); + (void) fprintf(fp,", ick_pin());\n"); + } + } + break; + + case READ_OUT: + if(pickcompile) ick_lose(IE256, emitlineno, (const char*) NULL); /* AIS */ + for (np = tn->u.node; np; np = np->rval) + { + if (np->lval->opcode == ick_TAIL || np->lval->opcode == ick_HYBRID) { + (void) fprintf(fp,"\t""ick_binout("); + prvar(np->lval, fp, 1); + (void) fprintf(fp,");\n"); + } + else { + (void) fprintf(fp, "\t""ick_pout("); + prexpr(np->lval, fp, 1); + (void) fprintf(fp, ");\n"); + } + } + break; + + case PIN: /* AIS, with some code borrowed from the GETS code */ + np = tn->u.node; + (void) fprintf(fp, "\t""TRISA = seq(~(("); + prexpr(np, fp, 0); + (void) fprintf(fp, " >> 16) & 255));\n"); + (void) fprintf(fp, "\t""TRISB = seq(~("); + prexpr(np, fp, 0); + (void) fprintf(fp, " >> 24));\n"); + (void) fprintf(fp, "\t""PORTA = seq(~("); + prexpr(np, fp, 0); + (void) fprintf(fp, " & 255));\n"); + (void) fprintf(fp, "\t""PORTB = seq(~(("); + prexpr(np, fp, 0); + (void) fprintf(fp, " >> 8) & 255));\n"); + { + atom* op; + int ignorable = 1; + assert(oblist != NULL); + for(op = oblist; op < obdex; op++) + { + if(op->type == np->opcode && + (unsigned long)op->intindex == np->constant) + { + ignorable &= op->ignorable; + } + } + if(!ignorable) + { /* Variable can't be ignored, and expression must be in range */ + (void) fprintf(fp,"\t"""); + prexpr(np, fp, 1); + (void) fprintf(fp, " = "); + } + else + { + np = tn->u.node; + (void) fprintf(fp,"\t""if(ignore%s%lu) ", + nameof(np->opcode,varstores), + np->constant); + prexpr(np, fp, 1); + (void) fprintf(fp, " = "); + } + } + (void) fprintf(fp,"(TRISB<<24) | (TRISA<<16) | (PORTB<<8) | PORTA;\n"); + break; + + case CREATE: /* By AIS */ + if(createsused == 0) goto splatme; + (void) fprintf(fp,"\t""ick_registercreation(\""); + prunknownstr(tn->u.node, fp); + (void) fprintf(fp,"\",%uU);\n",tn->u.target); + break; + + case COMPUCREATE: /* By AIS */ + if(createsused == 0) goto splatme; + (void) fprintf(fp,"\t""ick_registercreation(\""); + prunknownstr(tn->u.node->rval, fp); + (void) fprintf(fp,"\","); + prexpr(tn->u.node->lval, fp, 1); + (void) fprintf(fp,");\n"); + free(tn->u.node); /* don't free the rval */ + break; + + case UNKNOWN: /* By AIS */ + /* We generate a check to see if the unknown statement has gained a + meaning since it was compiled, or otherwise continue to the splattered + case. Not for PIC-INTERCAL, though. */ + if(!pickcompile) prunknown(tn->u.node, fp); + /*@fallthrough@*/ + case SPLATTERED: + /* AIS: The code previously here could access unallocated memory due to + a bug if the comment was a COME FROM target. The problem is that + emitlineno (the line to show an error on) is usually the line after + this one, but not always, and in this case the line after this one is + always what we want, so I copied the relevant part of the emitlineno + logic here to fix the bug. */ + splatme: + if (tn < tuples + ick_lineno - 1) + dim = tn[1].ick_lineno - tn->ick_lineno; + else + dim = iyylineno - tn->ick_lineno; + if (tn->sharedline) + ++dim; + (void) fprintf(fp, "\t""ick_lose(IE000, %d, \"%s\");\n", + emitlineno, nice_text(textlines + tn->ick_lineno, dim)); + break; + + case PREPROC: /* AIS: 'DO NOTHING', but not enterable into a program. This + is generated by the preprocessor. */ + fprintf(fp,"\t""; /* do nothing */\n"); + break; + + case COME_FROM: + case NEXTFROMLABEL: /* AIS */ + if(useickec) /* AIS */ + { + if(tn->type == COME_FROM) + { + fprintf(fp,"\t""ick_docomefromif(%uU,ick_lineno,({int i=0;", + tn->u.target); + emit_guard(tn,fp); /* re-emit the guard */ + fprintf(fp,"i=1;};i;}));\n"); + break; + } + else /* (tn->type == NEXTFROMLABEL) */ + { + fprintf(fp,"\t""ick_donextfromif(%uU,ick_lineno,({int i=0;", + tn->u.target); + emit_guard(tn,fp); /* re-emit the guard */ + fprintf(fp,"i=1;};i;}));\n"); + break; + } + } + (void) fprintf(fp, "if(0) {C%ld: %s;%s}\n", (long)(tn-tuples+1), + tn->type==NEXTFROMLABEL ? "ick_pushnext(truelineno+1)":"", + multithread?" NEXTTHREAD;":!useprintflow?"" + :" if(ick_printflow) " + "fprintf(stderr,\"[%d]\",ick_lineno);"); + /* AIS: Changed so all COME_FROMs have unique labels even if two + of them aim at the same line, and added the NEXT FROM case (which + involves hiding COME FROM labels in an unreachable if()). */ + break; + + case WHILE: /* AIS: fall through to the error, because this shouldn't + come up yet. */ + default: + ick_lose(IE778, emitlineno, (const char *)NULL); + /*@-unreachable@*/ break; /*@=unreachable@*/ + } + + if ((tn->type != COME_FROM && tn->type != NEXTFROMLABEL) + || /*AIS*/ useickec) + (void) fprintf(fp, " }\n"); + + skipcomment: + + if ((!useickec && (tn->type == COMPUCOME || tn->type == NEXTFROMEXPR)) + || tn->type == NEXTFROMGERUND || tn->type == GERUCOME ) + { /* By AIS */ + (void) fprintf(fp," else goto CCF%d;\n",compucomecount); + (void) fprintf(fp," ick_ccfc++;\n"); + /* Note that due to the semantics of setjmp, this has to be written as 2 + separate ifs. The MULTICOME macro expands to a non-multithreaded or + multithreaded function for handling a COME FROM clash. */ + (void) fprintf(fp," if(ick_ccfc==1||MULTICOME(%d,ick_cjb))\n" + "\t""if(setjmp(ick_cjb) == 0) goto CCF%d;\n", + emitlineno, compucomecount); + /* Of course, emitlineno is unlikely to be helpful! */ + if(tn->type == NEXTFROMEXPR || tn->type == NEXTFROMGERUND) + { + /* Stack up the statement we've NEXTed from */ + (void) fprintf(fp," ick_pushnext(truelineno+1);\n"); + } + (void) fprintf(fp," }\n"); + } + + /* AIS: Before any COMING FROM this line is done, we need to sort out + ONCE and AGAIN situations, unless this line was a NEXT or GO_BACK. + COME FROM is also excluded because it acts at the suckpoint, not + at the place it's written in the program. */ + if (tn->onceagainflag != onceagain_NORMAL && + tn->type != NEXT && tn->type != GO_BACK && tn->type != UNKNOWN && + ((tn->type != COME_FROM && tn->type != NEXTFROMLABEL) || useickec)) + { + /* See my comments against the NEXT code for more information. + This code is placed here so COME FROM ... ONCE constructs work + properly (the line is ick_abstained if the COME FROM is reached in + execution, or its suckpoint is reached in execution). */ + fprintf(fp," ick_oldabstain = ICKABSTAINED(%d);\n", (int)(tn - tuples)); + fprintf(fp," ICKABSTAINED(%d) = %s;\n", + (int)(tn - tuples), + tn->onceagainflag==onceagain_ONCE ? "ick_oldabstain ? ick_oldabstain : 1" + : "0"); + } + + /* AIS: This is where we start the COME FROM suckpoint code. */ + + /* AIS: The ickec version is very simple! We just finish the labeled + block started at the start of the command. */ + if(tn->label && useickec) + (void) fprintf(fp, "});\n"); + + /* AIS: We need to keep track of how many COME FROMs are aiming here + at runtime, if we don't have the very simple situation of no + COMPUCOMEs and a single-thread program (in which case the check + is done at compile-time by codecheck). Even without COMPUCOME, + this can change in a multithread program due to abstentions. */ + if((tn->ncomefrom && multithread) || (tn->label && compucomesused) + || gerucomesused) + (void) fprintf(fp, " ick_ccfc = 0;\n"); + /* AIS: For NEXTING FROM this line */ + if(nextfromsused && tn->ncomefrom) + { + (void) fprintf(fp, " truelineno = %d;\n", (int)(tn-tuples)); + } + /* + * If the statement that was just degenerated was a COME FROM target, + * emit the code for the jump to the COME FROM. + * AIS: Changed most of this to allow for multithreading. + */ + while(tn->ncomefrom && !useickec) /* acts as an if if singlethreading */ + { + tuple* cf; /* local to this block */ + if(multithread || compucomesused) generatecfjump = 1; + cf = tuples+comefromsearch(tn,tn->ncomefrom)-1; + if (yydebug || compile_only) + (void) fprintf(fp, + " /* line %03d is a suck point for the COME FROM " + "at line %03d */\n", tn->ick_lineno, cf->ick_lineno); + if (cf->onceagainflag != onceagain_NORMAL) + { /* Calculate ONCE/AGAIN when the suckpoint is passed */ + fprintf(fp," ick_oldabstain = ICKABSTAINED(%d);\n", (int)(cf - tuples)); + fprintf(fp," ICKABSTAINED(%d) = %s;\n", + (int)(cf - tuples), + cf->onceagainflag==onceagain_ONCE ? "ick_oldabstain ? ick_oldabstain : 1" + : "0"); + } + emit_guard(cf, fp); + if(multithread || compucomesused) + (void) fprintf(fp, + "\t""ick_ccfc++;\tif(ick_ccfc==1||MULTICOME(%d,ick_cjb)) " + "if(setjmp(ick_cjb) == 1) goto C%ld;\n }\n", + emitlineno, (long)(cf-tuples+1)); + else /* optimize for the simple case */ + (void) fprintf(fp, "\t""goto C%ld;\n }\n", (long)(cf-tuples+1)); + tn->ncomefrom--; + } + + /* AIS: If the statement has a label, it might be a + computed COME FROM target. Also check the flag that says this + code is needed in a multithread non-COMPUCOME program. + If (at runtime) ick_ccfc is nonzero, we know ick_cjb has already been set; + otherwise, set it now. In the case of a multithread non-COMPUCOME + program, the goto will just jump to a longjmp, switching to the + one and only one COME FROM that hasn't been given its own thread. + However, skip all the compucomes and gerucomes if preproc is set, + because COMING FROM a preproc should only ever be done by label. */ + if (((tn->label && compucomesused) || generatecfjump || gerucomesused) && + (!tn->preproc || generatecfjump) && (!useickec || gerucomesused)) + { + if(compucomesused) + { + (void) fprintf(fp, " ick_skipto = %u;\n", tn->label); + } + if(gerucomesused || nextfromsused) + { + (void) fprintf(fp, " truelineno = %d;\n", (int)(tn-tuples)); + } + if(generatecfjump) (void) fprintf(fp, " if(ick_ccfc) goto CCF%s;\n", + tn->preproc?"L":"0"); + if((compucomesused || gerucomesused) && !tn->preproc) + { /* check all the COMPUCOMES */ + (void) fprintf(fp, " %sif(setjmp(ick_cjb) == 0) goto CCF0;\n", + generatecfjump?"else ":""); + } + generatecfjump = 0; + /* AIS: If NEXT FROM's used, this might be a NEXT return target. + Don't generate case labels for NEXT, as it has them already. */ + if(nextfromsused && tn->type != NEXT) + { + (void) fprintf(fp, " case %u:;\n", (unsigned)(tn-tuples+1)); + } + } + + /* AIS: Now we've finished the statement, let's switch to the next + thread in a multithread program. */ + if(multithread) (void) fputs(" NEXTTHREAD;\n", fp); + else if(useprintflow) + (void) fputs(" if(ick_printflow) fprintf(stderr," + "\"[%d]\",ick_lineno);\n",fp); +} + +/* AIS: Generate prototypes for slat expressions, args to UNKNOWN */ +void emitslatproto(FILE* fp) +{ + node* np=firstslat; + const char* t="ick_type32"; + while(np) + { + fprintf(fp,"%s ick_og%lx(%s);\nvoid ick_os%lx(%s, void(*)());\n", + t,(unsigned long)np,t,(unsigned long)np,t); + np=np->nextslat; + } +} + +/* AIS: Generate bodies for slat expressions, args to UNKNOWN */ +void emitslat(FILE* fp) +{ + node* np=firstslat; + node* temp, *temp2; + const char* t="ick_type32"; + while(np) + { + fprintf(fp, + "void ick_os%lx(%s a, void(*f)())\n{\n static int l=0;\n" + " if(l)\n {\n if(!f) ick_lose(IE778, ick_lineno, (const char *)NULL);\n" + " f(a,0);\n return;\n }\n l=1;\n", + (unsigned long)np,t); + temp=cons(C_A, 0, 0); + revprexpr(np, fp, temp); /* revprexpr can't free */ + fprintf(fp," l=0;\n}\n"); + fprintf(fp,"%s ick_og%lx(%s t)\n{\n %s a;\n static int l=0;\n" + " if(l) return t;\n l=1;\n a=", + t,(unsigned long)np,t,t); + prexpr(np, fp, 0); + fprintf(fp,";\n l=0;\n return a;\n}\n"); + np=np->nextslat; + } + np=firstslat; + /* Note that the order in which the parser assembles nodes means + that we have to free the nodes in reverse order so we don't + free child nodes twice. */ + temp2=0; + while(np) + { + temp=np->nextslat; + np->nextslat=temp2; /* reverse the chain */ + temp2=np; + np=temp; + } + np=temp2; + while(np) + { + temp=np->nextslat; + nodefree(np); + np=temp; + } + /* JH: clear firstslat */ + firstslat = 0; +} + +/* feh.c ends here */