⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cceval.c

📁 KCC , a good c compiler, write by Ken Harrenstien
💻 C
📖 第 1 页 / 共 4 页
字号:
	case Q_ASDIV:			/* Check integral /= */
		if (icon == 0) {
		    advise("Division by zero ignored"); /* i/=0 => i */
		    return e->Nleft;
		} else if (icon == 1)
		    return e->Nleft;	/* i/=1 => i */
		else if (tissigned(e->Ntype))
		    e->Nop = N_NEG;	/* si/=(-1) => -si */
		break;			/*	(cannot do ui/(-1)) */

	case Q_ASMOD:			/* Check integral %= */
		if (!icon || icon == 1 || tissigned(e->Ntype)) {
		    if (icon == 0)		/* i%=0 => i=0 */
			advise("Division by zero");
		    e->Nright->Niconst = 0;	/* i%=1    => i=0   */
		    e->Nop = Q_ASGN;		/* si%=(-1) => si=0 */
		}				/* Note ui%(-1) is variable! */
		break;

	case Q_ASPLUS:			/* Check integral += */
		if (icon == 0)
		    return e->Nleft;	/* e+=0 => e */
		return evalincdec(e,
			(icon < 0)
			    ? N_PREDEC		/* e+=(-1) => --e */
			    : N_PREINC);	/* e-=1    => ++e */

	case Q_ASMINUS:			/* Check integral -= */
		if (icon == 0)
		    return e->Nleft;	/* e-=0 => e */
		return evalincdec(e,
			(icon < 0)
			    ? N_PREINC		/* e-=(-1) => ++e */
			    : N_PREDEC);	/* e-=1    => --e */

	case Q_ASLSH:			/* Check integral <<= and >>= */
	case Q_ASRSH:
		if (icon == 0)
		    return e->Nleft;	/* i<<=0 => i */
		break;
	case Q_ASAND:			/* Check integral &= */
		if (icon == 0)
		    e->Nop = Q_ASGN;	/* i&=0 => i=0 */
		else if (icon < 0)
		    return e->Nleft;	/* i&=(-1) => i */
		break;
	case Q_ASOR:			/* Check integral |= */
		if (icon == 0)
		    return e->Nleft;	/* i|=0 => i */
		if (icon < 0)
		    e->Nop = Q_ASGN;	/* i|=(-1) => i=(-1) */
		break;
	case Q_ASXOR:
		if (icon == 0)		/* Check integral ^= */
		    return e->Nleft;	/* i^=0 => i */
		if (icon < 0)
		    e->Nop = Q_COMPL;	/* i^=(-1) => ~i */
		break;

	} else if ((icon&(icon-1))==0
		 && tisunsign(e->Ntype)) {
	    /* Last hope - if constant is right-hand and a power of 2,
	    ** (yes, that expression works as a test!) then
	    ** good optimizations are possible for unsigned *,/,%.
	    ** Don't do it for * because multiply-to-memory is easy,
	    ** and we have no shift-to-memory instruction.
	    ** But for division it's probably worthwhile.
	    */
#if 0 /* See above comment */
	    if (e->Nop == Q_ASMPLY) {	/* ui*=log2(n) becomes ui<<=n */
		e->Nop = Q_ASLSH;
		e->Nright->Niconst = binexp(icon);
		e->Nright->Ntype = inttype;	/* Be safe */
		return e;
	    }
#endif
	    if (e->Nop == Q_ASDIV) {	/* ui/=log2(n) => ui>>=n */
		e->Nop = Q_ASRSH;
		e->Nright->Niconst = binexp(icon);
		e->Nright->Ntype = inttype;
		return e;
	    }
	    if (e->Nop == Q_ASMOD) {	/* ui%=log2(n) => ui&=(n-1) */
		e->Nop = Q_ASAND;
		e->Nright->Niconst = icon-1;
		/* preserve prior type of constant */
		return e;
	    }
	}
    } else if (tisfloat(e->Ntype)) {	/* Check floating types */
	if (e->Ntype->Tspec != TS_LNGDBL) {	/* Someday may be different */
	    double fcon;
	    if (e->Nright->Nop != N_FCONST)
		return e;			/* Just in case */
	    fcon = e->Nright->Nfconst;
	    if (fcon == 0.0 || fcon == 1.0 || fcon == -1.0) switch (e->Nop) {
	    case Q_ASMPLY:
		if (fcon == 0.0) {
		    e->Nop = Q_ASGN;		/* f*=0 => f=0 */
		    break;
		}
		/* Drop thru to check for f/0, f/1 and f/(-1) */
	    case Q_ASDIV:
		if (fcon == 0.0) {		/* f/=0 => f */
		    advise("Division by zero ignored");
		    return e->Nleft;
		} else if (fcon > 0.0)
		    return e->Nleft;		/* f*=1 or f/=1 => f */
		else e->Nop = N_NEG;		/* f*=(-1) or f/=(-1) => -f */
		break;
	    case Q_ASPLUS:
		if (fcon == 0.0)
		    return e->Nleft;		/* f+=0 => f */
		return evalincdec(e,
			(fcon > 0)
			    ? N_PREINC		/* f+=1    => ++f */
			    : N_PREDEC);	/* f+=(-1) => --f */
	    case Q_ASMINUS:
		if (fcon == 0.0)
		    return e->Nleft;		/* f-=0 => f */
		return evalincdec(e,
			(fcon > 0)
			    ? N_PREDEC		/* f-=1    => --f */
			    : N_PREINC);	/* f-=(-1) => ++f */
	    }
	}
    } else {	/* Not integral or float, must be pointer. */
	INT icon;
	if (e->Nright->Nop == N_ICONST	/* Ensure integer const op */
	  && (icon = e->Nright->Niconst) >= -1
	  && icon <= 1
	  && (e->Nop == Q_ASPLUS || e->Nop == Q_ASMINUS)) {
	    if (icon == 0) {		/* ptr += 0  => ptr  */
		/* Beware of funny array type conversion
		** sometimes applied to plus/minus pointer arithmetic;
		** result type may not be same as operand type.
		** Keep result type!!
		*/
		e->Nleft->Ntype = e->Ntype;	/* Propagate result type */
		return e->Nleft;
	    }
	    return evalincdec(e,
	    	(e->Nop==Q_ASPLUS)
		? (icon > 0 ? N_PREINC : N_PREDEC)	/* p+=1 => ++p */
		: (icon > 0 ? N_PREDEC : N_PREINC));	/* p-=1 => --p */
	}
    }

