/*
 *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
 *    use is allowed under the terms of the DICE-LICENSE FILE,
 *    DICE-LICENSE.TXT.
 */

/*
 *  RULES.C
 *
 *  Note that certain operations are available for store-down optimization.
 *  That is, char a, b; a = ~b; ... we can do the invert operation on a byte.
 *  These operators are:    and,or,xor,invert,add,sub
 *
 *  Note: void detection rules should only apply for TID_INT types since
 *  external arrays[] will have size 0
 *
 *  only code gen routines associated with data movement need
 *  understand bit field storage types and only GenCast() &
 *  assignment exp tree routines.
 */

#include "defs.h"

Prototype   void BinaryRules(Exp *);
Prototype   void BinaryArithRules(Exp *);
Prototype   void BinaryLogicRules(Exp *);
Prototype   void UnaryLogicRules(Exp *);
Prototype   void UnaryRules(Exp *);
Prototype   void UnaryArithRules(Exp *);
Prototype   void ShiftRules(Exp *);
Prototype   void AddRules(Exp *);
Prototype   void SubRules(Exp *);
Prototype   void FloatingRules(Exp *);
Prototype   void AssignRules(Exp *);
Prototype   void InitRules(Exp **, Type *);
Prototype   void CompareRules(Exp *, int);
Prototype   void MatchRules(Exp *);
Prototype   void OptBinaryArithRules(Exp *);
Prototype   void OptBinaryRules(Exp *);

Prototype   int  CheckConversion(Exp *, Type *, Type *);

Prototype   int  CreateBinaryResultStorage(Exp *, short);
Prototype   int  CreateUnaryResultStorage(Exp *, short);

Local	    int  CastIfConstantFit(Exp **, Type *);
Prototype   short   AutoResultStorage(Exp *);

/*
 *  Standard Binary Rules
 */

void
BinaryRules(exp)
Exp *exp;
{
    Type *t1;
    Type *t2;
    short doUnsigned;

    if (exp->ex_Flags & EF_ASSEQ)
	return(AssignRules(exp));

    t1 = exp->ex_ExpL->ex_Type;
    t2 = exp->ex_ExpR->ex_Type;
    doUnsigned = 0;

    Assert(t1);
    Assert(t2);

    if (t1->Id != TID_INT || t2->Id != TID_INT) {
	yerror(exp->ex_LexIdx, EERROR_EXPECTED_INT_TYPE);
	exp->ex_Type = &LongType;
	return;
    }
    if ((t1->Size==0 && t1->Id==TID_INT) || (t2->Size==0 && t2->Id==TID_INT)) {
	yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
	exp->ex_Type = &LongType;
	return;
    }


    if (t1->Id == TID_INT && (t1->Flags & TF_UNSIGNED))
	doUnsigned = 1;
    if (t2->Id == TID_INT && (t2->Flags & TF_UNSIGNED))
	doUnsigned = 1;

    if (t1->Id == TID_INT && (t1->Size < INT_SIZE || (doUnsigned && (t1->Flags & TF_UNSIGNED) == 0))) {
	if (doUnsigned)
	    InsertCast(&exp->ex_ExpL, &ULongType);
	else
	    InsertCast(&exp->ex_ExpL, &LongType);
    }
    if (t2->Id == TID_INT && (t2->Size < INT_SIZE || (doUnsigned && (t2->Flags & TF_UNSIGNED) == 0))) {
	if (doUnsigned)
	    InsertCast(&exp->ex_ExpR, &ULongType);
	else
	    InsertCast(&exp->ex_ExpR, &LongType);
    }
    if (doUnsigned)
	exp->ex_Type = &ULongType;
    else
	exp->ex_Type = &LongType;
}

void
BinaryArithRules(exp)
Exp *exp;
{
    if (exp->ex_ExpL->ex_Type->Id == TID_FLT || exp->ex_ExpR->ex_Type->Id == TID_FLT) {
	FloatingRules(exp);
	return;
    }
    BinaryRules(exp);
}


