#include "c.h"

static char rcsid[] = "$Id: enode.nw,v 2.11 1997/05/29 20:02:32 cwfraser Exp $";

static Tree addtree(int, Tree, Tree);
static Tree andtree(int, Tree, Tree);
static Tree cmptree(int, Tree, Tree);
static int compatible(Type, Type);
static int isnullptr(Tree e);
static Tree multree(int, Tree, Tree);
static Tree subtree(int, Tree, Tree);
#define isvoidptr(ty) \
        (isptr(ty) && unqual(ty->type) == voidtype)

Tree (*optree[])(int, Tree, Tree) = {
#define xx(a,b,c,d,e,f,g) e,
#define yy(a,b,c,d,e,f,g) e,
#include "token.h"
};
Tree call(Tree f, Type fty, Coordinate src) {
        int n = 0;
        Tree args = NULL, r = NULL, e;
        Type *proto, rty = unqual(freturn(fty));
        Symbol t3 = NULL;

        if (fty->u.f.oldstyle)
                proto = NULL;
        else
                proto = fty->u.f.proto;
        if (hascall(f))
                r = f;
        if (isstruct(rty))
                {
		        t3 = temporary(AUTO, unqual(rty));
		        if (rty->size == 0)
		                error("illegal use of incomplete type `%t'\n", rty);
		}
        if (t != ')')
                for (;;) {
                        Tree q = pointer(expr1(0));
			if (proto && *proto && *proto != voidtype)
			        {
				        Type aty;
				        q = value(q);
				        aty = assign(*proto, q);
				        if (aty)
				                q = cast(q, aty);
				        else
				                error("type error in argument %d to %s; found `%t' expected `%t'\n", n + 1, funcname(f),

				                        q->type, *proto);
				        if ((isint(q->type) || isenum(q->type))
				        && q->type->size != inttype->size)
				                q = cast(q, promote(q->type));
				        ++proto;
				}
			else
			        {
				        if (!fty->u.f.oldstyle && *proto == NULL)
				                error("too many arguments to %s\n", funcname(f));
				        q = value(q);
				        if (isarray(q->type) || q->type->size == 0)
				                error("type error in argument %d to %s; `%t' is illegal\n", n + 1, funcname(f), q->type);

				        else
				                q = cast(q, promote(q->type));
				}
			if (!IR->wants_argb && isstruct(q->type))
			        if (iscallb(q))
				        q = addrof(q);
				else {
				        Symbol t1 = temporary(AUTO, unqual(q->type));
				        q = asgn(t1, q);
				        q = tree(RIGHT, ptr(t1->type),
				                root(q), lvalue(idtree(t1)));
				}
			if (q->type->size == 0)
			        q->type = inttype;
			if (hascall(q))
			        r = r ? tree(RIGHT, voidtype, r, q) : q;
			args = tree(mkop(ARG, q->type), q->type, q, args);
			n++;
			if (Aflag >= 2 && n == 32)
			        warning("more than 31 arguments in a call to %s\n",
			                funcname(f));
                        if (t != ',')
                                break;
                        t = gettok();
                }
        expect(')');
        if (proto && *proto && *proto != voidtype)
                error("insufficient number of arguments to %s\n",
                        funcname(f));
        if (r)
                args = tree(RIGHT, voidtype, r, args);
        e = calltree(f, rty, args, t3);
        if (events.calls)
                apply(events.calls, &src, &e);
        return e;
}
Tree calltree(Tree f, Type ty, Tree args, Symbol t3) {
        Tree p;

        if (args)
                f = tree(RIGHT, f->type, args, f);
        if (isstruct(ty))
                assert(t3),
                p = tree(RIGHT, ty,
                        tree(CALL+B, ty, f, addrof(idtree(t3))),
                        idtree(t3));
        else {
                Type rty = ty;
                if (isenum(ty))
                        rty = unqual(ty)->type;
                if (!isfloat(rty))
                        rty = promote(rty);
                p = tree(mkop(CALL, rty), rty, f, NULL);
                if (isptr(ty) || p->type->size > ty->size)
                        p = cast(p, ty);
        }
        return p;
}
Tree vcall(Symbol func, Type ty, ...) {
        va_list ap;
        Tree args = NULL, e, f = pointer(idtree(func)), r = NULL;

        assert(isfunc(func->type));
        if (ty == NULL)
                ty = freturn(func->type);
        va_start(ap, ty);
        while ((e = va_arg(ap, Tree)) != NULL) {
                if (hascall(e))
                        r = r == NULL ? e : tree(RIGHT, voidtype, r, e);
                args = tree(mkop(ARG, e->type), e->type, e, args);
        }
        va_end(ap);
        if (r != NULL)
                args = tree(RIGHT, voidtype, r, args);
        return calltree(f, ty, args, NULL);
}
int iscallb(Tree e) {
        return e->op == RIGHT && e->kids[0] && e->kids[1]
                && e->kids[0]->op == CALL+B
                && e->kids[1]->op == INDIR+B
                && isaddrop(e->kids[1]->kids[0]->op)
                && e->kids[1]->kids[0]->u.sym->temporary;
}

