/* isx - intermediate sequenced executable -  the paraFlow
 * intermediate code.  A fairly standard "three address code"
 * that is a lower level representation than the parse tree but
 * higher level than actual machine code.  The format is
 *    opCode dest left right
 * which is interpreted as 
 *    dest = left opCode right
 * so
 *    + x 3 8		represents x = 3 + 8
 *    * t1 t2 t3	represents t1 = t2*t3
 *    = x y		represents x = y
 *    - u v             represents u = -v
 */

#include "common.h"
#include "hash.h"
#include "dlist.h"
#include "pfParse.h"
#include "pfToken.h"
#include "pfType.h"
#include "pfScope.h"
#include "pfCompile.h"
#include "ctar.h"
#include "recodedType.h"
#include "backEnd.h"
#include "isxLiveVar.h"
#include "isx.h"

static struct isxAddress *isxExpression(struct pfCompile *pfc, 
	struct pfParse *pp, struct hash *varHash, 
	double weight, struct dlList *iList);
/* Generate intermediate code for expression. Return destination */

char *isxValTypeToString(enum isxValType val)
/* Convert isxValType to string. */
{
switch (val)
    {
    case ivZero:
	return "zer";
    case ivBit:
	return "bit";
    case ivByte:
	return "byt";
    case ivShort:
	return "sho";
    case ivInt:
	return "int";
    case ivLong:
	return "lng";
    case ivFloat:
	return "flt";
    case ivDouble:
	return "dbl";
    case ivString:
	return "str";
    case ivObject:
	return "obj";
    case ivVar:
        return "var";
    case ivJump:
	return "lab";
    default:
        internalErr();
	return NULL;
    }
}



char *isxOpTypeToString(enum isxOpType val)
/* Convert isxValType to string. */
{
switch (val)
    {
    case poInit:
        return "poInit";
    case poAssign:
        return "poAssign";
    case poPlus:
	return "poPlus";
    case poMinus:
	return "poMinus";
    case poMul:
	return "poMul";
    case poDiv:
	return "poDiv";
    case poMod:
	return "poMod";
    case poBitAnd:
	return "poBitAnd";
    case poBitOr:
	return "poBitOr";
    case poBitXor:
	return "poBitXor";
    case poShiftLeft:
	return "poShiftLeft";
    case poShiftRight:
	return "poShiftRight";
    case poNegate:
	return "poNegate";
    case poFlipBits:
	return "poFlipBits";
    case poInput:
        return "poInput";
    case poCall:
	return "poCall";
    case poOutput:
        return "poOutput";
    case poLabel:
	return "poLabel";
    case poLoopStart:
        return "poLoopStart";
    case poLoopEnd:
        return "poLoopEnd";
    case poCondEnd:
        return "poCondEnd";
    case poCondStart:
        return "poCondStart";
    case poCondCase:
        return "poCondCase";
    case poJump:
	return "poJump";
    case poBeq:
	return "poBeq";
    case poBne:
	return "poBne";
    case poBlt:
	return "poBlt";
    case poBle:
	return "poBle";
    case poBgt:
	return "poBgt";
    case poBge:
	return "poBge";
    case poBz:
	return "poBz";
    case poBnz:
	return "poBnz";
    case poFuncStart:
        return "poFuncStart";
    case poFuncEnd:
        return "poFuncEnd";
    case poReturnVal:
        return "poReturnVal";
    case poCast:
        return "poCast";
    case poVarInit:
        return "poVarInit";
    case poVarAssign:
        return "poVarAssign";
    case poVarInput:
        return "poVarInput";
    case poIndirect:
        return "poIndirect";
    default:
        internalErr();
	return NULL;
    }
}

char *isxRegName(struct isxReg *reg, enum isxValType valType)
/* Get name of reg for given type. */
{
switch (valType)
    {
    case ivBit:
    case ivByte:
	return reg->byteName;
    case ivShort:
	return reg->shortName;
    case ivInt:
	return reg->intName;
    case ivLong:
	return reg->longName;
    case ivFloat:
	return reg->floatName;
    case ivDouble:
	return reg->doubleName;
    case ivObject:
    case ivString:
	return reg->pointerName;
    case ivVar:
	return reg->intName;
    default:
	internalErr();
	return NULL;
    }
}