/*
 *  Optimized Binary Arithmatic Rules.	This allows the two source
 *  operands to be of smaller types than the destination.  For
 *  example, in genarith.c/GenStar() (multiply), so the MULS/MULU
 *  instruction may be optimized in.
 *
 *  Note that in this case the result type is allowed to be either
 *  a short or a long, but not a char.	This compromise makes code
 *  generation easier.
 */

void
OptBinaryRules(exp)
Exp *exp;
{
    Type *t1;
    Type *t2;
    Type *t;
    short doUnsigned;
    short size;

    if (exp->ex_Flags & EF_ASSEQ)
	return(AssignRules(exp));

    t1 = exp->ex_ExpL->ex_Type;
    t2 = exp->ex_ExpR->ex_Type;
    doUnsigned = 0;

    Assert(t1);
    Assert(t2);

    if (t1->Id != TID_INT || t2->Id != TID_INT) {
	yerror(exp->ex_LexIdx, EERROR_EXPECTED_INT_TYPE);
	exp->ex_Type = &LongType;
	return;
    }
    if (t1->Size == 0 || t2->Size == 0) {
	yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
	exp->ex_Type = &LongType;
	return;
    }

    if (t1->Flags & TF_UNSIGNED)
	doUnsigned = 1;
    if (t2->Flags & TF_UNSIGNED)
	doUnsigned = 1;

    size = 2;
    if (t1->Size > size)
	size = t1->Size;
    if (t2->Size > size)
	size = t2->Size;

    if (doUnsigned) {
	if (size == 2)
	    t = &UShortType;
	else
	    t = &ULongType;
    } else {
	if (size == 2)
	    t = &ShortType;
	else
	    t = &LongType;
    }
    if (t1 != t)
	InsertCast(&exp->ex_ExpL, t);
    if (t2 != t)
	InsertCast(&exp->ex_ExpR, t);

    if (exp->ex_Type && exp->ex_Type->Id == TID_INT) {
	if (exp->ex_Type->Size == 1)
	    exp->ex_Type = (doUnsigned) ? &UShortType : &ShortType;
    } else {
	exp->ex_Type = (doUnsigned) ? &ULongType : &LongType;
    }
}

void
OptBinaryArithRules(exp)
Exp *exp;
{
    if (exp->ex_ExpL->ex_Type->Id == TID_FLT || exp->ex_ExpR->ex_Type->Id == TID_FLT) {
	FloatingRules(exp);
	return;
    }
    OptBinaryRules(exp);
}

/*
 *  For logic operations.  If the result type is known we
 *  optimize to the largest type of the three (two args and
 *  result type).
 */

void
BinaryLogicRules(exp)
Exp *exp;
{
    Exp *e1;
    Exp *e2;
    Type *type;

    if ((type = exp->ex_Type) == NULL || type->Id != TID_INT) {
	BinaryRules(exp);
	return;
    }

    /*
     *	Extend or truncate to the return type since all other bits will be
     *	ignored (and for logic operations we can cut it off like this no prob)
     */

    e1 = exp->ex_ExpL;
    e2 = exp->ex_ExpR;

    if (e1->ex_Type->Id == TID_FLT || e2->ex_Type->Id == TID_FLT) {
	yerror(exp->ex_LexIdx, EERROR_EXPECTED_INT_TYPE);
    }

    if (e1->ex_Type != type)
	InsertCast(&exp->ex_ExpL, type);
    if (e2->ex_Type != type)
	InsertCast(&exp->ex_ExpR, type);
}

/*
 *  The result is the type of the argument or the type the parent expects,
 *  whichever is larger.  Example (byte).  If parent expects byte we do not
 *  need to cast to <partype>,	but if it does not we MUST cast to the parent's
 *  type.
 *
 *	[00]80 ^ -1 (byte) -> [FF]7F
 */

