/* i_igspill: spill node i, removing it from index j in interference graph g */ static void i_igspill (i_local_t i, i_local_t j, ig_t g) { SCLASS(i) = STACK; /* Update storage location */ if (TYPE(i) == I_B) ADDR(i) = v_localb(ADDR(i)); else ADDR(i) = v_local(TYPE(i)); i_igremove(j, g); /* Remove node i from integer IG */ }
/* * Find a leaf type node that puts the value into a register. */ int findleaf(NODE *p, int cookie) { extern int *qtable[]; struct optab *q = NULL; /* XXX gcc */ int i, sh; int *ixp; F2DEBUG(("findleaf p %p (%s)\n", p, prcook(cookie))); F2WALK(p); ixp = qtable[p->n_op]; for (i = 0; ixp[i] >= 0; i++) { q = &table[ixp[i]]; F2DEBUG(("findleaf: ixp %d\n", ixp[i])); if (!acceptable(q)) /* target-dependent filter */ continue; if ((q->visit & cookie) == 0) continue; /* wrong registers */ if (ttype(p->n_type, q->rtype) == 0 || ttype(p->n_type, q->ltype) == 0) continue; /* Types must be correct */ F2DEBUG(("findleaf got types, rshape %s\n", prcook(q->rshape))); if (chcheck(p, q->rshape, 0) != SRDIR) continue; if (q->needs & REWRITE) break; /* Done here */ break; } if (ixp[i] < 0) { F2DEBUG(("findleaf failed\n")); if (setuni(p, cookie)) return FRETRY; return FFAIL; } F2DEBUG(("findleaf entry %d\n", ixp[i])); sh = ffs(cookie & q->visit & INREGS)-1; F2DEBUG(("findleaf: node %p (%s)\n", p, prcook(1 << sh))); p->n_su = MKIDX(ixp[i], 0); SCLASS(p->n_su, sh); return sh; }
void i_fg_build (void) { unsigned i; i_puint32 cp = i_buf; /* Start of code to analyze */ i_puint32 lp = i_lim; /* End of code to analyze */ i_uint32 op; /* Current opcode */ i_bb_t b; /* Current basic block */ num_bb = 0; b = bb_alloc(); b->h = cp; if (i_ralloctype == RA_EZ) { i_fg_root = i_fg_tail = b; b->t = lp - i_isize; return; } i_calls_cur = i_calls_lim = 0; NEW0(lbl2bb, i_lab_cur); NEW(fwdrefs, i_nbb); fwdref_cur = 0; i_fg_root = b; do { assert(b && !b->init); op = get_op(cp); switch(i_op2class[op]) { case I_BOP: case I_BOPF: case I_MOPR: case I_MOPRF: assert(!isimmed(op)); markuse(b, get_rs(cp)); markuse(b, get_rs2(cp)); markdef(b, get_rd(cp)); break; case I_BOPI: case I_MOPRI: case I_MOPRIF: assert(isimmed(op)); markuse(b, get_rs(cp)); markdef(b, get_rd(cp)); break; case I_MOPW: case I_MOPWF: assert(!isimmed(op)); markuse(b, get_rd(cp)); markuse(b, get_rs(cp)); markuse(b, get_rs2(cp)); break; case I_MOPWI: case I_MOPWIF: assert(isimmed(op)); markuse(b, get_rd(cp)); markuse(b, get_rs(cp)); break; case I_UOP: case I_UOPF: assert(!isimmed(op)); markuse(b, get_rs(cp)); markdef(b, get_rd(cp)); break; case I_UOPI: assert(isimmed(op)); markdef(b, get_rd(cp)); break; case I_SET: case I_SETF: markdef(b, get_rd(cp)); break; case I_LEA: case I_LEAF: SCLASS(get_rs(cp)) = STACK; markuse(b, get_rs(cp)); markdef(b, get_rd(cp)); break; case I_RET: case I_RETF: if (op != i_op_retv) markuse(b, get_rd(cp)); case I_RETI: b = bb_finalize(b, cp, lp, i_isize, false); break; case I_BR: case I_BRF: markuse(b, get_rs2(cp)); case I_BRI: markuse(b, get_rs(cp)); bb_linklbl(b, get_rd(cp)); b = bb_finalize(b, cp, lp, i_isize, true); break; case I_CALL: case I_CALLF: markuse(b, get_rs(cp)); case I_CALLI: case I_CALLIF: if (op != i_op_callv && op != i_op_callvi) markdef(b, get_rd(cp)); markcall(cp); b = bb_finalize(b, cp, lp, 2*i_isize, true); cp += i_isize; /* Calls are 2x as long as other insns */ break; case I_ARG: case I_ARGF: markuse(b, get_rd(cp)); break; case I_JMP: assert(get_rd(cp) < num_i); markuse(b, get_rd(cp)); b = bb_finalize(b, cp, lp, i_isize, false); break; case I_JMPI: bb_linklbl(b, get_imm(cp)); b = bb_finalize(b, cp, lp, i_isize, false); break; case I_MISC: switch (op) { case i_op_lbl: if (cp > b->h) /* If not at head of current block ... */ /* ... make this the head of a new block */ b = bb_finalize(b, cp-i_isize, lp, i_isize, true); lbl2bb[get_rd(cp)] = b; break; default: /* refmul, refdiv, self, nop */ break; } break; default: assert(0); } } while ((cp += i_isize) < lp); i_fg_tail = b; for (i = 0; i < fwdref_cur; i++) bb_linkbb(fwdrefs[i].src, lbl2bb[fwdrefs[i].dst]); #ifndef NDEBUG for (i = 0, b = i_fg_root; b; b = b->lnext) i++; assert(i == num_bb); #endif }
/* i_ig_color: color the interference graph g for storage class (INT/FP) sc */ void i_ig_color (int sc) { ig_t g, g2; /* Backup of interference graph */ i_local_t *nstk; /* Node stack: when removing a node from IG, push it onto this stack */ unsigned int ncnt = 0; /* Counter for node stack */ unsigned int nnodes; /* No. of nodes left in IG when coloring */ unsigned int nremoved; /* No. of nodes removed in 1 coloring pass */ i_local_t i, j; /* Dummy variable indices / counters */ unsigned int k; unsigned int reg; /* Temporary color variable */ unsigned int ncolors; /* Number of available registers */ /* Data for prioritized spilling */ i_ref_t threshref = 0.; /* Threshold refcnt (see below) */ i_local_t *deferspill = 0; /* Maintains info of which spills have been deferred */ i_local_t Vnum, Vbase; /* Number of vars of sclass sc, and their base offset in i_locals */ #ifndef NDEBUG int dcnt = 0; #endif if (sc == FP) { g = fig; Vnum = num_f; Vbase = i_loc_ilim; } else { g = iig; Vnum = num_i; Vbase = 0; } ncolors = i_reg_navail(sc);/* Find number of available registers */ NEW(nstk, Vnum); /* Allocate register stack */ g2 = i_igcopy(g); /* Make backup copy of IG */ NEW0(removed, Vnum); /* Mark whether a node has been removed */ DEBUG(i_igunparse(g)); if (i_dorefcnt) { /* Find maxref */ i_ref_t t = 0.; unsigned n = 0; NEW0(deferspill, Vnum); for (i = 0; i < Vnum; i++) { j = i + Vbase; /* Get real index of this variable */ if (! (PARAMP(j) || SCLASS(j) == STACK)) { t += REFCNT(j); n++; } } threshref = t/(n*2); /* For any clique (V,E) where |V| > # regs, spill first all v with refcnt < threshref; this is a little bogus (the right way would be to sort the v \in V and spill in order of increasing refcnt), but fast, and it works pretty well. */ } /* * Dismantle interference graph */ do { nremoved = 0; /* Reset number of nodes removed */ nnodes = 0; /* And number of nodes remaining */ /* Loop over all variables (ie. nodes in IG) */ for (i = 0; i < Vnum; i++) { if (removed[i]) continue; j = i + Vbase; /* Skip params: already allocated */ if (PARAMP(j)) { i_igremove(i, g); continue; } /* Remove non-register candidates */ if (SCLASS(j) == STACK) { i_igspill(j, i, g); continue; } /* Find valence of node i */ k = bv_num(row(i, g)); if (k == 0) { removed[i] = 1; continue; } if (k <= ncolors) { /* Valence <= number of colors: can color. Store index of node i for later use */ push(i, nstk, ncnt); /* Remove node i from graph */ i_igremove(i, g); ++nremoved; /* Increase the counter of removed nodes */ } else ++nnodes; /* Can't color: increase number of nodes left in graph */ } } while (nnodes > 0 && nremoved > 0); while (nnodes > 0) { /* Try nodes with high valence/low refcount */ i_local_t target = i_zero; unsigned int mc = 0; #ifndef NDEBUG if (i_debug) { printf("Initial spill loop, #%d (nnodes=%d,dorefcnt=%d)\n", dcnt++, nnodes, i_dorefcnt); } #endif for (i = 0; i < Vnum; i++) { if (removed[i]) continue; j = i + Vbase; /* Find valence of node i */ k = bv_num(row(i, g)); assert(k); if (k <= ncolors) { /* Can color: evidently we removed some of this node's neighbors on a previous iteration of this inner loop */ /* Store index of node i for later use */ push(i, nstk, ncnt); /* Remove node i from graph */ i_igremove(i, g); --nnodes; /* We have removed a node */ if (!nnodes) break; } else if (REFCNT(j) < 2*threshref && k > mc) { mc = k; target = i; } } if (nnodes) if (target != i_zero) { DEBUG(printf("Spilling(1) %d[n=%d,d=%d,k=%d,c=%f,t=%f]\n",\ target, nnodes, deferspill[target], mc, \ REFCNT(target+Vbase), threshref)); i_igspill(target+Vbase, target, g); DEBUG(i_igunparse(g)); nnodes--; } else { /* Found nothing to spill here */ assert(target == i_zero); break; } } DEBUG(dcnt = 0); while (nnodes > 0) { /* Final, aggressive spill loop */ #ifndef NDEBUG if (i_debug) { printf("Final spill loop, #%d (nnodes=%d)\n", dcnt++, nnodes); } #endif for (i = 0; i < Vnum; i++) { if (removed[i]) continue; j = i + Vbase; /* Find valence of node i */ k = bv_num(row(i, g)); assert(k); if (k <= ncolors) { /* Can color: evidently we removed some of this node's neighbors on a previous iteration of this inner loop */ /* Store index of node i for later use */ push(i, nstk, ncnt); /* Remove node i from graph */ i_igremove(i, g); } else if (!i_dorefcnt || deferspill[i] > 1 || REFCNT(j) < 3*threshref) { /* Small refcnt or already deferred: spill. */ DEBUG(printf("Spilling(2) %d[n=%d,d=%d,k=%d,c=%f,t=%f]\n",\ i, nnodes, deferspill[i], k, \ REFCNT(j), threshref)); i_igspill(j, i, g); DEBUG(i_igunparse(g)); } else { /* This node is referenced a lot; try deferring the spill. */ ++deferspill[i]; continue; /* Do not remove this node, yet */ } --nnodes; /* We have removed a node */ } } /* * Rebuild interference graph, coloring nodes */ while (ncnt) { /* Find a new node to add back to the IG */ pop(i, nstk, ncnt); /* Make unavailable the registers of all nodes adjacent to node i in the IG */ bv_eachbit(row(i, g2), i_rmreg, (void *)Vbase); reg = i_reg_get(sc); /* Get an int register */ j = i + Vbase; ADDR(j) = reg; SCLASS(j) = REGISTER; /* Set storage location info */ i_reg_mask(sc); /* Reset the set of available registers */ } i_reg_resetmask(sc); }
/* i_rmreg: make unavailable the registers of all nodes adjacent to node i */ static inline void i_rmreg (i_local_t i, bvt v, void *x) { i_local_t j = i + (i_local_t)x; if (SCLASS(j) == REGISTER && !PARAMP(j)) i_reg_use(i_ty2sc[TYPE(j)], ADDR(j)); }
/* * Find a UNARY op that satisfy the needs. * For now, the destination is always a register. * Both source and dest types must match, but only source (left) * shape is of interest. */ int finduni(NODE *p, int cookie) { extern int *qtable[]; struct optab *q; NODE *l, *r; int i, shl = 0, num = 4; int *ixp, idx = 0; int sh; F2DEBUG(("finduni tree: %s\n", prcook(cookie))); F2WALK(p); l = getlr(p, 'L'); if (p->n_op == CALL || p->n_op == FORTCALL || p->n_op == STCALL) r = p; else r = getlr(p, 'R'); ixp = qtable[p->n_op]; for (i = 0; ixp[i] >= 0; i++) { q = &table[ixp[i]]; F2DEBUG(("finduni: ixp %d\n", ixp[i])); if (!acceptable(q)) /* target-dependent filter */ continue; if (ttype(l->n_type, q->ltype) == 0) continue; /* Type must be correct */ F2DEBUG(("finduni got left type\n")); if (ttype(r->n_type, q->rtype) == 0) continue; /* Type must be correct */ F2DEBUG(("finduni got types\n")); if ((shl = chcheck(l, q->lshape, q->rewrite & RLEFT)) == SRNOPE) continue; F2DEBUG(("finduni got shapes %d\n", shl)); if ((cookie & q->visit) == 0) /* check correct return value */ continue; /* XXX - should check needs */ /* avoid clobbering of longlived regs */ /* let register allocator coalesce */ if ((q->rewrite & RLEFT) && (shl == SRDIR) /* && isreg(l) */) shl = SRREG; F2DEBUG(("finduni got cookie\n")); if (q->needs & REWRITE) break; /* Done here */ if (shl >= num) continue; num = shl; idx = ixp[i]; if (shl == SRDIR) break; } if (num == 4) { F2DEBUG(("finduni failed\n")); } else F2DEBUG(("finduni entry %d(%s)\n", idx, srtyp[num])); if (num == 4) { if (setuni(p, cookie)) return FRETRY; return FFAIL; } q = &table[idx]; sh = shswitch(-1, p->n_left, q->lshape, cookie, q->rewrite & RLEFT, num); if (sh == -1) sh = ffs(cookie & q->visit & INREGS)-1; if (sh == -1) sh = 0; F2DEBUG(("finduni: node %p (%s)\n", p, prcook(1 << sh))); p->n_su = MKIDX(idx, 0); SCLASS(p->n_su, sh); return sh; }
/* * Search for an UMUL table entry that can turn an indirect node into * a move from an OREG. */ int findumul(NODE *p, int cookie) { extern int *qtable[]; struct optab *q = NULL; /* XXX gcc */ int i, shl = 0, shr = 0, sh; int *ixp; F2DEBUG(("findumul p %p (%s)\n", p, prcook(cookie))); F2WALK(p); ixp = qtable[p->n_op]; for (i = 0; ixp[i] >= 0; i++) { q = &table[ixp[i]]; F2DEBUG(("findumul: ixp %d\n", ixp[i])); if (!acceptable(q)) /* target-dependent filter */ continue; if ((q->visit & cookie) == 0) continue; /* wrong registers */ if (ttype(p->n_type, q->rtype) == 0) continue; /* Types must be correct */ F2DEBUG(("findumul got types, rshape %s\n", prcook(q->rshape))); /* * Try to create an OREG of the node. * Fake left even though it's right node, * to be sure of conversion if going down left. */ if ((shl = chcheck(p, q->rshape, 0)) == SRNOPE) continue; shr = 0; if (q->needs & REWRITE) break; /* Done here */ F2DEBUG(("findumul got shape %s\n", srtyp[shl])); break; /* XXX search better matches */ } if (ixp[i] < 0) { F2DEBUG(("findumul failed\n")); if (setuni(p, cookie)) return FRETRY; return FFAIL; } F2DEBUG(("findumul entry %d(%s %s)\n", ixp[i], srtyp[shl], srtyp[shr])); sh = shswitch(-1, p, q->rshape, cookie, q->rewrite & RLEFT, shl); if (sh == -1) sh = ffs(cookie & q->visit & INREGS)-1; F2DEBUG(("findumul: node %p (%s)\n", p, prcook(1 << sh))); p->n_su = MKIDX(ixp[i], 0); SCLASS(p->n_su, sh); return sh; }
/* * Find a matching assign op. * * Level assignment for priority: * left right prio * - - - * direct direct 1 * direct REG 2 * direct OREG 3 * OREG direct 4 * OREG REG 5 * OREG OREG 6 */ int findasg(NODE *p, int cookie) { extern int *qtable[]; struct optab *q; int i, sh, shl, shr, lvl = 10; NODE *l, *r; int *ixp; struct optab *qq = NULL; /* XXX gcc */ int idx = 0, gol = 0, gor = 0; shl = shr = 0; F2DEBUG(("findasg tree: %s\n", prcook(cookie))); F2WALK(p); ixp = qtable[p->n_op]; l = getlr(p, 'L'); r = getlr(p, 'R'); for (i = 0; ixp[i] >= 0; i++) { q = &table[ixp[i]]; F2DEBUG(("findasg: ixp %d\n", ixp[i])); if (!acceptable(q)) /* target-dependent filter */ continue; if (ttype(l->n_type, q->ltype) == 0 || ttype(r->n_type, q->rtype) == 0) continue; /* Types must be correct */ if ((cookie & q->visit) == 0) continue; /* must get a result */ F2DEBUG(("findasg got types\n")); #ifdef mach_pdp11 /* XXX - check for other targets too */ if (p->n_op == STASG && ISPTR(l->n_type)) { /* Accept lvalue to be in register */ /* if struct assignment is given a pointer */ if ((shl = chcheck(l, q->lshape, q->rewrite & RLEFT)) == SRNOPE) continue; } else #endif { if ((shl = tshape(l, q->lshape)) == SRNOPE) continue; if (shl == SRREG) continue; } F2DEBUG(("findasg lshape %d\n", shl)); F2WALK(l); if ((shr = chcheck(r, q->rshape, q->rewrite & RRIGHT)) == SRNOPE) continue; F2DEBUG(("findasg rshape %d\n", shr)); F2WALK(r); if (q->needs & REWRITE) break; /* Done here */ if (lvl <= (shl + shr)) continue; lvl = shl + shr; qq = q; idx = ixp[i]; gol = shl; gor = shr; } if (lvl == 10) { F2DEBUG(("findasg failed\n")); if (setasg(p, cookie)) return FRETRY; return FFAIL; } F2DEBUG(("findasg entry %d(%s,%s)\n", idx, srtyp[gol], srtyp[gor])); sh = -1; sh = shswitch(sh, p->n_left, qq->lshape, cookie, qq->rewrite & RLEFT, gol); sh = shswitch(sh, p->n_right, qq->rshape, cookie, qq->rewrite & RRIGHT, gor); #ifdef mach_pdp11 /* XXX all targets? */ lvl = 0; if (cookie == FOREFF) lvl = RVEFF, sh = 0; else if (cookie == FORCC) lvl = RVCC, sh = 0; else if (sh == -1) { sh = ffs(cookie & qq->visit & INREGS)-1; #ifdef PCC_DEBUG if (sh == -1) comperr("findasg bad shape"); #endif SCLASS(lvl,sh); } else SCLASS(lvl,sh); p->n_su = MKIDX(idx, lvl); #else if (sh == -1) { if (cookie == FOREFF) sh = 0; else sh = ffs(cookie & qq->visit & INREGS)-1; } F2DEBUG(("findasg: node %p class %d\n", p, sh)); p->n_su = MKIDX(idx, 0); SCLASS(p->n_su, sh); #endif /* mach_pdp11 */ #ifdef FINDMOPS p->n_su &= ~ISMOPS; #endif return sh; }
/* * Find the best relation op for matching the two trees it has. * This is a sub-version of the function findops() above. * The instruction with the lowest grading is emitted. * * Level assignment for priority: * left right prio * - - - * direct direct 1 * direct OREG 2 # make oreg * OREG direct 2 # make oreg * OREG OREG 2 # make both oreg * direct REG 3 # put in reg * OREG REG 3 # put in reg, make oreg * REG direct 3 # put in reg * REG OREG 3 # put in reg, make oreg * REG REG 4 # put both in reg */ int relops(NODE *p) { extern int *qtable[]; struct optab *q; int i, shl = 0, shr = 0, sh; NODE *l, *r; int *ixp, idx = 0; int lvl = 10, gol = 0, gor = 0; F2DEBUG(("relops tree:\n")); F2WALK(p); l = getlr(p, 'L'); r = getlr(p, 'R'); ixp = qtable[p->n_op]; for (i = 0; ixp[i] >= 0; i++) { q = &table[ixp[i]]; F2DEBUG(("relops: ixp %d\n", ixp[i])); if (!acceptable(q)) /* target-dependent filter */ continue; if (ttype(l->n_type, q->ltype) == 0 || ttype(r->n_type, q->rtype) == 0) continue; /* Types must be correct */ F2DEBUG(("relops got types\n")); if ((shl = chcheck(l, q->lshape, 0)) == SRNOPE) continue; F2DEBUG(("relops lshape %d\n", shl)); F2WALK(p); if ((shr = chcheck(r, q->rshape, 0)) == SRNOPE) continue; F2DEBUG(("relops rshape %d\n", shr)); F2WALK(p); if (q->needs & REWRITE) break; /* Done here */ if (lvl <= (shl + shr)) continue; lvl = shl + shr; idx = ixp[i]; gol = shl; gor = shr; } if (lvl == 10) { F2DEBUG(("relops failed\n")); if (setbin(p)) return FRETRY; return FFAIL; } F2DEBUG(("relops entry %d(%s %s)\n", idx, srtyp[gol], srtyp[gor])); q = &table[idx]; (void)shswitch(-1, p->n_left, q->lshape, INREGS, q->rewrite & RLEFT, gol); (void)shswitch(-1, p->n_right, q->rshape, INREGS, q->rewrite & RRIGHT, gor); sh = 0; if (q->rewrite & RLEFT) sh = ffs(q->lshape & INREGS)-1; else if (q->rewrite & RRIGHT) sh = ffs(q->rshape & INREGS)-1; F2DEBUG(("relops: node %p\n", p)); p->n_su = MKIDX(idx, 0); SCLASS(p->n_su, sh); return 0; }
/* * Find the best instruction to evaluate the given tree. * Best is to match both subnodes directly, second-best is if * subnodes must be evaluated into OREGs, thereafter if nodes * must be put into registers. * Whether 2-op instructions or 3-op is preferred is depending on in * which order they are found in the table. * mtchno is set to the count of regs needed for its legs. */ int findops(NODE *p, int cookie) { extern int *qtable[]; struct optab *q, *qq = NULL; int i, shl, shr, *ixp, sh; int lvl = 10, idx = 0, gol = 0, gor = 0; NODE *l, *r; F2DEBUG(("findops node %p (%s)\n", p, prcook(cookie))); F2WALK(p); ixp = qtable[p->n_op]; l = getlr(p, 'L'); r = getlr(p, 'R'); for (i = 0; ixp[i] >= 0; i++) { q = &table[ixp[i]]; F2DEBUG(("findop: ixp %d str %s\n", ixp[i], q->cstring)); if (!acceptable(q)) /* target-dependent filter */ continue; if (ttype(l->n_type, q->ltype) == 0 || ttype(r->n_type, q->rtype) == 0) continue; /* Types must be correct */ if ((cookie & q->visit) == 0) continue; /* must get a result */ F2DEBUG(("findop got types\n")); if ((shl = chcheck(l, q->lshape, q->rewrite & RLEFT)) == SRNOPE) continue; F2DEBUG(("findop lshape %s\n", srtyp[shl])); F2WALK(l); if ((shr = chcheck(r, q->rshape, q->rewrite & RRIGHT)) == SRNOPE) continue; F2DEBUG(("findop rshape %s\n", srtyp[shr])); F2WALK(r); /* Help register assignment after SSA by preferring */ /* 2-op insns instead of 3-ops */ if (xssa && (q->rewrite & RLEFT) == 0 && shl == SRDIR) shl = SRREG; if (q->needs & REWRITE) break; /* Done here */ if (lvl <= (shl + shr)) continue; lvl = shl + shr; qq = q; idx = ixp[i]; gol = shl; gor = shr; } if (lvl == 10) { F2DEBUG(("findops failed\n")); if (setbin(p)) return FRETRY; return FFAIL; } F2DEBUG(("findops entry %d(%s,%s)\n", idx, srtyp[gol], srtyp[gor])); sh = -1; #ifdef mach_pdp11 if (cookie == FORCC && p->n_op != AND) /* XXX - fix */ cookie = INREGS; #else if (cookie == FORCC) cookie = INREGS; #endif sh = shswitch(sh, p->n_left, qq->lshape, cookie, qq->rewrite & RLEFT, gol); sh = shswitch(sh, p->n_right, qq->rshape, cookie, qq->rewrite & RRIGHT, gor); if (sh == -1) { if (cookie == FOREFF || cookie == FORCC) sh = 0; else sh = ffs(cookie & qq->visit & INREGS)-1; } F2DEBUG(("findops: node %p sh %d (%s)\n", p, sh, prcook(1 << sh))); p->n_su = MKIDX(idx, 0); SCLASS(p->n_su, sh); return sh; }
/* * Try to find constructs like "a = a + 1;" and match them together * with instructions like "incl a" or "addl $1,a". * * Level assignment for priority: * left right prio * - - - * direct direct 1 * direct REG 2 * direct OREG 3 * OREG direct 4 * OREG REG 5 * OREG OREG 6 */ int findmops(NODE *p, int cookie) { extern int *qtable[]; struct optab *q; int i, sh, shl, shr, lvl = 10; NODE *l, *r; int *ixp; struct optab *qq = NULL; /* XXX gcc */ int idx = 0, gol = 0, gor = 0; shl = shr = 0; F2DEBUG(("findmops tree: %s\n", prcook(cookie))); F2WALK(p); l = getlr(p, 'L'); r = getlr(p, 'R'); /* See if this is a usable tree to work with */ /* Currently only check for leaves */ if (optype(r->n_op) != BITYPE || treecmp(l, r->n_left) == 0) return FFAIL; F2DEBUG(("findmops is useable\n")); /* We can try to find a match. Use right op */ ixp = qtable[r->n_op]; l = getlr(r, 'L'); r = getlr(r, 'R'); for (i = 0; ixp[i] >= 0; i++) { q = &table[ixp[i]]; F2DEBUG(("findmops: ixp %d\n", ixp[i])); if (!acceptable(q)) /* target-dependent filter */ continue; if (ttype(l->n_type, q->ltype) == 0 || ttype(r->n_type, q->rtype) == 0) continue; /* Types must be correct */ F2DEBUG(("findmops got types\n")); switch (cookie) { case FOREFF: if ((q->visit & FOREFF) == 0) continue; /* Not only for side effects */ break; case FORCC: if ((q->visit & FORCC) == 0) continue; /* Not only for side effects */ break; default: if ((cookie & q->visit) == 0) continue; /* Won't match requested shape */ if (((cookie & INREGS & q->lshape) == 0) || !isreg(l)) continue; /* Bad return register */ break; } F2DEBUG(("findmops cookie\n")); /* * left shape must match left node. */ if ((shl = tshape(l, q->lshape)) != SRDIR && (shl != SROREG)) continue; F2DEBUG(("findmops lshape %s\n", srtyp[shl])); F2WALK(l); if ((shr = chcheck(r, q->rshape, 0)) == SRNOPE) continue; F2DEBUG(("findmops rshape %s\n", srtyp[shr])); /* * Only allow RLEFT. XXX */ if ((q->rewrite & (RLEFT|RRIGHT)) != RLEFT) continue; F2DEBUG(("rewrite OK\n")); F2WALK(r); if (q->needs & REWRITE) break; /* Done here */ if (lvl <= (shl + shr)) continue; lvl = shl + shr; qq = q; idx = ixp[i]; gol = shl; gor = shr; } if (lvl == 10) return FFAIL; F2DEBUG(("findmops entry %d(%s,%s)\n", idx, srtyp[gol], srtyp[gor])); /* * Now we're here and have a match. left is semi-direct and * right may be anything. */ sh = -1; sh = shswitch(sh, p->n_left, qq->lshape, cookie, qq->rewrite & RLEFT, gol); sh = shswitch(sh, r, qq->rshape, cookie, 0, gor); if (sh == -1) { if (cookie & (FOREFF|FORCC)) sh = 0; else sh = ffs(cookie & qq->visit & INREGS)-1; } F2DEBUG(("findmops done: node %p class %d\n", p, sh)); /* Trickery: Set table index on assign to op instead */ /* gencode() will remove useless nodes */ p->n_su = MKIDX(idx, 0); p->n_su |= ISMOPS; /* XXX tell gencode to reduce the right tree */ SCLASS(p->n_su, sh); return sh; }