#include "c.h"

static char rcsid[] = "$Id: gen.nw,v 2.24 1997/06/10 22:48:09 cwfraser Exp $";

#define readsreg(p) \
        (generic((p)->op)==INDIR && (p)->kids[0]->op==VREG+P)
#define setsrc(d) ((d) && (d)->x.regnode && \
        (d)->x.regnode->set == src->x.regnode->set && \
        (d)->x.regnode->mask&src->x.regnode->mask)

#define relink(a, b) ((b)->x.prev = (a), (a)->x.next = (b))

static Symbol   askfixedreg(Symbol);
static Symbol   askreg(Symbol, unsigned*);
static void     blkunroll(int, int, int, int, int, int, int[]);
static void     docall(Node);
static void     dumpcover(Node, int, int);
static void     dumpregs(char *, char *, char *);
static void     dumprule(int);
static void     dumptree(Node);
static unsigned emitasm(Node, int);
static void     genreload(Node, Symbol, int);
static void     genspill(Symbol, Node, Symbol);
static Symbol   getreg(Symbol, unsigned*, Node);
static int      getrule(Node, int);
static void     linearize(Node, Node);
static int      moveself(Node);
static void     prelabel(Node);
static Node*    prune(Node, Node*);
static void     putreg(Symbol);
static void     ralloc(Node);
static void     reduce(Node, int);
static int      reprune(Node*, int, int, Node);
static int      requate(Node);
static Node     reuse(Node, int);
static void     rewrite(Node);
static Symbol   spillee(Symbol, Node);
static void     spillr(Symbol, Node);
static int      uses(Node, unsigned);

int offset;

int maxoffset;

int framesize;
int argoffset;

int maxargoffset;

int dalign, salign;
int bflag = 0;  /* omit */
int dflag = 0;

int swap;

unsigned (*emitter)(Node, int) = emitasm;
static char NeedsReg[] = {
        0,                      /* unused */
        1,                      /* CNST */
        0, 0,                   /* ARG ASGN */
        1,                      /* INDIR  */
        0, 0, 1, 1,             /*  -  - CVF CVI */
        1, 0, 1, 1,             /* CVP - CVU NEG */
        1,                      /* CALL */
        1,                      /* LOAD */
        0,                      /* RET */
        1, 1, 1,                /* ADDRG ADDRF ADDRL */
        1, 1, 1, 1, 1,          /* ADD SUB LSH MOD RSH */
        1, 1, 1, 1,             /* BAND BCOM BOR BXOR */
        1, 1,                   /* DIV MUL */
        0, 0, 0, 0, 0, 0,       /* EQ GE GT LE LT NE */
        0, 0                   /* JUMP LABEL   */
};
Node head;

unsigned freemask[2];
unsigned usedmask[2];
unsigned tmask[2];
unsigned vmask[2];