void isxAddressDump(struct isxAddress *iad, FILE *f)
/* Print variable name or n/a */
{
fprintf(f, "%s:", isxValTypeToString(iad->valType));
/* Convert isxValType to string. */
switch (iad->adType)
    {
    case iadZero:
	fprintf(f, "#zero");
	break;
    case iadConst:
	{
	struct isxToken *tok = &iad->val.isxTok;
	switch(tok->type)
	    {
	    case pftInt:
		fprintf(f, "#%d", tok->val.i);
		break;
	    case pftLong:
		fprintf(f, "#%lld", tok->val.l);
		break;
	    case pftFloat:
		fprintf(f, "#%f", tok->val.x);
		break;
	    case pftString:
		fprintf(f, "#'%s'", tok->val.s);
		break;
	    default:
	        internalErr();
		break;
	    }
	break;
	}
    case iadRealVar:
    case iadTempVar:
	fprintf(f, "%s*%2.1f", iad->name, iad->weight);
	break;
    case iadInStack:
	fprintf(f, "in(%d)", iad->val.ioOffset);
        break;
    case iadOutStack:
	fprintf(f, "out(%d)", iad->val.ioOffset);
        break;
    case iadReturnVar:
	fprintf(f, "ret(%d)", iad->val.ioOffset);
        break;
    case iadOperator:
	fprintf(f, "#%s", iad->name);
	break;
    case iadLabel:
        fprintf(f, "@%s", iad->name);
	break;
    case iadLoopInfo:
	{
	struct isxLoopInfo *loopy = iad->val.loopy;
	struct isxLoopVar *lv;
        fprintf(f, "~%dx%d", loopy->iteration, 
		loopy->instructionCount);
	if (loopy->children == NULL)
	     fprintf(f, "[inner]");
	fprintf(f, "[");
	for (lv = loopy->hotLive; lv != NULL; lv = lv->next)
	    {
	    fprintf(f, "%s*%2.1f", lv->iad->name, lv->iad->weight);
	    if (lv->reg != NULL)
	        fprintf(f, "@%s", isxRegName(lv->reg, lv->iad->valType));
	    if (lv->next != NULL)
	        fprintf(f, ",");
	    }
	fprintf(f, "]");
	}
	break;
    case iadRecodedType:
	fprintf(f, "type(%d)", iad->recodedType);
	break;
    default:
        internalErr();
	break;
    }
if (iad->reg != NULL)
    fprintf(f, "@%s", isxRegName(iad->reg, iad->valType));
}

void isxDump(struct isx *isx, FILE *f)
/* Dump out isx code */
{
fprintf(f, "%s ", isxOpTypeToString(isx->opType));
if (isx->dest != NULL)
    {
    fprintf(f, " ");
    isxAddressDump(isx->dest, f);
    }
fprintf(f, " =");
if (isx->left != NULL)
    {
    fprintf(f, " ");
    isxAddressDump(isx->left, f);
    }
if (isx->right != NULL)
    {
    fprintf(f, " ");
    isxAddressDump(isx->right, f);
    }
if (isx->liveList != NULL)
    {
    struct isxLiveVar *live;
    fprintf(f, "\t{");
    for (live = isx->liveList; live != NULL; live = live->next)
        {
	struct isxAddress *iad = live->var;
	fprintf(f, "%s", iad->name);
	fprintf(f, ":%d", live->useCount);
	fprintf(f, "@%d", live->usePos[0]);
	if (live->useCount > 1)
	    fprintf(f, "@%d", live->usePos[1]);
	if (iad->reg != NULL)
	    fprintf(f, "~%s", isxRegName(iad->reg, iad->valType));
	if (live->next != NULL)
	    fprintf(f, ",");
	}
    fprintf(f, "}");
    }
}

void isxDumpList(struct dlList *list, FILE *f)
/* Dump all of isx in list */
{
struct dlNode *node;
for (node = list->head; !dlEnd(node); node = node->next)
    {
    isxDump(node->val, f);
    fprintf(f, "\n");
    }
}

struct isx *isxNew(struct pfCompile *pfc, enum isxOpType opType,
	struct isxAddress *dest, struct isxAddress *left,
	struct isxAddress *right)
/* Make new isx instruction. */
{
struct isx *isx;
AllocVar(isx);
isx->opType = opType;
isx->dest = dest;
isx->left = left;
isx->right = right;
return isx;
}

struct isx *isxAddNew(struct pfCompile *pfc, enum isxOpType opType,
	struct isxAddress *dest, struct isxAddress *left,
	struct isxAddress *right, struct dlList *iList)
/* Make new isx instruction, and hang it on iList */
{
struct isx *isx = isxNew(pfc, opType, dest, left, right);;
dlAddValTail(iList, isx);
return isx;
}

enum isxValType isxValTypeFromTy(struct pfCompile *pfc, struct pfType *ty)
/* Return isxValType corresponding to pfType  */
{
struct pfBaseType *base = ty->base;
if (base == pfc->bitType)
    return ivBit;
else if (base == pfc->byteType)
    return ivByte;
else if (base == pfc->shortType)
    return ivShort;
else if (base == pfc->intType)
    return ivInt;
else if (base == pfc->longType)
    return ivLong;
else if (base == pfc->floatType)
    return ivFloat;
else if (base == pfc->doubleType)
    return ivDouble;
else if (base == pfc->stringType || base == pfc->dyStringType)
    return ivString;
else if (base == pfc->varType)
    return ivVar;
else
    return ivObject;
}

static enum isxValType ppToIsxValType(struct pfCompile *pfc, 
	struct pfParse *pp)
/* Return isxValType corresponding to pp */
{
return isxValTypeFromTy(pfc, pp->ty);
}

