#include "c.h"


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))
		{
			long n;
			ty = unqual(r->type);
			n = unqual(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, cnsttree(signedptr, n), 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, 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, (unsigned long)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 = unqual(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(signedptr, n), r);
		if (isunsigned(r->type))
			r = cast(r, unsignedptr);
		else
			r = cast(r, signedptr);
		return simplify(SUB+P, ty, l, r);
	} else if (compatible(l->type, r->type)) {
		ty = unqual(l->type);
		n = unqual(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);
}