#endif
    return e;
}

/* EVALINCDEC - Optimize assign op to inc/dec.  Auxiliary for EVALASOP above.
**	Used when about to change an assignment operator (type TKTY_ASOP)
** into an increment/decrement operator, to check for and flush any
** internal casts inserted by the parser.  Although the code generator
** for assignment knows how to deal with these internal casts, the inc/dec
** part doesn't.  The reason for removing them here instead of in CCGEN2's
** gincdec() is just so that if there is a problem, we can still recover
** by not doing the optimization.
** See the info in INTERN.DOC for more details about TKTY_ASOP nodes.
*/
static NODE *
evalincdec(NODE *e, int op)  /* Expr node optimized and Desired inc/dec op */
{
    if (e->Nleft->Nop == N_CAST) {
	/* Internal cast, must remove it.  Do check to make sure it's
	** really a valid cast (paranoia dept).  Type of operand being
	** cast must be exactly the same as the asop result type.
	*/
	if (e->Ntype != e->Nleft->Nleft->Ntype) {
	    int_warn("evalincdec: bad cast");
	    return e;			/* Don't do it */
	}
	e->Nleft = e->Nleft->Nleft;	/* Snip out the cast */
    }
    e->Nop = op;
    return e;
}

/*
** Copy flags and such across from old top node
** Used when op has been folded out but we still want to keep its info
** WARNING!  This may not work if Nflag is really being used as
** something else.  See the union definition for NODE in cc.h.
*/

static NODE *
copyflag (NODE *new, NODE *old)
{
    new->Ntype = old->Ntype;
    new->Nflag = old->Nflag;
    return new;
}

/* SETLOG - Sets and returns a logical operator result.
**	Node given as arg must be that of the operator (not an operand).
*/
static NODE *
setlog(NODE *e, int res) /* Operator node and Result (either 0 or 1) */
{
    if (e->Ntype != inttype) {
	int_error("setlog: bad type");
	e->Ntype = inttype;
    }
    e->Nop = N_ICONST;
    e->Niconst = res;
    return e;
}

