diff options
Diffstat (limited to 'amd64')
| -rw-r--r-- | amd64/emit.c | 30 | ||||
| -rw-r--r-- | amd64/isel.c | 41 | ||||
| -rw-r--r-- | amd64/sysv.c | 2 |
3 files changed, 60 insertions, 13 deletions
diff --git a/amd64/emit.c b/amd64/emit.c index 160c207..cf8d325 100644 --- a/amd64/emit.c +++ b/amd64/emit.c @@ -58,6 +58,7 @@ addmemoper(struct oper mem, struct oper add) enum operpat { PNONE, PRAX, + PRCX, PGPR, PFPR, P1, /* imm = 1 */ @@ -93,6 +94,7 @@ opermatch(enum operpat pat, struct oper oper) switch (pat) { case PNONE: return !oper.t; case PRAX: return oper.t == OREG && oper.reg == RAX; + case PRCX: return oper.t == OREG && oper.reg == RCX; case PGPR: return oper.t == OREG && oper.reg <= R15; case PFPR: return oper.t == OREG && oper.reg >= XMM0; case P1: return oper.t == OIMM && oper.imm == 1; @@ -240,7 +242,7 @@ DEFINSTR2(Xmov, {4, PMEM, PFPR, "\xF3\x0F\x10", EN_MR}, /* MOVSS m32, xmm */ {8, PFPR, PFPR, "\xF2\x0F\x10", EN_RR}, /* MOVSD xmm, xmm */ {8, PFPR, PMEM, "\xF2\x0F\x10", EN_RM}, /* MOVSD xmm, m64 */ - {8, PMEM, PFPR, "\xF2\x0F\x10", EN_MR}, /* MOVSS m64, xmm */ + {8, PMEM, PFPR, "\xF2\x0F\x11", EN_MR}, /* MOVSS m64, xmm */ ) DEFINSTR2(Xmovsx4, {8, PGPR, PMEM, "\x63", EN_RM}, /* MOVSXD r64, m32 */ @@ -279,8 +281,13 @@ DEFINSTR2(Xadd, {8, PFPR, PMEM, "\xF2\x0F\x58", EN_RM}, /* ADDSD xmm, m64 */ ) DEFINSTR2(Xshl, - {4|8, PGPR, P1, "\xD1", EN_R, .ext=4}, /* SHL r32/64, 1 */ - {4|8, PGPR, PI32, "\xC1", EN_RI8, .ext=4}, /* SHL r32/64, imm */ + {4|8, PGPR, P1, "\xD1", EN_R, .ext=4}, /* SHL r32/64, 1 */ + {4|8, PGPR, PI32, "\xC1", EN_RI8, .ext=4}, /* SHL r32/64, imm */ + {4|8, PGPR, PRCX, "\xD3", EN_R, .ext=4}, /* SHL r32/64, CL */ +) +DEFINSTR1(Xidiv, + {4|8, PGPR, 0, "\xF7", EN_R, .ext=7}, /* IDIV r32/64 */ + {4|8, PMEM, 0, "\xF7", EN_M, .ext=7}, /* IDIV m32/64 */ ) DEFINSTR1(Xcall, {-1, PSYM, 0, "\xE8", EN_R32}, /* CALL rel32 */ @@ -329,6 +336,13 @@ mkimmregoper(union ref r) } static inline struct oper +mkdatregoper(union ref r) +{ + assert(isregref(r) || (r.t == RXCON && conht[r.i].deref)); + return ref2oper(r); +} + +static inline struct oper mkimmdatregoper(union ref r) { assert(isregref(r) || r.t == RICON || (r.t == RXCON && (conht[r.i].cls == KI4 || conht[r.i].deref))); @@ -454,6 +468,16 @@ emitinstr(uchar **pcode, uint *stktop, struct function *fn, struct block *blk, i assert(dst.t == OREG && ins->reg-1 == dst.reg); X(pcode, ksiz, dst, mkimmdatregoper(ins->r)); break; + case Odiv: case Orem: + switch (ins->cls) { + case KI8: B(0x48); /* REX.W */ + case KI4: B(0x99); /* CDQ/CQO */ + assert(mkregoper(ins->l).reg == RAX); + Xidiv(pcode, ksiz, mkdatregoper(ins->r)); + break; + case KF4: case KF8: assert(0); + } + break; case Omove: dst = ref2oper(ins->l); gencopy(pcode, ins->cls, dst, ins->r); diff --git a/amd64/isel.c b/amd64/isel.c index f99c8a2..e6cc692 100644 --- a/amd64/isel.c +++ b/amd64/isel.c @@ -6,14 +6,16 @@ static void fixarg(struct function *fn, union ref *r, struct instr *ins, struct block *blk, int *curi) { int sh; + enum op op = ins->op; + if (r->t == RXCON) { struct xcon *con = &conht[r->i]; - if (in_range(ins->op, Oshl, Oslr)) { + if (in_range(op, Oshl, Oslr)) { sh = con->cls == KI4 ? con->i4 : con->i8; goto ShiftImm; - } else if (in_range(ins->op, Oadd, Osub) && con->i8 == 2147483648) { + } else if (in_range(op, Oadd, Osub) && con->i8 == 2147483648) { /* add X, INT32MAX+1 -> sub X, INT32MIN */ - ins->op = Oadd + (ins->op == Oadd); + ins->op = Oadd + (op == Oadd); *r = mkintcon(fn, KI4, -2147483648); } else if (con->cls && (kisflt(con->cls) || con->cls == KI8)) { /* float immediates & >32b immediates are loaded from memory */ @@ -22,8 +24,12 @@ fixarg(struct function *fn, union ref *r, struct instr *ins, struct block *blk, if (con->cls == KI4 || con->cls == KF4) wr32le(data, con->i4); else wr64le(data, con->i8); *r = mkdatref(fn, siz, /*align*/siz, data, siz, /*deref*/1); - } - } else if (in_range(ins->op, Oshl, Oslr) && r->t == RICON) { + } 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)) { + 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)) { sh = r->i; ShiftImm: /* shift immediate is always 8bit */ *r = mkref(RICON, sh & 255); @@ -131,8 +137,9 @@ static void sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) { struct instr temp = {0}; + enum op op = ins->op; - switch (ins->op) { + switch (op) { case Oshl: case Osar: case Oslr: if (!iscon(ins->r)) { /* shift amount register is always CL */ @@ -144,10 +151,25 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) case Oulth: case Ougth: case Oulte: case Ougte: if (iscon(ins->l)) { /* lth imm, x -> gth x, imm */ - ins->op = ((ins->op - Olth) ^ 1) + Olth; + ins->op = ((op - Olth) ^ 1) + Olth; rswap(ins->l, ins->r); } goto ALU; + 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(fn, &ins->r, ins, blk, curi); /* make sure rhs is memory or reg */ + ins->l = mkref(RREG, RAX); + 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); + break; case Osub: if (iscon(ins->l)) { /* sub imm, x -> sub x, imm; neg x */ @@ -168,6 +190,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) break; } } + /* fallthru */ case Omul: case Oumul: case Oand: case Oxor: case Oior: case Oequ: case Oneq: @@ -177,8 +200,8 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) case Oneg: case Onot: case Oexts1: case Oextu1: case Oexts2: case Oextu2: case Oexts4: case Oextu4: ALU: - if (!(ins->op == Oadd && kisint(ins->cls))) /* 3-address add is lea */ - if (!(in_range(ins->op, Omul, Oumul) && kisint(ins->cls) && isimm32(ins->r))) /* for (I)MUL r,r/m,imm */ + if (!(op == Oadd && kisint(ins->cls))) /* 3-address add is lea */ + if (!(in_range(op, Omul, Oumul) && kisint(ins->cls) && isimm32(ins->r))) /* for (I)MUL r,r/m,imm */ ins->inplace = 1; if (ins->l.t != RTMP && ins->l.t != RREG) ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l)); diff --git a/amd64/sysv.c b/amd64/sysv.c index 1128cc2..7d391dc 100644 --- a/amd64/sysv.c +++ b/amd64/sysv.c @@ -139,7 +139,7 @@ const char amd64_rnames[][6] = { const struct mctarg t_amd64_sysv = { .gpr0 = RAX, .ngpr = R15 - RAX + 1, - .spr = RSP, + .fpr = RBP, .fpr0 = XMM0, .nfpr = XMM15 - XMM0 + 1, .rcallee = {{1<<RBX | 1<<R12 | 1<<R13 | 1<<R14 | 1<<R15}}, .rglob = {{1<<RSP | 1<<RBP}}, |