void
UnaryLogicRules(exp)
Exp *exp;
{
    Exp *e1;
    Type *type = exp->ex_Type;
    Type *t1;

    if (type == NULL || type->Id != TID_INT) {
	UnaryRules(exp);
	return;
    }
    e1 = exp->ex_ExpL;
    t1 = e1->ex_Type;

    if (type->Size == 0) {
	yerror(e1->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
	type = &LongType;
    }
    if (t1->Id == TID_FLT) {
	yerror(e1->ex_LexIdx, EERROR_EXPECTED_INT_TYPE);
	type = &LongType;
    }

    if (type != t1)
	InsertCast(&exp->ex_ExpL, type);
}

void
UnaryRules(exp)
Exp *exp;
{
    Type *t;
    short doUnsigned;

    t = exp->ex_ExpL->ex_Type;
    doUnsigned = 0;

    Assert(t);

    if (t->Id == TID_INT && (t->Flags & TF_UNSIGNED))
	doUnsigned = 1;

    if (t->Id == TID_INT && t->Size < INT_SIZE) {
	if (t->Size == 0)
	    yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
	if (doUnsigned)
	    InsertCast(&exp->ex_ExpL, &ULongType);
	else
	    InsertCast(&exp->ex_ExpL, &LongType);
    }
    if (doUnsigned)
	exp->ex_Type = &ULongType;
    else
	exp->ex_Type = &LongType;
}

void
UnaryArithRules(exp)
Exp *exp;
{
    Exp *e1 = exp->ex_ExpL;

    if (e1->ex_Type->Id == TID_FLT) {
	exp->ex_Type = e1->ex_Type;
	return;
    }
    UnaryRules(exp);
}

void
ShiftRules(exp)
Exp *exp;
{
    Exp *e1 = exp->ex_ExpL;
    Exp *e2 = exp->ex_ExpR;
    Type *t1 = e1->ex_Type;
    Type *t2 = e2->ex_Type;

    Assert(t1);
    Assert(t2);

    if (exp->ex_Flags & EF_ASSEQ) {
	exp->ex_Type = t1;
	return;
    }

    if (t1->Size != 4) {
    	if (t1->Size == 0)
	    yerror(e1->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
	if (t1->Flags & TF_UNSIGNED)
	    InsertCast(&exp->ex_ExpL, &ULongType);
	else
	    InsertCast(&exp->ex_ExpL, &LongType);
    }
    exp->ex_Type = exp->ex_ExpL->ex_Type;
}

/*
 *  AddRules(exp)
 *
 *  Basically handle the special case of (ary/ptr + int) and (int + ary/ptr)
 *  Note that in this case the size of the 'int' is not cast.
 */

void
AddRules(exp)
Exp *exp;
{
    Type *t1 = exp->ex_ExpL->ex_Type;
    Type *t2 = exp->ex_ExpR->ex_Type;

    Assert(t1);
    Assert(t2);
    exp->ex_Token = 0;

    if ((t1->Id == TID_PTR || t1->Id == TID_ARY) && t2->Id == TID_INT) {
    	if (t1->SubType->Size == 0 && t1->SubType->Id == TID_INT)
	    yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
        if (t2->Size == 0)
	    yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
        exp->ex_Token = 1;
	exp->ex_Type = t1;
	/* xxx if autoassign insert type */
    } else if (t1->Id == TID_INT && (t2->Id == TID_PTR || t2->Id == TID_ARY)) {
	Exp *etmp = exp->ex_ExpL;

    	if (t1->Size == 0)
	    yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
        if (t2->SubType->Size == 0 && t2->SubType->Id == TID_INT)
	    yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);

	exp->ex_ExpL = exp->ex_ExpR;
	exp->ex_ExpR = etmp;
	exp->ex_Type = t2;
	exp->ex_Token = 1;
	if (exp->ex_Flags & EF_ASSEQ)
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_PTR_ARITH);
	/* xxx if autoassign insert type */
    } else {
    	if (t1->Size == 0)
	    yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
    	if (t2->Size == 0)
	    yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);

	if (exp->ex_Type && exp->ex_Type->Id == TID_INT && t1->Id == TID_INT && t2->Id == TID_INT) {
	    if (exp->ex_Type != t1)
		InsertCast(&exp->ex_ExpL, exp->ex_Type);
	    if (exp->ex_Type != t2)
		InsertCast(&exp->ex_ExpR, exp->ex_Type);
	} else {
	    BinaryArithRules(exp);
	}
    }
}