/* EVALCAST - called to evaluate a constant cast expression
**	Arg is a N_CAST node; operand is one of N_ICONST, N_PCONST, N_FCONST.
** To minimize the number of conversion combinations, values are always stored
** in the constant node using the largest possible type.
** For integers this is "long":
**	If the type is unsigned, unused bits in Niconst will be 0.
**	If signed, then sign extension has been done in Niconst.
** For floating-point this is "double":
**	If the type is float, precision will have been truncated (2nd word 0).
**
*/

static NODE *
evalcast(NODE *e)
{
    NODE *cn = e->Nleft;	/* Get pointer to constant node */
    TYPE *tfrom = cn->Ntype;	/* Converting from this typespec */
    TYPE *tto = e->Ntype;	/* to this one */

    switch ((int) e->Ncast) {
	case CAST_ILL:		/* Illegal cast from error? */
	    return e;		/* Should already have complained */

	case CAST_NONE:		/* no actual conversion needed */
	    break;		/* Just copy flags and set new type */

	case CAST_VOID:		/* Any type to void type (discard constant) */
	    cn->Nop = N_VCONST;	/* Change node op to special value */
	    break;

	case CAST_IT_IT:	/* Integer type to integer type */
	    cn->Niconst = tolong(tto, cn->Niconst);
	    break;
	case CAST_FP_IT:	/* Floating-point type to integer type */
	    cn->Niconst = tolong(tto, (long) cn->Nfconst);
	    cn->Nop = N_ICONST;
	    break;
	case CAST_EN_IT:	/* Enumeration type to integer type */
	case CAST_PT_IT:	/* Pointer type to integer type */
	    cn->Niconst = tolong(tto, (long) cn->Niconst);
	    cn->Nop = N_ICONST;
	    break;

	case CAST_FP_FP:	/* Floating-point type to floating-pt type */
	    switch (castidx(tfrom->Tspec, tto->Tspec)) {
		case castidx(TS_DOUBLE,TS_FLOAT):
		case castidx(TS_LNGDBL,TS_FLOAT):
		    cn->Nfconst = (float) cn->Nfconst;
		    break;
		case castidx(TS_FLOAT,TS_DOUBLE):
		case castidx(TS_FLOAT,TS_LNGDBL):
		    cn->Nfconst = (double)(float) cn->Nfconst;
		    break;
		case castidx(TS_LNGDBL,TS_DOUBLE):
		case castidx(TS_DOUBLE,TS_LNGDBL):
		    break;
		default:
		    int_warn("evalcast: bad fp_fp");
		    break;
	    }
	    break;

	case CAST_IT_FP:	/* Integer type to floating-point type */
	    switch (tto->Tspec) {
		case TS_FLOAT:
		    if (tissigned(tfrom))
			cn->Nfconst = (float) cn->Niconst;
		    else
			cn->Nfconst = (float) (unsigned long) cn->Niconst;
		    break;
		case TS_DOUBLE:
		case TS_LNGDBL:
		    if (tissigned(tfrom))
			cn->Nfconst = (double) cn->Niconst;
		    else
			cn->Nfconst = (double) (unsigned long) cn->Niconst;
		    break;
	    }
	    cn->Nop = N_FCONST;
	    break;

	case CAST_EN_EN:	/* Enumeration type to enumeration type */
	case CAST_IT_EN:	/* Integer type to enumeration type */
	    break;

	/* The only pointer-pointer conversions we can guarantee are
	** those from byte ptrs to word ptrs.  Going the other way
	** depends on the type and the KCC runtime section and will
	** produce different results, so we have to do it at run time.
	*/
	case CAST_PT_PT:	/* Pointer type to pointer type */
	    if (tisbytepointer(tfrom) && !tisbytepointer(tto))
		    cn->Niconst = (long)(INT *)(char *)(cn->Niconst);
	    else return e;
	    break;

	case CAST_IT_PT:	/* Integer type to pointer type */
	    cn->Nop = N_PCONST;
	    break;

	default:
	    int_warn("evalcast: bad cast: %d", (int) e->Ncast);
	    return e;
    }
    return copyflag(cn, e);
}

