#include "all.h" enum Imm { Iother, Iplo12, Iphi12, Iplo24, Inlo12, Inhi12, Inlo24}; static enum Immimm(Con *c, int k, int64_t *pn){ int64_t n; int i; if (c->type != CBits) return Iother; n = c->bits.i; if (k == Kw) n = (int32_t)n; i = Iplo12; if (n < 0) { i = Inlo12; n = -n; } *pn = n; if ((n & 0x000fff) == n) return i; if ((n & 0xfff000) == n) return i + 1; if ((n & 0xffffff) == n) return i + 2; return Iother;} intarm64_logimm(uint64_t x, int k){ uint64_t n; if (k == Kw) x = (x & 0xffffffff) | x << 32; if (x & 1) x = ~x; if (x == 0) return 0; if (x == 0xaaaaaaaaaaaaaaaa) return 1; n = x & 0xf; if (0x1111111111111111 * n == x) goto Check; n = x & 0xff; if (0x0101010101010101 * n == x) goto Check; n = x & 0xffff; if (0x0001000100010001 * n == x) goto Check; n = x & 0xffffffff; if (0x0000000100000001 * n == x) goto Check; n = x;Check: return (n & (n + (n & -n))) == 0;} static voidfixarg(Ref *pr, int k, int phi, Fn *fn){ char buf[32]; Con *c, cc; Ref r0, r1, r2, r3; int s, n; r0 = *pr; switch (rtype(r0)) { case RCon: c = &fn->con[r0.val]; if (T.apple && c->type == CAddr && c->sym.type == SThr) { r1 = newtmp("isel", Kl, fn); *pr = r1; if (c->bits.i) { r2 = newtmp("isel", Kl, fn); memset(&cc, 0, sizeof(cc)); cc.type = CBits; cc.bits.i = c->bits.i; r3 = newcon(&cc, fn); emit(Oadd, Kl, r1, r2, r3); r1 = r2; } emit(Ocopy, Kl, r1, TMP(R0), R); r1 = newtmp("isel", Kl, fn); r2 = newtmp("isel", Kl, fn); emit(Ocall, 0, R, r1, CALL(33)); emit(Ocopy, Kl, TMP(R0), r2, R); emit(Oload, Kl, r1, r2, R); cc = *c; cc.bits.i = 0; r3 = newcon(&cc, fn); emit(Ocopy, Kl, r2, r3, R); break; } if (KBASE(k) == 0 && phi) return; r1 = newtmp("isel", k, fn); if (KBASE(k) == 0) { emit(Ocopy, k, r1, r0, R); } else { n = stashbits(&c->bits, KWIDE(k) ? 8 : 4); vgrow(&fn->con, ++fn->ncon); c = &fn->con[fn->ncon-1]; sprintf(buf, "\"%sfp%d\"", T.asloc, n); memset(c, 0, sizeof(*c)); c->type = CAddr; c->sym.id = intern(buf); r2 = newtmp("isel", Kl, fn); emit(Oload, k, r1, r2, R); emit(Ocopy, Kl, r2, CON(c-fn->con), R); } *pr = r1; break; case RTmp: s = fn->tmp[r0.val].slot; if (s == -1) break; r1 = newtmp("isel", Kl, fn); emit(Oaddr, Kl, r1, SLOT(s), R); *pr = r1; break; }} static intselcmp(Ref arg[2], int k, Fn *fn){ Ref r, *iarg; Con *c; int swap, cmp, fix; int64_t n; if (KBASE(k) == 1) { emit(Oafcmp, k, R, arg[0], arg[1]); iarg = curi->arg; fixarg(&iarg[0], k, 0, fn); fixarg(&iarg[1], k, 0, fn); return 0; } swap = rtype(arg[0]) == RCon; if (swap) { r = arg[1]; arg[1] = arg[0]; arg[0] = r; } fix = 1; cmp = Oacmp; r = arg[1]; if (rtype(r) == RCon) { c = &fn->con[r.val]; switch (imm(c, k, &n)) { default: break; case Iplo12: case Iphi12: fix = 0; break; case Inlo12: case Inhi12: cmp = Oacmn; r = getcon(n, fn); fix = 0; break; } } emit(cmp, k, R, arg[0], r); iarg = curi->arg; fixarg(&iarg[0], k, 0, fn); if (fix) fixarg(&iarg[1], k, 0, fn); return swap;} static intcallable(Ref r, Fn *fn){ Con *c; if (rtype(r) == RTmp) return 1; if (rtype(r) == RCon) { c = &fn->con[r.val]; if (c->type == CAddr) if (c->bits.i == 0) return 1; } return 0;} static voidsel(Ins i, Fn *fn){ Ref *iarg; Ins *i0; int ck, cc; if (INRANGE(i.op, Oalloc, Oalloc1)) { i0 = curi - 1; salloc(i.to, i.arg[0], fn); fixarg(&i0->arg[0], Kl, 0, fn); return; } if (iscmp(i.op, &ck, &cc)) { emit(Oflag, i.cls, i.to, R, R); i0 = curi; if (selcmp(i.arg, ck, fn)) i0->op += cmpop(cc); else i0->op += cc; return; } if (i.op == Ocall) if (callable(i.arg[0], fn)) { emiti(i); return; } if (i.op != Onop) { emiti(i); iarg = curi->arg; /* fixarg() can change curi */ fixarg(&iarg[0], argcls(&i, 0), 0, fn); fixarg(&iarg[1], argcls(&i, 1), 0, fn); }} static voidseljmp(Blk *b, Fn *fn){ Ref r; Ins *i, *ir; int ck, cc, use; if (b->jmp.type == Jret0 || b->jmp.type == Jjmp || b->jmp.type == Jhlt) return; assert(b->jmp.type == Jjnz); r = b->jmp.arg; use = -1; b->jmp.arg = R; ir = 0; i = &b->ins[b->nins]; while (i > b->ins) if (req((--i)->to, r)) { use = fn->tmp[r.val].nuse; ir = i; break; } if (ir && use == 1 && iscmp(ir->op, &ck, &cc)) { if (selcmp(ir->arg, ck, fn)) cc = cmpop(cc); b->jmp.type = Jjf + cc; memset(ir, 0, sizeof(*ir)); ir->op = Onop; } else { Ref ref[] = {r, CON_Z}; selcmp(ref, Kw, fn); b->jmp.type = Jjfine; }} voidarm64_isel(Fn *fn){ Blk *b, **sb; Ins *i; Phi *p; uint n, al; int64_t sz; /* assign slots to fast allocs */ b = fn->start; /* specific to NAlign == 3 */ /* or change n=4 and sz /= 4 below */ for (al=Oalloc, n=4; al<=Oalloc1; al++, n*=2) for (i=b->ins; i<&b->ins[b->nins]; i++) if (i->op == al) { if (rtype(i->arg[0]) != RCon) break; sz = fn->con[i->arg[0].val].bits.i; if (sz < 0 || sz >= INT_MAX-15) err("invalid alloc size %"PRId64, sz); sz = (sz + n-1) & -n; sz /= 4; fn->tmp[i->to.val].slot = fn->slot; fn->slot += sz; memset(i, 0, sizeof(*i)); i->op = Onop; } for (b=fn->start; b; b=b->link) { curi = &insb[NIns]; Blk *blk[3] = {b->s1, b->s2, 0}; for (sb=blk; *sb; sb++) for (p=(*sb)->phi; p; p=p->link) { for (n=0; p->blk[n] != b; n++) assert(n+1 < p->narg); fixarg(&p->arg[n], p->cls, 1, fn); } seljmp(b, fn); for (i=&b->ins[b->nins]; i!=b->ins;) sel(*--i, fn); b->nins = &insb[NIns] - curi; idup(&b->ins, curi, b->nins); } if (debug['I']) { fprintf(stderr, "\n> After instruction selection:\n"); printfn(fn, stderr); }}
#include "all.h"
enum Imm {
Iother,
Iplo12,
Iphi12,
Iplo24,
Inlo12,
Inhi12,
Inlo24
};
static enum Imm
imm(Con *c, int k, int64_t *pn)
{
int64_t n;
int i;
if (c->type != CBits)
return Iother;
n = c->bits.i;
if (k == Kw)
n = (int32_t)n;
i = Iplo12;
if (n < 0) {
i = Inlo12;
n = -n;
}
*pn = n;
if ((n & 0x000fff) == n)
return i;
if ((n & 0xfff000) == n)
return i + 1;
if ((n & 0xffffff) == n)
return i + 2;
return Iother;
}
int
arm64_logimm(uint64_t x, int k)
{
uint64_t n;
if (k == Kw)
x = (x & 0xffffffff) | x << 32;
if (x & 1)
x = ~x;
if (x == 0)
return 0;
if (x == 0xaaaaaaaaaaaaaaaa)
return 1;
n = x & 0xf;
if (0x1111111111111111 * n == x)
goto Check;
n = x & 0xff;
if (0x0101010101010101 * n == x)
goto Check;
n = x & 0xffff;
if (0x0001000100010001 * n == x)
goto Check;
n = x & 0xffffffff;
if (0x0000000100000001 * n == x)
goto Check;
n = x;
Check:
return (n & (n + (n & -n))) == 0;
}
static void
fixarg(Ref *pr, int k, int phi, Fn *fn)
{
char buf[32];
Con *c, cc;
Ref r0, r1, r2, r3;
int s, n;
r0 = *pr;
switch (rtype(r0)) {
case RCon:
c = &fn->con[r0.val];
if (T.apple
&& c->type == CAddr
&& c->sym.type == SThr) {
r1 = newtmp("isel", Kl, fn);
*pr = r1;
if (c->bits.i) {
r2 = newtmp("isel", Kl, fn);
memset(&cc, 0, sizeof(cc));
cc.type = CBits;
cc.bits.i = c->bits.i;
r3 = newcon(&cc, fn);
emit(Oadd, Kl, r1, r2, r3);
r1 = r2;
}
emit(Ocopy, Kl, r1, TMP(R0), R);
r1 = newtmp("isel", Kl, fn);
r2 = newtmp("isel", Kl, fn);
emit(Ocall, 0, R, r1, CALL(33));
emit(Ocopy, Kl, TMP(R0), r2, R);
emit(Oload, Kl, r1, r2, R);
cc = *c;
cc.bits.i = 0;
r3 = newcon(&cc, fn);
emit(Ocopy, Kl, r2, r3, R);
break;
}
if (KBASE(k) == 0 && phi)
return;
r1 = newtmp("isel", k, fn);
if (KBASE(k) == 0) {
emit(Ocopy, k, r1, r0, R);
} else {
n = stashbits(&c->bits, KWIDE(k) ? 8 : 4);
vgrow(&fn->con, ++fn->ncon);
c = &fn->con[fn->ncon-1];
sprintf(buf, "\"%sfp%d\"", T.asloc, n);
memset(c, 0, sizeof(*c));
c->type = CAddr;
c->sym.id = intern(buf);
r2 = newtmp("isel", Kl, fn);
emit(Oload, k, r1, r2, R);
emit(Ocopy, Kl, r2, CON(c-fn->con), R);
}
*pr = r1;
break;
case RTmp:
s = fn->tmp[r0.val].slot;
if (s == -1)
break;
r1 = newtmp("isel", Kl, fn);
emit(Oaddr, Kl, r1, SLOT(s), R);
*pr = r1;
break;
}
}
static int
selcmp(Ref arg[2], int k, Fn *fn)
{
Ref r, *iarg;
Con *c;
int swap, cmp, fix;
int64_t n;
if (KBASE(k) == 1) {
emit(Oafcmp, k, R, arg[0], arg[1]);
iarg = curi->arg;
fixarg(&iarg[0], k, 0, fn);
fixarg(&iarg[1], k, 0, fn);
return 0;
}
swap = rtype(arg[0]) == RCon;
if (swap) {
r = arg[1];
arg[1] = arg[0];
arg[0] = r;
}
fix = 1;
cmp = Oacmp;
r = arg[1];
if (rtype(r) == RCon) {
c = &fn->con[r.val];
switch (imm(c, k, &n)) {
default:
break;
case Iplo12:
case Iphi12:
fix = 0;
break;
case Inlo12:
case Inhi12:
cmp = Oacmn;
r = getcon(n, fn);
fix = 0;
break;
}
}
emit(cmp, k, R, arg[0], r);
iarg = curi->arg;
fixarg(&iarg[0], k, 0, fn);
if (fix)
fixarg(&iarg[1], k, 0, fn);
return swap;
}
static int
callable(Ref r, Fn *fn)
{
Con *c;
if (rtype(r) == RTmp)
return 1;
if (rtype(r) == RCon) {
c = &fn->con[r.val];
if (c->type == CAddr)
if (c->bits.i == 0)
return 1;
}
return 0;
}
static void
sel(Ins i, Fn *fn)
{
Ref *iarg;
Ins *i0;
int ck, cc;
if (INRANGE(i.op, Oalloc, Oalloc1)) {
i0 = curi - 1;
salloc(i.to, i.arg[0], fn);
fixarg(&i0->arg[0], Kl, 0, fn);
return;
}
if (iscmp(i.op, &ck, &cc)) {
emit(Oflag, i.cls, i.to, R, R);
i0 = curi;
if (selcmp(i.arg, ck, fn))
i0->op += cmpop(cc);
else
i0->op += cc;
return;
}
if (i.op == Ocall)
if (callable(i.arg[0], fn)) {
emiti(i);
return;
}
if (i.op != Onop) {
emiti(i);
iarg = curi->arg; /* fixarg() can change curi */
fixarg(&iarg[0], argcls(&i, 0), 0, fn);
fixarg(&iarg[1], argcls(&i, 1), 0, fn);
}
}
static void
seljmp(Blk *b, Fn *fn)
{
Ref r;
Ins *i, *ir;
int ck, cc, use;
if (b->jmp.type == Jret0
|| b->jmp.type == Jjmp
|| b->jmp.type == Jhlt)
return;
assert(b->jmp.type == Jjnz);
r = b->jmp.arg;
use = -1;
b->jmp.arg = R;
ir = 0;
i = &b->ins[b->nins];
while (i > b->ins)
if (req((--i)->to, r)) {
use = fn->tmp[r.val].nuse;
ir = i;
break;
}
if (ir && use == 1
&& iscmp(ir->op, &ck, &cc)) {
if (selcmp(ir->arg, ck, fn))
cc = cmpop(cc);
b->jmp.type = Jjf + cc;
memset(ir, 0, sizeof(*ir));
ir->op = Onop;
}
else {
Ref ref[] = {r, CON_Z};
selcmp(ref, Kw, fn);
b->jmp.type = Jjfine;
}
}
void
arm64_isel(Fn *fn)
{
Blk *b, **sb;
Ins *i;
Phi *p;
uint n, al;
int64_t sz;
/* assign slots to fast allocs */
b = fn->start;
/* specific to NAlign == 3 */ /* or change n=4 and sz /= 4 below */
for (al=Oalloc, n=4; al<=Oalloc1; al++, n*=2)
for (i=b->ins; i<&b->ins[b->nins]; i++)
if (i->op == al) {
if (rtype(i->arg[0]) != RCon)
break;
sz = fn->con[i->arg[0].val].bits.i;
if (sz < 0 || sz >= INT_MAX-15)
err("invalid alloc size %"PRId64, sz);
sz = (sz + n-1) & -n;
sz /= 4;
fn->tmp[i->to.val].slot = fn->slot;
fn->slot += sz;
memset(i, 0, sizeof(*i));
i->op = Onop;
}
for (b=fn->start; b; b=b->link) {
curi = &insb[NIns];
Blk *blk[3] = {b->s1, b->s2, 0};
for (sb=blk; *sb; sb++)
for (p=(*sb)->phi; p; p=p->link) {
for (n=0; p->blk[n] != b; n++)
assert(n+1 < p->narg);
fixarg(&p->arg[n], p->cls, 1, fn);
}
seljmp(b, fn);
for (i=&b->ins[b->nins]; i!=b->ins;)
sel(*--i, fn);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
}
if (debug['I']) {
fprintf(stderr, "\n> After instruction selection:\n");
printfn(fn, stderr);
}
}