aboutsummaryrefslogtreecommitdiffhomepage
path: root/amd64
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2023-06-10 14:22:03 +0200
committerlemon <lsof@mailbox.org>2023-06-10 14:22:03 +0200
commit5ac04c7a3ec11d939a3773876b6924e1ae39f1a5 (patch)
tree28712d39b25ee80bc971cced1d2cba8387694412 /amd64
parent22e20cf906fa3904eedfe34efc58a7b56116ce42 (diff)
isel skeleton
Diffstat (limited to 'amd64')
-rw-r--r--amd64/all.h15
-rw-r--r--amd64/emit.c458
-rw-r--r--amd64/isel.c247
-rw-r--r--amd64/sysv.c1
4 files changed, 607 insertions, 114 deletions
diff --git a/amd64/all.h b/amd64/all.h
index b8bb699..3b08761 100644
--- a/amd64/all.h
+++ b/amd64/all.h
@@ -12,6 +12,21 @@ enum reg {
#undef R
};
+static inline void
+wr32le(uchar *p, uint x)
+{
+ p[0] = x >> 0; p[1] = x >> 8;
+ p[2] = x >> 16; p[3] = x >> 24;
+}
+
+static inline void
+wr64le(uchar *p, uvlong x)
+{
+ wr32le(p+0, x>>00);
+ wr32le(p+4, x>>32);
+}
+
+void amd64_isel(struct function *);
void amd64_emit(struct function *);
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/amd64/emit.c b/amd64/emit.c
index 1dd6797..b7289d8 100644
--- a/amd64/emit.c
+++ b/amd64/emit.c
@@ -1,48 +1,46 @@
#include "all.h"
#include "../obj.h"
-static inline void
-wr32le(uchar *p, uint x)
-{
- p[0] = x >> 0; p[1] = x >> 8;
- p[2] = x >> 16; p[3] = x >> 24;
-}
-
#define B(b) (*(*pcode)++ = (b))
#define D(xs, N) (memcpy(*pcode, (xs), (N)), (*pcode) += (N))
#define I32(w) (wr32le(*pcode, (w)), *pcode += 4)
#define DS(S) D(S, sizeof S - 1)
-enum operkind { ONONE, OMEM, OGPR, OFPR, OIMM };
-enum size { Z8, Z16, Z32, Z64 };
+enum operkind { ONONE, OREG, OIMM, OMEM, ORDAT };
static struct oper {
- uchar siz : 4, t : 4;
+ uchar t;
struct { uchar shift, index, base; }; /* OMEM */
union {
- uchar reg; /* OGPR/FPR */
+ uchar reg; /* OREG */
int disp; /* OMEM */
int imm; /* OIMM */
+ int dat; /* ORDAT */
};
} ioper[MAXINSTR];
-#define mkoper(t, sz, ...) ((struct oper){(sz), (t), __VA_ARGS__})
-#define reg2oper(R, sz) mkoper(OGPR + ((R) >= XMM0), sz, .reg = (R))
-static const char size4cls[] = {Z64, [KI4] = Z32, [KI8] = Z64, [KPTR] = Z64, [KF4] = Z32, [KF8] = Z64};
+#define mkoper(t, ...) ((struct oper){(t), __VA_ARGS__})
+#define reg2oper(R) (assert((uint)(R) <= XMM15), mkoper(OREG, .reg = (R)))
+
+static struct oper mkmemoper(union ref);
static struct oper
ref2oper(union ref r)
{
- extern struct xcon conht[];
switch (r.t) {
case RTMP: return ioper[r.i];
- case RICON: return mkoper(OIMM, Z32, .imm = r.i);
+ case RREG: return reg2oper(r.i);
+ case RICON: return mkoper(OIMM, .imm = r.i);
case RXCON:
- assert(conht[r.i].cls == KI4);
- return mkoper(OIMM, Z32, .imm = conht[r.i].i4);
+ if (conht[r.i].cls == KI4)
+ return mkoper(OIMM, .imm = conht[r.i].i4);
+ else if (conht[r.i].deref)
+ return mkoper(ORDAT, .dat = conht[r.i].dat);
+ assert(0);
+ case RMORE: return mkmemoper(r);
default: assert(0);
}
}
-enum { NOBASE = RBP, NOINDEX = RSP };
+enum { NOBASE = 99, NOINDEX = 99 };
static struct oper
addmemoper(struct oper mem, struct oper add)
@@ -50,55 +48,91 @@ addmemoper(struct oper mem, struct oper add)
assert(mem.t == OMEM);
if (add.t == OIMM) {
mem.disp += add.imm;
- } else if (add.t == OGPR) {
+ } else if (add.t == OREG) {
assert(mem.index == NOINDEX);
mem.index = add.reg;
}
return mem;
}
+enum operpat {
+ PRAX,
+ PGPR,
+ PFPR,
+ P1, /* imm = 1 */
+ PI8,
+ PI32,
+ PMEM,
+};
enum operenc {
- EN_RR = 1, /* reg, reg with /r */
+ EN_R = 1, /* reg with /r */
+ EN_RR, /* reg, reg with /r */
EN_MR, /* mem, reg with /r */
EN_RM, /* reg, mem with /r */
+ EN_RI8, /* reg, imm8 with /0 */
EN_RI32, /* reg, imm32 with /0 */
+ EN_I32, /* imm32 */
EN_OI, /* reg, imm32 with op + reg */
};
struct desc {
- uchar siz;
- uchar td, ts;
+ uchar psiz; /* subset of {1,2,4,8} */
+ uchar ptd, pts; /* bitsets of enum operpat */
const char *opc;
- uchar ext;
- uchar operenc;
+ uchar operenc; /* enum operenc */
+ uchar ext; /* ModR/M.reg opc extension */
+ bool r8 : 1;
};
+static inline bool
+opermatch(enum operpat pat, struct oper oper)
+{
+ switch (pat) {
+ case PRAX: return oper.t == OREG && oper.reg == RAX;
+ 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;
+ case PI8: return oper.t == OIMM && (uint)(oper.imm+128) < 256;
+ case PI32: return oper.t == OIMM;
+ case PMEM: return in_range(oper.t, OMEM, ORDAT);
+ }
+ assert(0);
+}
+
static void
-encode(uchar **pcode, const struct desc *tab, int ntab, struct oper dst, struct oper src)
+encode(uchar **pcode, const struct desc *tab, int ntab, uint siz, struct oper dst, struct oper src)
{
- const char *opc;
+ const uchar *opc;
int nopc, mod, rex;
bool sib = 0;
uchar reg;
struct oper mem;
const struct desc *en = NULL;
-
+
for (int i = 0; i < ntab; ++i) {
- // efmt("<%d, %d>, <%d, %d>, <%d, %d>\n", tab[i].siz, dst.siz, tab[i].td, dst.t, tab[i].ts, src.t);
- if (dst.siz == tab[i].siz && tab[i].td == dst.t && tab[i].ts == src.t) {
+ if ((tab[i].psiz & siz) && opermatch(tab[i].ptd, dst) && opermatch(tab[i].pts, src)) {
en = &tab[i];
break;
}
}
assert(en && "no match for instr");
- rex = (en->siz == Z64) << 3; /* REX.W */
- opc = en->opc;
- nopc = strlen(opc);
+ if (en->ptd == PFPR) dst.reg &= 15;
+ if (en->pts == PFPR) src.reg &= 15;
+ opc = (uchar *)en->opc;
+ nopc = strlen(en->opc);
+ /* mandatory prefixes go before REX */
+ if (*opc == 0x66 || *opc == 0xF2 || *opc == 0xF3)
+ B(*opc++), --nopc;
+ rex = -(en->ptd != PFPR && en->pts != PFPR) & (siz == 8) << 3; /* REX.W */
switch (en->operenc) {
case EN_RR: /* mod = 11; reg = dst; rm = src */
rex |= (dst.reg >> 3) << 2; /* REX.R */
rex |= (src.reg >> 3) << 0; /* REX.B */
if (rex) B(0x40 | rex);
+ else if (en->r8 && in_range(dst.reg, RSP, RDI)) {
+ /* /r8 needs REX to encode SP,BP,SI,DI (otherwise -> AH..BH) */
+ B(0x40);
+ }
D(opc, nopc);
B(0300 | (dst.reg & 7) << 3 | (src.reg & 7));
break;
@@ -110,30 +144,46 @@ encode(uchar **pcode, const struct desc *tab, int ntab, struct oper dst, struct
mem = src;
reg = dst.reg;
Mem:
+ if (mem.t == ORDAT) { /* RIP-relative addressing with relocation */
+ mod = 0;
+ mem.disp = mem.dat;
+ mem.base = RBP;
+ sib = 0;
+ if (rex) B(0x40 | rex);
+ goto EmitMem;
+ }
rex |= ( reg >> 3) << 2; /* REX.R */
rex |= (mem.base >> 3) << 0; /* REX.B */
if (rex) B(0x40 | rex);
- else if (en->siz == Z8 && in_range(reg, RSP, RDI))
- /* /r8 needs REX to encode SP,BP,SI,DI (otherwise -> AH..BH) */
- B(0x40);
+ else if (en->r8 && in_range(reg, RSP, RDI)) B(0x40);
if (mem.index == NOINDEX && mem.shift == 0) sib = 0;
+ else sib = 1;
mod = !mem.disp ? 0 /* disp = 0 -> mod = 00 */
: (uint)(mem.disp + 128) < 256 ? 1 /* disp8 -> mod = 01 */
: 2; /* disp32 -> mod = 10 */
if (mod == 0 && (mem.base == RBP || mem.base == R13)) mod = 1;
if (mem.base == RSP || mem.base == R12) sib = 1;
+ EmitMem:
D(opc, nopc);
B(mod << 6 | (reg & 7) << 3 | (sib ? 4 : mem.base));
if (sib)
B(mem.shift << 6 | (mem.index & 7) << 3 | (mem.base & 7));
if (mod == 1) B(mem.disp);
- else if (mod == 2) I32(mem.disp);
+ else if (mod == 2 || (mod == 0 && mem.base == RBP)) I32(mem.disp);
break;
- case EN_RI32:
+ case EN_R: case EN_RI32: case EN_RI8:
rex |= (dst.reg >> 3) << 0; /* REX.B */
if (rex) B(0x40 | rex);
D(opc, nopc);
B(0300 | en->ext << 3 | (dst.reg & 7));
+ if (en->operenc == EN_RI32)
+ I32(src.imm);
+ else if (en->operenc == EN_RI8)
+ B(src.imm);
+ break;
+ case EN_I32:
+ if (rex) B(0x40 | rex);
+ D(opc, nopc);
I32(src.imm);
break;
case EN_OI:
@@ -146,36 +196,266 @@ encode(uchar **pcode, const struct desc *tab, int ntab, struct oper dst, struct
}
}
-#define DEFINSTR2(X, ...) \
- static void \
- X(uchar **pcode, struct oper dst, struct oper src) \
- { \
- static const struct desc tab[] = { __VA_ARGS__ }; \
- encode(pcode, tab, arraylength(tab), dst, src); \
+#define DEFINSTR2(X, ...) \
+ static void \
+ X(uchar **pcode, uint siz, struct oper dst, struct oper src) \
+ { \
+ static const struct desc tab[] = { __VA_ARGS__ }; \
+ encode(pcode, tab, arraylength(tab), siz, dst, src); \
}
DEFINSTR2(Xmov,
- {Z8, OMEM, OGPR, "\x88", 0, EN_MR}, /* MOV m8, r8 */
- {Z16, OMEM, OGPR, "\x66\x89", 0, EN_MR}, /* MOV m16, r16 */
- {Z32, OGPR, OGPR, "\x89", 0, EN_RR}, /* MOV r32, r32 */
- {Z32, OMEM, OGPR, "\x89", 0, EN_MR}, /* MOV m32, r32 */
- {Z32, OGPR, OMEM, "\x8B", 0, EN_RM}, /* MOV r32, m32 */
- {Z32, OGPR, OIMM, "\xB8", 0, EN_OI}, /* MOV r32, imm */
- {Z64, OGPR, OGPR, "\x89", 0, EN_RR}, /* MOV r64, r64 */
- {Z64, OMEM, OGPR, "\x89", 0, EN_MR}, /* MOV m64, r64 */
- {Z64, OGPR, OMEM, "\x8B", 0, EN_RM}, /* MOV r64, m64 */
- {Z32, OGPR, OIMM, "\xB8", 0, EN_OI}, /* MOV r64, imm */
+ {1, PMEM, PGPR, "\x88", EN_MR, .r8=1}, /* MOV m8, r8 */
+ {2, PMEM, PGPR, "\x66\x89", EN_MR}, /* MOV m16, r16 */
+ {4|8, PGPR, PGPR, "\x89", EN_RR}, /* MOV r32/64, r32/64 */
+ {4|8, PMEM, PGPR, "\x89", EN_MR}, /* MOV m32/64, r32/64 */
+ {4|8, PGPR, PMEM, "\x8B", EN_RM}, /* MOV r32/64, m32/64 */
+ {4|8, PGPR, PI32, "\xB8", EN_OI}, /* MOV r32/64, imm */
+ {4, PFPR, PFPR, "\xF3\x0F\x10", EN_RR}, /* MOVSS xmm, xmm */
+ {4, PFPR, PMEM, "\xF3\x0F\x10", EN_RM}, /* MOVSS xmm, m32 */
+ {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 */
+)
+DEFINSTR2(Xmovsx4,
+ {8, PGPR, PMEM, "\x63", EN_RM}, /* MOVSXD r64, m32 */
+ {8, PGPR, PGPR, "\x63", EN_RR}, /* MOVSXD r64, r32 */
+ {4, PGPR, PMEM, "\x8B", EN_RM}, /* MOV r32, m32 */
+ {4, PGPR, PGPR, "\x89", EN_RR}, /* MOV r32, r32 */
+)
+DEFINSTR2(Xmovsx2,
+ {4|8, PGPR, PMEM, "\x0F\xBF", EN_RM}, /* MOVSX r64, m16 */
+ {4|8, PGPR, PGPR, "\x0F\xBF", EN_RR}, /* MOVSX r64, r16 */
+)
+DEFINSTR2(Xmovsx1,
+ {4|8, PGPR, PMEM, "\x0F\xBE", EN_RM}, /* MOVSX r64, m8 */
+ {4|8, PGPR, PGPR, "\x0F\xBE", EN_RR, .r8=1}, /* MOVSX r64, r8 */
+)
+DEFINSTR2(Xmovzx2,
+ {4|8, PGPR, PMEM, "\x0F\xB7", EN_RM}, /* MOVZX r64, m16 */
+ {4|8, PGPR, PGPR, "\x0F\xB7", EN_RR}, /* MOVZX r64, r16 */
+)
+DEFINSTR2(Xmovzx1,
+ {4|8, PGPR, PMEM, "\x0F\xB6", EN_RM}, /* MOVZX r64, m8 */
+ {4|8, PGPR, PGPR, "\x0F\xB6", EN_RR, .r8=1}, /* MOVZX r64, r8 */
)
DEFINSTR2(Xlea,
- {Z32, OGPR, OMEM, "\x8D", 0, EN_RM}, /* LEA r32,m32 */
- {Z64, OGPR, OMEM, "\x8D", 0, EN_RM}, /* LEA r64,m64 */
+ {4|8, PGPR, PMEM, "\x8D", EN_RM}, /* LEA r32/64,m32/64 */
)
DEFINSTR2(Xadd,
- {Z32, OGPR, OGPR, "\x03", 0, EN_RR},
- {Z32, OGPR, OIMM, "\x81", 0, EN_RI32},
- {Z64, OGPR, OGPR, "\x03", 0, EN_RR},
- {Z64, OGPR, OIMM, "\x81", 0, EN_RI32},
+ {4|8, PGPR, PGPR, "\x03", EN_RR}, /* ADD r32/64, r32/64 */
+ {4|8, PGPR, PI8, "\x83", EN_RI8}, /* ADD r32/64, imm8 */
+ {4|8, PRAX, PI32, "\x05", EN_I32}, /* ADD eax/rax, imm */
+ {4|8, PGPR, PI32, "\x81", EN_RI32}, /* ADD r32/64, imm */
+ { 8, PGPR, PMEM, "\x03", EN_RM}, /* ADD r64, m64 */
+ {4, PFPR, PFPR, "\xF3\x0F\x58", EN_RR}, /* ADDSS xmm, xmm */
+ {8, PFPR, PFPR, "\xF2\x0F\x58", EN_RR}, /* ADDSD xmm, xmm */
+ {4, PFPR, PMEM, "\xF3\x0F\x58", EN_RM}, /* ADDSS xmm, m32 */
+ {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 */
+)
+
+static void
+Xpush(uchar **pcode, enum reg reg)
+{
+ assert(in_range(reg, RAX, R15));
+ if (reg >> 3) B(0x44); /* REX.R */
+ B(0x50 + (reg & 7));
+}
+
+static void
+Xpop(uchar **pcode, enum reg reg)
+{
+ assert(in_range(reg, RAX, R15));
+ if (reg >> 3) B(0x44); /* REX.R */
+ B(0x58 + (reg & 7));
+}
+
+#define ismemref(ref) ((ref).t == RTMP && ioper[(ref).i].t == OMEM)
+#define isregref(ref) ((ref).t == RREG || ((ref).t == RTMP && ioper[(ref).i].t == OREG))
+
+static inline struct oper
+mkregoper(union ref r)
+{
+ assert(r.t == RREG || (r.t == RTMP && ioper[r.i].t == RREG));
+ return r.t == RREG ? reg2oper(r.i) : ioper[r.i];
+}
+
+static inline struct oper
+mkimmoper(union ref r)
+{
+ assert(r.t == RICON || (r.t == RXCON && conht[r.i].cls == KI4));
+ return mkoper(OIMM, .imm = r.t == RICON ? r.i : conht[r.i].i4);
+}
+
+static inline struct oper
+mkimmregoper(union ref r)
+{
+ assert(isregref(r) || r.t == RICON || (r.t == RXCON && conht[r.i].cls == KI4));
+ 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)));
+ return ref2oper(r);
+}
+
+static struct oper
+mkmemoper(union ref r)
+{
+ if (r.t == RTMP) {
+ struct oper wop = ioper[r.i];
+ if (wop.t == OMEM) return wop;
+ assert(wop.t == OREG);
+ return mkoper(OMEM, .base = wop.reg, .index = NOINDEX);
+ } else if (r.t == RMORE) {
+ struct addr *addr = &addrtab.p[r.i];
+ return mkoper(OMEM, .base = addr->base.t ? mkregoper(addr->base).reg : NOBASE,
+ .index = addr->index.t ? mkregoper(addr->index).reg : NOINDEX,
+ .disp = addr->disp,
+ .shift = addr->shift);
+ } else {
+ return mkoper(OMEM, .base = isregref(r) ? ref2oper(r).reg : NOBASE,
+ .index = NOINDEX,
+ .disp = isregref(r) ? 0 : mkimmoper(r).imm);
+ }
+}
+
+static void
+gencopy(uchar **pcode, enum irclass cls, struct oper dst, union ref val)
+{
+ assert(dst.t == OREG);
+ if (val.t == RMORE) {
+ /* this is a LEA, but maybe it can be lowered to a 2-address instruction */
+ struct addr *addr = &addrtab.p[val.i];
+ if (addr->base.t && dst.reg == mkregoper(addr->base).reg) { /* base = dst */
+ if (addr->index.t && !addr->disp && !addr->shift){
+ /* lea Rx, [Rx + Ry] -> add Rx, Ry */
+ Xadd(pcode, cls2siz[cls], dst, mkregoper(addr->index));
+ return;
+ } else if (!addr->index.t) {
+ if (!addr->disp) /* lea Rx, [Rx] -> mov Rx, Rx */
+ Xmov(pcode, cls2siz[cls], dst, dst);
+ else /* lea Rx, [Rx + Imm] -> add Rx, Imm */
+ Xadd(pcode, cls2siz[cls], dst, mkoper(OIMM, .imm = addr->disp));
+ return;
+ }
+ } else if (addr->index.t && dst.reg == mkregoper(addr->index).reg) { /* index = dst */
+ if (addr->base.t && !addr->disp && !addr->shift) {
+ /* lea Rx, [Ry + Rx] -> add Rx, Ry */
+ Xadd(pcode, cls2siz[cls], dst, mkregoper(addr->base));
+ return;
+ } else if (!addr->base.t) {
+ if (!addr->disp && !addr->shift) /* lea Rx, [Rx] -> mov Rx, Rx */
+ Xmov(pcode, cls2siz[cls], dst, dst);
+ else if (!addr->shift) /* lea Rx, [Rx + Imm] -> add Rx, Imm */
+ Xadd(pcode, cls2siz[cls], dst, mkoper(OIMM, .imm = addr->disp));
+ else if (!addr->disp) /* lea Rx, [Rx LSL s] -> shl Rx, s */
+ Xshl(pcode, cls2siz[cls], dst, mkoper(OIMM, .imm = addr->shift));
+ else
+ goto Lea;
+ return;
+ }
+ }
+ /* normal (not 2-address) case */
+ Lea:
+ Xlea(pcode, cls2siz[cls], dst, ref2oper(val));
+ } else if (val.t == RXCON && conht[val.i].isdat && !conht[val.i].deref) {
+ Xlea(pcode, cls2siz[cls], dst, mkoper(ORDAT, .dat = conht[val.i].dat));
+ } else {
+ struct oper src = mkimmregoper(val);
+ if (memcmp(&dst, &src, sizeof dst) != 0)
+ Xmov(pcode, cls2siz[cls], dst, src);
+ }
+}
+
+static void
+emitinstr(uchar **pcode, uint *stktop, struct function *fn, struct block *blk, int ii, struct instr *ins)
+{
+ struct oper dst, src;
+ uchar ksiz = cls2siz[ins->cls];
+ void (*X)(uchar **, uint, struct oper, struct oper) = NULL;
+
+ if (oisalloca(ins->op)) {
+ uint alignlog2 = ins->op - Oalloca1;
+ uint siz = ins->l.i << alignlog2;
+ *stktop += siz;
+ *stktop = alignup(*stktop, 1 << alignlog2);
+ ioper[ins - instrtab] = mkoper(OMEM, .base = RBP, .index = NOINDEX, .disp = -*stktop);
+ } else if (oisstore(ins->op)) {
+ dst = mkmemoper(ins->l);
+ if (ins->r.t == RPARAM) {
+ int off = 8;
+ struct abiarg abi;
+ for (int i = 0; i < ins->r.i; ++i) {
+ abi = fn->abiarg[ins->r.i];
+ if (abi.reg == -1)
+ off = alignup(off + typedata[abi.ty.dat].siz, 8);
+ }
+ assert(abi.reg == -1 && "reg par");
+ assert(!"nyi");
+ } else {
+ Xmov(pcode, 1 << (ins->op - Ostore1), dst, mkimmregoper(ins->r));
+ }
+ } else switch (ins->op) {
+ default: assert(!"nyi ins");
+ case Onop: break;
+ case Oexts1: src = mkregoper(ins->l); goto Movsx1;
+ case Oextu1: src = mkregoper(ins->l); goto Movzx1;
+ case Oexts2: src = mkregoper(ins->l); goto Movsx2;
+ case Oextu2: src = mkregoper(ins->l); goto Movzx2;
+ case Oexts4: src = mkregoper(ins->l); goto Movsx4;
+ case Oextu4: src = mkregoper(ins->l); goto Movzx4;
+ case Oloads1: src = mkmemoper(ins->l); Movsx1: Xmovsx1(pcode, ksiz, reg2oper(ins->reg-1), src); break;
+ case Oloadu1: src = mkmemoper(ins->l); Movzx1: Xmovzx1(pcode, ksiz, reg2oper(ins->reg-1), src); break;
+ case Oloads2: src = mkmemoper(ins->l); Movsx2: Xmovsx2(pcode, ksiz, reg2oper(ins->reg-1), src); break;
+ case Oloadu2: src = mkmemoper(ins->l); Movzx2: Xmovzx2(pcode, ksiz, reg2oper(ins->reg-1), src); break;
+ case Oloads4: src = mkmemoper(ins->l); Movsx4: Xmovsx4(pcode, ksiz, reg2oper(ins->reg-1), src); break;
+ case Oloadu4: src = mkmemoper(ins->l); Movzx4: Xmov(pcode, 4, reg2oper(ins->reg-1), src); break;
+ case Oloadf4: case Oloadf8:
+ case Oloadi8: Xmov(pcode, ksiz, reg2oper(ins->reg-1), mkmemoper(ins->l)); break;
+ case Oadd: X = Xadd; goto ALU2;
+ case Oshl: X = Xshl; goto ALU2;
+ ALU2:
+ dst = ref2oper(ins->l);
+ assert(dst.t == OREG && ins->reg-1 == dst.reg);
+ X(pcode, ksiz, dst, mkimmdatregoper(ins->r));
+ break;
+ case Omove:
+ dst = ref2oper(ins->l);
+ gencopy(pcode, ins->cls, dst, ins->r);
+ break;
+ case Ocopy:
+ dst = reg2oper(ins->reg-1);
+ gencopy(pcode, ins->cls, dst, ins->l);
+ break;
+ }
+ if (ins->reg) ioper[ins - instrtab] = reg2oper(ins->reg-1);
+}
+
+static void
+calleesave(uchar **pcode, struct function *fn)
+{
+ if (bstest(fn->regusage, RBX)) Xpush(pcode, RBX);
+ for (int r = R12; r <= R15; ++r)
+ if (bstest(fn->regusage, r))
+ Xpush(pcode, r);
+}
+
+static void
+calleerestore(uchar **pcode, struct function *fn)
+{
+ for (int r = R15; r >= R12; --r)
+ if (bstest(fn->regusage, r))
+ Xpop(pcode, r);
+ if (bstest(fn->regusage, RBX)) Xpop(pcode, RBX);
+}
static bool /* stack frame size <= 128? */
smallstack(struct function *fn)
@@ -196,6 +476,7 @@ smallstack(struct function *fn)
return 1;
}
+/* align code using NOPs */
static void
aligncode(uchar **pcode, int align)
{
@@ -216,14 +497,10 @@ aligncode(uchar **pcode, int align)
}
}
-#define ismemref(ref) ((ref).t == RTMP && ioper[(ref).i].t == OMEM)
-#define isregref(ref) ((ref).t == RTMP && in_range(ioper[(ref).i].t, OGPR, OFPR))
-
static void
emitbin(struct function *fn)
{
struct block *blk;
- const struct instr *ins;
uchar *rspdisp;
uint stktop = 0;
bool stack8 = smallstack(fn);
@@ -234,6 +511,7 @@ emitbin(struct function *fn)
/** prologue **/
/* push rbp; mov rbp, rsp */
DS("\x55\x48\x89\xE5");
+ calleesave(pcode, fn);
/* sub rsp, <stack size> */
if (stack8)
DS("\x48\x83\xEC"), rspdisp = *pcode, DS("\xAA");
@@ -243,59 +521,11 @@ emitbin(struct function *fn)
blk = fn->entry;
do {
for (int i = 0; i < blk->ins.n; ++i) {
- struct oper dst, src;
- ins = &instrtab[blk->ins.p[i]];
- if (oisalloca(ins->op)) {
- uint alignlog2 = ins->op - Oalloca1;
- uint siz = ins->l.i << alignlog2;
- stktop += siz;
- stktop = alignup(stktop, 1 << alignlog2);
- ioper[ins - instrtab] = mkoper(OMEM, alignlog2, .base = RBP, .index = NOINDEX, .disp = -stktop);
- } else if (oisstore(ins->op)) {
- dst = ref2oper(ins->l);
- if (!ismemref(ins->l)) {
- assert(isregref(ins->l));
- dst = mkoper(OMEM, ins->op - Ostore1, .base = dst.reg, .index = NOINDEX);
- }
- if (ins->r.t == RPARAM) {
- struct abiarg abi = fn->abiarg[ins->r.i];
- assert(abi.reg >= 0 && "stk");
- Xmov(pcode, ref2oper(ins->l), mkoper(OGPR, ioper[ins->l.t].siz, .reg = abi.reg));
- } else {
- src = ref2oper(ins->r);
- Xmov(pcode, ref2oper(ins->l), ref2oper(ins->r));
- }
- } else if (oisload(ins->op)) {
- src = ref2oper(ins->l);
- if (src.t == OGPR) src = mkoper(OMEM, size4cls[ins->cls], .base = src.reg, .index = NOINDEX);
- assert(ins->reg);
- assert(src.t == OMEM);
- if (in_range(ins->op, Oloads4, Oloadi8)) {
- Xmov(pcode, reg2oper(ins->reg-1, size4cls[ins->cls]), src);
- } else assert(0);
- } else switch (ins->op) {
- default: if (ins->reg) assert(!"nyi ins");
- case Oadd:
- if (ismemref(ins->l)) {
- assert(ins->reg);
- Xlea(pcode, reg2oper(ins->reg-1, size4cls[ins->cls]),
- addmemoper(ioper[ins->l.i], ref2oper(ins->r)));
- } else {
- struct oper l = ref2oper(ins->l);
- assert(in_range(l.t, OGPR, OFPR) && ins->reg && ins->reg-1 == l.reg);
- Xadd(pcode, l, ref2oper(ins->r));
- }
- break;
- }
- if (ins->reg) ioper[ins - instrtab] = reg2oper(ins->reg-1, size4cls[ins->cls]);
+ emitinstr(pcode, &stktop, fn, blk, i, &instrtab[blk->ins.p[i]]);
}
if (blk->jmp.t == Jret) {
- if (blk->jmp.arg[0].t) {
- struct oper rval = ref2oper(blk->jmp.arg[0]);
- if (!in_range(rval.t, OGPR, OFPR) || rval.reg != fn->abiret[0].reg)
- Xmov(pcode, reg2oper(fn->abiret[0].reg, size4cls[fn->abiret[0].ty.cls]), rval);
- }
/* epilogue */
+ calleerestore(pcode, fn);
DS("\xC9\xC3"); /* leave; ret */
}
} while ((blk = blk->lnext) != fn->entry);
diff --git a/amd64/isel.c b/amd64/isel.c
new file mode 100644
index 0000000..7883e28
--- /dev/null
+++ b/amd64/isel.c
@@ -0,0 +1,247 @@
+#include "all.h"
+
+static bool mkaddr(struct function *fn, union ref *r);
+
+static void
+fixarg(struct function *fn, union ref *r, struct instr *ins, struct block *blk, int *curi)
+{
+ int sh;
+ if (r->t == RXCON) {
+ struct xcon *con = &conht[r->i];
+ if (in_range(ins->op, Oshl, Oslr)) {
+ sh = con->cls == KI4 ? con->i4 : con->i8;
+ goto ShiftImm;
+ } else if (in_range(ins->op, Oadd, Osub) && con->i8 == 2147483648) {
+ /* add X, INT32MAX+1 -> sub X, INT32MIN */
+ ins->op = Oadd + (ins->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 */
+ uchar data[8];
+ uint siz = cls2siz[con->cls];
+ 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) {
+ sh = r->i;
+ ShiftImm: /* shift immediate is always 8bit */
+ *r = mkref(RICON, sh & 255);
+ } else if (r->t == RPARAM) {
+ if (fn->abiarg[r->i].reg != -1) {
+ *r = mkref(RREG, fn->abiarg[r->i].reg);
+ }
+ }
+}
+
+#define iscon(r) (in_range((r).t, RICON, RXCON))
+#define isimm32(r) ((r).t == RICON || ((r).t == RXCON && conht[(r).i].cls == KI4))
+#define rswap(a,b) do { union ref _t = (a); (a) = (b); (b) = _t; } while (0)
+
+static bool
+acon(struct function *fn, struct addr *addr, union ref r)
+{
+ vlong a = addr->disp;
+ if (r.t == RICON) {
+ a += r.i;
+ } else {
+ assert(r.t == RXCON && kisint(conht[r.i].cls));
+ a += conht[r.i].cls == KI4 ? conht[r.i].i4 : conht[r.i].i8;
+ }
+ if ((int)a == a) {
+ addr->disp = a;
+ return 1;
+ }
+ return 0;
+}
+
+static bool
+ascale(struct function *fn, struct addr *addr, union ref a, union ref b)
+{
+ if (b.t != RICON) return 0;
+ if (addr->index.t) return 0;
+ if (a.t != RTMP && a.t != RREG) return 0;
+ if ((unsigned)b.i > 3) return 0;
+ addr->shift = b.i;
+ addr->index = a;
+ return 1;
+}
+
+static bool
+aadd(struct function *fn, struct addr *addr, union ref r, bool rec)
+{
+ struct instr *ins = &instrtab[r.i];
+ if (r.t == RTMP && ins->op == Oadd) {
+ if (!aadd(fn, addr, ins->l, rec)) return 0;
+ if (!aadd(fn, addr, ins->r, rec)) return 0;
+ ins->skip = 1;
+ } else if (r.t == RTMP && ins->op == Oshl) {
+ if (!ascale(fn, addr, ins->l, ins->r)) return 0;
+ ins->skip = 1;
+ } else if (!rec && r.t == RTMP && ins->op == Ocopy && ins->l.t == RMORE) {
+ struct addr save = *addr, *addr2 = &addrtab.p[ins->l.i];
+ if ((!addr2->base.t || aadd(fn, addr, addr2->base, 1))
+ && aadd(fn, addr, mkintcon(fn, KI4, addr2->disp), 1)
+ && (!addr2->index.t || ascale(fn, addr, addr2->index, mkref(RICON, addr2->shift))))
+ {
+ ins->skip = 1;
+ } else {
+ *addr = save;
+ goto Ref;
+ }
+ } else if (iscon(r)) return acon(fn, addr, r);
+ else Ref: {
+ if (!addr->base.t) addr->base = r;
+ else if (!addr->index.t) addr->index = r;
+ else return 0;
+ }
+ return 1;
+}
+
+static bool
+mkaddr(struct function *fn, union ref *r)
+{
+ struct addr addr = { 0 };
+ struct instr *ins = &instrtab[r->i];
+ if (r->t == RMORE) return 1;
+ else if (r->t != RTMP) return 0;
+ else if (ins->op == Oadd) {
+ if (ins->l.t == RTMP && instrtab[ins->l.i].op == Ocopy && instrtab[ins->l.i].l.t == RMORE)
+ /* put ADDR in rhs because this code is dumb and it might be better */
+ rswap(ins->l, ins->r);
+ if (!aadd(fn, &addr, ins->l, 0)) return 0;
+ if (!aadd(fn, &addr, ins->r, 0)) return 0;
+ ins->skip = 1;
+ } else if (ins->op == Oshl) {
+ if (!ascale(fn, &addr, ins->l, ins->r)) return 0;
+ ins->skip = 1;
+ }
+ else return 0;
+ vpush(&addrtab, addr);
+ *r = mkref(RMORE, addrtab.n-1);
+ return 1;
+}
+
+/* is add instruction with this arg a candidate to transform into efective addr? */
+static bool
+addarg4addrp(union ref r)
+{
+ struct instr *ins = &instrtab[r.i];
+ if (r.t != RTMP) return 0;
+ return ins->op == Oshl || (ins->op == Ocopy && ins->l.t == RMORE) || ins->op == Oadd;
+}
+
+static void
+sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
+{
+ struct instr temp = {0};
+
+ switch (ins->op) {
+ case Oshl: case Osar: case Oslr:
+ if (!iscon(ins->r)) {
+ /* shift amount register is always CL */
+ insertinstr(blk, (*curi)++, mkinstr(Omove, KI4, mkref(RREG, RCX), ins->r));
+ ins->r = mkref(RREG, RCX);
+ }
+ goto ALU;
+ 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 */
+ ins->op = ((ins->op - Olth) ^ 1) + Olth;
+ rswap(ins->l, ins->r);
+ }
+ goto ALU;
+ case Osub:
+ if (iscon(ins->l)) {
+ /* sub imm, x -> sub x, imm; neg x */
+ struct instr sub = *ins;
+ rswap(sub.l, sub.r);
+ ins->op = Oneg;
+ ins->l = insertinstr(blk, (*curi)++, sub);
+ ins->r = NOREF;
+ }
+ goto ALU;
+ case Oadd:
+ if (kisint(ins->cls) && (addarg4addrp(ins->l) || addarg4addrp(ins->r))) {
+ temp.op = Ocopy;
+ temp.cls = ins->cls;
+ temp.l = mkref(RTMP, ins - instrtab);
+ if (mkaddr(fn, &temp.l)) {
+ *ins = temp;
+ break;
+ }
+ }
+ case Omul: case Oumul:
+ case Oand: case Oxor: case Oior:
+ case Oequ: case Oneq:
+ /* commutative ops */
+ if (iscon(ins->l))
+ rswap(ins->l, ins->r);
+ 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 */
+ ins->inplace = 1;
+ if (ins->l.t != RTMP && ins->l.t != RREG)
+ ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l));
+ if (ins->r.t)
+ fixarg(fn, &ins->r, ins, blk, curi);
+ break;
+ case Oloads1: case Oloadu1: case Oloads2: case Oloadu2:
+ case Oloads4: case Oloadu4: case Oloadi8: case Oloadf4: case Oloadf8:
+ if (ins->l.t != RTMP && ins->l.t != RREG)
+ ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l));
+ mkaddr(fn, &ins->l);
+ break;
+ case Ostore1: case Ostore2: case Ostore4: case Ostore8:
+ if (ins->l.t != RTMP && ins->l.t != RREG)
+ ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l));
+ mkaddr(fn, &ins->l);
+ fixarg(fn, &ins->r, ins, blk, curi);
+ break;
+ case Ocopy:
+ fixarg(fn, &ins->l, ins, blk, curi);
+ break;
+ }
+}
+
+void
+amd64_isel(struct function *fn)
+{
+ struct block *blk = fn->entry;
+
+ do {
+ struct instr *ins;
+ for (int i = 0; i < blk->phi.n; ++i) {
+ struct phi *phi = &phitab.p[instrtab[blk->phi.p[i]].l.i];
+ for (int i = 0; i < phi->n; ++i) {
+ fixarg(fn, &phi->ref[i], NULL, NULL, NULL);
+ }
+ }
+ for (int i = 0; i < blk->ins.n; ++i) {
+ ins = &instrtab[blk->ins.p[i]];
+ sel(fn, ins, blk, &i);
+ }
+ if (blk->jmp.t == Jret) {
+ if (blk->jmp.arg[0].t) {
+ insertinstr(blk, blk->ins.n, mkinstr(Omove, fn->abiret[0].ty.cls,
+ mkref(RREG, fn->abiret[0].reg), blk->jmp.arg[0]));
+ blk->jmp.arg[0] = mkref(RREG, fn->abiret[0].reg);
+ if (blk->jmp.arg[1].t) {
+ insertinstr(blk, blk->ins.n, mkinstr(Omove, fn->abiret[1].ty.cls,
+ mkref(RREG, fn->abiret[1].reg), blk->jmp.arg[1]));
+ blk->jmp.arg[1] = mkref(RREG, fn->abiret[1].reg);
+ }
+ }
+ }
+ } while ((blk = blk->lnext) != fn->entry);
+
+ if (ccopt.dbg.i) {
+ efmt("after isel:\n");
+ irdump(fn, fn->name);
+ }
+}
+
+/* vim:set ts=3 sw=3 expandtab: */
diff --git a/amd64/sysv.c b/amd64/sysv.c
index da7469a..69ebce4 100644
--- a/amd64/sysv.c
+++ b/amd64/sysv.c
@@ -143,6 +143,7 @@ const struct mctarg t_amd64_sysv = {
.rnames = amd64_rnames,
.abiret = abiret,
.abiarg = abiarg,
+ .isel = amd64_isel,
.emit = amd64_emit
};