Symbol mkreg(char *fmt, int n, int mask, int set) {
  Symbol p;

  NEW0(p, PERM);
  p->x.name = stringf(fmt, n);
  NEW0(p->x.regnode, PERM);
  p->x.regnode->number = n;
  p->x.regnode->mask = mask<<n;
  p->x.regnode->set = set;
  return p;
}
Symbol mkwildcard(Symbol *syms) {
  Symbol p;

  NEW0(p, PERM);
  p->x.name = "wildcard";
  p->x.wildcard = syms;
  return p;
}
void mkauto(Symbol p) {
  assert(p->sclass == AUTO);
  offset = roundup(offset + p->type->size, p->type->align);
  p->x.offset = -offset;
  p->x.name = stringd(-offset);
}
void blockbeg(Env *e) {
  e->offset = offset;
  e->freemask[IREG] = freemask[IREG];
  e->freemask[FREG] = freemask[FREG];
}
void blockend(Env *e) {
  if (offset > maxoffset)
    maxoffset = offset;
  offset = e->offset;
  freemask[IREG] = e->freemask[IREG];
  freemask[FREG] = e->freemask[FREG];
}
int mkactual(int align, int size) {
  int n = roundup(argoffset, align);

  argoffset = n + size;
  return n;
}
static void docall(Node p) {
  p->syms[1] = p->syms[0];
  p->syms[0] = intconst(argoffset);
  if (argoffset > maxargoffset)
    maxargoffset = argoffset;
  argoffset = 0;
}
void blkcopy(int dreg, int doff, int sreg, int soff, int size, int tmp[]) {
  assert(size >= 0);
  if (size == 0)
    return;
  else if (size <= 2)
    blkunroll(size, dreg, doff, sreg, soff, size, tmp);
  else if (size == 3) {
    blkunroll(2, dreg, doff,   sreg, soff,   2, tmp);
    blkunroll(1, dreg, doff+2, sreg, soff+2, 1, tmp);
  }
  else if (size <= 16) {
    blkunroll(4, dreg, doff, sreg, soff, size&~3, tmp);
    blkcopy(dreg, doff+(size&~3),
      sreg, soff+(size&~3), size&3, tmp);
  }
  else
    (*IR->x.blkloop)(dreg, doff, sreg, soff, size, tmp);
}
static void blkunroll(int k, int dreg, int doff, int sreg, int soff, int size, int tmp[]) {
  int i;

  assert(IR->x.max_unaligned_load);
  if (k > IR->x.max_unaligned_load
  && (k > salign || k > dalign))
    k = IR->x.max_unaligned_load;
  for (i = 0; i+k < size; i += 2*k) {
    (*IR->x.blkfetch)(k, soff+i,   sreg, tmp[0]);
    (*IR->x.blkfetch)(k, soff+i+k, sreg, tmp[1]);
    (*IR->x.blkstore)(k, doff+i,   dreg, tmp[0]);
    (*IR->x.blkstore)(k, doff+i+k, dreg, tmp[1]);
  }
  if (i < size) {
    (*IR->x.blkfetch)(k, i+soff, sreg, tmp[0]);
    (*IR->x.blkstore)(k, i+doff, dreg, tmp[0]);
  }
}
void parseflags(int argc, char *argv[]) {
  int i;

  for (i = 0; i < argc; i++)
    if (strcmp(argv[i], "-d") == 0)
      dflag = 1;
    else if (strcmp(argv[i], "-b") == 0)    /* omit */
      bflag = 1;          /* omit */
}
static int getrule(Node p, int nt) {
  int rulenum;

  assert(p);
  rulenum = (*IR->x._rule)(p->x.state, nt);
  if (!rulenum) {
    fprint(stderr, "(%x->op=%s at %w is corrupt.)\n", p, opname(p->op), &src);
    assert(0);
  }
  return rulenum;
}
static void reduce(Node p, int nt) {
  int rulenum, i;
  short *nts;
  Node kids[10];

  p = reuse(p, nt);
  rulenum = getrule(p, nt);
  nts = IR->x._nts[rulenum];
  (*IR->x._kids)(p, rulenum, kids);
  for (i = 0; nts[i]; i++)
    reduce(kids[i], nts[i]);
  if (IR->x._isinstruction[rulenum]) {
    assert(p->x.inst == 0 || p->x.inst == nt);
    p->x.inst = nt;
    if (p->syms[RX] && p->syms[RX]->temporary) {
      debug(fprint(stderr, "(using %s)\n", p->syms[RX]->name));
      p->syms[RX]->x.usecount++;
    }
  }
}
static Node reuse(Node p, int nt) {
  struct _state {
    short cost[1];
  };
  Symbol r = p->syms[RX];

  if (generic(p->op) == INDIR && p->kids[0]->op == VREG+P
  && r->u.t.cse && p->x.mayrecalc
  && ((struct _state*)r->u.t.cse->x.state)->cost[nt] == 0)
    return r->u.t.cse;
  else
    return p;
}

