mirror of
git://c9x.me/qbe.git
synced 2026-08-02 15:44:39 +00:00
We need to add the offset after loading from the GOT. This is a fix for the issue reported in [0], and removes the offsets in isel to avoid the problem with large offsets in the naive patch. This is similar to what is done on amd64. Tested with cproc bootstrap using extern on aarch64. [0] https://lists.sr.ht/~mpu/qbe/%3Cd71fac06-1208-4e4f-85a4-e5edc7fd979c@app.fastmail.com%3E#%3C2F4DUTRJ9GA5V.3VK2QXK848F7U@mforney.org%3E
319 lines
5.6 KiB
C
319 lines
5.6 KiB
C
#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 = -(uint64_t)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 (c->type == CAddr && ((c->sym.type & SExt)
|
|
|| (T.apple && (c->sym.type & SThr)))) {
|
|
r1 = newtmp("isel", Kl, fn);
|
|
*pr = r1;
|
|
if (c->bits.i) {
|
|
r2 = newtmp("isel", Kl, fn);
|
|
cc = (Con){.type = CBits};
|
|
cc.bits.i = c->bits.i;
|
|
r3 = newcon(&cc, fn);
|
|
emit(Oadd, Kl, r1, r2, r3);
|
|
r1 = r2;
|
|
}
|
|
if (T.apple && (c->sym.type & SThr)) {
|
|
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);
|
|
r1 = r2;
|
|
}
|
|
cc = *c;
|
|
cc.bits.i = 0;
|
|
r3 = newcon(&cc, fn);
|
|
emit(Ocopy, Kl, r1, 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.i, KWIDE(k) ? 8 : 4);
|
|
vgrow(&fn->con, ++fn->ncon);
|
|
c = &fn->con[fn->ncon-1];
|
|
sprintf(buf, "\"%sfp%d\"", T.asloc, n);
|
|
*c = (Con){.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;
|
|
*ir = (Ins){.op = Onop};
|
|
}
|
|
else {
|
|
selcmp((Ref[]){r, CON_Z}, 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;
|
|
*i = (Ins){.op = Onop};
|
|
}
|
|
|
|
for (b=fn->start; b; b=b->link) {
|
|
curi = &insb[NIns];
|
|
for (sb=(Blk*[3]){b->s1, b->s2, 0}; *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);
|
|
idup(b, curi, &insb[NIns]-curi);
|
|
}
|
|
|
|
if (debug['I']) {
|
|
fprintf(stderr, "\n> After instruction selection:\n");
|
|
printfn(fn, stderr);
|
|
}
|
|
}
|