struct isxAddress *isxConstAddress(enum pfTokType tokType,
	union pfTokVal tokVal, enum isxValType valType)
/* Get place to put constant. */
{
struct isxAddress *iad;
AllocVar(iad);
iad->adType = iadConst;
iad->valType = valType;
iad->val.isxTok.type = tokType;
iad->val.isxTok.val = tokVal;
return iad;
}

static struct isxAddress *zeroAddress()
/* Get place representing zero or nil. */
{
static struct isxAddress zero;
return &zero;
}

struct isxAddress *isxTempVarAddress(struct pfCompile *pfc, struct hash *hash,
	double weight, enum isxValType valType)
/* Create a new temporary var */
{
struct isxAddress *iad;
char buf[18];
safef(buf, sizeof(buf), "$t%X", ++(pfc->tempLabelMaker));
AllocVar(iad);
iad->adType = iadTempVar;
iad->valType = valType;
iad->weight = weight;
hashAddSaveName(hash, buf, iad, &iad->name);
return iad;
}


static struct isxAddress *varAddress(struct pfVar *var, struct hash *hash,
	double weight, enum isxValType valType)
/* Create reference to a real var */
{
struct isxAddress *iad = hashFindVal(hash, var->cName);
if (iad == NULL)
    {
    AllocVar(iad);
    iad->adType = iadRealVar;
    iad->valType = valType;
    iad->val.var = var;
    hashAddSaveName(hash, var->cName, iad, &iad->name);
    }
iad->weight += weight;
return iad;
}

struct isxAddress *isxCallAddress(char *name)
/* Create reference to a function */
{
struct isxAddress *iad;
AllocVar(iad);
iad->adType = iadOperator;
iad->name = name;
return iad;
}

struct isxAddress *isxRecodedTypeAddress(struct pfCompile *pfc, 
	struct pfType *ty)
{
struct isxAddress *iad;
AllocVar(iad);
iad->adType = iadRecodedType;
iad->recodedType = recodedTypeId(pfc, ty);
iad->valType = ivInt;
return iad;
}

struct isxAddress *isxIoAddress(int offset, enum isxValType valType,
	enum isxAddressType adType)
/* Return reference to an io stack address */
{
struct isxAddress *iad;
AllocVar(iad);
iad->adType = adType;
iad->valType = valType;
iad->val.ioOffset = offset;
return iad;
}

static struct isxAddress *operatorAddress(char *name)
/* Create reference to a build-in operator call */
{
struct isxAddress *iad;
AllocVar(iad);
iad->adType = iadOperator;
iad->valType = ivJump;
iad->name = name;
return iad;
}

struct isxAddress *isxTempLabelAddress(struct pfCompile *pfc)
/* Create reference to a temporary label for jumping to. */
{
struct isxAddress *iad;
char buf[18];
safef(buf, sizeof(buf), "L%d", ++pfc->isxLabelMaker);
AllocVar(iad);
iad->adType = iadLabel;
iad->valType = ivJump;
iad->name = cloneString(buf);
return iad;
}

static struct isxAddress *isxBinaryOp(struct pfCompile *pfc, 
	struct pfParse *pp, struct hash *varHash, 
	enum isxOpType op, double weight, struct dlList *iList)
/* Generate intermediate code for expression. Return destination */
{
struct isxAddress *left = isxExpression(pfc, pp->children,
	varHash, weight, iList);
struct isxAddress *right = isxExpression(pfc, pp->children->next,
	varHash, weight, iList);
struct isxAddress *dest = isxTempVarAddress(pfc, varHash, weight, ppToIsxValType(pfc,pp));
isxAddNew(pfc, op, dest, left, right, iList);
return dest;
}

static struct isxAddress *isxUnaryOp(struct pfCompile *pfc, 
	struct pfParse *pp, struct hash *varHash, 
	enum isxOpType op, double weight, struct dlList *iList)
/* Generate intermediate code for unary expression. Return destination */
{
struct isxAddress *target = isxExpression(pfc, pp->children,
	varHash, weight, iList);
struct isxAddress *dest = isxTempVarAddress(pfc, varHash, weight, 
	ppToIsxValType(pfc,pp));
isxAddNew(pfc, op, dest, target, NULL, iList);
return dest;
}

static void calcFieldOffsets(struct pfCompile *pfc, struct pfBaseType *base)
/* Figure out offset of fields in structure. */
{
struct pfBackEnd *backEnd = pfc->backEnd;
int offset = 0, i;
struct pfType *ty;
static enum isxValType header[] = {
    ivInt, /* _pf_refCount */
    ivObject, /* _pf_cleanup */
    ivObject, /* _pf_polyTable */
    };

/* Skip over header */
for (i=0; i<ArraySize(header); ++i)
    {
    offset += backEnd->dataSize(backEnd, header[i]);
    offset = backEnd->alignData(backEnd, offset, header[i]);
    }
for (ty = base->fields; ty != NULL; ty = ty->next)
    {
    enum isxValType valType = isxValTypeFromTy(pfc, ty);
    offset = backEnd->alignData(backEnd, offset, valType);
    ty->fieldOffset  = offset;
    offset += backEnd->dataSize(backEnd, valType);
    }
base->hasFieldOffsets = TRUE;
}