int mayrecalc(Node p) {
  int op;

  assert(p && p->syms[RX]);
  if (p->syms[RX]->u.t.cse == NULL)
    return 0;
  op = generic(p->syms[RX]->u.t.cse->op);
  if (op == CNST || op == ADDRF || op == ADDRG || op == ADDRL) {
    p->x.mayrecalc = 1;
    return 1;
  } else
    return 0;
}
static Node *prune(Node p, Node pp[]) {
  if (p == NULL)
    return pp;
  p->x.kids[0] = p->x.kids[1] = p->x.kids[2] = NULL;
  if (p->x.inst == 0)
    return prune(p->kids[1], prune(p->kids[0], pp));
  else if (p->syms[RX] && p->syms[RX]->temporary
  && p->syms[RX]->x.usecount < 2) {
    p->x.inst = 0;
    debug(fprint(stderr, "(clobbering %s)\n", p->syms[RX]->name));
    return prune(p->kids[1], prune(p->kids[0], pp));
  }
  else {
    prune(p->kids[1], prune(p->kids[0], &p->x.kids[0]));
    *pp = p;
    return pp + 1;
  }
}

#define ck(i) return (i) ? 0 : LBURG_MAX

int range(Node p, int lo, int hi) {
  Symbol s = p->syms[0];

  switch (specific(p->op)) {
  case ADDRF+P:
  case ADDRL+P: ck(s->x.offset >= lo && s->x.offset <= hi);
  case CNST+I:  ck(s->u.c.v.i  >= lo && s->u.c.v.i  <= hi);
  case CNST+U:  ck(s->u.c.v.u  >= lo && s->u.c.v.u  <= hi);
  case CNST+P:  ck(s->u.c.v.p  == 0  && lo <= 0 && hi >= 0);
  }
  return LBURG_MAX;
}
static void dumptree(Node p) {
  if (p->op == VREG+P && p->syms[0]) {
    fprint(stderr, "VREGP(%s)", p->syms[0]->name);
    return;
  } else if (generic(p->op) == LOAD) {
    fprint(stderr, "LOAD(");
    dumptree(p->kids[0]);
    fprint(stderr, ")");
    return;
  }
  fprint(stderr, "%s(", opname(p->op));
  switch (generic(p->op)) {
  case CNST: case LABEL:
  case ADDRG: case ADDRF: case ADDRL:
    if (p->syms[0])
      fprint(stderr, "%s", p->syms[0]->name);
    break;
  case RET:
    if (p->kids[0])
      dumptree(p->kids[0]);
    break;
  case CVF: case CVI: case CVP: case CVU: case JUMP:
  case ARG: case BCOM: case NEG: case INDIR:
    dumptree(p->kids[0]);
    break;
  case CALL:
    if (optype(p->op) != B) {
      dumptree(p->kids[0]);
      break;
    }
    /* else fall thru */
  case EQ: case NE: case GT: case GE: case LE: case LT:
  case ASGN: case BOR: case BAND: case BXOR: case RSH: case LSH:
  case ADD: case SUB:  case DIV: case MUL: case MOD:
    dumptree(p->kids[0]);
    fprint(stderr, ", ");
    dumptree(p->kids[1]);
    break;
  default: assert(0);
  }
  fprint(stderr, ")");
}
static void dumpcover(Node p, int nt, int in) {
  int rulenum, i;
  short *nts;
  Node kids[10];

  p = reuse(p, nt);
  rulenum = getrule(p, nt);
  nts = IR->x._nts[rulenum];
  fprint(stderr, "dumpcover(%x) = ", p);
  for (i = 0; i < in; i++)
    fprint(stderr, " ");
  dumprule(rulenum);
  (*IR->x._kids)(p, rulenum, kids);
  for (i = 0; nts[i]; i++)
    dumpcover(kids[i], nts[i], in+1);
}

