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 */