/*
 *  SubRules()	Basically handle ptr - ptr and let other routines
 *		worry about other combinations.
 */

void
SubRules(exp)
Exp *exp;
{
    Type *t1 = exp->ex_ExpL->ex_Type;
    Type *t2 = exp->ex_ExpR->ex_Type;

    if (t1->Id == TID_ARY) {
	InsertCast(&exp->ex_ExpL, TypeToPtrType(t1->SubType));
	t1 = exp->ex_ExpL->ex_Type;
    }
    if (t2->Id == TID_ARY) {
	InsertCast(&exp->ex_ExpR, TypeToPtrType(t2->SubType));
	t2 = exp->ex_ExpR->ex_Type;
    }
    if (t1->Id == TID_PTR && t2->Id == TID_PTR) {
	exp->ex_Token = 2;
	exp->ex_Type = &LongType;
    } else {
	if (t2->Id == TID_PTR)
	    yerror(exp->ex_ExpR->ex_LexIdx, EERROR_ILLEGAL_PTR_ARITH);
	AddRules(exp);
    }
}

/*
 *  floating rules (binary).  One or both sides are FP, cast to the largest
 *  of the two, except if EF_ASSEQ cast to lhs.
 */

void
FloatingRules(exp)
Exp *exp;
{
    Type *t1 = exp->ex_ExpL->ex_Type;
    Type *t2 = exp->ex_ExpR->ex_Type;

#ifdef REGISTERED
    if (exp->ex_Flags & EF_ASSEQ) {
	if (t1 != t2)
	    InsertCast(&exp->ex_ExpR, t1);
	exp->ex_Type = t1;
	return;
    }
    if (t1->Id == TID_FLT && t2->Id == TID_FLT) {
	if (t1->Size > t2->Size)
	    InsertCast(&exp->ex_ExpR, t1);
	else if (t2->Size > t1->Size) {
	    InsertCast(&exp->ex_ExpL, t2);
	    t1 = t2;
	}
    } else if (t1->Id == TID_FLT) {
	InsertCast(&exp->ex_ExpR, t1);
    } else {
	InsertCast(&exp->ex_ExpL, t2);
	t1 = t2;
    }
    exp->ex_Type = t1;
#else
    yerror(exp->ex_LexIdx, EUNREG_FLOATINGPT);
#endif
}

/*
 *  cast the right side to the left side's type
 *
 *  exception:	if left side is a bitfield right side is cast to a long
 *		if right side is a bitfield it is cast to a long
 *
 *		assign takes this into account
 */

void
AssignRules(exp)
Exp *exp;
{
    Assert(exp->ex_ExpL);
    Assert(exp->ex_ExpR);

    InitRules(&exp->ex_ExpR, exp->ex_ExpL->ex_Type);

    /*
     * Old code: returned the right hand type.
     *
     * New code: returns the left hand type if a bitfield, else the righthand
     * type.
     *
     * Properly, this code should always return the LEFT hand type.  It
     * turns out that the right hand and left hand types are the same in all
     * cases except for bitfields.  at least, they are supposed to be... I'm
     * not 100% sure, so for release 13 I am fixing it to return the left 
     * hand type JUST for bitfields, so as not to break something that is
     * known to work.
     */

    if (exp->ex_ExpL->ex_Type->Id == TID_BITFIELD)
	exp->ex_Type = exp->ex_ExpL->ex_Type;
    else
	exp->ex_Type = exp->ex_ExpR->ex_Type;
}

/*
 *  cast the right side to the left side's type
 *
 *  exception:	if left side is a bitfield right side is cast to a long
 *		if right side is a bitfield it is cast to a long
 *
 *		assign takes this into account
 */