static void dumprule(int rulenum) {
  assert(rulenum);
  fprint(stderr, "%s / %s", IR->x._string[rulenum],
    IR->x._templates[rulenum]);
  if (!IR->x._isinstruction[rulenum])
    fprint(stderr, "\n");
}
static unsigned emitasm(Node p, int nt) {
  int rulenum;
  short *nts;
  char *fmt;
  Node kids[10];

  p = reuse(p, nt);
  rulenum = getrule(p, nt);
  nts = IR->x._nts[rulenum];
  fmt = IR->x._templates[rulenum];
  assert(fmt);
  if (IR->x._isinstruction[rulenum] && p->x.emitted)
    print("%s", p->syms[RX]->x.name);
  else if (*fmt == '#')
    (*IR->x.emit2)(p);
  else {
    if (*fmt == '?') {
      fmt++;
      assert(p->kids[0]);
      if (p->syms[RX] == p->x.kids[0]->syms[RX])
        while (*fmt++ != '\n')
          ;
    }
    for ((*IR->x._kids)(p, rulenum, kids); *fmt; fmt++)
      if (*fmt != '%')
        putchar(*fmt);
      else if (*++fmt == 'F')
        print("%d", framesize);
      else if (*fmt >= '0' && *fmt <= '9')
        emitasm(kids[*fmt - '0'], nts[*fmt - '0']);
      else if (*fmt >= 'a' && *fmt < 'a' + NELEMS(p->syms))
        fputs(p->syms[*fmt - 'a']->x.name, stdout);
      else
        putchar(*fmt);
  }
  return 0;
}
void emit(Node p) {
  for (; p; p = p->x.next) {
    assert(p->x.registered);
    if (p->x.equatable && requate(p) || moveself(p))
      ;
    else
      (*emitter)(p, p->x.inst);
    p->x.emitted = 1;
  }
}
static int moveself(Node p) {
  return p->x.copy
  && p->syms[RX]->x.name == p->x.kids[0]->syms[RX]->x.name;
}
int move(Node p) {
  p->x.copy = 1;
  return 1;
}
static int requate(Node q) {
  Symbol src = q->x.kids[0]->syms[RX];
  Symbol tmp = q->syms[RX];
  Node p;
  int n = 0;

  debug(fprint(stderr, "(requate(%x): tmp=%s src=%s)\n", q, tmp->x.name, src->x.name));
  for (p = q->x.next; p; p = p->x.next)
    if (p->x.copy && p->syms[RX] == src
    &&  p->x.kids[0]->syms[RX] == tmp)
      debug(fprint(stderr, "(requate arm 0 at %x)\n", p)),
      p->syms[RX] = tmp;
    else if (setsrc(p->syms[RX]) && !moveself(p) && !readsreg(p))
      return 0;
    else if (p->x.spills)
      return 0;
    else if (generic(p->op) == CALL && p->x.next)
      return 0;
    else if (p->op == LABEL+V && p->x.next)
      return 0;
    else if (p->syms[RX] == tmp && readsreg(p))
      debug(fprint(stderr, "(requate arm 5 at %x)\n", p)),
      n++;
    else if (p->syms[RX] == tmp)
      break;
  debug(fprint(stderr, "(requate arm 7 at %x)\n", p));
  assert(n > 0);
  for (p = q->x.next; p; p = p->x.next)
    if (p->syms[RX] == tmp && readsreg(p)) {
      p->syms[RX] = src;
      if (--n <= 0)
        break;
    }
  return 1;
}
static void prelabel(Node p) {
  if (p == NULL)
    return;
  prelabel(p->kids[0]);
  prelabel(p->kids[1]);
  if (NeedsReg[opindex(p->op)])
    setreg(p, (*IR->x.rmap)(opkind(p->op)));
  switch (generic(p->op)) {
  case ADDRF: case ADDRL:
    if (p->syms[0]->sclass == REGISTER)
      p->op = VREG+P;
    break;
  case INDIR:
    if (p->kids[0]->op == VREG+P)
      setreg(p, p->kids[0]->syms[0]);
    break;
  case ASGN:
    if (p->kids[0]->op == VREG+P)
      rtarget(p, 1, p->kids[0]->syms[0]);
    break;
  case CVI: case CVU: case CVP:
    if (optype(p->op) != F
    &&  opsize(p->op) <= p->syms[0]->u.c.v.i)
      p->op = LOAD + opkind(p->op);
    break;
  }
  (IR->x.target)(p);
}
void setreg(Node p, Symbol r) {
  p->syms[RX] = r;
}
void rtarget(Node p, int n, Symbol r) {
  Node q = p->kids[n];

  assert(q);
  assert(r->sclass == REGISTER || !r->x.wildcard);
  assert(q->syms[RX]);
  if (r != q->syms[RX] && !q->syms[RX]->x.wildcard) {
    q = newnode(LOAD + opkind(q->op),
      q, NULL, q->syms[0]);
    if (r->u.t.cse == p->kids[n])
      r->u.t.cse = q;
    p->kids[n] = p->x.kids[n] = q;
    q->x.kids[0] = q->kids[0];
  }
  setreg(q, r);
  debug(fprint(stderr, "(targeting %x->x.kids[%d]=%x to %s)\n", p, n, p->kids[n], r->x.name));
}
static void rewrite(Node p) {
  assert(p->x.inst == 0);
  prelabel(p);
  debug(dumptree(p));
  debug(fprint(stderr, "\n"));
  (*IR->x._label)(p);
  debug(dumpcover(p, 1, 0));
  reduce(p, 1);
}
Node gen(Node forest) {
  int i;
  struct node sentinel;
  Node dummy, p;

  head = forest;
  for (p = forest; p; p = p->link) {
    assert(p->count == 0);
    if (generic(p->op) == CALL)
      docall(p);
    else if (   generic(p->op) == ASGN
    && generic(p->kids[1]->op) == CALL)
      docall(p->kids[1]);
    else if (generic(p->op) == ARG)
      (*IR->x.doarg)(p);
    rewrite(p);
    p->x.listed = 1;
  }
  for (p = forest; p; p = p->link)
    prune(p, &dummy);
  relink(&sentinel, &sentinel);
  for (p = forest; p; p = p->link)
    linearize(p, &sentinel);
  forest = sentinel.x.next;
  assert(forest);
  sentinel.x.next->x.prev = NULL;
  sentinel.x.prev->x.next = NULL;
  for (p = forest; p; p = p->x.next)
    for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
      assert(p->x.kids[i]->syms[RX]);
      if (p->x.kids[i]->syms[RX]->temporary) {
        p->x.kids[i]->x.prevuse =
          p->x.kids[i]->syms[RX]->x.lastuse;
        p->x.kids[i]->syms[RX]->x.lastuse = p->x.kids[i];
      }
    }
  for (p = forest; p; p = p->x.next) {
    ralloc(p);
    if (p->x.listed && NeedsReg[opindex(p->op)]
    && (*IR->x.rmap)(opkind(p->op))) {
      assert(generic(p->op) == CALL || generic(p->op) == LOAD);
      putreg(p->syms[RX]);
    }
  }
  return forest;
}
int notarget(Node p) {
  return p->syms[RX]->x.wildcard ? 0 : LBURG_MAX;
}
static void putreg(Symbol r) {
  assert(r && r->x.regnode);
  freemask[r->x.regnode->set] |= r->x.regnode->mask;
  debug(dumpregs("(freeing %s)\n", r->x.name, NULL));
}
static Symbol askfixedreg(Symbol s) {
  Regnode r = s->x.regnode;
  int n = r->set;

  if (r->mask&~freemask[n])
    return NULL;
  else {
    freemask[n] &= ~r->mask;
    usedmask[n] |=  r->mask;
    return s;
  }
}
static Symbol askreg(Symbol rs, unsigned rmask[]) {
  int i;

  if (rs->x.wildcard == NULL)
    return askfixedreg(rs);
  for (i = 31; i >= 0; i--) {
    Symbol r = rs->x.wildcard[i];
    if (r != NULL
    && !(r->x.regnode->mask&~rmask[r->x.regnode->set])
    && askfixedreg(r))
      return r;
  }
  return NULL;
}