static Tree addtree(int op, Tree l, Tree r) {
        Type ty = inttype;

        if (isarith(l->type) && isarith(r->type)) {
                ty = binary(l->type, r->type);
                l = cast(l, ty);
		r = cast(r, ty);         
        } else if (isptr(l->type) && isint(r->type))
                return addtree(ADD, r, l);
        else if (  isptr(r->type) && isint(l->type)
        && !isfunc(r->type->type))
                {
		        int n;
		        ty = unqual(r->type);
		        n = ty->type->size;
			if (n == 0)
			        error("unknown size for type `%t'\n", ty->type);
		        l = cast(l, promote(l->type));
		        if (n > 1)
		                l = multree(MUL, consttree(n, inttype), l);
		        if (YYcheck && !isaddrop(r->op))                /* omit */
		                return nullcall(ty, YYcheck, r, l);     /* omit */
		        return simplify(ADD, ty, l, r);
		}

        else
                typeerror(op, l, r);
        return simplify(op, ty, l, r);
}

Tree cnsttree(Type ty, ...) {
        Tree p = tree(mkop(CNST,ty), ty, NULL, NULL);
        va_list ap;

        va_start(ap, ty);
        switch (ty->op) {
        case INT:     p->u.v.i = va_arg(ap, long); break;
        case UNSIGNED:p->u.v.u = va_arg(ap, unsigned long)&ones(8*ty->size); break;
        case FLOAT:   p->u.v.d = va_arg(ap, long double); break;
        case POINTER: p->u.v.p = va_arg(ap, void *); break;
        default: assert(0);
        }
        va_end(ap);
        return p;
}

Tree consttree(unsigned n, Type ty) {
        if (isarray(ty))
                ty = atop(ty);
        else assert(isint(ty));
        return cnsttree(ty, (unsigned long)n);
}
static Tree cmptree(int op, Tree l, Tree r) {
        Type ty;

        if (isarith(l->type) && isarith(r->type)) {
                ty = binary(l->type, r->type);
                l = cast(l, ty);
		r = cast(r, ty);
        } else if (compatible(l->type, r->type)) {
                ty = unsignedptr;
                l = cast(l, ty);
		r = cast(r, ty);
        } else {
                ty = unsignedtype;
                typeerror(op, l, r);
        }
        return simplify(mkop(op,ty), inttype, l, r);
}
static int compatible(Type ty1, Type ty2) {
        return isptr(ty1) && !isfunc(ty1->type)
            && isptr(ty2) && !isfunc(ty2->type)
            && eqtype(unqual(ty1->type), unqual(ty2->type), 0);
}
static int isnullptr(Tree e) {
        Type ty = unqual(e->type);

        return generic(e->op) == CNST
            && (ty->op == INT      && e->u.v.i == 0
             || ty->op == UNSIGNED && e->u.v.u == 0
             || isvoidptr(ty)      && e->u.v.p == NULL);
}
Tree eqtree(int op, Tree l, Tree r) {
        Type xty = l->type, yty = r->type;

        if (isptr(xty) && isnullptr(r)
        ||  isptr(xty) && !isfunc(xty->type) && isvoidptr(yty)
        ||  (isptr(xty) && isptr(yty)
	    && eqtype(unqual(xty->type), unqual(yty->type), 1))) {
                Type ty = unsignedptr;
                l = cast(l, ty);
		r = cast(r, ty);
                return simplify(mkop(op,ty), inttype, l, r);
        }
        if (isptr(yty) && isnullptr(l)
        ||  isptr(yty) && !isfunc(yty->type) && isvoidptr(xty))
                return eqtree(op, r, l);
        return cmptree(op, l, r);
}

