aboutsummaryrefslogtreecommitdiffhomepage
path: root/amd64/emit.c
diff options
context:
space:
mode:
Diffstat (limited to 'amd64/emit.c')
-rw-r--r--amd64/emit.c458
1 files changed, 344 insertions, 114 deletions
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);