void
InitRules(pexp, t1)
Exp **pexp;
Type *t1;
{
    Exp *exp = *pexp;
    Type *t2;
    int errorcode = EERROR_ILLEGAL_ASSIGNMENT;

    t2 = exp->ex_Type;

    Assert(t1);
    Assert(t2);

    if ((t1->Size == 0) ||
        (t2->Size == 0 && t2->Id == TID_INT))
    {
	errorcode = EERROR_UNEXPECTED_VOID_TYPE;
    }

    else if (t1->Id == TID_BITFIELD)
    {
    	if (t1->Flags & TF_UNSIGNED)
	    t1 = &ULongType;
	else
	    t1 = &LongType;
	if (t2->Id != TID_INT)
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_BITFIELD_OP);
	InsertCast(pexp, t1);

    	/*
    	 * XXX ifdef'd out code entirely ... totally broken XXX
    	 *
    	 * First of all, it is overriding ex_Type for the RHS which
    	 * screws up storage access to the rhs.  Secondly, it cannot 
    	 * deal with a bitfield in the rhs.
    	 */
#ifdef NOTDEF
	if (t1->Flags & TF_UNSIGNED)
	    t1 = &ULongType;
	else
	    t1 = &LongType;
	if (t2->Id != TID_INT)
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_BITFIELD_OP);

	/*
	 *  do not insert cast as this is the lhs.  GENASS.C handles assignment
	 *  by not attempting to thrust lhs storage into rhs's return storage.
	 */

	exp->ex_Type = t1;