Type assign(Type xty, Tree e) {
        Type yty = unqual(e->type);

        xty = unqual(xty);
        if (isenum(xty))
                xty = xty->type;
        if (xty->size == 0 || yty->size == 0)
                return NULL;
        if ( isarith(xty) && isarith(yty)
	||  isstruct(xty) && xty == yty)
	        return xty;
	if (isptr(xty) && isnullptr(e))
	        return xty;
	if ((isvoidptr(xty) && isptr(yty)
	  || isptr(xty)     && isvoidptr(yty))
	&& (  (isconst(xty->type)    || !isconst(yty->type))
	   && (isvolatile(xty->type) || !isvolatile(yty->type))))
	        return xty;

	if ((isptr(xty) && isptr(yty)
	    && eqtype(unqual(xty->type), unqual(yty->type), 1))
	&&  (  (isconst(xty->type)    || !isconst(yty->type))
	    && (isvolatile(xty->type) || !isvolatile(yty->type))))
	        return xty;
	if (isptr(xty) && isptr(yty)
	&& (  (isconst(xty->type)    || !isconst(yty->type))
	   && (isvolatile(xty->type) || !isvolatile(yty->type)))) {
	        Type lty = unqual(xty->type), rty = unqual(yty->type);
	        if (isenum(lty) && rty == inttype
	        ||  isenum(rty) && lty == inttype) {
	                if (Aflag >= 1)
	                        warning("assignment between `%t' and `%t' is compiler-dependent\n",
	                                xty, yty);
	                return xty;
	        }
	}
	return NULL;
}
Tree asgntree(int op, Tree l, Tree r) {
        Type aty, ty;

        r = pointer(r);
        ty = assign(l->type, r);
        if (ty)
                r = cast(r, ty);
        else {
                typeerror(ASGN, l, r);
                if (r->type == voidtype)
                        r = retype(r, inttype);
                ty = r->type;
        }
        if (l->op != FIELD)
                l = lvalue(l);
        aty = l->type;
	if (isptr(aty))
	        aty = unqual(aty)->type;
	if ( isconst(aty)
	||  isstruct(aty) && unqual(aty)->u.sym->u.s.cfields)
	        if (isaddrop(l->op)
	        && !l->u.sym->computed && !l->u.sym->generated)
	                error("assignment to const identifier `%s'\n",
	                        l->u.sym->name);
	        else
	                error("assignment to const location\n");
	if (l->op == FIELD) {
	        long n = 8*l->u.field->type->size - fieldsize(l->u.field);
	        if (n > 0 && isunsigned(l->u.field->type))
	                r = bittree(BAND, r,
	                        cnsttree(r->type, fieldmask(l->u.field)));
	        else if (n > 0) {
	                if (r->op == CNST+I) {
	                        n = r->u.v.i;
	                        if (n&(1<<(fieldsize(l->u.field)-1)))
	                                n |= ~0UL<<fieldsize(l->u.field);
	                        r = cnsttree(r->type, n);
	                } else
	                        r = shtree(RSH,
	                                shtree(LSH, r, cnsttree(inttype, n)),
	                                cnsttree(inttype, n));
	        }
	}
	if (isstruct(ty) && isaddrop(l->op) && iscallb(r))
	        return tree(RIGHT, ty,
	                tree(CALL+B, ty, r->kids[0]->kids[0], l),
	                idtree(l->u.sym));
        return tree(mkop(op,ty), ty, l, r);
}
Tree condtree(Tree e, Tree l, Tree r) {
        Symbol t1;
        Type ty, xty = l->type, yty = r->type;
        Tree p;

        if (isarith(xty) && isarith(yty))
	        ty = binary(xty, yty);
	else if (eqtype(xty, yty, 1))
	        ty = unqual(xty);
	else if (isptr(xty)   && isnullptr(r))
	        ty = xty;
	else if (isnullptr(l) && isptr(yty))
	        ty = yty;
	else if (isptr(xty) && !isfunc(xty->type) && isvoidptr(yty)
	||       isptr(yty) && !isfunc(yty->type) && isvoidptr(xty))
	        ty = voidptype;
	else if ((isptr(xty) && isptr(yty)
		 && eqtype(unqual(xty->type), unqual(yty->type), 1)))
	        ty = xty;
	else {
	        typeerror(COND, l, r);
	        return consttree(0, inttype);
	}
	if (isptr(ty)) {
	        ty = unqual(unqual(ty)->type);
	        if (isptr(xty) && isconst(unqual(xty)->type)
	        ||  isptr(yty) && isconst(unqual(yty)->type))
	                ty = qual(CONST, ty);
	        if (isptr(xty) && isvolatile(unqual(xty)->type)
	        ||  isptr(yty) && isvolatile(unqual(yty)->type))
	                ty = qual(VOLATILE, ty);
	        ty = ptr(ty);
	}
	switch (e->op) {
	case CNST+I: return cast(e->u.v.i != 0   ? l : r, ty);
	case CNST+U: return cast(e->u.v.u != 0   ? l : r, ty);
	case CNST+P: return cast(e->u.v.p != 0   ? l : r, ty);
	case CNST+F: return cast(e->u.v.d != 0.0 ? l : r, ty);
	}
	if (ty != voidtype && ty->size > 0) {
	        t1 = genident(REGISTER, unqual(ty), level);
	/*      t1 = temporary(REGISTER, unqual(ty)); */
	        l = asgn(t1, l);
	        r = asgn(t1, r);
	} else
	        t1 = NULL;
        p = tree(COND, ty, cond(e),
                tree(RIGHT, ty, root(l), root(r)));
        p->u.sym = t1;
        return p;
}
/* addrof - address of p */
Tree addrof(Tree p) {
        Tree q = p;

        for (;;)
                switch (generic(q->op)) {
                case RIGHT:
                        assert(q->kids[0] || q->kids[1]);
                        q = q->kids[1] ? q->kids[1] : q->kids[0];
                        continue;
                case ASGN:
                        q = q->kids[1];
                        continue;
                case COND: {
                        Symbol t1 = q->u.sym;
                        q->u.sym = 0;
                        q = idtree(t1);
                        /* fall thru */
                        }
                case INDIR:
                        if (p == q)
                                return q->kids[0];
                        q = q->kids[0];
                        return tree(RIGHT, q->type, root(p), q);
                default:
                        error("addressable object required\n");
                        return value(p);
                }
}

