aboutsummaryrefslogtreecommitdiffhomepage
path: root/x86_64/isel.c
diff options
context:
space:
mode:
Diffstat (limited to 'x86_64/isel.c')
-rw-r--r--x86_64/isel.c660
1 files changed, 660 insertions, 0 deletions
diff --git a/x86_64/isel.c b/x86_64/isel.c
new file mode 100644
index 0000000..5d373f3
--- /dev/null
+++ b/x86_64/isel.c
@@ -0,0 +1,660 @@
+#include "all.h"
+#include "../endian.h"
+
+enum flag {
+ ZF = 1 << 0,
+ SF = 1 << 1,
+ CF = 1 << 2,
+ OF = 1 << 3,
+ CLOBF = 1 << 4,
+};
+
+/* flags modified by each integer op */
+static const uchar opflags[NOPER] = {
+ [Oneg] = ZF|CLOBF,
+ [Oadd] = ZF|CLOBF,
+ [Osub] = ZF|CLOBF,
+ [Omul] = CLOBF,
+ [Odiv] = CLOBF,
+ [Oudiv] = CLOBF,
+ [Orem] = CLOBF,
+ [Ourem] = CLOBF,
+ [Oand] = ZF|CLOBF,
+ [Oior] = ZF|CLOBF,
+ [Oxor] = ZF|CLOBF,
+ [Oshl] = ZF|CLOBF,
+ [Osar] = ZF|CLOBF,
+ [Oslr] = ZF|CLOBF,
+ [Oequ] = ZF|CLOBF,
+ [Oneq] = ZF|CLOBF,
+ [Olth] = ZF|CLOBF,
+ [Ogth] = ZF|CLOBF,
+ [Olte] = ZF|CLOBF,
+ [Ogte] = ZF|CLOBF,
+ [Oulth] = ZF|CLOBF,
+ [Ougth] = ZF|CLOBF,
+ [Oulte] = ZF|CLOBF,
+ [Ougte] = ZF|CLOBF,
+ [Ocall] = CLOBF,
+};
+
+static int iflagsrc = -1;
+
+static void
+picfixsym(union ref *r, struct block *blk, int *curi)
+{
+ if (!ccopt.pic || !isaddrcon(*r,0)) return;
+ *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, .l = *r));
+}
+
+/* map alloca tmp -> stack frame displacement (0 if not alloca) */
+static ushort *stkslots;
+static uint nstkslots;
+
+#define isstkslot(r) ((r).t == RTMP && (r).i < nstkslots && stkslots[(r).i])
+
+static void
+fixarg(union ref *r, struct instr *ins, struct block *blk, int *curi)
+{
+ int sh;
+ enum op op = ins ? ins->op : 0;
+
+ if (r->t == RXCON) {
+ struct xcon *con = &conht[r->i];
+ if (in_range(op, Oshl, Oslr) && r == &ins->r) {
+ sh = con->i;
+ goto ShiftImm;
+ } else if (in_range(op, Oadd, Osub) && con->i == 2147483648 && r == &ins->r) {
+ /* add X, INT32MAX+1 -> sub X, INT32MIN */
+ ins->op = Oadd + (op == Oadd);
+ *r = mkintcon(KI32, -2147483648);
+ } else if (kisflt(con->cls) && con->i == 0) {
+ /* copy of positive float zero -> regular zero, that emit() will turn into xor x,x */
+ if (in_range(op, Ocopy, Omove) || op == Ophi)
+ *r = ZEROREF;
+ else
+ *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, con->cls, ZEROREF));
+ } else if (con->cls >= KI64) {
+ /* float immediates & 64bit immediates are loaded from memory */
+ uchar data[8];
+ uint ksiz = cls2siz[con->cls];
+ union type ctype;
+ /* can't use memory arg in rhs if lhs is memory */
+ bool docopy = &ins->l != r && (oisstore(ins->op) || ins->l.t == RADDR);
+ if (con->cls <= KPTR && in_range(ins->op, Ocopy, Omove)) /* in this case we can use movabs */
+ return;
+ else if (!docopy || con->cls >= KF32) {
+ if (con->cls != KF32) {
+ wr64le(data, con->i);
+ ctype = mktype(con->cls == KF64 ? TYDOUBLE : TYVLONG);
+ } else {
+ union { float f; int i; } pun = { con->f };
+ wr32le(data, pun.i);
+ ctype = mktype(TYFLOAT);
+ }
+ *r = mkdatref(NULL, ctype, ksiz, /*align*/ksiz, data, ksiz, /*deref*/1);
+ }
+ if (docopy)
+ *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, con->cls, *r));
+ } else if (ins->op != Omove && con->issym && r == &ins->r) {
+ *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, mkaddr((struct addr){*r})));
+ } else if (in_range(op, Odiv, Ourem) && kisint(ins->cls))
+ goto DivImm;
+ } else if (r->t == RICON && in_range(op, Odiv, Ourem) && kisint(ins->cls) && r == &ins->r) {
+ DivImm: /* there is no division by immediate, must be copied to a register */
+ *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, *r));
+ } else if (r->t == RICON && in_range(op, Oshl, Oslr) && r == &ins->r) {
+ sh = r->i;
+ ShiftImm: /* shift immediate is always 8bit */
+ *r = mkref(RICON, sh & 255);
+ } else if (isstkslot(*r)) {
+ struct instr adr = mkinstr(Oadd, KPTR, mkref(RREG, RBP), mkintcon(KI32, -stkslots[r->i]));
+ if (in_range(op, Ocopy, Omove))
+ *ins = adr;
+ else
+ *r = insertinstr(blk, (*curi)++, adr);
+ }
+ picfixsym(r, blk, curi);
+}
+
+#define isimm32(r) (iscon(r) && concls(r) == KI32)
+
+static void
+selcall(struct function *fn, struct instr *ins, struct block *blk, int *curi)
+{
+ const struct call *call = &calltab.p[ins->r.i];
+ int iarg = *curi - 1;
+ enum irclass cls;
+ uint argstksiz = alignup(call->argstksiz, 16);
+
+ for (int i = call->narg - 1; i >= 0; --i) {
+ struct abiarg abi = call->abiarg[i];
+ struct instr *arg;
+ for (;; --iarg) {
+ assert(iarg >= 0 && i >= 0 && "arg?");
+ if ((arg = &instrtab[blk->ins.p[iarg]])->op == Oarg)
+ break;
+ }
+
+ if (!abi.isstk) {
+ assert(!abi.ty.isagg);
+ *arg = mkinstr(Omove, call->abiarg[i].ty.cls, mkref(RREG, abi.reg), arg->r);
+ } else {
+ union ref adr = mkaddr((struct addr){mkref(RREG, RSP), .disp = abi.stk});
+ int iargsave = iarg;
+ if (!abi.ty.isagg) { /* scalar arg in stack */
+ *arg = mkinstr(Ostore8+ilog2(cls2siz[abi.ty.cls]), 0, adr, arg->r);
+ if (isaddrcon(arg->r,1) || arg->r.t == RADDR)
+ arg->r = insertinstr(blk, iarg++, mkinstr(Ocopy, abi.ty.cls, arg->r));
+ else
+ fixarg(&ins->r, ins, blk, &iarg);
+ } else { /* aggregate arg in stack, callee stack frame destination address */
+ *arg = mkinstr(Ocopy, KPTR, adr);
+ }
+ *curi += iarg - iargsave;
+ }
+ }
+ if (call->argstksiz) {
+ union ref disp = mkref(RICON, argstksiz);
+ insertinstr(blk, iarg--, (struct instr){Osub, KPTR, .keep=1, .reg = RSP+1, .l=mkref(RREG,RSP), disp});
+ ++*curi;
+ insertinstr(blk, *curi+1, (struct instr){Oadd, KPTR, .keep=1, .reg = RSP+1, .l=mkref(RREG,RSP), disp});
+ }
+ if (isimm32(ins->l))
+ ins->l = mkaddr((struct addr){.base = ins->l});
+ else if (isintcon(ins->l))
+ ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, ins->l));
+
+ if (call->vararg >= 0 && ins->l.t == RTMP) {
+ /* variadic calls write number of sse regs used to AL, so mark it as clobbered such that
+ * the function pointer of an indirect calls does not get allocated to RAX by regalloc */
+ insertinstr(blk, (*curi)++, mkinstr(Omove, KPTR, mkref(RREG, RAX), mkref(RREG, RAX)));
+ }
+ cls = ins->cls;
+ ins->cls = 0;
+ if (cls) {
+ /* duplicate to reuse same TMP ref */
+ insertinstr(blk, (*curi)++, *ins);
+ *ins = mkinstr(Ocopy, cls, mkref(RREG, call->abiret[0].reg));
+ for (int i = 1; i <= 2; ++i) {
+ if (*curi + i >= blk->ins.n) break;
+ if (instrtab[blk->ins.p[*curi + i]].op == Ocall2r) {
+ ins = &instrtab[blk->ins.p[*curi += i]];
+ *ins = mkinstr(Ocopy, ins->cls, mkref(RREG, call->abiret[1].reg));
+ break;
+ }
+ }
+ }
+}
+
+static bool
+aimm(struct addr *addr, int disp)
+{
+ vlong a = addr->disp;
+ a += disp;
+ if ((int)a == a) {
+ addr->disp = a;
+ return 1;
+ }
+ return 0;
+}
+
+static bool
+acon(struct addr *addr, union ref r)
+{
+ vlong a = addr->disp;
+ assert(isintcon(r));
+ a += intconval(r);
+ if ((int)a == a) {
+ addr->disp = a;
+ return 1;
+ }
+ return 0;
+}
+
+static bool
+ascale(struct addr *addr, union ref a, union ref b)
+{
+ if (b.t != RICON) return 0;
+ if (addr->index.bits) return 0;
+ if ((unsigned)b.i > 3) return 0;
+ if (a.t == RREG) {
+ Scaled:
+ addr->index = a;
+ addr->shift = b.i;
+ return 1;
+ } else if (a.t == RTMP) {
+ struct instr *ins = &instrtab[a.i];
+ /* factor out shifted immediate from 'shl {add %x, imm}, s' */
+ /* XXX maybe we shouldn't do this here because it should be done by a generic
+ * arithemetic optimization pass ? */
+ if (ins->op == Oadd && (ins->l.t == RREG || ins->l.t == RTMP) && isintcon(ins->r)) {
+ vlong a = ((vlong) addr->disp + intconval(ins->r)) * (1 << b.i);
+ if (a != (int) a) return 0;
+ addr->disp = a;
+ addr->index = ins->l;
+ addr->shift = b.i;
+ return 1;
+ } else {
+ goto Scaled;
+ }
+ }
+ return 0;
+}
+
+static bool
+aadd(struct addr *addr, struct block *blk, int *curi, union ref r)
+{
+ if (isstkslot(r)) {
+ if (addr->base.bits || !aimm(addr, -stkslots[r.i])) goto Ref;
+ addr->base = mkref(RREG, RBP);
+ } else if (r.t == RTMP) {
+ struct instr *ins = &instrtab[r.i];
+ if (ins->op == Oadd) {
+ if (!aadd(addr, blk, curi, ins->l)) goto Ref;
+ if (!aadd(addr, blk, curi, ins->r)) goto Ref;
+ ins->skip = 1;
+ } else if (ins->op == Oshl) {
+ if (!ascale(addr, ins->l, ins->r)) goto Ref;
+ ins->skip = 1;
+ } else if (ins->op == Ocopy && ins->l.t == RADDR) {
+ struct addr save = *addr, *addr2 = &addrht[ins->l.i];
+ if ((!addr2->base.bits || aadd(addr, blk, curi, addr2->base))
+ && aimm(addr, addr2->disp)
+ && (!addr2->index.bits || ascale(addr, addr2->index, mkref(RICON, addr2->shift))))
+ {
+ ins->skip = 1;
+ } else {
+ *addr = save;
+ goto Ref;
+ }
+ } else if (ins->op == Ocopy) {
+ if (!aadd(addr, blk, curi, ins->l)) goto Ref;
+ ins->skip = 1;
+ } else goto Ref;
+ } else if (isnumcon(r)) {
+ return acon(addr, r);
+ } else if (isaddrcon(r,1)) {
+ if (!addr->base.bits && !isaddrcon(addr->index,1)) addr->base = r;
+ else return 0;
+ } else if (r.t == RREG) {
+ /* temporaries are single assignment, but register aren't, so they can't be *
+ * safely hoisted into an address value, unless they have global lifetime */
+ if (!rstest(mctarg->rglob, r.i)) return 0;
+ Ref:
+ if (isstkslot(r) && (addr->base.bits || addr->index.bits)) {
+ r = insertinstr(blk, (*curi)++, mkinstr(Oadd, KPTR, mkref(RREG, RBP), mkref(RICON, -stkslots[r.i])));
+ }
+ if (!addr->base.bits) addr->base = r;
+ else if (!addr->index.bits) addr->index = r;
+ else return 0;
+ } else return 0;
+ return 1;
+}
+
+static bool
+fuseaddr(union ref *r, struct block *blk, int *curi)
+{
+ struct addr addr = { 0 };
+
+ if (isaddrcon(*r,1)) return 1;
+ if (r->t == RADDR) {
+ const struct addr *a0 = &addrht[r->i];
+ if (aadd(&addr, blk, curi, a0->base)
+ && (!addr.index.bits || ascale(&addr, a0->index, mkref(RICON, a0->shift)))
+ && aadd(&addr, blk, curi, mkintcon(KPTR, a0->disp))) {
+ *r = mkaddr(addr);
+ }
+ return 1;
+ }
+ if (r->t != RTMP) return 0;
+ if (!aadd(&addr, blk, curi, *r)) return 0;
+
+ if (isaddrcon(addr.base,0) && (ccopt.pic || (ccopt.pie && addr.index.bits))) {
+ /* pic needs to load from GOT */
+ /* pie cannot encode RIP-relative address with index register */
+ /* first load symbol address into a temp register */
+ union ref temp = mkaddr((struct addr){.base = addr.base, .disp = ccopt.pic ? 0 : addr.disp});
+ addr.base = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, .l = temp));
+ if (!ccopt.pic) addr.disp = 0;
+ }
+
+ if (!addr.base.bits) {
+ /* absolute int address in disp */
+ if (addr.index.bits) return 0;
+ addr.base = mkintcon(KPTR, addr.disp);
+ addr.disp = 0;
+ }
+
+ *r = mkaddr(addr);
+ return 1;
+}
+
+/* is add instruction with this arg a candidate to transform into efective addr? */
+static bool
+addarg4addrp(union ref r)
+{
+ struct instr *ins;
+ if (r.t == RXCON && !conht[r.i].cls && !conht[r.i].deref) return 1; /* sym or dat ref */
+ if (r.t != RTMP) return 0;
+ if (isstkslot(r)) return 1;
+ ins = &instrtab[r.i];
+ return ins->op == Oshl || (ins->op == Ocopy && ins->l.t == RADDR) || ins->op == Oadd;
+}
+
+static void
+loadstoreaddr(struct block *blk, union ref *r, int *curi)
+{
+ if (isimm32(*r)) {
+ *r = mkaddr((struct addr){.base = *r});
+ } else if (isaddrcon(*r, 0)) {
+ picfixsym(r, blk, curi);
+ } else if (r->t == RTMP) {
+ if (addarg4addrp(*r)) fuseaddr(r, blk, curi);
+ } else if (r->t != RREG) {
+ *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, *r));
+ }
+}
+
+static bool
+arithfold(struct instr *ins)
+{
+ if (isnumcon(ins->l) && (!ins->r.t || isnumcon(ins->r))) {
+ union ref r;
+ bool ok = ins->r.t ? foldbinop(&r, ins->op, ins->cls, ins->l, ins->r) : foldunop(&r, ins->op, ins->cls, ins->l);
+ assert(ok && "fold?");
+ *ins = mkinstr(Ocopy, insrescls(*ins), r);
+ return 1;
+ }
+ return 0;
+}
+
+static void
+sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
+{
+ uint siz, alignlog2;
+ int t = ins - instrtab;
+ struct instr temp = {0};
+ enum op op = ins->op;
+
+ if (oisarith(ins->op) && arithfold(ins)) {
+ fixarg(&ins->l, ins, blk, curi);
+ return;
+ }
+
+ switch (op) {
+ default: assert(0);
+ case Onop: break;
+ case Oalloca1: case Oalloca2: case Oalloca4: case Oalloca8: case Oalloca16:
+ alignlog2 = ins->op - Oalloca1;
+ assert(ins->l.i > 0);
+ siz = ins->l.i << alignlog2;
+ fn->stksiz += siz;
+ fn->stksiz = alignup(fn->stksiz, 1 << alignlog2);
+ if (fn->stksiz > (1<<16)-1) error(NULL, "'%s' stack frame too big", fn->name);
+ stkslots[t] = fn->stksiz;
+ *ins = mkinstr(Onop,0,);
+ break;
+ case Oparam:
+ assert(ins->l.t == RICON && ins->l.i < fn->nabiarg);
+ if (!fn->abiarg[ins->l.i].isstk)
+ *ins = mkinstr(Ocopy, ins->cls, mkref(RREG, fn->abiarg[ins->l.i].reg));
+ else /* stack */
+ *ins = mkinstr(Oadd, KPTR, mkref(RREG, RBP), mkref(RICON, 16+fn->abiarg[ins->l.i].stk));
+ break;
+ case Oarg:
+ fixarg(&ins->r, ins, blk, curi);
+ break;
+ case Ocall:
+ selcall(fn, ins, blk, curi);
+ break;
+ case Ocall2r: assert(0);
+ case Ointrin:
+ break;
+ case Oshl: case Osar: case Oslr:
+ if (!iscon(ins->r)) {
+ /* shift amount register is always CL */
+ insertinstr(blk, (*curi)++, mkinstr(Omove, KI32, mkref(RREG, RCX), ins->r));
+ ins->r = mkref(RREG, RCX);
+ }
+ goto ALU;
+ case Oequ: case Oneq:
+ case Olth: case Ogth: case Olte: case Ogte:
+ case Oulth: case Ougth: case Oulte: case Ougte:
+ if (iscon(ins->l)) {
+ /* lth imm, x -> gth x, imm */
+ if (!in_range(ins->op, Oequ, Oneq))
+ ins->op = ((op - Olth) ^ 1) + Olth;
+ rswap(ins->l, ins->r);
+ }
+ if (ins->l.t != RTMP && ins->l.t != RREG)
+ ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l));
+ else
+ fixarg(&ins->l, ins, blk, curi);
+ fixarg(&ins->r, ins, blk, curi);
+ break;
+ case Odiv: case Oudiv: case Orem: case Ourem:
+ if (kisflt(ins->cls)) goto ALU;
+ /* TODO fuse div/rem pair */
+
+ /* (I)DIV dividend is always in RDX:RAX, output also in those regs */
+ insertinstr(blk, (*curi)++, mkinstr(Omove, ins->cls, mkref(RREG, RAX), ins->l));
+ /* mark RDX as clobbered. sign/zero-extending RAX into RDX is handled in emit() */
+ insertinstr(blk, (*curi)++, mkinstr(Omove, ins->cls, mkref(RREG, RDX), mkref(RREG, RDX)));
+ fixarg(&ins->r, ins, blk, curi); /* make sure rhs is memory or reg */
+ ins->l = mkref(RREG, RAX);
+ ins->keep = 1;
+ if (op == Orem) ins->op = Odiv;
+ else if (op == Ourem) ins->op = Oudiv;
+ insertinstr(blk, (*curi)++, *ins); /* duplicate ins to reuse tmp ref */
+ *ins = mkinstr(Ocopy, ins->cls, mkref(RREG, op < Orem ? RAX : RDX)); /* get output */
+ temp = mkinstr(Ocopy, ins->cls, mkref(RREG, op < Orem ? RDX : RAX)); /* clobber other reg*/
+ insertinstr(blk, ++(*curi), temp);
+ /* swap instrs so that clobber goes first */
+ t = blk->ins.p[*curi - 1];
+ blk->ins.p[*curi - 1] = blk->ins.p[*curi - 0];
+ blk->ins.p[*curi - 0] = t;
+ break;
+ case Osub:
+ if (isintcon(ins->l)) {
+ /* sub imm, x -> sub x, imm; neg x */
+ fixarg(&ins->l, ins, blk, curi);
+ ins->inplace = 1;
+ struct instr sub = *ins;
+ rswap(sub.l, sub.r);
+ ins->op = op = Oneg;
+ ins->l = insertinstr(blk, (*curi)++, sub);
+ ins->r = NOREF;
+ goto ALU;
+ } else if (kisint(ins->cls) && isintcon(ins->r)) {
+ ins->op = op = Oadd;
+ ins->r = mkintcon(concls(ins->r), -intconval(ins->r));
+ } else {
+ goto ALU;
+ }
+ /* fallthru */
+ case Oadd:
+ if (kisint(ins->cls)) {
+ if ((addarg4addrp(ins->l) || addarg4addrp(ins->r))) {
+ temp.op = Ocopy;
+ temp.cls = ins->cls;
+ temp.l = mkref(RTMP, t);
+ if (fuseaddr(&temp.l, blk, curi)) {
+ *ins = temp;
+ break;
+ }
+ }
+ }
+ /* fallthru */
+ case Omul:
+ case Oand: case Oxor: case Oior:
+ /* commutative ops */
+ if (iscon(ins->l))
+ rswap(ins->l, ins->r);
+ goto ALU;
+ case Oneg:
+ if (kisflt(ins->cls)) {
+ /* flip sign bit with XORPS/D */
+ static const uvlong sd[2] = {0x8000000000000000,0x8000000000000000};
+ static const uint sf[4] = {0x80000000,80000000,0x80000000,80000000};
+ ins->op = Oxor;
+ ins->r = mkdatref(NULL, mktype(ins->cls == KF32 ? TYFLOAT : TYDOUBLE), /*siz*/16,
+ /*align*/16, ins->cls == KF32 ? (void *)sf : sd, /*siz*/16, /*deref*/1);
+ }
+ /* fallthru */
+ case Onot:
+ ALU:
+ if (!(op == Oadd && kisint(ins->cls))) /* 3-address add is lea */
+ if (!(op == Omul && kisint(ins->cls) && isimm32(ins->r))) /* for (I)MUL r,r/m,imm */
+ ins->inplace = 1;
+ if (iscon(ins->l)) {
+ fixarg(&ins->l, ins, blk, curi);
+ ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l));
+ }
+ if (ins->r.bits)
+ case Omove:
+ fixarg(&ins->r, ins, blk, curi);
+ if (op == Oadd && isaddrcon(ins->r,1)) /* no 3-address add if rhs is mem */
+ ins->inplace = 1;
+ break;
+ case Oloads8: case Oloadu8: case Oloads16: case Oloadu16:
+ case Oloads32: case Oloadu32: case Oloadi64: case Oloadf32: case Oloadf64:
+ loadstoreaddr(blk, &ins->l, curi);
+ break;
+ case Ostore8: case Ostore16: case Ostore32: case Ostore64:
+ loadstoreaddr(blk, &ins->l, curi);
+ if (isaddrcon(ins->r,1) || ins->r.t == RADDR)
+ ins->r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, ins->r));
+ else
+ fixarg(&ins->r, ins, blk, curi);
+ break;
+ case Ocvtu32f:
+ fixarg(&ins->l, ins, blk, curi);
+ ins->l = insertinstr(blk, (*curi)++, mkinstr(Oextu32, KI64, ins->l));
+ ins->op = Ocvts64f;
+ break;
+ case Ocvtf32u: case Ocvtf64u:
+ fixarg(&ins->l, ins, blk, curi);
+ if (ins->cls == KI32) {
+ ins->l = insertinstr(blk, (*curi)++, mkinstr(ins->op == Ocvtf32u ? Ocvtf32s : Ocvtf64s, KI64, ins->l));
+ ins->op = Oextu32;
+ } else assert(!"nyi flt -> u64");
+ break;
+ case Ocvtf32f64: case Ocvtf64f32: case Ocvtf32s: case Ocvtf64s: case Ocvts32f: case Ocvts64f:
+ case Ocvtu64f:
+ case Oexts8: case Oextu8: case Oexts16: case Oextu16: case Oexts32: case Oextu32:
+ if (isnumcon(ins->l)) {
+ union ref it;
+ bool ok = foldunop(&it, ins->op, ins->cls, ins->l);
+ assert(ok);
+ ins->op = Ocopy;
+ ins->l = it;
+ break;
+ }
+ case Ocopy:
+ fixarg(&ins->l, ins, blk, curi);
+ break;
+ case Oxvaprologue:
+ fuseaddr(&ins->l, blk, curi);
+ assert(ins->l.t == RADDR);
+ /* !this must be the first instruction */
+ assert(*curi == 1);
+ assert(blk == fn->entry);
+ t = blk->ins.p[0];
+ blk->ins.p[0] = blk->ins.p[1];
+ blk->ins.p[1] = t;
+ break;
+ }
+}
+
+static void
+seljmp(struct function *fn, struct block *blk)
+{
+ if (blk->jmp.t == Jb && blk->jmp.arg[0].bits) {
+ int curi = blk->ins.n;
+ fixarg(&blk->jmp.arg[0], NULL, blk, &curi);
+ union ref c = blk->jmp.arg[0];
+ if (c.t != RTMP) {
+ enum irclass cls = c.t == RICON ? KI32 : c.t == RXCON && conht[c.i].cls ? conht[c.i].cls : KPTR;
+ int curi = blk->ins.n;
+
+ c = insertinstr(blk, blk->ins.n, mkinstr(Ocopy, cls, c));
+ sel(fn, &instrtab[c.i], blk, &curi);
+ }
+ if (iflagsrc == c.i /* test cmp */
+ && (oiscmp(instrtab[c.i].op) || instrtab[c.i].op == Oand || instrtab[c.i].op == Osub)) {
+ instrtab[c.i].keep = 1;
+ } else {
+ if (!(opflags[instrtab[c.i].op] & ZF) || blk->ins.n == 0 || c.i != blk->ins.p[blk->ins.n - 1]) {
+ struct instr *ins;
+ int curi = blk->ins.n;
+ blk->jmp.arg[0] = insertinstr(blk, blk->ins.n, mkinstr(Oneq, instrtab[c.i].cls, c, ZEROREF));
+ ins = &instrtab[blk->jmp.arg[0].i];
+ if (kisflt(ins->cls)) {
+ ins->r = insertinstr(blk, curi, mkinstr(Ocopy, ins->cls, ZEROREF));
+ }
+ ins->keep = 1;
+ } else if (instrtab[c.i].op == Oadd) {
+ /* prevent a 3-address add whose flag results are used from becoming a LEA */
+ instrtab[c.i].inplace = 1;
+ }
+ }
+ } else if (blk->jmp.t == Jret) {
+ if (blk->jmp.arg[0].bits) {
+ int curi;
+ union ref r = mkref(RREG, fn->abiret[0].reg);
+ struct instr *ins = &instrtab[insertinstr(blk, blk->ins.n, mkinstr(Omove, fn->abiret[0].ty.cls, r , blk->jmp.arg[0])).i];
+ curi = blk->ins.n;
+ fixarg(&ins->r, ins, blk, &curi);
+ blk->jmp.arg[0] = r;
+ if (blk->jmp.arg[1].bits) {
+ r = mkref(RREG, fn->abiret[1].reg);
+ ins = &instrtab[insertinstr(blk, blk->ins.n, mkinstr(Omove, fn->abiret[1].ty.cls, r, blk->jmp.arg[1])).i];
+ curi = blk->ins.n;
+ fixarg(&ins->r, ins, blk, &curi);
+ blk->jmp.arg[1] = r;
+ }
+ }
+ }
+}
+
+void
+x86_64_isel(struct function *fn)
+{
+ extern int ninstr;
+ struct block *blk = fn->entry;
+
+ fn->stksiz = 0;
+ stkslots = xcalloc((nstkslots = ninstr) * sizeof *stkslots);
+ do {
+ int i;
+ for (i = 0; i < blk->phi.n; ++i) {
+ struct instr *ins = &instrtab[blk->phi.p[i]];
+ union ref *phi = phitab.p[ins->l.i];
+ for (int i = 0; i < blk->npred; ++i) {
+ int curi = blkpred(blk, i)->ins.n;
+ fixarg(&phi[i], ins, blkpred(blk, i), &curi);
+ }
+ }
+ iflagsrc = -1;
+ for (i = 0; i < blk->ins.n; ++i) {
+ struct instr *ins = &instrtab[blk->ins.p[i]];
+ sel(fn, ins, blk, &i);
+ if (ins->op < countof(opflags) && kisint(insrescls(*ins))) {
+ if (opflags[ins->op] & ZF) iflagsrc = ins - instrtab;
+ else if (opflags[ins->op] & CLOBF) iflagsrc = -1;
+ }
+ }
+ seljmp(fn, blk);
+ } while ((blk = blk->lnext) != fn->entry);
+ free(stkslots);
+
+ if (ccopt.dbg.i) {
+ bfmt(ccopt.dbgout, "<< After isel >>\n");
+ irdump(fn);
+ }
+
+ fn->prop = 0;
+}
+
+/* vim:set ts=3 sw=3 expandtab: */