aboutsummaryrefslogtreecommitdiffhomepage
path: root/amd64
diff options
context:
space:
mode:
Diffstat (limited to 'amd64')
-rw-r--r--amd64/emit.c30
-rw-r--r--amd64/isel.c41
-rw-r--r--amd64/sysv.c2
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}},