/* TOLONG - Convert a long integer value to some intermediate type and
**	then back to a long value.
**	Note special handling for chars, which KCC allows to
**	have varied byte sizes.
*/
static long
tolong(TYPE *t, long val)
{
    switch(t->Tspec) {
	case TS_SHORT:	return (short) val;
	case TS_INT:	return (int) val;
	case TS_LONG:	return (long) val;
	case TS_USHORT:	return (unsigned short) val;
	case TS_UINT:	return (unsigned int) val;
	case TS_ULONG:	return (unsigned long) val;

	case TS_BITF:
	case TS_CHAR:  /* return (char) val;          */
	    if (val & (1 << (tbitsize(t)-1)))	/* If sign bit set */
		return val | (((long)-1) << tbitsize(t));
	    /* Else drop through to handle like unsigned */

	case TS_UBITF:
	case TS_UCHAR: /* return (unsigned char) val; */
	    return val & ((1<<tbitsize(t))-1);	/* Mask off */

	default:
	    int_error("tolong: bad type");
	    return 0;
    }
}

#if 0
static NODE *
setnode(NODE *new, NODE *old,int nop)
{
    new->Ntype = old->Ntype;
    new->Nflag = old->Nflag;
    new->Nop = nop;
    return new;
}
#endif

/* ---------------------------------------- */
/*      commute tree to canonical form      */
/* ---------------------------------------- */

#define HBAR	64			/* basic unit of complexity */

#define IDMASK	    (HBAR-1)		/* mask for quantum fluctuations */
#define	IDWEIGHT(s) (hash(s->Sname)&IDMASK) /* to permute for common subs */

#define	BWEIGHT	(4*HBAR)		/* weight for binary operator */
#define	IWEIGHT	0			/* weight for integer */
#define	SWEIGHT	(2*HBAR)		/* weight for string const */
#define	MWEIGHT	HBAR			/* weight for struct member */
#define CWEIGHT	(2*HBAR)		/* weight for coercion, unary */
#define	FWEIGHT	(32*HBAR)		/* weight for fun call - v expensive */
#define	QWEIGHT	(2*HBAR)		/* weight for ternary */

static INT
ecanon(NODE *n)
{
    NODE *t;
    INT x, y;

    /*
    ** Return a weight for the tree, and commute subtrees.
    ** This function has two purposes:
    **   - To rearrange commutative expressions so that the more
    **     expensive operation is performed first, so that fewer
    **     registers need be allocated at once and so that moves
    **     from memory are more likely to be folded into the ops.
    **   - To permute equivalent expressions to the same canonical
    **     form, so that common subexpression elimination may be
    **     more likely to find them.
    */

    if (n) switch (n->Nop) {
    case N_ICONST:
	return IWEIGHT;
    case Q_IDENT:
	return IDWEIGHT(n->Nid)
		/* Hack so new array/funct conversion code
		** comes out looking the same as before --KLH
		*/
		+ (
		((n->Ntype != n->Nid->Stype) || n->Ntype->Tspec == TS_FUNCT)
			? CWEIGHT : 0);

    case N_SCONST:
	return SWEIGHT;
    case Q_DOT:
    case Q_MEMBER:
	return ecanon(n->Nleft) + MWEIGHT;
    case N_EXPRLIST:
	if (n->Nleft == NULL) return ecanon(n->Nright);
	return ecanon(n->Nleft) + ecanon(n->Nright);
    case N_CAST:
	return ecanon(n->Nleft) + CWEIGHT;
    case N_FNCALL:
	return (n->Nright == NULL) ? FWEIGHT : ecanon(n->Nright)+FWEIGHT;
    default:
	switch (tok[n->Nop].tktype) {
	case TKTY_BINOP:
	    x = ecanon(n->Nleft);
	    y = ecanon(n->Nright);
	    if (y > x && (n->Nop == Q_PLUS || n->Nop == Q_MPLY)) {
		t = n->Nleft;
		n->Nleft = n->Nright;
		n->Nright = t;
	    }
            return x + y + BWEIGHT;
	case TKTY_ASOP:
	case TKTY_BOOLOP:
	    return ecanon(n->Nleft) + ecanon(n->Nright) + BWEIGHT;
	case TKTY_UNOP:
	case TKTY_BOOLUN:
            return ecanon(n->Nleft)+CWEIGHT;
	case TKTY_TERNARY:
	    x = ecanon(n->Nright->Nleft);
	    y = ecanon(n->Nright->Nright);
	    if (y > x) x = y;
            return ecanon(n->Nleft) + x + QWEIGHT;
	default:
	    break;
	}
    }
    return 0;
}

#if 0	/* Comments about discarded values */

	There are three ways used internally within KCC to indicate
to the code generator that an expression's value is to be discarded:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -