#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);
}