#endif
	return;
    }

    /* exp->ex_Type = t1; */


    /*
     *	Convert array of x to pointer to x
     */

    if (t2->Id == TID_ARY)
    {
	InsertCast(pexp, t2 = TypeToPtrType(t2->SubType));
    }

    if (t1->Id == t2->Id)
    {
        errorcode = 0;

        /* If they are both pointers, make sure that they point to the same */
        /* thing before issuing a warning.                                  */
        if (t1->Id == TID_PTR)
        {
	   CheckPointerType(exp->ex_LexIdx, exp->ex_LexIdx, t1, t2);
	}
        /* Not pointers, but we should check to see that the objects are the */
        /* same size (since they are the same type).  This check currently   */
        /* lets through structures which are the same size, but since we     */
        /* are not expecting them here we can ignore it for now???           */
	else if (t1->Size != t2->Size)
	{
	    if (t1->Id != TID_INT && t1->Id != TID_FLT)
	       errorcode = EERROR_ILLEGAL_ASSIGNMENT;
	}
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * */
    /*                                                     */
    /* Check for conversions from a pointer to an integer  */
    /*                                                     */
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * */
    else if (t1->Id == TID_PTR && t2->Id == TID_INT)
    {
        errorcode = EWARN_INT_PTR_CONVERSION;

        /* If we are converting from a constant integer to a pointer, */
        /* we can let them get away with converting a constant NULL   */
	if (exp->ex_Stor.st_Type == ST_IntConst)
	{
	    /* Supress the error message when they are casting a constant NULL */
	    if (exp->ex_Stor.st_IntConst == 0)
	       errorcode = 0;
	}
	else
	{
	    /* We need to tell them that converting from an integer to a */
	    /* pointer is a bad thing.  We do want to give them a more   */
	    /* meaningful message when they are doing something that is  */
	    /* of a different size.                                      */
	    if (t1->Size != t2->Size)
	       errorcode = EERROR_ILLEGAL_PTR_INT_SIZE;
	}
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * */
    /*                                                     */
    /* Check for conversions from an integer to a pointer  */
    /*                                                     */
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * */
    else if (t1->Id == TID_INT && t2->Id == TID_PTR)
    {
	if (t1->Size != t2->Size)
	    errorcode = EERROR_ILLEGAL_PTR_INT_SIZE;
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * */
    /*                                                     */
    /* Check for conversions between floats and integers   */
    /*                                                     */
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * */
    else if ((t1->Id == TID_INT && t2->Id == TID_FLT) ||
        (t1->Id == TID_FLT && t2->Id == TID_INT))
    {
        /* This type of conversion is really considered silent */
        errorcode = 0;
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * */
    /*                                                     */
    /* When we get here, we have run out of cases to check */
    /* We must therefore assume that what they are doing   */
    /* is illegal and give them an error message           */
    /*                                                     */
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * */

    if (errorcode)
       yerror(exp->ex_LexIdx, errorcode);
    InsertCast(pexp, t1);
}



/*
 *  CompareRules(exp)
 *
 *	If either side is a bit field it is cast into a long/ulong.
 *
 *	Whichever side is the largest type, the other side is promoted.  For
 *	example, if one side is a short and the other a byte then the
 *	byte is promoted to a short.
 *
 *	If either side is unsigned or a pointer then both sides are promoted
 *	to unsigned.
 *
 *	If one side is an integer constant and the other side is an
 *	integer quantity attempt to fit the integer constant into
 *	the size of the int.
 *
 *	If either side is fp convert both sides to whichever is largest
 */

void
CompareRules(exp, bigok)
Exp *exp;
int bigok;
{
    Type *t1 = exp->ex_ExpL->ex_Type;
    Type *t2 = exp->ex_ExpR->ex_Type;
    short doUnsigned = 0;
    short size;
    Type *type = &VoidType;

    Assert(t1);
    Assert(t2);

    if (exp->ex_Flags & EF_ASSEQ)
	Assert(0);
    exp->ex_Type = &LongType;

    if (t1->Id == TID_STRUCT || t1->Id == TID_UNION || t2->Id == TID_STRUCT || t2->Id == TID_UNION) {
	if (!bigok)
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_STRUCT_OP);
	else if (t1->Size != t2->Size)
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_STRUCT_OP);
	return;
    }

    if (t1->Id == TID_FLT || t2->Id == TID_FLT) {
	FloatingRules(exp);		/*  messes up ex_Type	*/
	exp->ex_Type = &LongType;
	return;
    }

    if (t1->Size != t2->Size) {
	if (exp->ex_ExpL->ex_Stor.st_Type == ST_IntConst && t2->Id == TID_INT) {
	    if (CastIfConstantFit(&exp->ex_ExpL, t2))
		return;
	}
	if (exp->ex_ExpR->ex_Stor.st_Type == ST_IntConst && t1->Id == TID_INT) {
	    if (CastIfConstantFit(&exp->ex_ExpR, t1))
		return;
	}
    }

    if (t1->Id == TID_INT && (t1->Flags & TF_UNSIGNED))
	doUnsigned = 1;
    if (t2->Id == TID_INT && (t2->Flags & TF_UNSIGNED))
	doUnsigned = 1;

    size = (t1->Size > t2->Size) ? t1->Size : t2->Size;

    if (t1->Id == TID_ARY)
	InsertCast(&exp->ex_ExpL, t1 = TypeToPtrType(t1->SubType));
    if (t2->Id == TID_ARY)
	InsertCast(&exp->ex_ExpR, t2 = TypeToPtrType(t2->SubType));

    if (t1->Size == 0)
	yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);
    if (t2->Size == 0)
	yerror(exp->ex_LexIdx, EERROR_UNEXPECTED_VOID_TYPE);

    if (t1->Id == TID_PTR && t2->Id == TID_PTR) {
	CheckPointerType(exp->ex_LexIdx, exp->ex_LexIdx, t1, t2);
	return;
    } else if (t1->Id == TID_PTR || t2->Id == TID_PTR) {
	if (t1->Id == TID_INT) {
	    Exp *e1 = exp->ex_ExpL;
	    if (e1->ex_Stor.st_Type != ST_IntConst || e1->ex_Stor.st_IntConst != 0)
		yerror(e1->ex_LexIdx, EWARN_PTR_INT_MISMATCH);
	    type = t2;
	}
	if (t2->Id == TID_INT) {
	    Exp *e2 = exp->ex_ExpR;
	    if (e2->ex_Stor.st_Type != ST_IntConst || e2->ex_Stor.st_IntConst != 0)
		yerror(e2->ex_LexIdx, EWARN_PTR_INT_MISMATCH);
	    type = t1;
	}
	doUnsigned = 1;
    } else {
	switch(size) {
	case 1:
	    type = (doUnsigned) ? &UCharType : &CharType;
	    break;
	case 2:
	    type = (doUnsigned) ? &UShortType : &ShortType;
	    break;
	case 4:
	    type = (doUnsigned) ? &ULongType : &LongType;
	    break;
	default:
	    yerror(exp->ex_LexIdx, ESOFT_ILLEGAL_COMPARE);
	    break;
	}
    }
    InsertCast(&exp->ex_ExpL, type);
    InsertCast(&exp->ex_ExpR, type);
}