/* andtree - construct tree for l [&& ||] r */
static Tree andtree(int op, Tree l, Tree r) {
        if (!isscalar(l->type) || !isscalar(r->type))
                typeerror(op, l, r);
        return simplify(op, inttype, cond(l), cond(r));
}

/* asgn - generate tree for assignment of expr e to symbol p sans qualifiers */
Tree asgn(Symbol p, Tree e) {
        if (isarray(p->type))
                e = tree(ASGN+B, p->type, idtree(p),
                        tree(INDIR+B, e->type, e, NULL));
        else {
                Type ty = p->type;
                p->type = unqual(p->type);
                if (isstruct(p->type) && p->type->u.sym->u.s.cfields) {
                        p->type->u.sym->u.s.cfields = 0;
                        e = asgntree(ASGN, idtree(p), e);
                        p->type->u.sym->u.s.cfields = 1;
                } else
                        e = asgntree(ASGN, idtree(p), e);
                p->type = ty;
        }
        return e;
}

/* bittree - construct tree for l [& | ^ %] r */
Tree bittree(int op, Tree l, Tree r) {
        Type ty = inttype;

        if (isint(l->type) && isint(r->type)) {
                ty = binary(l->type, r->type);
                l = cast(l, ty);
		r = cast(r, ty);         
        } else
                typeerror(op, l, r);
        return simplify(op, ty, l, r);
}