static Symbol getreg(Symbol s, unsigned mask[], Node p) {
  Symbol r = askreg(s, mask);
  if (r == NULL) {
    r = spillee(s, p);
    assert(r);
    spill(r->x.regnode->mask, r->x.regnode->set, p);
    r = askreg(s, mask);
    assert(r);
  }
  assert(r->x.regnode);
  r->x.regnode->vbl = NULL;
  return r;
}
int askregvar(Symbol p, Symbol regs) {
  Symbol r;

  assert(p);
  if (p->sclass != REGISTER)
    return 0;
  else if (!isscalar(p->type)) {
    p->sclass = AUTO;
    return 0;
  }
  else if (p->temporary) {
    p->x.name = "?";
    return 1;
  }
  else if ((r = askreg(regs, vmask)) != NULL) {
    p->x.regnode = r->x.regnode;
    p->x.regnode->vbl = p;
    p->x.name = r->x.name;
    debug(dumpregs("(allocating %s to symbol %s)\n", p->x.name, p->name));
    return 1;
  }
  else {
    p->sclass = AUTO;
    return 0;
  }
}
static void linearize(Node p, Node next) {
  int i;

  for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++)
    linearize(p->x.kids[i], next);
  relink(next->x.prev, p);
  relink(p, next);
  debug(fprint(stderr, "(listing %x)\n", p));
}
static void ralloc(Node p) {
  int i;
  unsigned mask[2];

  mask[0] = tmask[0];
  mask[1] = tmask[1];
  assert(p);
  debug(fprint(stderr, "(rallocing %x)\n", p));
  for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
    Node kid = p->x.kids[i];
    Symbol r = kid->syms[RX];
    assert(r && kid->x.registered);
    if (r->sclass != REGISTER && r->x.lastuse == kid)
      putreg(r);
  }
  if (!p->x.registered && NeedsReg[opindex(p->op)]
  && (*IR->x.rmap)(opkind(p->op))) {
    Symbol sym = p->syms[RX], set = sym;
    assert(sym);
    if (sym->temporary)
      set = (*IR->x.rmap)(opkind(p->op));
    assert(set);
    if (set->sclass != REGISTER) {
      Symbol r;
      if (*IR->x._templates[getrule(p, p->x.inst)] == '?')
        for (i = 1; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
          Symbol r = p->x.kids[i]->syms[RX];
          assert(p->x.kids[i]->x.registered);
          assert(r && r->x.regnode);
          assert(sym->x.wildcard || sym != r);
          mask[r->x.regnode->set] &= ~r->x.regnode->mask;
        }
      r = getreg(set, mask, p);
      if (sym->temporary) {
        Node q;
        r->x.lastuse = sym->x.lastuse;
        for (q = sym->x.lastuse; q; q = q->x.prevuse) {
          q->syms[RX] = r;
          q->x.registered = 1;
          if (sym->u.t.cse && q->x.copy)
            q->x.equatable = 1;
        }
      } else {
        p->syms[RX] = r;
        r->x.lastuse = p;
      }
      debug(dumpregs("(allocating %s to node %x)\n", r->x.name, (char *) p));
    }
  }
  p->x.registered = 1;
  (*IR->x.clobber)(p);
}
static Symbol spillee(Symbol set, Node here) {
  Symbol bestreg = NULL;
  int bestdist = -1, i;

  assert(set);
  if (!set->x.wildcard)
    return set;
  for (i = 31; i >= 0; i--) {
    Symbol ri = set->x.wildcard[i];
    if (ri != NULL && ri->x.lastuse
    && ri->x.regnode->mask&tmask[ri->x.regnode->set]) {
      Regnode rn = ri->x.regnode;
      Node q = here;
      int dist = 0;
      for (; q && !uses(q, rn->mask); q = q->x.next)
        dist++;
      if (q && dist > bestdist) {
        bestdist = dist;
        bestreg = ri;
      }
    }
  }
  return bestreg;
}
static int uses(Node p, unsigned mask) {
  int i;
  Node q;

  for (i = 0; i < NELEMS(p->x.kids)
    && (q = p->x.kids[i]) != NULL; i++)
    if (q->x.registered
    && mask&q->syms[RX]->x.regnode->mask)
      return 1;
  return 0;
}
static void spillr(Symbol r, Node here) {
  int i;
  Symbol tmp;
  Node p = r->x.lastuse;
  assert(p);
  while (p->x.prevuse)
    assert(r == p->syms[RX]),
    p = p->x.prevuse;
  assert(p->x.registered && !readsreg(p));
  tmp = newtemp(AUTO, optype(p->op), opsize(p->op));
  genspill(r, p, tmp);
  for (p = here->x.next; p; p = p->x.next)
    for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
      Node k = p->x.kids[i];
      if (k->x.registered && k->syms[RX] == r)
        genreload(p, tmp, i);
    }
  putreg(r);
}
static void genspill(Symbol r, Node last, Symbol tmp) {
  Node p, q;
  Symbol s;
  unsigned ty;

  debug(fprint(stderr, "(spilling %s to local %s)\n", r->x.name, tmp->x.name));
  debug(fprint(stderr, "(genspill: "));
  debug(dumptree(last));
  debug(fprint(stderr, ")\n"));
  ty = opkind(last->op);
  NEW0(s, FUNC);
  s->sclass = REGISTER;
  s->x.name = r->x.name;
  s->x.regnode = r->x.regnode;
  s->x.regnode->vbl = s;
  q = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, s);
  q = newnode(INDIR + ty, q, NULL, NULL);
  p = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, tmp);
  p = newnode(ASGN + ty, p, q, NULL);
  p->x.spills = 1;
  rewrite(p);
  prune(p, &q);
  q = last->x.next;
  linearize(p, q);
  for (p = last->x.next; p != q; p = p->x.next) {
    ralloc(p);
    assert(!p->x.listed || !NeedsReg[opindex(p->op)] || !(*IR->x.rmap)(opkind(p->op)));
  }
}