/*
 *  Compare rules but result is the combined type, not an integer.
 *  ( question-colon operator )
 */

void
MatchRules(exp)
Exp *exp;
{
    Type *t1 = exp->ex_ExpL->ex_Type;
    Type *t2 = exp->ex_ExpR->ex_Type;

    if (t1->Size == 0 && t2->Size == 0 && t1->Id != TID_ARY && t2->Id != TID_ARY) {
	exp->ex_Type = &VoidType;
    } else {
	CompareRules(exp, 1);
	exp->ex_Type = exp->ex_ExpL->ex_Type;
    }
}

/*
 *  For CompareRules().  If we are comparing an integer quantity < sizeof(int)
 *  to an integer constant and that constant fits in the quantity size, cast
 *  the constant to the quantity type instead of the type to an int
 */

Local  int
CastIfConstantFit(pexp, type)
Exp **pexp;
Type *type;
{
    Exp *exp = *pexp;	/*  integer constant  */
    short uns = exp->ex_Type->Flags & TF_UNSIGNED;

    if (uns && !(type->Flags & TF_UNSIGNED))	/*  not so simple */
	return(0);

    if (uns) {
	switch(type->Size) {
	case 1:
	    if (exp->ex_Stor.st_UIntConst < 0x100) {
		InsertCast(pexp, &UCharType);
		return(1);
	    }
	    break;
	case 2:
	    if (exp->ex_Stor.st_UIntConst < 0x10000) {
		InsertCast(pexp, &UShortType);
		return(1);
	    }
	    break;
	case 4:
	    break;
	default:
	    Assert(0);
	}
    } else {
	switch(type->Size) {
	case 1:
	    if (exp->ex_Stor.st_IntConst >= -128 && exp->ex_Stor.st_IntConst < 128) {
		InsertCast(pexp, &CharType);
		return(1);
	    }
	    break;
	case 2:
	    if (exp->ex_Stor.st_IntConst >= -32768 && exp->ex_Stor.st_IntConst < 32768) {
		InsertCast(pexp, &ShortType);
		return(1);
	    }
	case 4:
	    break;
	default:
	    Assert(0);
	}
    }
    return(0);
}


/*
 *  Result Storage creation required?
 *
 *  0	yes
 *  1	no, result storage already exists (even if no return storage)
 *  2	no, there is no return storage
 *
 */

short
AutoResultStorage(exp)
Exp *exp;
{
    uword flags = exp->ex_Flags;

    if (flags & EF_COND) {
	if (flags & EF_CONDACK) /*  was able to handle condition */
	    return(1);
    }
    if (flags & (EF_CRES|EF_PRES|EF_STACKACK))
	return(1);
    if (flags & EF_ASSEQ) {	/*  exp->ex_Stor == e1->ex_Stor */
	/*
	 *  ass= (+=, -=, ...).  If a bit field, we allocate normal
	 *  storage which will be bfsto'd later
	 */

	if (exp->ex_ExpL->ex_Token == TokBFExt) {
	    Assert(exp->ex_ExpL->ex_ExpL->ex_Flags & EF_LHSASSEQ);
	    AllocTmpStorage(&exp->ex_Stor, exp->ex_Type, NULL);
	} else {
	    ReuseStorage(&exp->ex_ExpL->ex_Stor, &exp->ex_Stor);
	}
	if (exp->ex_Flags & EF_RNU)
	    FreeStorage(&exp->ex_Stor); 	/*  no return storage,	*/
	return(1);			    /*	but still have result storage	*/
    }
    if (flags & EF_RNU) {	    /*	no result   */
	exp->ex_Stor.st_Size = 0;   /*	make illegal	*/
	return(2);
    }
    return(0);
}