static int findFieldOffset(struct pfCompile *pfc,
	struct pfBaseType *base, char *field)
/* Figure out offset of field. */
{
struct pfType *ty;
if (!base->hasFieldOffsets)
    calcFieldOffsets(pfc, base);
for (ty = base->fields; ty != NULL; ty = ty->next)
    {
    if (sameString(ty->fieldName, field))
        return ty->fieldOffset;
    }
internalErr();
return 0;
}

static struct isxAddress *isxDotRval(struct pfCompile *pfc, 
	struct pfParse *pp, struct hash *varHash, 
	double weight, struct dlList *iList)
/* Generate intermediate code for obj.value (except for on left
 * side of an equality) */
{
struct pfParse *ppObj = pp->children;
struct pfParse *ppField = ppObj->next;
struct isxAddress *left = isxExpression(pfc, ppObj, varHash, weight, iList);
struct pfType *ty = ppObj->ty;
struct pfBaseType *base = ty->base;
struct isxAddress *right = NULL;
struct isxAddress *dest = isxTempVarAddress(pfc, varHash, weight, 
	ppToIsxValType(pfc,pp));
int offset = findFieldOffset(pfc, base, ppField->name);

if (offset != 0)
    {
    union pfTokVal tokVal;
    tokVal.i = offset;
    right = isxConstAddress(pftInt, tokVal, ivInt);
    }
isxAddNew(pfc, poIndirect, dest, left, right, iList);
return dest;
}

static void isxOpEquals(struct pfCompile *pfc, 
	struct pfParse *pp, struct hash *varHash, 
	enum isxOpType op, double weight, struct dlList *iList)
{
struct pfParse *use = pp->children;
struct isxAddress *exp = isxExpression(pfc, use->next,
	varHash, weight, iList);
struct isxAddress *dest = varAddress(use->var, varHash, 
	weight, ppToIsxValType(pfc, use));
isxAddNew(pfc, op, dest, dest, exp, iList);
}

static struct isxAddress *isxStringCat(struct pfCompile *pfc,
	struct pfParse *pp, struct hash *varHash, double weight,
	struct dlList *iList)
/* Create string cat op */
{
uglyAbort("Can't handle stringCat yet");
return NULL;
}

static struct isxAddress *isxVarExpression(struct pfCompile *pfc, 
	struct pfParse *pp, struct hash *varHash, 
	double weight, struct dlList *iList)
/* Generate intermediate code for expression that may begin with
 * pptCastTypedToVar. */
{
struct isxAddress *iad;
struct pfType *type;
int recodedType;
if (pp == NULL)
    {
    iad = zeroAddress();
    type = pfc->intFullType;
    }
else
    {
    if (pp->type == pptCastTypedToVar)
       pp = pp->children;
    iad = isxExpression(pfc, pp, varHash, weight, iList);
    type = pp->ty;
    }
iad->recodedType = recodedTypeId(pfc, type);
iad->isVar = TRUE;
return iad;
}

static struct isxAddress *isxInTupleAddresses(struct pfCompile *pfc, 
	struct pfParse *inTuple, struct hash *varHash, double weight, 
	struct dlList *iList)
/* Generate addresses (which may involving evaluating expressions to temps)
 * for all input vars in tuple. */
{
struct pfParse *p;
struct isxAddress *source, *next, *sourceList = NULL;

for (p = inTuple->children; p != NULL; p = p->next)
    {
    if (p->ty->base == pfc->varType)
        {
	source = isxVarExpression(pfc, p, varHash, weight, iList);
	slAddHead(&sourceList, source);
#ifdef OLD
	dest = isxIoAddress(offset, source->valType, iadInStack);
	isxAddNew(pfc, poVarInput, dest, source, NULL, iList);
#endif /* OLD */
	}
    else
	{
	for (source = isxExpression(pfc, p, varHash, weight, iList);
	    source != NULL; source = next)
	    {
	    next = source->next;
	    slAddHead(&sourceList, source);
#ifdef OLD
	    dest = isxIoAddress(offset, source->valType, iadInStack);
	    isxAddNew(pfc, poInput, dest, source, NULL, iList);
#endif /* OLD */
	    }
	}
    }
slReverse(&sourceList);
return sourceList;
}

static void isxCodeIn(struct pfCompile *pfc, struct isxAddress *sourceList,
	struct hash *varHash, double weight, struct dlList *iList, 
	int offset)
/* Generate code to push addresses on stack for function call. */
{
struct isxAddress *source, *dest;
for (source = sourceList; source != NULL; source = source->next, ++offset)
    {
    enum isxOpType op = (source->isVar ? poVarInput : poInput);
    dest = isxIoAddress(offset, source->valType, iadInStack);
    isxAddNew(pfc, op, dest, source, NULL, iList);
    }
}


static struct isxAddress *isxCall(struct pfCompile *pfc,
	struct pfParse *pp, struct hash *varHash, double weight,
	struct dlList *iList)
/* Create call op */
{
struct pfParse *function = pp->children;
struct pfParse *inTuple = function->next;
struct pfParse *p;
struct pfType *outTuple = function->ty->children->next, *ty;
struct isxAddress *source, *sourceList, *dest, *destList = NULL;
int offset;

sourceList = isxInTupleAddresses(pfc, inTuple, varHash, weight, iList);
isxCodeIn(pfc, sourceList, varHash, weight, iList, 1);
source = isxCallAddress(function->var->cName);
isxAddNew(pfc, poCall, NULL, source, NULL, iList);
for (ty = outTuple->children, offset=0; ty != NULL; ty = ty->next, ++offset)
    {
    enum isxValType valType = isxValTypeFromTy(pfc, ty);
    source = isxIoAddress(offset, valType, iadOutStack);
    dest = isxTempVarAddress(pfc, varHash, weight, valType);
    slAddTail(&destList, dest);
    isxAddNew(pfc, poOutput, dest, source, NULL, iList);
    }
return destList;
}

static struct isxAddress *isxClassFromTuple(struct pfCompile *pfc,
	struct pfParse *pp, struct hash *varHash, double weight,
	struct dlList *iList)
/* Push tuple on stack and call class creator from it. */
{
struct pfParse *tuple = pp->children, *p;
struct isxAddress *sourceList, *source, *dest, *recoded;


/* Get parameters */
sourceList = isxInTupleAddresses(pfc, tuple, varHash, weight, iList);

/* Generate call to save type parameter */
dest = isxIoAddress(0, ivInt, iadOutStack);
recoded = isxRecodedTypeAddress(pfc, pp->ty);
isxAddNew(pfc, poInput, dest, recoded, NULL, iList);

/* Push parameters */
isxCodeIn(pfc, sourceList, varHash, weight, iList, 1);

/* Generate function call itself. */
source = isxCallAddress("_zx_tuple_to_class");
isxAddNew(pfc, poCall, NULL, source, NULL, iList);

/* Generate code to get return value. */
source = isxIoAddress(0, ivObject, iadOutStack);
dest = isxTempVarAddress(pfc, varHash, weight, ivObject);
isxAddNew(pfc, poOutput, dest, source, NULL, iList);
return dest;
}

void isxAddReturnInfo(struct pfCompile *pfc, struct pfParse *outTuple, 
	struct hash *varHash, struct dlList *iList)
/* Add instructions for return parameters. */
{
struct pfParse *pp;
int offset=slCount(outTuple->children)-1;
/* Code generator's job is easier if we reverse these. */
slReverse(&outTuple->children);
for (pp = outTuple->children; pp != NULL; pp = pp->next, --offset)
    {
    struct isxAddress *source = varAddress(pp->var, varHash, 1.0, 
	ppToIsxValType(pfc, pp));
    struct isxAddress *dest = isxIoAddress(offset, source->valType, iadReturnVar);
    isxAddNew(pfc, poReturnVal, dest, source, NULL, iList);
    }
slReverse(&outTuple->children);
}

static struct isxAddress *isxExpression(struct pfCompile *pfc, 
	struct pfParse *pp, struct hash *varHash, 
	double weight, struct dlList *iList)