/* multree - construct tree for l [* /] r */
static Tree multree(int op, Tree l, Tree r) {
        Type ty = inttype;

        if (isarith(l->type) && isarith(r->type)) {
                ty = binary(l->type, r->type);
                l = cast(l, ty);
		r = cast(r, ty);         
        } else
                typeerror(op, l, r);
        return simplify(op, ty, l, r);
}

/* shtree - construct tree for l [>> <<] r */
Tree shtree(int op, Tree l, Tree r) {
        Type ty = inttype;

        if (isint(l->type) && isint(r->type)) {
                ty = promote(l->type);
                l = cast(l, ty);
                r = cast(r, inttype);
        } else
                typeerror(op, l, r);
        return simplify(op, ty, l, r);
}

/* subtree - construct tree for l - r */
static Tree subtree(int op, Tree l, Tree r) {
        long n;
        Type ty = inttype;

        if (isarith(l->type) && isarith(r->type)) {
                ty = binary(l->type, r->type);
                l = cast(l, ty);
		r = cast(r, ty);         
        } else if (isptr(l->type) && !isfunc(l->type->type) && isint(r->type)) {
                ty = unqual(l->type);
                n = ty->type->size;
		if (n == 0)
		        error("unknown size for type `%t'\n", ty->type);
                r = cast(r, promote(r->type));
                if (n > 1)
                        r = multree(MUL, cnsttree(inttype, n), r);
                return simplify(SUB+P, ty, l, r);
        } else if (compatible(l->type, r->type)) {
                ty = unqual(l->type);
                n = ty->type->size;
		if (n == 0)
		        error("unknown size for type `%t'\n", ty->type);
                l = simplify(SUB+U, unsignedptr,
                        cast(l, unsignedptr), cast(r, unsignedptr));
                return simplify(DIV+I, longtype,
                        cast(l, longtype), cnsttree(longtype, n));
        } else
                typeerror(op, l, r);
        return simplify(op, ty, l, r);
}

/* typeerror - issue "operands of op have illegal types `l' and `r'" */
void typeerror(int op, Tree l, Tree r) {
        int i;
        static struct { int op; char *name; } ops[] = {
                ASGN, "=",      INDIR, "*",     NEG,  "-",
                ADD,  "+",      SUB,   "-",     LSH,  "<<",
                MOD,  "%",      RSH,   ">>",    BAND, "&",
                BCOM, "~",      BOR,   "|",     BXOR, "^",
                DIV,  "/",      MUL,   "*",     EQ,   "==",
                GE,   ">=",     GT,    ">",     LE,   "<=",
                LT,   "<",      NE,    "!=",    AND,  "&&",
                NOT,  "!",      OR,    "||",    COND, "?:",
                0, 0
        };

        op = generic(op);
        for (i = 0; ops[i].op; i++)
                if (op == ops[i].op)
                        break;
        assert(ops[i].name);
        if (r)
                error("operands of %s have illegal types `%t' and `%t'\n",
                        ops[i].name, l->type, r->type);
        else
                error("operand of unary %s has illegal type `%t'\n", ops[i].name,
                        l->type);
}