/*
 *  These functions return true if result storage is available.  Result
 *  storage is not available when the result will not be used, but we
 *  must be careful about EF_ASSEQ since ASSEQ does have result storage,
 *  just no return storage to the higher level.
 *
 *  returns 0	<fillmeXXX>
 *	    1	<fillmeXXX>
 *	    2	temporary allocated
 */

int
CreateBinaryResultStorage(Exp *exp, short freeSub)
{
    int r = 0;

    if (freeSub) {
	if (exp->ex_ExpL->ex_Stor.st_Type != ST_RegIndex)
	    FreeStorage(&exp->ex_ExpL->ex_Stor);
	if (exp->ex_ExpR->ex_Stor.st_Type != ST_RegIndex)
	    FreeStorage(&exp->ex_ExpR->ex_Stor);
    }

    Assert(exp->ex_Type);
    switch (AutoResultStorage(exp)) {
    case 0:
	Assert(exp->ex_ExpR);
	AllocTmpStorage(&exp->ex_Stor, exp->ex_Type, (freeSub) ? &exp->ex_ExpR->ex_Stor : NULL);
	r = 2;
	break;
    case 1:
	r = 1;
	break;
    }
    if (freeSub) {
	if (exp->ex_ExpL->ex_Stor.st_Type == ST_RegIndex)
	    FreeStorage(&exp->ex_ExpL->ex_Stor);
	if (exp->ex_ExpR->ex_Stor.st_Type == ST_RegIndex)
	    FreeStorage(&exp->ex_ExpR->ex_Stor);
    }
    return(r);
}

int
CreateUnaryResultStorage(Exp *exp, short freeSub)
{
    if (freeSub)
	FreeStorage(&exp->ex_ExpL->ex_Stor);

    Assert(exp->ex_Type);
    switch (AutoResultStorage(exp)) {
    case 0:
	if (exp->ex_Type == &VoidType)
	    AllocTmpStorage(&exp->ex_Stor, exp->ex_Type, NULL);
	else
	    AllocTmpStorage(&exp->ex_Stor, exp->ex_Type, &exp->ex_ExpL->ex_Stor);
    case 1:
	return(1);
    }
    return(0);
}

int
CheckConversion(exp, t1, t2)
Exp *exp;
Type *t1;
Type *t2;
{
    switch(t1->Id) {
    case TID_INT:
    case TID_BITFIELD:
    case TID_FLT:
	switch(t2->Id) {
	case TID_INT:
	case TID_BITFIELD:
	case TID_FLT:
	    break;
	case TID_PTR:
	case TID_ARY:
	    yerror(exp->ex_LexIdx, EWARN_INT_PTR_CONVERSION);
	    break;
	case TID_PROC:
	case TID_STRUCT:
	case TID_UNION:
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_INT_CONVERSION);
	    return(0);
	}
	break;
    case TID_PTR:
    case TID_ARY:
	switch(t2->Id) {
	case TID_INT:
	case TID_BITFIELD:
	    yerror(exp->ex_LexIdx, EWARN_PTR_INT_CONVERSION);
	    break;
	case TID_FLT:
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_PTR_CONVERSION);
	    return(0);
	case TID_PTR:
	case TID_ARY:
	    CheckPointerType(exp->ex_LexIdx, exp->ex_LexIdx, t1, t2);
/*	    if (ProtoOnlyOpt && t1->SubType->Size != t2->SubType->Size && t1->SubType->Size && t2->SubType->Size)
		yerror(exp->ex_LexIdx, EWARN_PTR_PTR_MISMATCH); */
	    break;
	case TID_PROC:
	case TID_STRUCT:
	case TID_UNION:
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_PTR_CONVERSION);
	    return(0);
	}
	break;
    case TID_PROC:
	yerror(exp->ex_LexIdx, EERROR_ILLEGAL_CAST);
	break;
    case TID_STRUCT:
    case TID_UNION:
	if (t1->Size != t2->Size) {
	    yerror(exp->ex_LexIdx, EERROR_ILLEGAL_STRUCT_CVT);
	    return(0);
	}
	break;
    }
    return(1);
}
