948 lines
19 KiB
C
948 lines
19 KiB
C
#include "l.h"
|
|
|
|
Prog *divuconst(Prog *, uvlong, int, int, int);
|
|
Prog *divconst(Prog *, vlong, int, int, int);
|
|
Prog *modconst(Prog *, vlong, int, int, int);
|
|
void excise(Prog *);
|
|
|
|
void
|
|
noops(void)
|
|
{
|
|
Prog *p, *p1, *q, *q1, *q2;
|
|
int o, curframe, curbecome, maxbecome, shift;
|
|
|
|
/*
|
|
* find leaf subroutines
|
|
* become sizes
|
|
* frame sizes
|
|
* strip NOPs
|
|
* expand RET and other macros
|
|
* expand BECOME pseudo
|
|
* use conditional moves where appropriate
|
|
*/
|
|
|
|
if(debug['v'])
|
|
Bprint(&bso, "%5.2f noops\n", cputime());
|
|
Bflush(&bso);
|
|
|
|
curframe = 0;
|
|
curbecome = 0;
|
|
maxbecome = 0;
|
|
curtext = 0;
|
|
|
|
q = P;
|
|
for(p = firstp; p != P; p = p->link) {
|
|
|
|
/* find out how much arg space is used in this TEXT */
|
|
if(p->to.type == D_OREG && p->to.reg == REGSP)
|
|
if(p->to.offset > curframe)
|
|
curframe = p->to.offset;
|
|
|
|
switch(p->as) {
|
|
case ATEXT:
|
|
if(curtext && curtext->from.sym) {
|
|
curtext->from.sym->frame = curframe;
|
|
curtext->from.sym->become = curbecome;
|
|
if(curbecome > maxbecome)
|
|
maxbecome = curbecome;
|
|
}
|
|
curframe = 0;
|
|
curbecome = 0;
|
|
|
|
p->mark |= LABEL|LEAF|SYNC;
|
|
if(p->link)
|
|
p->link->mark |= LABEL;
|
|
curtext = p;
|
|
break;
|
|
|
|
/* don't mess with what we don't understand */
|
|
case AWORD:
|
|
case ACALL_PAL:
|
|
/* etc. */
|
|
p->mark |= LABEL;
|
|
for(q1=p->link; q1 != P; q1 = q1->link) {
|
|
q1->mark |= LABEL;
|
|
if(q1->as != AXORNOT) /* used as NOP in PALcode */
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ARET:
|
|
/* special form of RET is BECOME */
|
|
if(p->from.type == D_CONST)
|
|
if(p->from.offset > curbecome)
|
|
curbecome = p->from.offset;
|
|
|
|
if(p->link != P)
|
|
p->link->mark |= LABEL;
|
|
break;
|
|
|
|
case ANOP:
|
|
q1 = p->link;
|
|
q->link = q1; /* q is non-nop */
|
|
q1->mark |= p->mark;
|
|
continue;
|
|
|
|
case AJSR:
|
|
if(curtext != P)
|
|
curtext->mark &= ~LEAF;
|
|
case ABEQ:
|
|
case ABNE:
|
|
case ABGE:
|
|
case ABGT:
|
|
case ABLE:
|
|
case ABLT:
|
|
case ABLBC:
|
|
case ABLBS:
|
|
case AFBEQ:
|
|
case AFBNE:
|
|
case AFBGE:
|
|
case AFBGT:
|
|
case AFBLE:
|
|
case AFBLT:
|
|
case AJMP:
|
|
p->mark |= BRANCH;
|
|
q1 = p->cond;
|
|
if(q1 != P) {
|
|
while(q1->as == ANOP) {
|
|
q1 = q1->link;
|
|
p->cond = q1;
|
|
}
|
|
if(!(q1->mark & LEAF)) {
|
|
if (q1->mark & LABEL)
|
|
q1->mark |= LABEL2;
|
|
else
|
|
q1->mark |= LABEL;
|
|
}
|
|
} else
|
|
p->mark |= LABEL;
|
|
q1 = p->link;
|
|
if(q1 != P) {
|
|
if (q1->mark & LABEL)
|
|
q1->mark |= LABEL2;
|
|
else
|
|
q1->mark |= LABEL;
|
|
}
|
|
else
|
|
p->mark |= LABEL; /* ??? */
|
|
break;
|
|
|
|
case ADIVQ:
|
|
case ADIVQU:
|
|
case AMODQ:
|
|
case AMODQU:
|
|
case ADIVL:
|
|
case ADIVLU:
|
|
case AMODL:
|
|
case AMODLU:
|
|
if(p->from.type == D_CONST /*&& !debug['d']*/)
|
|
continue;
|
|
if(prog_divq == P)
|
|
initdiv();
|
|
if(curtext != P)
|
|
curtext->mark &= ~LEAF;
|
|
break;
|
|
}
|
|
q = p;
|
|
}
|
|
|
|
if(curtext && curtext->from.sym) {
|
|
curtext->from.sym->frame = curframe;
|
|
curtext->from.sym->become = curbecome;
|
|
if(curbecome > maxbecome)
|
|
maxbecome = curbecome;
|
|
}
|
|
|
|
if(debug['b'])
|
|
print("max become = %d\n", maxbecome);
|
|
xdefine("ALEFbecome", STEXT, maxbecome);
|
|
|
|
curtext = 0;
|
|
for(p = firstp; p != P; p = p->link) {
|
|
switch(p->as) {
|
|
case ATEXT:
|
|
curtext = p;
|
|
break;
|
|
case AJSR:
|
|
if(curtext != P && curtext->from.sym != S && curtext->to.offset >= 0) {
|
|
o = maxbecome - curtext->from.sym->frame;
|
|
if(o <= 0)
|
|
break;
|
|
/* calling a become or calling a variable */
|
|
if(p->to.sym == S || p->to.sym->become) {
|
|
curtext->to.offset += o;
|
|
if(debug['b']) {
|
|
curp = p;
|
|
print("%D calling %D increase %d\n",
|
|
&curtext->from, &p->to, o);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(p = firstp; p != P; p = p->link) {
|
|
o = p->as;
|
|
switch(o) {
|
|
case ATEXT:
|
|
curtext = p;
|
|
autosize = p->to.offset + 8;
|
|
if(autosize <= 8)
|
|
if(curtext->mark & LEAF) {
|
|
p->to.offset = -8;
|
|
autosize = 0;
|
|
}
|
|
if (autosize & 4)
|
|
autosize += 4;
|
|
|
|
q = p;
|
|
if(autosize)
|
|
q = genIRR(p, ASUBQ, autosize, NREG, REGSP);
|
|
else if(!(curtext->mark & LEAF)) {
|
|
if(debug['v'])
|
|
Bprint(&bso, "save suppressed in: %s\n",
|
|
curtext->from.sym->name);
|
|
Bflush(&bso);
|
|
curtext->mark |= LEAF;
|
|
}
|
|
|
|
if(curtext->mark & LEAF) {
|
|
if(curtext->from.sym)
|
|
curtext->from.sym->type = SLEAF;
|
|
break;
|
|
}
|
|
|
|
genstore(q, AMOVL, REGLINK, 0LL, REGSP);
|
|
break;
|
|
|
|
case ARET:
|
|
nocache(p);
|
|
if(p->from.type == D_CONST)
|
|
goto become;
|
|
if(curtext->mark & LEAF) {
|
|
if(!autosize) {
|
|
p->as = AJMP;
|
|
p->from = zprg.from;
|
|
p->to.type = D_OREG;
|
|
p->to.offset = 0;
|
|
p->to.reg = REGLINK;
|
|
break;
|
|
}
|
|
|
|
p->as = AADDQ;
|
|
p->from.type = D_CONST;
|
|
p->from.offset = autosize;
|
|
p->to.type = D_REG;
|
|
p->to.reg = REGSP;
|
|
|
|
q = prg();
|
|
q->as = AJMP;
|
|
q->line = p->line;
|
|
q->to.type = D_OREG;
|
|
q->to.offset = 0;
|
|
q->to.reg = REGLINK;
|
|
q->mark |= BRANCH;
|
|
|
|
q->link = p->link;
|
|
p->link = q;
|
|
break;
|
|
}
|
|
p->as = AMOVL;
|
|
p->from.type = D_OREG;
|
|
p->from.offset = 0;
|
|
p->from.reg = REGSP;
|
|
p->to.type = D_REG;
|
|
p->to.reg = REGLINK;
|
|
|
|
q = p;
|
|
if(autosize)
|
|
q = genIRR(p, AADDQ, autosize, NREG, REGSP);
|
|
|
|
q1 = prg();
|
|
q1->as = AJMP;
|
|
q1->line = p->line;
|
|
q1->to.type = D_OREG;
|
|
q1->to.offset = 0;
|
|
q1->to.reg = REGLINK;
|
|
q1->mark |= BRANCH;
|
|
|
|
q1->link = q->link;
|
|
q->link = q1;
|
|
break;
|
|
|
|
become:
|
|
if(curtext->mark & LEAF) {
|
|
|
|
q = prg();
|
|
q->line = p->line;
|
|
q->as = AJMP;
|
|
q->from = zprg.from;
|
|
q->to = p->to;
|
|
q->cond = p->cond;
|
|
q->link = p->link;
|
|
q->mark |= BRANCH;
|
|
p->link = q;
|
|
|
|
p->as = AADDQ;
|
|
p->from = zprg.from;
|
|
p->from.type = D_CONST;
|
|
p->from.offset = autosize;
|
|
p->to = zprg.to;
|
|
p->to.type = D_REG;
|
|
p->to.reg = REGSP;
|
|
|
|
break;
|
|
}
|
|
q = prg();
|
|
q->line = p->line;
|
|
q->as = AJMP;
|
|
q->from = zprg.from;
|
|
q->to = p->to;
|
|
q->cond = p->cond;
|
|
q->link = p->link;
|
|
q->mark |= BRANCH;
|
|
p->link = q;
|
|
|
|
q = genIRR(p, AADDQ, autosize, NREG, REGSP);
|
|
|
|
p->as = AMOVL;
|
|
p->from = zprg.from;
|
|
p->from.type = D_OREG;
|
|
p->from.offset = 0;
|
|
p->from.reg = REGSP;
|
|
p->to = zprg.to;
|
|
p->to.type = D_REG;
|
|
p->to.reg = REGLINK;
|
|
|
|
break;
|
|
|
|
|
|
/* All I wanted was a MOVB... */
|
|
case AMOVB:
|
|
case AMOVW:
|
|
/* rewrite sign extend; could use v3 extension in asmout case 1 */
|
|
if (p->to.type == D_REG) {
|
|
nocache(p);
|
|
shift = (p->as == AMOVB) ? (64-8) : (64-16);
|
|
if (p->from.type == D_REG) {
|
|
p->as = ASLLQ;
|
|
p->reg = p->from.reg;
|
|
p->from.type = D_CONST;
|
|
p->from.offset = shift;
|
|
q = genIRR(p, ASRAQ, shift, p->to.reg, p->to.reg);
|
|
break;
|
|
}
|
|
else {
|
|
p->as = (p->as == AMOVB) ? AMOVBU : AMOVWU;
|
|
q = genIRR(p, ASLLQ, shift, p->to.reg, p->to.reg);
|
|
q = genIRR(q, ASRAQ, shift, p->to.reg, p->to.reg);
|
|
}
|
|
}
|
|
/* fall through... */
|
|
case AMOVBU:
|
|
case AMOVWU:
|
|
if(!debug['x'])
|
|
break; /* use BWX extension */
|
|
o = p->as;
|
|
nocache(p);
|
|
if (p->from.type == D_OREG) {
|
|
if (p->to.type != D_REG)
|
|
break;
|
|
p->as = AMOVQU;
|
|
q = genXXX(p, AEXTBL, &p->to, REGTMP2, &p->to);
|
|
if (o == AMOVW || o == AMOVWU)
|
|
q->as = AEXTWL;
|
|
p->to.reg = REGTMP2;
|
|
if ((p->from.offset & 7) != 0 || aclass(&p->from) != C_SOREG) {
|
|
q1 = genXXX(p, AMOVA, &p->from, NREG, &q->to);
|
|
q1->from.offset &= 7;
|
|
q->from = q->to;
|
|
}
|
|
else
|
|
q->from.reg = p->from.reg;
|
|
if (o == AMOVB || o == AMOVW)
|
|
genXXX(q, o, &q->to, NREG, &q->to);
|
|
}
|
|
else if (p->to.type == D_OREG) {
|
|
if (aclass(&p->from) == C_ZCON) {
|
|
p->from.type = D_REG;
|
|
p->from.reg = REGZERO;
|
|
}
|
|
else if (p->from.type != D_REG)
|
|
break;
|
|
p->as = AMOVQU;
|
|
q = genRRR(p, AMSKBL, p->to.reg, REGTMP2, REGTMP2);
|
|
q1 = genRRR(q, AINSBL, p->to.reg, p->from.reg, REGTMP);
|
|
if (o == AMOVW || o == AMOVWU) {
|
|
q->as = AMSKWL;
|
|
q1->as = AINSWL;
|
|
}
|
|
q2 = genXXX(q1, AOR, &q->to, REGTMP, &q->to);
|
|
genXXX(q2, AMOVQU, &q->to, NREG, &p->to);
|
|
p->from = p->to;
|
|
p->to = q->to;
|
|
if ((p->from.offset & 7) != 0 || aclass(&p->from) != C_SOREG) {
|
|
q->from.reg = REGTMP;
|
|
q1->from.reg = REGTMP;
|
|
q = genXXX(p, AMOVA, &p->from, NREG, &q->from);
|
|
q->from.offset &= 7;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ASLLL:
|
|
p->as = ASLLQ;
|
|
p = genXXX(p, AADDL, &p->to, REGZERO, &p->to);
|
|
break;
|
|
|
|
case ASRLL:
|
|
if (p->to.type != D_REG) {
|
|
diag("illegal dest type in %P", p);
|
|
break;
|
|
}
|
|
if (p->reg == NREG)
|
|
p->reg = p->to.reg;
|
|
|
|
q = genXXX(p, ASRLQ, &p->from, REGTMP, &p->to);
|
|
|
|
p->as = AZAP;
|
|
p->from.type = D_CONST;
|
|
p->from.offset = 0xf0;
|
|
p->to.reg = REGTMP;
|
|
p = q;
|
|
|
|
p = genXXX(p, AADDL, &p->to, REGZERO, &p->to);
|
|
break;
|
|
|
|
case ASRAL:
|
|
p->as = ASRAQ;
|
|
break;
|
|
|
|
case ADIVQ:
|
|
case ADIVQU:
|
|
case AMODQ:
|
|
case AMODQU:
|
|
case ADIVL:
|
|
case ADIVLU:
|
|
case AMODL:
|
|
case AMODLU:
|
|
/* if (debug['d'])
|
|
print("%P\n", p); */
|
|
if(p->to.type != D_REG)
|
|
break;
|
|
/*if(debug['d'] && p->from.type == D_CONST) {
|
|
q = genRRR(p, p->as, REGTMP, p->reg, p->to.reg);
|
|
p->as = AMOVQ;
|
|
p->reg = NREG;
|
|
p->to.reg = REGTMP;
|
|
p = q;
|
|
}*/
|
|
if(p->from.type == D_CONST) {
|
|
if (p->reg == NREG)
|
|
p->reg = p->to.reg;
|
|
switch (p->as) {
|
|
case ADIVQ:
|
|
q = divconst(p, p->from.offset, p->reg, p->to.reg, 64);
|
|
break;
|
|
case ADIVQU:
|
|
q = divuconst(p, p->from.offset, p->reg, p->to.reg, 64);
|
|
break;
|
|
case AMODQ:
|
|
q = modconst(p, p->from.offset, p->reg, p->to.reg, 64);
|
|
break;
|
|
case AMODQU:
|
|
q = divuconst(p, p->from.offset, p->reg, REGTMP2, 64);
|
|
q = genIRR(q, AMULQ, p->from.offset, REGTMP2, REGTMP2);
|
|
q = genRRR(q, ASUBQ, REGTMP2, p->reg, p->to.reg);
|
|
break;
|
|
case ADIVL:
|
|
q = divconst(p, p->from.offset, p->reg, p->to.reg, 32);
|
|
break;
|
|
case ADIVLU:
|
|
q = divuconst(p, p->from.offset, p->reg, p->to.reg, 32);
|
|
break;
|
|
case AMODL:
|
|
q = modconst(p, p->from.offset, p->reg, p->to.reg, 32);
|
|
break;
|
|
case AMODLU:
|
|
q = divuconst(p, p->from.offset, p->reg, REGTMP2, 32);
|
|
q = genIRR(q, AMULQ, p->from.offset, REGTMP2, REGTMP2);
|
|
q = genRRR(q, ASUBQ, REGTMP2, p->reg, p->to.reg);
|
|
break;
|
|
}
|
|
excise(p);
|
|
p = q;
|
|
break;
|
|
}
|
|
if(p->from.type != D_REG){
|
|
diag("bad instruction %P", p);
|
|
break;
|
|
}
|
|
o = p->as;
|
|
q = genIRR(p, ASUBQ, 16LL, NREG, REGSP);
|
|
q = genstore(q, AMOVQ, p->from.reg, 8LL, REGSP);
|
|
if (o == ADIVL || o == ADIVL || o == AMODL || o == AMODLU)
|
|
q->as = AMOVL;
|
|
|
|
q = genRRR(q, AMOVQ, p->reg, NREG, REGTMP);
|
|
if (p->reg == NREG)
|
|
q->from.reg = p->to.reg;
|
|
|
|
/* CALL appropriate */
|
|
q1 = prg();
|
|
q1->link = q->link;
|
|
q->link = q1;
|
|
|
|
q1->as = AJSR;
|
|
q1->line = p->line;
|
|
q1->to.type = D_BRANCH;
|
|
q1->cond = divsubr(o);
|
|
q1->mark |= BRANCH;
|
|
q = q1;
|
|
|
|
q = genRRR(q, AMOVQ, REGTMP, NREG, p->to.reg);
|
|
q = genIRR(q, AADDQ, 16LL, NREG, REGSP);
|
|
excise(p);
|
|
p = q;
|
|
break;
|
|
|
|
/* Attempt to replace {cond. branch, mov} with a cmov */
|
|
/* XXX warning: this is all a bit experimental */
|
|
case ABEQ:
|
|
case ABNE:
|
|
case ABGE:
|
|
case ABGT:
|
|
case ABLE:
|
|
case ABLT:
|
|
case ABLBC:
|
|
case ABLBS:
|
|
q = p->link;
|
|
if (q == P)
|
|
break;
|
|
q1 = q->link;
|
|
if (q1 != p->cond || q1 == P)
|
|
break;
|
|
/*print("%P\n", q); /* */
|
|
if (q->to.type != D_REG)
|
|
break;
|
|
if (q->from.type != D_REG && (q->from.type != D_CONST || q->from.name != D_NONE))
|
|
break;
|
|
if (q->mark&LABEL2)
|
|
break;
|
|
/* print("%P\n", q); /* */
|
|
if (q->as != AMOVQ) /* XXX can handle more than this! */
|
|
break;
|
|
q->as = (p->as^1) + ACMOVEQ-ABEQ; /* sleazy hack */
|
|
q->reg = p->from.reg; /* XXX check CMOVx operand order! */
|
|
excise(p); /* XXX p's LABEL? */
|
|
if (!(q1->mark&LABEL2))
|
|
q1->mark &= ~LABEL;
|
|
break;
|
|
case AFBEQ:
|
|
case AFBNE:
|
|
case AFBGE:
|
|
case AFBGT:
|
|
case AFBLE:
|
|
case AFBLT:
|
|
q = p->link;
|
|
if (q == P)
|
|
break;
|
|
q1 = q->link;
|
|
if (q1 != p->cond || q1 == P)
|
|
break;
|
|
if (q->from.type != D_FREG || q->to.type != D_FREG)
|
|
break;
|
|
/* print("%P\n", q); /* */
|
|
if (q->mark&LABEL2)
|
|
break;
|
|
if (q->as != AMOVT) /* XXX can handle more than this! */
|
|
break;
|
|
q->as = (p->as^1) + AFCMOVEQ-AFBEQ; /* sleazy hack */
|
|
q->reg = p->from.reg; /* XXX check CMOVx operand order! */
|
|
excise(p); /* XXX p's LABEL? */
|
|
if (!(q1->mark&LABEL2))
|
|
q1->mark &= ~LABEL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
curtext = P;
|
|
q = P; /* p - 1 */
|
|
q1 = firstp; /* top of block */
|
|
o = 0; /* count of instructions */
|
|
for(p = firstp; p != P; p = p1) {
|
|
p1 = p->link;
|
|
o++;
|
|
if(p->mark & NOSCHED){
|
|
if(q1 != p){
|
|
sched(q1, q);
|
|
}
|
|
for(; p != P; p = p->link){
|
|
if(!(p->mark & NOSCHED))
|
|
break;
|
|
q = p;
|
|
}
|
|
p1 = p;
|
|
q1 = p;
|
|
o = 0;
|
|
continue;
|
|
}
|
|
if(p->mark & (LABEL|SYNC)) {
|
|
if(q1 != p)
|
|
sched(q1, q);
|
|
q1 = p;
|
|
o = 1;
|
|
}
|
|
if(p->mark & (BRANCH|SYNC)) {
|
|
sched(q1, p);
|
|
q1 = p1;
|
|
o = 0;
|
|
}
|
|
if(o >= NSCHED) {
|
|
sched(q1, p);
|
|
q1 = p1;
|
|
o = 0;
|
|
}
|
|
q = p;
|
|
}
|
|
}
|
|
|
|
void
|
|
nocache(Prog *p)
|
|
{
|
|
p->optab = 0;
|
|
p->from.class = 0;
|
|
p->to.class = 0;
|
|
}
|
|
|
|
/* XXX use of this may lose important LABEL flags, check that this isn't happening (or fix) */
|
|
void
|
|
excise(Prog *p)
|
|
{
|
|
Prog *q;
|
|
|
|
q = p->link;
|
|
*p = *q;
|
|
}
|
|
|
|
void
|
|
initdiv(void)
|
|
{
|
|
Sym *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8;
|
|
Prog *p;
|
|
|
|
s1 = lookup("_divq", 0);
|
|
s2 = lookup("_divqu", 0);
|
|
s3 = lookup("_modq", 0);
|
|
s4 = lookup("_modqu", 0);
|
|
s5 = lookup("_divl", 0);
|
|
s6 = lookup("_divlu", 0);
|
|
s7 = lookup("_modl", 0);
|
|
s8 = lookup("_modlu", 0);
|
|
for(p = firstp; p != P; p = p->link)
|
|
if(p->as == ATEXT) {
|
|
if(p->from.sym == s1)
|
|
prog_divq = p;
|
|
if(p->from.sym == s2)
|
|
prog_divqu = p;
|
|
if(p->from.sym == s3)
|
|
prog_modq = p;
|
|
if(p->from.sym == s4)
|
|
prog_modqu = p;
|
|
if(p->from.sym == s5)
|
|
prog_divl = p;
|
|
if(p->from.sym == s6)
|
|
prog_divlu = p;
|
|
if(p->from.sym == s7)
|
|
prog_modl = p;
|
|
if(p->from.sym == s8)
|
|
prog_modlu = p;
|
|
}
|
|
if(prog_divq == P) {
|
|
diag("undefined: %s", s1->name);
|
|
prog_divq = curtext;
|
|
}
|
|
if(prog_divqu == P) {
|
|
diag("undefined: %s", s2->name);
|
|
prog_divqu = curtext;
|
|
}
|
|
if(prog_modq == P) {
|
|
diag("undefined: %s", s3->name);
|
|
prog_modq = curtext;
|
|
}
|
|
if(prog_modqu == P) {
|
|
diag("undefined: %s", s4->name);
|
|
prog_modqu = curtext;
|
|
}
|
|
if(prog_divl == P) {
|
|
diag("undefined: %s", s5->name);
|
|
prog_divl = curtext;
|
|
}
|
|
if(prog_divlu == P) {
|
|
diag("undefined: %s", s6->name);
|
|
prog_divlu = curtext;
|
|
}
|
|
if(prog_modl == P) {
|
|
diag("undefined: %s", s7->name);
|
|
prog_modl = curtext;
|
|
}
|
|
if(prog_modlu == P) {
|
|
diag("undefined: %s", s8->name);
|
|
prog_modlu = curtext;
|
|
}
|
|
}
|
|
|
|
Prog *
|
|
divsubr(int o)
|
|
{
|
|
switch(o) {
|
|
case ADIVQ:
|
|
return prog_divq;
|
|
case ADIVQU:
|
|
return prog_divqu;
|
|
case AMODQ:
|
|
return prog_modq;
|
|
case AMODQU:
|
|
return prog_modqu;
|
|
case ADIVL:
|
|
return prog_divl;
|
|
case ADIVLU:
|
|
return prog_divlu;
|
|
case AMODL:
|
|
return prog_modl;
|
|
case AMODLU:
|
|
return prog_modlu;
|
|
default:
|
|
diag("bad op %A in divsubr", o);
|
|
return prog_modlu;
|
|
}
|
|
}
|
|
|
|
Prog*
|
|
divuconst(Prog *p, uvlong y, int num, int quot, int bits)
|
|
{
|
|
int logy, i, shift;
|
|
uvlong k, m, n, mult, tmp, msb;
|
|
|
|
if(num == NREG)
|
|
num = quot;
|
|
if(y == 0) {
|
|
diag("division by zero");
|
|
return p;
|
|
}
|
|
if(y == 1)
|
|
return genRRR(p, AMOVQ, num, NREG, quot);
|
|
|
|
if(num == REGTMP || quot == REGTMP)
|
|
diag("bad register in divuconst");
|
|
|
|
tmp = y;
|
|
for(logy = -1; tmp != 0; logy++)
|
|
tmp >>= 1;
|
|
|
|
msb = (1LL << (bits-1));
|
|
if((y & (y-1)) == 0) /* power of 2 */
|
|
return genIRR(p, ASRLQ, logy, num, quot);
|
|
if(y > msb)
|
|
return genIRR(p, ACMPUGE, y, num, quot);
|
|
|
|
/* k = (-2^(bits+logy)) % y */
|
|
m = msb/y;
|
|
n = msb%y;
|
|
if(debug['d'])
|
|
Bprint(&bso, "divuconst: y=%lld msb=%lld m=%lld n=%lld\n",
|
|
y, msb, m, n);
|
|
for(i = 0; i <= logy; i++) {
|
|
m *= 2LL;
|
|
n *= 2LL;
|
|
if(n > y) {
|
|
m += 1LL;
|
|
n -= y;
|
|
}
|
|
}
|
|
if(debug['d'])
|
|
Bprint(&bso, "divuconst: y=%lld msb=%lld m=%lld n=%lld\n",
|
|
y, msb, m, n);
|
|
k = y - n;
|
|
if(k > (1LL << logy)) {
|
|
mult = 2LL*m + 1LL;
|
|
bits++;
|
|
} else
|
|
mult = m + 1LL;
|
|
|
|
shift = bits + logy;
|
|
if(debug['d'])
|
|
Bprint(&bso, "divuconst: y=%lld mult=%lld shift=%d bits=%d k=%lld\n",
|
|
y, mult, shift, bits, k);
|
|
if(bits <= 32) {
|
|
p = genIRR(p, AMOVQ, mult, NREG, REGTMP);
|
|
p = genRRR(p, AEXTLL, REGZERO, num, quot);
|
|
p = genRRR(p, AMULQ, REGTMP, quot, quot);
|
|
p = genIRR(p, ASRLQ, shift, quot, quot);
|
|
p = genRRR(p, AADDL, quot, REGZERO, quot);
|
|
return p;
|
|
}
|
|
if(bits == 33) {
|
|
if(shift < 64) {
|
|
mult <<= (64-shift);
|
|
shift = 64;
|
|
}
|
|
p = genIRR(p, AMOVQ, mult, NREG, REGTMP);
|
|
p = genRRR(p, AEXTLL, REGZERO, num, quot);
|
|
p = genRRR(p, AUMULH, REGTMP, quot, quot);
|
|
if(shift != 64)
|
|
p = genIRR(p, ASRLQ, shift-64, quot, quot);
|
|
p = genRRR(p, AADDL, quot, REGZERO, quot);
|
|
return p;
|
|
}
|
|
if(bits <= 64) {
|
|
if(shift < 64) {
|
|
mult <<= (64-shift);
|
|
shift = 64;
|
|
}
|
|
p = genIRR(p, AMOVQ, mult, NREG, REGTMP);
|
|
p = genRRR(p, AUMULH, REGTMP, num, quot);
|
|
if(shift != 64)
|
|
p = genIRR(p, ASRLQ, shift-64, quot, quot);
|
|
return p;
|
|
}
|
|
|
|
p = genIRR(p, AMOVQ, mult, NREG, REGTMP);
|
|
p = genRRR(p, AUMULH, REGTMP, num, REGTMP);
|
|
p = genRRR(p, AADDQ, num, REGTMP, quot);
|
|
p = genRRR(p, ACMPUGT, REGTMP, quot, REGTMP);
|
|
p = genIRR(p, ASLLQ, 128-shift, REGTMP, REGTMP);
|
|
p = genIRR(p, ASRLQ, shift-64, quot, quot);
|
|
p = genRRR(p, AADDQ, REGTMP, quot, quot);
|
|
return p;
|
|
}
|
|
|
|
Prog *
|
|
divconst(Prog *p, vlong y, int num, int quot, int bits)
|
|
{
|
|
vlong yabs;
|
|
Prog *q;
|
|
|
|
yabs = y;
|
|
if (y < 0)
|
|
yabs = -y;
|
|
q = genRRR(p, ASUBQ, num, REGZERO, REGTMP2);
|
|
if (num != quot)
|
|
q = genRRR(q, AMOVQ, num, NREG, quot);
|
|
q = genRRR(q, ACMOVGT, REGTMP2, REGTMP2, quot);
|
|
q = divuconst(q, yabs, quot, quot, bits-1);
|
|
q = genRRR(q, ASUBQ, quot, REGZERO, REGTMP);
|
|
q = genRRR(q, (y < 0)? ACMOVLT: ACMOVGT, REGTMP, REGTMP2, quot);
|
|
return q;
|
|
}
|
|
|
|
Prog *
|
|
modconst(Prog *p, vlong y, int num, int quot, int bits)
|
|
{
|
|
vlong yabs;
|
|
Prog *q;
|
|
|
|
yabs = y;
|
|
if (y < 0)
|
|
yabs = -y;
|
|
q = genRRR(p, ASUBQ, num, REGZERO, REGTMP2);
|
|
q = genRRR(q, ACMOVLT, num, REGTMP2, REGTMP2);
|
|
q = divuconst(q, yabs, REGTMP2, REGTMP2, bits-1);
|
|
q = genRRR(q, ASUBQ, REGTMP2, REGZERO, REGTMP);
|
|
q = genRRR(q, ACMOVLT, REGTMP, num, REGTMP2);
|
|
q = genIRR(q, AMULQ, yabs, REGTMP2, REGTMP2);
|
|
q = genRRR(q, ASUBQ, REGTMP2, num, quot);
|
|
return q;
|
|
}
|
|
|
|
Prog *
|
|
genXXX(Prog *q, int op, Adr *from, int reg, Adr *to)
|
|
{
|
|
Prog *p;
|
|
|
|
p = prg();
|
|
p->as = op;
|
|
p->line = q->line;
|
|
p->from = *from;
|
|
p->to = *to;
|
|
p->reg = reg;
|
|
p->link = q->link;
|
|
q->link = p;
|
|
return p;
|
|
}
|
|
|
|
Prog *
|
|
genRRR(Prog *q, int op, int from, int reg, int to)
|
|
{
|
|
Prog *p;
|
|
|
|
p = prg();
|
|
p->as = op;
|
|
p->line = q->line;
|
|
p->from.type = D_REG;
|
|
p->from.reg = from;
|
|
p->to.type = D_REG;
|
|
p->to.reg = to;
|
|
p->reg = reg;
|
|
p->link = q->link;
|
|
q->link = p;
|
|
return p;
|
|
}
|
|
|
|
Prog *
|
|
genIRR(Prog *q, int op, vlong v, int reg, int to)
|
|
{
|
|
Prog *p;
|
|
|
|
p = prg();
|
|
p->as = op;
|
|
p->line = q->line;
|
|
p->from.type = D_CONST;
|
|
p->from.offset = v;
|
|
p->to.type = D_REG;
|
|
p->to.reg = to;
|
|
p->reg = reg;
|
|
p->link = q->link;
|
|
q->link = p;
|
|
return p;
|
|
}
|
|
|
|
Prog *
|
|
genstore(Prog *q, int op, int from, vlong offset, int to)
|
|
{
|
|
Prog *p;
|
|
|
|
p = prg();
|
|
p->as = op;
|
|
p->line = q->line;
|
|
p->from.type = D_REG;
|
|
p->from.reg = from;
|
|
p->to.type = D_OREG;
|
|
p->to.reg = to;
|
|
p->to.offset = offset;
|
|
p->reg = NREG;
|
|
p->link = q->link;
|
|
q->link = p;
|
|
return p;
|
|
}
|
|
|
|
Prog *
|
|
genload(Prog *q, int op, vlong offset, int from, int to)
|
|
{
|
|
Prog *p;
|
|
|
|
p = prg();
|
|
p->as = op;
|
|
p->line = q->line;
|
|
p->from.type = D_OREG;
|
|
p->from.offset = offset;
|
|
p->from.reg = from;
|
|
p->to.type = D_REG;
|
|
p->to.reg = to;
|
|
p->reg = NREG;
|
|
p->link = q->link;
|
|
q->link = p;
|
|
return p;
|
|
}
|