static void genreload(Node p, Symbol tmp, int i) {
  Node q;
  int ty;

  debug(fprint(stderr, "(replacing %x with a reload from %s)\n", p->x.kids[i], tmp->x.name));
  debug(fprint(stderr, "(genreload: "));
  debug(dumptree(p->x.kids[i]));
  debug(fprint(stderr, ")\n"));
  ty = opkind(p->x.kids[i]->op);
  q = newnode(ADDRL+P + sizeop(IR->ptrmetric.size), NULL, NULL, tmp);
  p->x.kids[i] = newnode(INDIR + ty, q, NULL, NULL);
  rewrite(p->x.kids[i]);
  prune(p->x.kids[i], &q);
  reprune(&p->kids[1], reprune(&p->kids[0], 0, i, p), i, p);
  prune(p, &q);
  linearize(p->x.kids[i], p);
}
static int reprune(Node *pp, int k, int n, Node p) {
  struct node x, *q = *pp;

  if (q == NULL || k > n)
    return k;
  else if (q->x.inst == 0)
    return reprune(&q->kids[1],
      reprune(&q->kids[0], k, n, p), n, p);
  if (k == n) {
    debug(fprint(stderr, "(reprune changes %x from %x to %x)\n", pp, *pp, p->x.kids[n]));
    *pp = p->x.kids[n];
    x = *p;
    (IR->x.target)(&x);
  }
  return k + 1;
}
void spill(unsigned mask, int n, Node here) {
  int i;
  Node p;

  here->x.spills = 1;
  usedmask[n] |= mask;
  if (mask&~freemask[n])
    for (p = here; p; p = p->x.next)
      for (i = 0; i < NELEMS(p->x.kids) && p->x.kids[i]; i++) {
        Symbol r = p->x.kids[i]->syms[RX];
        assert(r);
        if (p->x.kids[i]->x.registered && r->x.regnode->set == n
        && r->x.regnode->mask&mask)
          spillr(r, here);
      }
}
static void dumpregs(char *msg, char *a, char *b) {
  fprint(stderr, msg, a, b);
  fprint(stderr, "(free[0]=%x)\n", freemask[0]);
  fprint(stderr, "(free[1]=%x)\n", freemask[1]);
}

int getregnum(Node p) {
  assert(p && p->syms[RX] && p->syms[RX]->x.regnode);
  return p->syms[RX]->x.regnode->number;
}


unsigned regloc(Symbol p) {
  assert(p && p->sclass == REGISTER && p->sclass == REGISTER && p->x.regnode);
  return p->x.regnode->set<<8 | p->x.regnode->number;
}

