From 24bcc929477751b056e81e7772dc2bb3d11ce4a5 Mon Sep 17 00:00:00 2001 From: lemon Date: Fri, 12 Dec 2025 17:40:35 +0100 Subject: s/amd64/x86_64/ --- x86_64/isel.c | 660 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 660 insertions(+) create mode 100644 x86_64/isel.c (limited to 'x86_64/isel.c') 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: */ -- cgit v1.2.3