/* Generate intermediate code for expression. Return destination */
{
switch (pp->type)
    {
    case pptConstBit:
    case pptConstByte:
    case pptConstChar:
    case pptConstShort:
    case pptConstInt:
    case pptConstLong:
    case pptConstFloat:
    case pptConstDouble:
    case pptConstString:
	return isxConstAddress(pp->tok->type, pp->tok->val, 
		ppToIsxValType(pfc, pp));
    case pptVarUse:
	return varAddress(pp->var, varHash, weight, ppToIsxValType(pfc, pp));
    case pptPlus:
	return isxBinaryOp(pfc, pp, varHash, poPlus, weight, iList);
    case pptMinus:
	return isxBinaryOp(pfc, pp, varHash, poMinus, weight, iList);
    case pptMul:
	return isxBinaryOp(pfc, pp, varHash, poMul, weight, iList);
    case pptDiv:
	return isxBinaryOp(pfc, pp, varHash, poDiv, weight, iList);
    case pptMod:
	return isxBinaryOp(pfc, pp, varHash, poMod, weight, iList);
    case pptBitAnd:
	return isxBinaryOp(pfc, pp, varHash, poBitAnd, weight, iList);
    case pptBitOr:
	return isxBinaryOp(pfc, pp, varHash, poBitOr, weight, iList);
    case pptBitXor:
	return isxBinaryOp(pfc, pp, varHash, poBitXor, weight, iList);
    case pptShiftLeft:
	return isxBinaryOp(pfc, pp, varHash, poShiftLeft, weight, iList);
    case pptShiftRight:
	return isxBinaryOp(pfc, pp, varHash, poShiftRight, weight, iList);
    case pptNegate:
        return isxUnaryOp(pfc, pp, varHash, poNegate, weight, iList);
    case pptFlipBits:
        return isxUnaryOp(pfc, pp, varHash, poFlipBits, weight, iList);
    case pptStringCat:
        return isxStringCat(pfc, pp, varHash, weight, iList);
    case pptCall:
        return isxCall(pfc, pp, varHash, weight, iList);
    case pptClassAllocFromTuple:
        return isxClassFromTuple(pfc, pp, varHash, weight, iList);
    case pptDot:
        return isxDotRval(pfc, pp, varHash, weight, iList);
    case pptCastBitToByte:
    case pptCastBitToChar:
    case pptCastBitToShort:
    case pptCastBitToInt:
    case pptCastBitToLong:
    case pptCastBitToFloat:
    case pptCastBitToDouble:
    case pptCastBitToString:
    case pptCastByteToBit:
    case pptCastByteToChar:
    case pptCastByteToShort:
    case pptCastByteToInt:
    case pptCastByteToLong:
    case pptCastByteToFloat:
    case pptCastByteToDouble:
    case pptCastByteToString:
    case pptCastCharToBit:
    case pptCastCharToByte:
    case pptCastCharToShort:
    case pptCastCharToInt:
    case pptCastCharToLong:
    case pptCastCharToFloat:
    case pptCastCharToDouble:
    case pptCastCharToString:
    case pptCastShortToBit:
    case pptCastShortToByte:
    case pptCastShortToChar:
    case pptCastShortToInt:
    case pptCastShortToLong:
    case pptCastShortToFloat:
    case pptCastShortToDouble:
    case pptCastShortToString:
    case pptCastIntToBit:
    case pptCastIntToByte:
    case pptCastIntToChar:
    case pptCastIntToShort:
    case pptCastIntToLong:
    case pptCastIntToFloat:
    case pptCastIntToDouble:
    case pptCastIntToString:
    case pptCastLongToBit:
    case pptCastLongToByte:
    case pptCastLongToChar:
    case pptCastLongToShort:
    case pptCastLongToInt:
    case pptCastLongToFloat:
    case pptCastLongToDouble:
    case pptCastLongToString:
    case pptCastFloatToBit:
    case pptCastFloatToByte:
    case pptCastFloatToChar:
    case pptCastFloatToShort:
    case pptCastFloatToInt:
    case pptCastFloatToLong:
    case pptCastFloatToDouble:
    case pptCastFloatToString:
    case pptCastDoubleToBit:
    case pptCastDoubleToByte:
    case pptCastDoubleToChar:
    case pptCastDoubleToShort:
    case pptCastDoubleToInt:
    case pptCastDoubleToLong:
    case pptCastDoubleToFloat:
    case pptCastDoubleToString:
        return isxUnaryOp(pfc, pp, varHash, poCast, weight, iList);
    default:
	pfParseDump(pp, 3, uglyOut);
        internalErr();
	return NULL;
    }
}

static void cmpAndJump(struct pfCompile *pfc, struct pfParse *cond, 
	struct hash *varHash, struct isxAddress *trueLabel, 
	struct isxAddress *falseLabel,enum isxOpType op,  
	double weight, struct dlList *iList)
/* Generate code that jumps to the true label if cond is true, and
 * to the false label otherwise. */
{
struct pfParse *left = cond->children;
struct pfParse *right = left->next;
struct isxAddress *leftAddr = isxExpression(pfc, left, varHash, weight, iList);
struct isxAddress *rightAddr = isxExpression(pfc,right, varHash, weight, iList);
isxAddNew(pfc, op, trueLabel, leftAddr, rightAddr, iList);
if (falseLabel)
    isxAddNew(pfc, poJump, falseLabel, NULL, NULL, iList);
}


static enum pfParseType invLogOp(enum pfParseType op)
/* Convert != to ==, > to <=, etc. */
{
switch (op)
     {
     case pptGreater: 		return pptLessOrEquals;
     case pptGreaterOrEquals: 	return pptLess;
     case pptLess: 		return pptGreaterOrEquals;
     case pptLessOrEquals:	return pptGreater;
     case pptSame:		return pptNotSame;
     case pptNotSame:		return pptSame;
     case pptLogAnd:		return pptLogOr;
     case pptLogOr:		return pptLogAnd;
     default:
          {
	  return op;
	  }
     }
}


static void isxConditionalJump(struct pfCompile *pfc, struct pfParse *cond, 
	struct hash *varHash, struct isxAddress *trueLabel, 
	struct isxAddress *falseLabel, boolean invert, 
	double weight, struct dlList *iList)
