945 lines
20 KiB
C
945 lines
20 KiB
C
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*
|
|
* Author: Hans van Staveren
|
|
*/
|
|
#include <assert.h>
|
|
#include "param.h"
|
|
#include "types.h"
|
|
#include "tes.h"
|
|
#include "line.h"
|
|
#include "lookup.h"
|
|
#include "proinf.h"
|
|
#include "alloc.h"
|
|
#include "reg.h"
|
|
#include "pattern.h"
|
|
#include <em_spec.h>
|
|
#include <em_mnem.h>
|
|
#include "optim.h"
|
|
#include "ext.h"
|
|
|
|
#undef CHK_HASH /* print numbers patterns are hashed to */
|
|
#ifdef CHK_HASH
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#define ILLHASH 0177777
|
|
short pathash[256]; /* table of indices into pattern[] */
|
|
|
|
int opind = 0; /* second index of next matrix */
|
|
byte transl[op_plast - op_pfirst + 1][3] =
|
|
{
|
|
/* LLP */
|
|
{ op_LLP, op_lol, op_ldl },
|
|
/* LEP */
|
|
{ op_LEP, op_loe, op_lde },
|
|
/* SLP */
|
|
{ op_SLP, op_stl, op_sdl },
|
|
/* SEP */
|
|
{ op_SEP, op_ste, op_sde } };
|
|
|
|
/* Forward declarations */
|
|
static int repl_mul(register line_p, line_p *, line_p *);
|
|
static int optimize(void);
|
|
static int basicblock(line_p *);
|
|
|
|
static void opcheck(register byte *bp)
|
|
{
|
|
if (((*bp) & BMASK) >= op_pfirst)
|
|
*bp = transl[((*bp) & BMASK) - op_pfirst][opind];
|
|
}
|
|
|
|
/*
|
|
* The hashing method used is believed to be reasonably efficient.
|
|
* A minor speed improvement could be obtained by keeping a boolean
|
|
* array telling which opcode has any patterns starting with it.
|
|
* Currently only about one third of the opcodes actually have a
|
|
* pattern starting with it, but they are the most common ones.
|
|
* Estimated improvement possible: about 2%
|
|
*/
|
|
|
|
static void hashpatterns(void)
|
|
{
|
|
short index;
|
|
register byte *bp, *tp;
|
|
register short i;
|
|
unsigned short hashvalue;
|
|
byte *save;
|
|
int patlen;
|
|
|
|
if (pointersize == wordsize)
|
|
opind = 1;
|
|
else if (pointersize == 2 * wordsize)
|
|
opind = 2;
|
|
index = lastind; /* set by mktab */
|
|
while (index != 0)
|
|
{
|
|
bp = &pattern[index];
|
|
tp = &bp[PO_MATCH];
|
|
i = *tp++ & BMASK;
|
|
if (i == BMASK)
|
|
{
|
|
i = *tp++ & BMASK;
|
|
i |= (*tp++ & BMASK) << 8;
|
|
}
|
|
save = tp;
|
|
patlen = i;
|
|
while (i--)
|
|
opcheck(tp++);
|
|
if ((*tp++ & BMASK) == BMASK)
|
|
tp += 2;
|
|
i = *tp++ & BMASK;
|
|
if (i == BMASK)
|
|
{
|
|
i = *tp++ & BMASK;
|
|
i |= (*tp++ & BMASK) << 8;
|
|
}
|
|
while (i--)
|
|
{
|
|
opcheck(tp++);
|
|
if ((*tp++ & BMASK) == BMASK)
|
|
tp += 2;
|
|
}
|
|
|
|
/*
|
|
* Now the special opcodes are filled
|
|
* in properly, we can hash the pattern
|
|
*/
|
|
|
|
hashvalue = 0;
|
|
tp = save;
|
|
switch (patlen)
|
|
{
|
|
default: /* 3 or more */
|
|
hashvalue = (hashvalue << 4) ^ (*tp++ & BMASK);
|
|
case 2:
|
|
hashvalue = (hashvalue << 4) ^ (*tp++ & BMASK);
|
|
case 1:
|
|
hashvalue = (hashvalue << 4) ^ (*tp++ & BMASK);
|
|
}
|
|
assert(hashvalue!= ILLHASH);
|
|
i = index;
|
|
index = (bp[PO_NEXT] & BMASK) | (bp[PO_NEXT + 1] << 8);
|
|
bp[PO_HASH] = hashvalue >> 8;
|
|
hashvalue &= BMASK;
|
|
bp[PO_NEXT] = pathash[hashvalue] & BMASK;
|
|
bp[PO_NEXT + 1] = pathash[hashvalue] >> 8;
|
|
pathash[hashvalue] = i;
|
|
#ifdef CHK_HASH
|
|
fprintf(stderr,"%d\n",hashvalue);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int peephole(void)
|
|
{
|
|
static bool phashed = FALSE;
|
|
|
|
if (!phashed)
|
|
{
|
|
hashpatterns();
|
|
phashed = TRUE;
|
|
}
|
|
return optimize();
|
|
}
|
|
|
|
static int optimize(void)
|
|
{
|
|
register num_p *npp, np;
|
|
register int instr;
|
|
bool madeopt;
|
|
|
|
madeopt = basicblock(&instrs);
|
|
for (npp = curpro.numhash; npp < &curpro.numhash[NNUMHASH]; npp++)
|
|
for (np = *npp; np != (num_p) 0; np = np->n_next)
|
|
{
|
|
if (!np->n_line)
|
|
continue;
|
|
if (np->n_line->l_next == (line_p) 0)
|
|
continue;
|
|
instr = np->n_line->l_next->l_instr & BMASK;
|
|
if (instr == op_lab || instr == op_bra)
|
|
np->n_repl = np->n_line->l_next->l_a.la_np;
|
|
else if (basicblock(&np->n_line->l_next))
|
|
madeopt = TRUE;
|
|
}
|
|
return madeopt;
|
|
}
|
|
|
|
static offset oabs(offset off)
|
|
{
|
|
|
|
return (off >= 0 ? off : -off);
|
|
}
|
|
|
|
static line_p repline(eval_t ev, int patlen)
|
|
{
|
|
register line_p lp;
|
|
register iarg_p iap;
|
|
register sym_p sp;
|
|
offset diff, newdiff;
|
|
|
|
assert(ev.e_typ != EV_UNDEF);
|
|
switch (ev.e_typ)
|
|
{
|
|
case EV_CONST:
|
|
if ((short) ev.e_v.e_con == ev.e_v.e_con)
|
|
{
|
|
if (CANMINI((short ) ev.e_v.e_con))
|
|
lp = newline((short) (ev.e_v.e_con) + Z_OPMINI);
|
|
else
|
|
{
|
|
lp = newline(OPSHORT);
|
|
lp->l_a.la_short = (short) ev.e_v.e_con;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lp = newline(OPOFFSET);
|
|
lp->l_a.la_offset = ev.e_v.e_con;
|
|
}
|
|
return (lp);
|
|
case EV_NUMLAB:
|
|
lp = newline(OPNUMLAB);
|
|
lp->l_a.la_np = ev.e_v.e_np;
|
|
return (lp);
|
|
default: /* fragment + offset */
|
|
/*
|
|
* There is a slight problem here, because we have to
|
|
* map fragment+offset to symbol+offset.
|
|
* Fortunately the fragment we have must be the fragment
|
|
* of one of the symbols in the matchpattern.
|
|
* So a short search should do the job.
|
|
*/
|
|
sp = (sym_p) 0;
|
|
for (iap = &iargs[patlen - 1]; iap >= iargs; iap--)
|
|
if (iap->ia_ev.e_typ == ev.e_typ)
|
|
{
|
|
/*
|
|
* Although lint complains, diff is not used
|
|
* before set.
|
|
*
|
|
* The proof is left as an exercise to the
|
|
* reader.
|
|
*/
|
|
newdiff = oabs(iap->ia_sp->s_value - ev.e_v.e_con);
|
|
if (sp == (sym_p) 0 || newdiff < diff)
|
|
{
|
|
sp = iap->ia_sp;
|
|
diff = newdiff;
|
|
}
|
|
}
|
|
assert(sp != (sym_p ) 0);
|
|
if (diff == 0)
|
|
{
|
|
lp = newline(OPSYMBOL);
|
|
lp->l_a.la_sp = sp;
|
|
}
|
|
else
|
|
{
|
|
diff = ev.e_v.e_con - sp->s_value;
|
|
if ((short) diff == diff)
|
|
{
|
|
lp = newline(OPSVAL);
|
|
lp->l_a.la_sval.lasv_short = (short) diff;
|
|
lp->l_a.la_sval.lasv_sp = sp;
|
|
}
|
|
else
|
|
{
|
|
lp = newline(OPLVAL);
|
|
lp->l_a.la_lval.lalv_offset = diff;
|
|
lp->l_a.la_lval.lalv_sp = sp;
|
|
}
|
|
}
|
|
return (lp);
|
|
}
|
|
}
|
|
|
|
static offset rotate(offset w, offset amount)
|
|
{
|
|
offset highmask, lowmask;
|
|
|
|
#ifndef LONGOFF
|
|
assert(wordsize<=4);
|
|
#endif
|
|
highmask = (offset) (-1) << amount;
|
|
lowmask = ~highmask;
|
|
if (wordsize != 4)
|
|
highmask &= wordsize == 2 ? 0xFFFF : 0xFF;
|
|
return (((w << amount) & highmask)
|
|
| ((w >> (8 * wordsize - amount)) & lowmask));
|
|
}
|
|
|
|
eval_t undefres =
|
|
{ EV_UNDEF };
|
|
|
|
static eval_t compute(register expr_p pexp)
|
|
{
|
|
eval_t leaf1, leaf2, res;
|
|
register int i;
|
|
register sym_p sp;
|
|
offset mask;
|
|
|
|
switch (nparam[pexp->ex_operator])
|
|
{
|
|
default:
|
|
assert(FALSE);
|
|
case 2:
|
|
leaf2 = compute(&enodes[pexp->ex_rnode]);
|
|
if (leaf2.e_typ == EV_UNDEF
|
|
|| (nonumlab[pexp->ex_operator] && leaf2.e_typ == EV_NUMLAB)
|
|
|| (onlyconst[pexp->ex_operator] && leaf2.e_typ != EV_CONST))
|
|
return (undefres);
|
|
case 1:
|
|
leaf1 = compute(&enodes[pexp->ex_lnode]);
|
|
if (leaf1.e_typ == EV_UNDEF
|
|
|| (nonumlab[pexp->ex_operator] && leaf1.e_typ == EV_NUMLAB)
|
|
|| (onlyconst[pexp->ex_operator] && leaf1.e_typ != EV_CONST))
|
|
return (undefres);
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
res.e_typ = EV_CONST;
|
|
res.e_v.e_con = 0;
|
|
switch (pexp->ex_operator)
|
|
{
|
|
default:
|
|
assert(FALSE);
|
|
break;
|
|
case EX_CON:
|
|
res.e_v.e_con = (offset) pexp->ex_lnode;
|
|
break;
|
|
case EX_ARG:
|
|
return (iargs[pexp->ex_lnode - 1].ia_ev);
|
|
case EX_CMPEQ:
|
|
if (leaf1.e_typ != leaf2.e_typ)
|
|
return (undefres);
|
|
if (leaf1.e_typ == EV_NUMLAB)
|
|
{
|
|
if (leaf1.e_v.e_np == leaf2.e_v.e_np)
|
|
res.e_v.e_con = 1;
|
|
break;
|
|
}
|
|
if (leaf1.e_v.e_con == leaf2.e_v.e_con)
|
|
res.e_v.e_con = 1;
|
|
break;
|
|
case EX_CMPNE:
|
|
if (leaf1.e_typ != leaf2.e_typ)
|
|
{
|
|
res.e_v.e_con = 1;
|
|
break;
|
|
}
|
|
if (leaf1.e_typ == EV_NUMLAB)
|
|
{
|
|
if (leaf1.e_v.e_np != leaf2.e_v.e_np)
|
|
res.e_v.e_con = 1;
|
|
break;
|
|
}
|
|
if (leaf1.e_v.e_con != leaf2.e_v.e_con)
|
|
res.e_v.e_con = 1;
|
|
break;
|
|
case EX_CMPGT:
|
|
if (leaf1.e_typ != leaf2.e_typ)
|
|
return (undefres);
|
|
res.e_v.e_con = leaf1.e_v.e_con > leaf2.e_v.e_con;
|
|
break;
|
|
case EX_CMPGE:
|
|
if (leaf1.e_typ != leaf2.e_typ)
|
|
return (undefres);
|
|
res.e_v.e_con = leaf1.e_v.e_con >= leaf2.e_v.e_con;
|
|
break;
|
|
case EX_CMPLT:
|
|
if (leaf1.e_typ != leaf2.e_typ)
|
|
return (undefres);
|
|
res.e_v.e_con = leaf1.e_v.e_con < leaf2.e_v.e_con;
|
|
break;
|
|
case EX_CMPLE:
|
|
if (leaf1.e_typ != leaf2.e_typ)
|
|
return (undefres);
|
|
res.e_v.e_con = leaf1.e_v.e_con <= leaf2.e_v.e_con;
|
|
break;
|
|
case EX_OR2:
|
|
if (leaf1.e_v.e_con != 0)
|
|
return (leaf1);
|
|
leaf2 = compute(&enodes[pexp->ex_rnode]);
|
|
if (leaf2.e_typ != EV_CONST)
|
|
return (undefres);
|
|
return (leaf2);
|
|
case EX_AND2:
|
|
if (leaf1.e_v.e_con == 0)
|
|
return (leaf1);
|
|
leaf2 = compute(&enodes[pexp->ex_rnode]);
|
|
if (leaf2.e_typ != EV_CONST)
|
|
return (undefres);
|
|
return (leaf2);
|
|
case EX_OR1:
|
|
res.e_v.e_con = leaf1.e_v.e_con | leaf2.e_v.e_con;
|
|
break;
|
|
case EX_XOR1:
|
|
res.e_v.e_con = leaf1.e_v.e_con ^ leaf2.e_v.e_con;
|
|
break;
|
|
case EX_AND1:
|
|
res.e_v.e_con = leaf1.e_v.e_con & leaf2.e_v.e_con;
|
|
break;
|
|
case EX_TIMES:
|
|
res.e_v.e_con = leaf1.e_v.e_con * leaf2.e_v.e_con;
|
|
break;
|
|
case EX_DIVIDE:
|
|
res.e_v.e_con = leaf1.e_v.e_con / leaf2.e_v.e_con;
|
|
break;
|
|
case EX_MOD:
|
|
res.e_v.e_con = leaf1.e_v.e_con % leaf2.e_v.e_con;
|
|
break;
|
|
case EX_LSHIFT:
|
|
res.e_v.e_con = leaf1.e_v.e_con << leaf2.e_v.e_con;
|
|
break;
|
|
case EX_RSHIFT:
|
|
res.e_v.e_con = leaf1.e_v.e_con >> leaf2.e_v.e_con;
|
|
break;
|
|
case EX_UMINUS:
|
|
res.e_v.e_con = -leaf1.e_v.e_con;
|
|
break;
|
|
case EX_NOT:
|
|
res.e_v.e_con = !leaf1.e_v.e_con;
|
|
break;
|
|
case EX_COMP:
|
|
res.e_v.e_con = ~leaf1.e_v.e_con;
|
|
break;
|
|
case EX_PLUS:
|
|
if (leaf1.e_typ >= EV_FRAG)
|
|
{
|
|
if (leaf2.e_typ >= EV_FRAG)
|
|
return (undefres);
|
|
res.e_typ = leaf1.e_typ;
|
|
}
|
|
else
|
|
res.e_typ = leaf2.e_typ;
|
|
res.e_v.e_con = leaf1.e_v.e_con + leaf2.e_v.e_con;
|
|
break;
|
|
case EX_MINUS:
|
|
if (leaf1.e_typ >= EV_FRAG)
|
|
{
|
|
if (leaf2.e_typ == EV_CONST)
|
|
res.e_typ = leaf1.e_typ;
|
|
else if (leaf2.e_typ != leaf1.e_typ)
|
|
return (undefres);
|
|
}
|
|
else if (leaf2.e_typ >= EV_FRAG)
|
|
return (undefres);
|
|
res.e_v.e_con = leaf1.e_v.e_con - leaf2.e_v.e_con;
|
|
break;
|
|
case EX_POINTERSIZE:
|
|
res.e_v.e_con = pointersize;
|
|
break;
|
|
case EX_WORDSIZE:
|
|
res.e_v.e_con = wordsize;
|
|
break;
|
|
case EX_NOTREG:
|
|
res.e_v.e_con = !inreg(leaf1.e_v.e_con);
|
|
break;
|
|
case EX_DEFINED:
|
|
leaf1 = compute(&enodes[pexp->ex_lnode]);
|
|
res.e_v.e_con = leaf1.e_typ != EV_UNDEF;
|
|
break;
|
|
case EX_SAMESIGN:
|
|
res.e_v.e_con = (leaf1.e_v.e_con ^ leaf2.e_v.e_con) >= 0;
|
|
break;
|
|
case EX_ROM:
|
|
if ((sp = iargs[pexp->ex_lnode - 1].ia_sp) != (sym_p) 0
|
|
&& sp->s_rom != (offset *) 0)
|
|
{
|
|
leaf2 = compute(&enodes[pexp->ex_rnode]);
|
|
if (leaf2.e_typ != EV_CONST || leaf2.e_v.e_con < 0||
|
|
leaf2.e_v.e_con >= MAXROM)
|
|
return (undefres);
|
|
res.e_v.e_con = sp->s_rom[(int) (leaf2.e_v.e_con)];
|
|
break;
|
|
}
|
|
else
|
|
return (undefres);
|
|
case EX_SFIT:
|
|
mask = 0;
|
|
for (i = leaf2.e_v.e_con - 1; i < (int)(8 * sizeof(offset)); i++)
|
|
mask |= ((offset) 1) << i;
|
|
res.e_v.e_con = (leaf1.e_v.e_con & mask) == 0
|
|
|| (leaf1.e_v.e_con & mask) == mask;
|
|
break;
|
|
case EX_UFIT:
|
|
mask = 0;
|
|
for (i = leaf2.e_v.e_con; i < (int)(8 * sizeof(offset)); i++)
|
|
mask |= ((offset) 1) << i;
|
|
res.e_v.e_con = (leaf1.e_v.e_con & mask) == 0;
|
|
break;
|
|
case EX_ROTATE:
|
|
res.e_v.e_con = rotate(leaf1.e_v.e_con, leaf2.e_v.e_con);
|
|
break;
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
#ifdef ALLOWSPECIAL
|
|
extern bool special();
|
|
#endif
|
|
|
|
static bool tryrepl(line_p *lpp, register byte *bp, int patlen)
|
|
{
|
|
int rpllen, instr, rplval;
|
|
register line_p lp;
|
|
line_p replacement, *rlpp, tp;
|
|
|
|
rpllen = *bp++ & BMASK;
|
|
if (rpllen == BMASK)
|
|
{
|
|
rpllen = *bp++ & BMASK;
|
|
rpllen |= (*bp++ & BMASK) << 8;
|
|
}
|
|
#ifdef ALLOWSPECIAL
|
|
if (rpllen == 1 && *bp == 0)
|
|
return(special(lpp,bp+1,patlen));
|
|
#endif
|
|
replacement = (line_p) 0;
|
|
rlpp = &replacement;
|
|
while (rpllen--)
|
|
{
|
|
instr = *bp++ & BMASK;
|
|
rplval = *bp++ & BMASK;
|
|
if (rplval == BMASK)
|
|
{
|
|
rplval = (*bp++ & BMASK);
|
|
rplval |= (*bp++ & BMASK) << 8;
|
|
}
|
|
if (rplval)
|
|
lp = repline(compute(&enodes[rplval]), patlen);
|
|
else
|
|
lp = newline(OPNO);
|
|
|
|
/*
|
|
* One replacement instruction is generated,
|
|
* link in list and proceed with the next one.
|
|
*/
|
|
|
|
if (instr == op_lab)
|
|
lp->l_a.la_np->n_line = lp;
|
|
*rlpp = lp;
|
|
rlpp = &lp->l_next;
|
|
lp->l_instr = instr;
|
|
}
|
|
|
|
/*
|
|
* Replace instructions matched by the created replacement
|
|
*/
|
|
|
|
OPTIM((bp[0]&BMASK)|(bp[1]&BMASK)<<8);
|
|
for (lp = *lpp; patlen > 0; patlen--, tp = lp, lp = lp->l_next)
|
|
;
|
|
tp->l_next = (line_p) 0;
|
|
*rlpp = lp;
|
|
lp = *lpp;
|
|
*lpp = replacement;
|
|
while (lp != (line_p) 0)
|
|
{
|
|
tp = lp->l_next;
|
|
oldline(lp);
|
|
lp = tp;
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
static bool trypat(line_p *lpp, register byte *bp, int len)
|
|
{
|
|
register iarg_p iap;
|
|
int i, patlen;
|
|
register line_p lp;
|
|
eval_t result;
|
|
|
|
patlen = *bp++ & BMASK;
|
|
if (patlen == BMASK)
|
|
{
|
|
patlen = *bp++ & BMASK;
|
|
patlen |= (*bp++ & BMASK) << 8;
|
|
}
|
|
if (len == 3)
|
|
{
|
|
if (patlen < 3)
|
|
return (FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (patlen != len)
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
* Length is ok, now check opcodes
|
|
*/
|
|
|
|
for (i = 0, lp = *lpp; i < patlen && lp != (line_p) 0; i++, lp = lp->l_next)
|
|
if (lp->l_instr != *bp++)
|
|
return (FALSE);
|
|
if (i != patlen)
|
|
return (FALSE);
|
|
|
|
/*
|
|
* opcodes are also correct, now comes the hard part
|
|
*/
|
|
|
|
for (i = 0, lp = *lpp, iap = iargs; i < patlen; i++, iap++, lp = lp->l_next)
|
|
{
|
|
switch (lp->l_optyp)
|
|
{
|
|
case OPNO:
|
|
iap->ia_ev.e_typ = EV_UNDEF;
|
|
break;
|
|
default:
|
|
iap->ia_ev.e_typ = EV_CONST;
|
|
iap->ia_ev.e_v.e_con = (lp->l_optyp & BMASK) - Z_OPMINI;
|
|
break;
|
|
case OPSHORT:
|
|
iap->ia_ev.e_typ = EV_CONST;
|
|
iap->ia_ev.e_v.e_con = lp->l_a.la_short;
|
|
break;
|
|
#ifdef LONGOFF
|
|
case OPOFFSET:
|
|
iap->ia_ev.e_typ = EV_CONST;
|
|
iap->ia_ev.e_v.e_con = lp->l_a.la_offset;
|
|
break;
|
|
#endif
|
|
case OPNUMLAB:
|
|
iap->ia_ev.e_typ = EV_NUMLAB;
|
|
iap->ia_ev.e_v.e_np = lp->l_a.la_np;
|
|
break;
|
|
case OPSYMBOL:
|
|
iap->ia_ev.e_typ = lp->l_a.la_sp->s_frag;
|
|
iap->ia_sp = lp->l_a.la_sp;
|
|
iap->ia_ev.e_v.e_con = lp->l_a.la_sp->s_value;
|
|
break;
|
|
case OPSVAL:
|
|
iap->ia_ev.e_typ = lp->l_a.la_sval.lasv_sp->s_frag;
|
|
iap->ia_sp = lp->l_a.la_sval.lasv_sp;
|
|
iap->ia_ev.e_v.e_con = lp->l_a.la_sval.lasv_sp->s_value
|
|
+ lp->l_a.la_sval.lasv_short;
|
|
break;
|
|
#ifdef LONGOFF
|
|
case OPLVAL:
|
|
iap->ia_ev.e_typ = lp->l_a.la_lval.lalv_sp->s_frag;
|
|
iap->ia_sp = lp->l_a.la_lval.lalv_sp;
|
|
iap->ia_ev.e_v.e_con = lp->l_a.la_lval.lalv_sp->s_value
|
|
+ lp->l_a.la_lval.lalv_offset;
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
i = *bp++ & BMASK;
|
|
if (i == BMASK)
|
|
{
|
|
i = *bp++ & BMASK;
|
|
i |= (*bp++ & BMASK) << 8;
|
|
}
|
|
if (i != 0)
|
|
{
|
|
/* there is a condition */
|
|
result = compute(&enodes[i]);
|
|
if (result.e_typ != EV_CONST || result.e_v.e_con == 0)
|
|
return (FALSE);
|
|
}
|
|
return (tryrepl(lpp, bp, patlen));
|
|
}
|
|
|
|
static int basicblock(line_p *alpp)
|
|
{
|
|
register line_p *lpp, lp;
|
|
unsigned short hash[3];
|
|
line_p *next;
|
|
register byte *bp;
|
|
int i;
|
|
short index;
|
|
bool madeopt;
|
|
int count = 0;
|
|
|
|
lpp = alpp;
|
|
madeopt = FALSE;
|
|
while ((*lpp) != (line_p) 0 && ((*lpp)->l_instr & BMASK) != op_lab)
|
|
{
|
|
lp = *lpp;
|
|
next = &lp->l_next;
|
|
hash[0] = lp->l_instr & BMASK;
|
|
lp = lp->l_next;
|
|
if (lp != (line_p) 0)
|
|
{
|
|
hash[1] = (hash[0] << 4) ^ (lp->l_instr & BMASK);
|
|
lp = lp->l_next;
|
|
if (lp != (line_p) 0)
|
|
hash[2] = (hash[1] << 4) ^ (lp->l_instr & BMASK);
|
|
else
|
|
hash[2] = ILLHASH;
|
|
}
|
|
else
|
|
{
|
|
hash[1] = ILLHASH;
|
|
hash[2] = ILLHASH;
|
|
}
|
|
|
|
/*
|
|
* hashvalues computed. Try for longest pattern first
|
|
*/
|
|
|
|
for (i = 2; i >= 0; i--)
|
|
{
|
|
index = pathash[hash[i] & BMASK];
|
|
while (index != 0)
|
|
{
|
|
bp = &pattern[index];
|
|
if ((bp[PO_HASH] & BMASK) == (hash[i] >> 8))
|
|
if (trypat(lpp, &bp[PO_MATCH], i + 1))
|
|
{
|
|
madeopt = TRUE;
|
|
next = lpp;
|
|
i = 0; /* dirty way of double break */
|
|
break;
|
|
}
|
|
index = (bp[PO_NEXT] & BMASK) | (bp[PO_NEXT + 1] << 8);
|
|
}
|
|
}
|
|
if (lpp == next)
|
|
{
|
|
count++;
|
|
if (count > 1000)
|
|
{
|
|
/* probably loop in table */
|
|
fprintf(stderr,
|
|
"Warning: possible loop in patterns; call an expert\n");
|
|
next = &((*lpp)->l_next);
|
|
count = 0;
|
|
}
|
|
}
|
|
else
|
|
count = 0;
|
|
lpp = next;
|
|
}
|
|
lpp = alpp;
|
|
if (repl_muls)
|
|
{
|
|
while ((lp = *lpp) != (line_p) 0 && (lp->l_instr & BMASK) != op_lab)
|
|
{
|
|
line_p b_repl, e_repl;
|
|
int cnt;
|
|
|
|
if ((cnt = (lp->l_instr & BMASK)) != op_loc && cnt != op_ldc)
|
|
{
|
|
lpp = &lp->l_next;
|
|
continue;
|
|
}
|
|
|
|
cnt = repl_mul(lp, &b_repl, &e_repl);
|
|
|
|
lp = *lpp;
|
|
if (cnt > 0 && cnt <= repl_muls)
|
|
{
|
|
*lpp = b_repl;
|
|
e_repl->l_next = lp->l_next->l_next;
|
|
oldline(lp->l_next);
|
|
oldline(lp);
|
|
lpp = &e_repl->l_next;
|
|
madeopt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
while (b_repl != (line_p) 0)
|
|
{
|
|
line_p n = b_repl->l_next;
|
|
|
|
oldline(b_repl);
|
|
b_repl = n;
|
|
}
|
|
lpp = &lp->l_next;
|
|
}
|
|
}
|
|
}
|
|
return madeopt;
|
|
}
|
|
|
|
static int repl_mul(register line_p lp, line_p *b, line_p *e)
|
|
{
|
|
register line_p next = lp->l_next;
|
|
int ins;
|
|
int sz;
|
|
unsigned long n;
|
|
int n0, n1;
|
|
int virgin = 1;
|
|
int retval = 0;
|
|
|
|
*b = 0;
|
|
if (!next)
|
|
return 0;
|
|
if ((ins = (next->l_instr & BMASK)) != op_mli && ins != op_mlu)
|
|
{
|
|
return 0;
|
|
}
|
|
switch (next->l_optyp)
|
|
{
|
|
case OPNO:
|
|
return 0;
|
|
case OPSHORT:
|
|
sz = next->l_a.la_short;
|
|
break;
|
|
#ifdef LONGOFF
|
|
case OPOFFSET:
|
|
sz = next->l_a.la_offset;
|
|
break;
|
|
#endif
|
|
default:
|
|
sz = (next->l_optyp & BMASK) - Z_OPMINI;
|
|
break;
|
|
}
|
|
if (ins == op_loc && sz != wordsize)
|
|
return 0;
|
|
if (ins == op_ldc && sz != 2 * wordsize)
|
|
return 0;
|
|
if (!repl_longmuls && sz != wordsize)
|
|
return 0;
|
|
switch (lp->l_optyp)
|
|
{
|
|
case OPSHORT:
|
|
n = (long) lp->l_a.la_short;
|
|
break;
|
|
#ifdef LONGOFF
|
|
case OPOFFSET:
|
|
n = lp->l_a.la_offset;
|
|
break;
|
|
#endif
|
|
default:
|
|
n = (long) ((lp->l_optyp & BMASK) - Z_OPMINI);
|
|
break;
|
|
}
|
|
|
|
#define newinstr(res, opcode, val) (*(res) = newline((short)(val)+Z_OPMINI), (*(res))->l_instr = (opcode))
|
|
|
|
while (n)
|
|
{
|
|
/* first find "0*1*$" in n */
|
|
for (n1 = 0; n & 1; n >>= 1)
|
|
++n1; /* count "1" bits */
|
|
if (n)
|
|
for (n0 = 0; !(n & 1); n >>= 1) /* count "0" bits */
|
|
++n0;
|
|
else
|
|
n0 = 0;
|
|
|
|
if (n1 == 0)
|
|
{
|
|
if (n0)
|
|
{
|
|
newinstr(b, op_loc, n0);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_slu, sz);
|
|
b = &((*b)->l_next);
|
|
retval++;
|
|
}
|
|
}
|
|
else if (n1 == 1)
|
|
{
|
|
if (virgin)
|
|
{
|
|
newinstr(b, op_dup, sz);
|
|
b = &((*b)->l_next);
|
|
virgin = 0;
|
|
}
|
|
else
|
|
{
|
|
newinstr(b, op_exg, sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_dup, 2 * sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_asp, sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_adu, sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_exg, sz);
|
|
b = &((*b)->l_next);
|
|
retval++;
|
|
}
|
|
if (n)
|
|
{
|
|
newinstr(b, op_loc, n0 + n1);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_slu, sz);
|
|
b = &((*b)->l_next);
|
|
retval++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (virgin)
|
|
{
|
|
newinstr(b, op_dup, sz);
|
|
b = &((*b)->l_next);
|
|
if (sz == wordsize)
|
|
{
|
|
newinstr(b, op_loc, 0);
|
|
b = &((*b)->l_next);
|
|
}
|
|
else
|
|
{
|
|
newinstr(b, op_ldc, 0);
|
|
b = &((*b)->l_next);
|
|
}
|
|
newinstr(b, op_exg, sz);
|
|
b = &((*b)->l_next);
|
|
virgin = 0;
|
|
}
|
|
else
|
|
{
|
|
newinstr(b, op_exg, sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_dup, 2 * sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_asp, sz);
|
|
b = &((*b)->l_next);
|
|
}
|
|
newinstr(b, op_sbu, sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_exg, sz);
|
|
b = &((*b)->l_next);
|
|
retval++;
|
|
if (n1 != 8 * sz)
|
|
{
|
|
newinstr(b, op_loc, n1);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_slu, sz);
|
|
b = &((*b)->l_next);
|
|
retval++;
|
|
newinstr(b, op_exg, sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_dup, 2 * sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_asp, sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_adu, sz);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_exg, sz);
|
|
b = &((*b)->l_next);
|
|
retval++;
|
|
}
|
|
if (n0)
|
|
{
|
|
newinstr(b, op_loc, n0);
|
|
b = &((*b)->l_next);
|
|
newinstr(b, op_slu, sz);
|
|
b = &((*b)->l_next);
|
|
retval++;
|
|
}
|
|
}
|
|
}
|
|
newinstr(b, op_asp, sz);
|
|
if (virgin)
|
|
{
|
|
b = &((*b)->l_next);
|
|
newinstr(b, sz == wordsize ? op_loc : op_ldc, 0);
|
|
}
|
|
*e = *b;
|
|
return retval == 0 ? 1 : retval;
|
|
#undef newinstr
|
|
}
|