/* Generate code that jumps to the true label if cond is true, and
 * to the false label otherwise. */
{
enum pfParseType ppt = cond->type;
if (invert)
    ppt = invLogOp(ppt);
switch (ppt)
    {
    case pptLess:
	cmpAndJump(pfc, cond, varHash, trueLabel, falseLabel, poBlt, 
		weight, iList);
        break;
    case pptLessOrEquals:
	cmpAndJump(pfc, cond, varHash, trueLabel, falseLabel, poBle, 
		weight, iList);
        break;
    case pptGreaterOrEquals:
	cmpAndJump(pfc, cond, varHash, trueLabel, falseLabel, poBge, 
		weight, iList);
        break;
    case pptGreater:
	cmpAndJump(pfc, cond, varHash, trueLabel, falseLabel, poBgt, 
		weight, iList);
        break;
    case pptSame:
	cmpAndJump(pfc, cond, varHash, trueLabel, falseLabel, poBeq, 
		weight, iList);
        break;
    case pptNotSame:
	cmpAndJump(pfc, cond, varHash, trueLabel, falseLabel, poBne, 
		weight, iList);
        break;
    case pptLogOr:
        {
	struct pfParse *left = cond->children;
	struct pfParse *right = left->next;
	isxConditionalJump(pfc, left, varHash, trueLabel, NULL, invert,
		weight, iList);
	isxConditionalJump(pfc, right, varHash, trueLabel, falseLabel, invert,
		weight, iList);
	break;
	}
    case pptLogAnd:
        {
	struct pfParse *left = cond->children;
	struct pfParse *right = left->next;
	isxConditionalJump(pfc, left, varHash, falseLabel, NULL, !invert,
		weight, iList);
	isxConditionalJump(pfc, right, varHash, trueLabel, falseLabel, invert,
		weight, iList);
	break;
	}
    case pptNot:
        {
	isxConditionalJump(pfc, cond->children, varHash, trueLabel, falseLabel, 
	    !invert, weight, iList);
	break;
	}
    case pptCastByteToBit:
    case pptCastShortToBit:
    case pptCastIntToBit:
    case pptCastLongToBit:
    case pptCastFloatToBit:
    case pptCastDoubleToBit:
    case pptCastStringToBit:
        {
	struct isxAddress *source = isxExpression(pfc, cond->children, 
		varHash, weight, iList);
	enum isxOpType op = (invert ? poBz : poBnz);
	isxAddNew(pfc, op, trueLabel, source, NULL, iList);
	if (falseLabel)
	    isxAddNew(pfc, poJump, falseLabel, NULL, NULL, iList);
	break;
	}
    case pptConstBit:
        {
	int isTrue = cond->tok->val.i;
	if (isTrue)
	    isxAddNew(pfc, poJump, trueLabel, NULL, NULL, iList);
	else
	    isxAddNew(pfc, poJump, falseLabel, NULL, NULL, iList);
	break;
	}
    default:
	{
	internalErrAt(cond->tok);
	break;
	}
    }
}


static void isxAssign(struct pfCompile *pfc, 
	struct pfParse *pp, struct hash *varHash, 
	double weight, struct dlList *iList)
/* Code an assignment */
{
struct pfParse *use = pp->children;
struct pfParse *val = use->next;
if (use->type == pptVarUse)
    {
    struct isxAddress *source = isxExpression(pfc, val, varHash, 
	    weight, iList);
    struct isxAddress *dest = varAddress(use->var, varHash, 
	    weight, ppToIsxValType(pfc, use));
    if (source->adType == iadTempVar)
	{
	struct isx *prevIsx = iList->tail->val;
	prevIsx->dest = dest;
	}
    else
	{
	isxAddNew(pfc, poAssign, dest, source, NULL, iList);
	}
    }
else if (use->type == pptTuple)
    {
    struct isxAddress *sourceList = isxExpression(pfc, val, varHash, 
	    weight, iList);
    int tupSize = slCount(use->children);
    struct isxAddress *source;
    struct pfParse *pp;
    assert(tupSize == slCount(sourceList));
    for (source = sourceList,pp=use->children; 
    	source != NULL; source = source->next, pp=pp->next)
        {
	struct isxAddress *dest;
	if (pp->type != pptVarInit && pp->type != pptVarUse)
	     internalErrAt(pp->tok);
	dest = varAddress(pp->var, varHash, weight, ppToIsxValType(pfc, pp));
	isxAddNew(pfc, poAssign, dest, source, NULL, iList);
	}
    }
else
    {
    internalErrAt(pp->tok);
    }
}




void isxStatement(struct pfCompile *pfc, struct pfParse *pp, 
	struct hash *varHash, double weight, struct dlList *iList)
/* Generate intermediate code for statement. */
{
switch (pp->type)
    {
    case pptCompound:
    case pptTuple:
        {
	for (pp = pp->children; pp != NULL; pp = pp->next)
	    isxStatement(pfc, pp, varHash, weight, iList);
	break;
	}
    case pptVarInit:
	{
	struct pfParse *init = pp->children->next->next;
	enum isxValType valType = ppToIsxValType(pfc, pp);
	struct isxAddress *dest = varAddress(pp->var, varHash, 
		weight, valType);
	struct isxAddress *source;
	if (valType == ivVar)
	    {
	    source = isxVarExpression(pfc, init, varHash, weight, iList);
	    isxAddNew(pfc, poVarInit, dest, source, NULL, iList);
	    }
	else
	    {
	    if (init)
		source = isxExpression(pfc, init, varHash, weight, iList);
	    else
		source = zeroAddress();
	    isxAddNew(pfc, poInit, dest, source, NULL, iList);
	    }
	break;
	}
    case pptAssign:
	isxAssign(pfc, pp, varHash, weight, iList);
	break;
    case pptPlusEquals:
	isxOpEquals(pfc, pp, varHash, poPlus, weight, iList);
	break;
    case pptMinusEquals:
	isxOpEquals(pfc, pp, varHash, poMinus, weight, iList);
	break;
    case pptMulEquals:
	isxOpEquals(pfc, pp, varHash, poMul, weight, iList);
	break;
    case pptDivEquals:
	isxOpEquals(pfc, pp, varHash, poDiv, weight, iList);
	break;
    case pptIf:
        {
	struct pfParse *test = pp->children;
	struct pfParse *truePp = test->next;
	struct pfParse *falsePp = truePp->next;
	struct isxAddress *trueLabel = isxTempLabelAddress(pfc);
	struct isxAddress *falseLabel = isxTempLabelAddress(pfc);
	struct isxAddress *endLabel = isxTempLabelAddress(pfc);
	isxConditionalJump(pfc, test, varHash, trueLabel, falseLabel, FALSE,
		weight, iList);
	isxAddNew(pfc, poCondStart, NULL, NULL, NULL, iList);
	isxAddNew(pfc, poCondCase, trueLabel, NULL, NULL, iList);
	isxStatement(pfc, truePp, varHash, weight*0.6, iList);
	isxAddNew(pfc, poJump, endLabel, NULL, NULL, iList);
	isxAddNew(pfc, poCondCase, falseLabel, NULL, NULL, iList);
	if (falsePp)
	    isxStatement(pfc, falsePp, varHash, weight*0.6, iList);
	isxAddNew(pfc, poCondEnd, endLabel, NULL, NULL, iList);
	break;
	}
    case pptFor:
        {
	struct pfParse *init = pp->children;
	struct pfParse *test = init->next;
	struct pfParse *end = test->next;
	struct pfParse *body = end->next;
	struct isxAddress *startLabel = isxTempLabelAddress(pfc);
	struct isxAddress *condLabel = isxTempLabelAddress(pfc);
	struct isxAddress *endLabel = isxTempLabelAddress(pfc);
	double loopWeight = weight*10;
	isxStatement(pfc, init, varHash, weight, iList);
	isxAddNew(pfc, poLoopStart, startLabel, NULL, condLabel, iList);
	isxStatement(pfc, body, varHash, loopWeight, iList);
	isxStatement(pfc, end, varHash, loopWeight, iList);
	isxAddNew(pfc, poLabel, condLabel, NULL, NULL, iList);
	isxConditionalJump(pfc, test, varHash, startLabel, NULL, 
		FALSE, loopWeight, iList);
	isxAddNew(pfc, poLoopEnd, endLabel, NULL, NULL, iList);
	break;
	}
    case pptNop:
        break;
    default:
	isxExpression(pfc, pp, varHash, weight, iList);
	break;
    }
}

static void rIsxModuleVars(struct pfCompile *pfc, struct pfParse *pp, 
	struct hash *varHash, struct dlList *iList)
/* Convert module-level and static function variables to members of iList. */
{
double weight = 1.0;
switch (pp->type)
    {
    case pptVarInit:
	{
	struct pfVar *var = pp->var;
	struct pfScope *scope = var->scope;
	if (!scope->isLocal)	
	    {
	    struct isxAddress *dest = varAddress(pp->var, varHash, 
		    weight,  ppToIsxValType(pfc, pp));
	    struct isxAddress *source;
	    struct pfParse *init = pp->children->next->next;
	    boolean doInit = FALSE;
	    doInit =  (init != NULL && pfParseIsConst(init));
	    if (doInit)
		source = isxExpression(pfc, init, varHash, weight, iList);
	    else
		source = zeroAddress();
	    isxAddNew(pfc, poInit, dest, source, NULL, iList);
	    }
	else if (var->ty->access == paStatic)
	    internalErrAt(pp->tok);	// FIXME
        break;
	}
    }
for (pp = pp->children; pp != NULL; pp = pp->next)
    rIsxModuleVars(pfc, pp, varHash, iList);
}

struct isxList *isxListNew()
/* Create new empty isxList */
{
struct isxList *isxList;
AllocVar(isxList);
isxList->iList = dlListNew(0);
return isxList;
}

struct isxList *isxModuleVars(struct pfCompile *pfc, struct pfParse *module)
/* Convert module-level and static function variables to an isxList */
{
struct isxList *isxList = isxListNew();
struct hash *varHash = hashNew(0);
rIsxModuleVars(pfc, module, varHash, isxList->iList);
hashFree(&varHash);
return isxList;
}

