aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--amd64/all.h15
-rw-r--r--amd64/emit.c458
-rw-r--r--amd64/isel.c247
-rw-r--r--amd64/sysv.c1
-rw-r--r--common.h9
-rw-r--r--ir.c22
-rw-r--r--ir.h26
-rw-r--r--irdump.c51
-rw-r--r--main.c1
-rw-r--r--op.def10
-rw-r--r--parse.c21
-rw-r--r--regalloc.c25
-rw-r--r--test/hello.c5
-rw-r--r--test/test3.c16
15 files changed, 742 insertions, 167 deletions
diff --git a/Makefile b/Makefile
index b9fe7fe..6ce0ce7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-SRC=main.c io.c mem.c parse.c lex.c type.c targ.c eval.c ir.c irdump.c abi0.c regalloc.c amd64/sysv.c amd64/emit.c obj.c
+SRC=main.c io.c mem.c parse.c lex.c type.c targ.c eval.c ir.c irdump.c abi0.c regalloc.c amd64/sysv.c amd64/isel.c amd64/emit.c obj.c
CFLAGS=-Wall -std=c11 -pedantic
OBJ=$(patsubst %.c,obj/%.o,$(SRC))
DEP=$(OBJ:.o=.d)
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
};
diff --git a/common.h b/common.h
index f45ce62..11741de 100644
--- a/common.h
+++ b/common.h
@@ -99,6 +99,7 @@ struct option {
struct {
bool p : 1, /* after parsing */
a : 1, /* after abi0 */
+ i : 1, /* after isel */
r : 1; /* after regalloc */
} dbg;
};
@@ -336,7 +337,7 @@ enum { BSNBIT = 8 * sizeof(uvlong) };
#define BSSIZE(nbit) ((nbit) / BSNBIT + ((nbit) % BSNBIT != 0))
static inline bool
-bstest(struct bitset *bs, uint i)
+bstest(const struct bitset *bs, uint i)
{
return bs[i / BSNBIT].u >> i % BSNBIT & 1;
}
@@ -359,6 +360,12 @@ bszero(struct bitset bs[/*siz*/], uint siz)
memset(bs, 0, siz * sizeof *bs);
}
+static inline void
+bscopy(struct bitset dst[/*siz*/], const struct bitset src[/*siz*/], uint siz)
+{
+ while (--siz) dst++->u = src++->u;
+}
+
static inline bool
bsiter(uint *i, struct bitset bs[/*siz*/], uint siz)
{
diff --git a/ir.c b/ir.c
index 0074040..e8a7173 100644
--- a/ir.c
+++ b/ir.c
@@ -10,17 +10,22 @@ static int instrfreelist;
struct calltab calltab;
struct phitab phitab;
struct dattab dattab;
+struct addrtab addrtab;
void
irinit(struct function *fn)
{
static struct call callsbuf[64];
static struct phi phisbuf[64];
+ static struct irdat datsbuf[64];
+ static struct addr addrsbuf[64];
ninstr = 0;
instrfreelist = -1;
vinit(&calltab, callsbuf, arraylength(callsbuf));
vinit(&phitab, phisbuf, arraylength(phisbuf));
+ vinit(&dattab, datsbuf, arraylength(datsbuf));
+ vinit(&addrtab, addrsbuf, arraylength(addrsbuf));
if (!type2cls[TYINT]) {
for (int i = TYBOOL; i <= TYUVLONG; ++i) {
int siz = targ_primsizes[i];
@@ -46,7 +51,7 @@ addcon(const struct xcon *con)
{
uint h = hashb(0, con, sizeof *con);
uint i = h, n = arraylength(conht);
- assert(con->issym || con->cls);
+ assert(con->issym || con->isdat || con->cls);
for (;; ++i) {
i &= arraylength(conht) - 1;
if (!conht[i].issym && !conht[i].cls) {
@@ -127,13 +132,13 @@ mkirtype(union type t)
union ref
mkintcon(struct function *fn, enum irclass k, vlong i)
{
- if (i < 1ll << 28 && i >= -(1ll << 28)) {
+ if (i < 1l << 28 && i >= -(1l << 28)) {
return mkref(RICON, i);
} else if (k == KI4) {
- struct xcon con = { 0, k, .i4 = i };
+ struct xcon con = { .cls = k, .i4 = i };
return mkref(RXCON, addcon(&con));
} else {
- struct xcon con = { 0, k, .i8 = i };
+ struct xcon con = { .cls = k, .i8 = i };
return mkref(RXCON, addcon(&con));
}
}
@@ -141,7 +146,7 @@ mkintcon(struct function *fn, enum irclass k, vlong i)
union ref
mkfltcon(struct function *fn, enum irclass k, double f)
{
- struct xcon con = { 0, k };
+ struct xcon con = { .cls = k };
if (k == KF4) con.fs = f;
else con.fd = f;
return mkref(RXCON, addcon(&con));
@@ -150,12 +155,12 @@ mkfltcon(struct function *fn, enum irclass k, double f)
union ref
mksymref(struct function *fn, const char *s)
{
- struct xcon con = { 1, KPTR, .sym = s };
+ struct xcon con = { .issym = 1, .sym = s };
return mkref(RXCON, addcon(&con));
}
union ref
-mkdatref(struct function *fn, uint siz, uint align, const void *bytes, uint n)
+mkdatref(struct function *fn, uint siz, uint align, const void *bytes, uint n, bool deref)
{
struct irdat dat = { align, 0, siz };
if (siz <= 8) memcpy(dat.sdat, bytes, n < siz ? n : siz);
@@ -164,7 +169,7 @@ mkdatref(struct function *fn, uint siz, uint align, const void *bytes, uint n)
vpushn(&dat.dat, bytes, n);
}
vpush(&dattab, dat);
- return mkref(RDAT, dattab.n - 1);
+ return mkref(RXCON, addcon(&(struct xcon){.isdat = 1, .deref = deref, .dat = dattab.n - 1}));
}
struct instr
@@ -374,6 +379,7 @@ irfini(struct function *fn)
extern int nerror;
if (!nerror) {
abi0(fn);
+ mctarg->isel(fn);
regalloc(fn);
mctarg->emit(fn);
}
diff --git a/ir.h b/ir.h
index 1b5e20f..bdace5e 100644
--- a/ir.h
+++ b/ir.h
@@ -31,10 +31,11 @@ struct irdat {
};
struct xcon {
- bool issym;
+ bool issym, isdat, deref;
uchar cls;
union {
const char *sym;
+ int dat;
int i4;
vlong i8;
float fs;
@@ -64,11 +65,11 @@ struct phi {
enum refkind {
RNONE,
RTMP, /* reference to another instruction's result */
+ RREG, /* machine register */
RPARAM, /* function param */
RICON, /* small integer constants */
RXCON, /* other constants (incl. external symbols) */
- RDAT, /* reference to irdat */
- RMORE, /* reference to extra data for Ocall and Ophi */
+ RMORE, /* Ocall -> calltab idx, Ophi -> phitab idx, else -> addrtab idx */
RTYPE, /* irtype */
};
@@ -77,6 +78,11 @@ union ref {
uint bits;
};
+struct addr {
+ union ref base, index;
+ int shift, disp;
+};
+
enum op {
Oxxx,
#define _(o,...) O##o,
@@ -98,7 +104,9 @@ enum intrin {
struct instr {
uchar op, cls;
- ushort reg; /* 0 -> unallocated; else reg + 1 */
+ uchar skip : 1; /* ignore during codegen: forms part of one machine instruction */
+ uchar inplace : 1; /* set (by isel) for instructions which modify its first arg in place */
+ uchar reg; /* 0 -> no reg; else reg + 1 */
union ref l, r;
};
@@ -115,6 +123,8 @@ struct block {
struct block *lprev, *lnext;
};
+enum { MAXREGS = 64 };
+
struct function {
struct arena *arena;
const char *name;
@@ -124,10 +134,9 @@ struct function {
uint nblk;
ushort nabiarg, nabiret;
bool globl;
+ struct bitset regusage[1];
};
-enum { MAXREGS = 64 };
-
struct mctarg {
short gpr0, /* first gpr */
ngpr, /* gpr count */
@@ -155,6 +164,7 @@ struct mctarg {
*/
int (*abiarg)(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype);
+ void (*isel)(struct function *);
void (*emit)(struct function *);
};
@@ -164,9 +174,11 @@ extern uchar type2cls[];
extern uchar cls2siz[];
extern const uchar siz2intcls[];
extern struct instr instrtab[];
+extern struct xcon conht[];
extern struct calltab {vec_of(struct call);} calltab;
extern struct phitab {vec_of(struct phi);} phitab;
extern struct dattab {vec_of(struct irdat);} dattab;
+extern struct addrtab {vec_of(struct addr);} addrtab;
#define NOREF ((union ref) {0})
#define ZEROREF ((union ref) {{ RICON, 0 }})
#define mkref(t, x) ((union ref) {{ (t), (x) }})
@@ -180,7 +192,7 @@ union irtype mkirtype(union type);
union ref mkintcon(struct function *, enum irclass, vlong);
union ref mkfltcon(struct function *, enum irclass, double);
union ref mksymref(struct function *, const char *);
-union ref mkdatref(struct function *, uint siz, uint align, const void *, uint n);
+union ref mkdatref(struct function *, uint siz, uint align, const void *, uint n, bool deref);
struct instr mkalloca(uint siz, uint align);
void conputdat(struct irdat *, uint off, enum typetag t, const void *dat);
union ref mkcallarg(struct function *, union irtype ret, uint narg, int vararg);
diff --git a/irdump.c b/irdump.c
index c51ed63..1bd8cff 100644
--- a/irdump.c
+++ b/irdump.c
@@ -1,6 +1,5 @@
#include "ir.h"
-extern struct xcon conht[];
static int nextdat;
#define aisprint(c) in_range(c, ' ', '~')
@@ -8,7 +7,7 @@ static int nextdat;
static void
pridat(const struct irdat *dat)
{
- efmt("dat ^%d(align %d, size %d):\n\t", dat - dattab.p, dat->align, dat->siz);
+ efmt("%s .%d(align %d, size %d):\n\t", dat->mut ? "dat" : "rodat", dat - dattab.p, dat->align, dat->siz);
assert(!dat->syms);
if (dat->siz <= 8) {
efmt("b ");
@@ -82,6 +81,9 @@ dumpref(enum op o, union ref ref)
else
efmt("%%%d", ref.i);
break;
+ case RREG:
+ efmt("%s", mctarg->rnames[ref.i]);
+ break;
case RPARAM: efmt("%%param%d", ref.i); break;
case RICON:
if (o == Ointrin) efmt("\"%s\"", intrinname[ref.i]);
@@ -89,7 +91,9 @@ dumpref(enum op o, union ref ref)
break;
case RXCON:
con = &conht[ref.i];
+ if (con->deref) efmt("[");
if (con->issym) efmt("$%s", con->sym);
+ else if (con->isdat) efmt("$.%d", con->dat);
else switch (con->cls) {
case KI4: efmt("%d", con->i4); break;
case KI8: efmt("%ld", con->i8); break;
@@ -98,9 +102,7 @@ dumpref(enum op o, union ref ref)
case KF8: efmt("%fd", con->fd); break;
default: assert(0);
}
- break;
- case RDAT:
- efmt("$^%d", ref.i);
+ if (con->deref) efmt("]");
break;
case RTYPE:
prityp(ref2type(ref));
@@ -108,12 +110,29 @@ dumpref(enum op o, union ref ref)
case RMORE:
if (o == Ophi) {
struct phi *phi = &phitab.p[ref.i];
+ assert(phitab.n > ref.i);
for (int i = 0; i < phi->n; ++i) {
if (i) efmt(", ");
efmt("@%d ", phi->blk[i]->id);
dumpref(0, phi->ref[i]);
}
- } else assert(0);
+ } else {
+ struct addr *addr = &addrtab.p[ref.i];
+ bool k = 0;
+ assert(addrtab.n > ref.i);
+ efmt("addr [");
+ if ((k = addr->base.t)) dumpref(0, addr->base);
+ if (addr->index.t) {
+ if (k) efmt(" + ");
+ dumpref(0, addr->index);
+ if (addr->shift)
+ efmt(" * %d", 1<<addr->shift);
+ k = 1;
+ }
+ if (k && addr->disp) efmt(" + %d", addr->disp);
+ assert(k);
+ efmt("]");
+ }
break;
default: assert(!"ref");
}
@@ -153,15 +172,19 @@ dumpinst(const struct instr *ins)
{
int i;
efmt(" ");
- if (ins->reg) {
- if (ins->cls)
- efmt("%s ", clsname[ins->cls]);
- efmt("%s = ", mctarg->rnames[ins->reg - 1]);
- } else if (ins->cls) {
- efmt("%s %%%d", clsname[ins->cls], ins - instrtab);
- efmt(" = ");
+ if (ins->op == Omove) {
+ efmt("move %s ", clsname[ins->cls]);
+ } else {
+ if (ins->reg) {
+ if (ins->cls)
+ efmt("%s ", clsname[ins->cls]);
+ efmt("%s = ", mctarg->rnames[ins->reg - 1]);
+ } else if (ins->cls && !ins->skip) {
+ efmt("%s %%%d", clsname[ins->cls], ins - instrtab);
+ efmt(" = ");
+ }
+ efmt("%s ", opname[ins->op]);
}
- efmt("%s ", opname[ins->op]);
for (i = 0; i < opnarg[ins->op]; ++i) {
if (i) efmt(", ");
if (i == 1 && (ins->op == Ocall || ins->op == Ointrin)) {
diff --git a/main.c b/main.c
index 0e91510..5e3e635 100644
--- a/main.c
+++ b/main.c
@@ -48,6 +48,7 @@ optparse(const char **file, const char **targ, char **args)
while (*++arg) switch (*arg | 32) {
case 'p': ccopt.dbg.p = 1; break;
case 'a': ccopt.dbg.a = 1; break;
+ case 'i': ccopt.dbg.i = 1; break;
case 'r': ccopt.dbg.r = 1; break;
default: warn(NULL, "-d: invalid debug flag %'c", *arg);
}
diff --git a/op.def b/op.def
index 752d43e..706f308 100644
--- a/op.def
+++ b/op.def
@@ -1,6 +1,7 @@
/* OP NARG */
+_(nop, 0)
_(copy, 1)
-_(move, 1)
+_(move, 2)
_(neg, 1)
_(not, 1)
_(cvtf4s, 1)
@@ -31,15 +32,18 @@ _(and, 2)
_(ior, 2)
_(xor, 2)
_(shl, 2)
-_(slr, 2)
_(sar, 2)
-_(shr, 2)
+_(slr, 2)
_(equ, 2)
_(neq, 2)
_(lth, 2)
+_(gth, 2)
_(lte, 2)
+_(gte, 2)
_(ulth, 2)
+_(ugth, 2)
_(ulte, 2)
+_(ugte, 2)
_(alloca1, 1)
_(alloca2, 1)
_(alloca4, 1)
diff --git a/parse.c b/parse.c
index 44e6a4f..e97c46a 100644
--- a/parse.c
+++ b/parse.c
@@ -1026,7 +1026,7 @@ expraddr(struct function *fn, const struct expr *ex)
}
break;
case ESTRLIT:
- return mkdatref(fn, ex->s.n+1, /*align*/ 1, ex->s.p, ex->s.n);
+ return mkdatref(fn, ex->s.n+1, /*align*/ 1, ex->s.p, ex->s.n, /*deref*/0);
case EDEREF:
return exprvalue(fn, ex->sub);
case EGETF:
@@ -1125,6 +1125,7 @@ cvt(struct function *fn, enum typetag to, enum typetag from, union ref ref)
}
else if (kfrom == KI4 && issignedt(from)) ins.op = Oexts4;
else if (kfrom == KI4) ins.op = Oextu4;
+ else if (ref.t == RXCON && kfrom == KI8) return mkintcon(fn, KI4, (int)(conht[ref.i].i8));
else ins.op = Ocopy;
}
return addinstr(fn, ins);
@@ -1422,7 +1423,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard)
ins.op = Oshl;
goto BinArith;
case ESHR:
- ins.op = issigned(ex->ty) ? Osar : Oshr;
+ ins.op = issigned(ex->ty) ? Osar : Oslr;
goto BinArith;
case ESUB:
ins.op = Osub;
@@ -1486,22 +1487,20 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard)
ins.op = Olte;
goto Cmp;
case EGTH:
- ins.op = Olth;
- swp = 1;
+ ins.op = Ogth;
goto Cmp;
case EGTE:
- ins.op = Olte;
- swp = 1;
+ ins.op = Ogte;
Cmp:
ty = cvtarith(sub[0].ty, sub[1].ty);
if (!ty.t) ty.t = TYPTR;
if (isunsigned(ty) && in_range(ins.op, Olth, Olte))
ins.op += Oulth - Olth;
- ins.l = compileexpr(fn, &sub[0^swp], discard);
- ins.r = compileexpr(fn, &sub[1^swp], discard);
+ ins.l = compileexpr(fn, &sub[0], discard);
+ ins.r = compileexpr(fn, &sub[1], discard);
if (discard) return NOREF;
- ins.l = cvt(fn, ty.t, sub[0^swp].ty.t, ins.l);
- ins.r = cvt(fn, ty.t, sub[1^swp].ty.t, ins.r);
+ ins.l = cvt(fn, ty.t, sub[0].ty.t, ins.l);
+ ins.r = cvt(fn, ty.t, sub[1].ty.t, ins.r);
ins.cls = cls;
return addinstr(fn, ins);
case ESET:
@@ -1533,7 +1532,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard)
ins.op = Oshl;
goto Compound;
case ESETSHR:
- ins.op = issigned(ex->ty) ? Osar : Oshr;
+ ins.op = issigned(ex->ty) ? Osar : Oslr;
goto Compound;
case ESETSUB:
ins.op = Osub;
diff --git a/regalloc.c b/regalloc.c
index 5caedda..ccec452 100644
--- a/regalloc.c
+++ b/regalloc.c
@@ -1,6 +1,7 @@
#include "ir.h"
static struct bitset taken[1];
+static struct bitset globusage[1];
static void
def(struct instr *ins)
@@ -9,6 +10,12 @@ def(struct instr *ins)
bsclr(taken, ins->reg - 1);
}
+static void
+take(int r) {
+ bsset(taken, r);
+ bsset(globusage, r);
+}
+
static int
nextreg(enum irclass cls)
{
@@ -24,7 +31,7 @@ nextreg(enum irclass cls)
} else assert(0);
for (i = r0; i < rend; ++i) {
if (!bstest(taken, i)) {
- bsset(taken, i);
+ take(i);
return i;
}
}
@@ -40,7 +47,11 @@ use(struct block *blk, enum op op, int hint, union ref *ref)
struct phi *phi = &phitab.p[ref->i];
for (int i = 0; i < phi->n; ++i)
use(blk, 0, hint, &phi->ref[i]);
- } else assert("ext?");
+ } else {
+ struct addr *addr = &addrtab.p[ref->i];
+ if (addr->base.t) use(blk, 0, hint, &addr->base);
+ if (addr->index.t) use(blk, 0, hint, &addr->index);
+ }
return;
} else if (ref->t != RTMP) return;
@@ -63,11 +74,12 @@ use(struct block *blk, enum op op, int hint, union ref *ref)
hint = call->abiret[0].reg;
}
if (hint != -1 && !bstest(taken, hint)) {
- bsset(taken, hint);
+ take(hint);
ins->reg = hint + 1;
} else {
ins->reg = nextreg(ins->cls) + 1;
}
+ *ref = mkref(RREG, ins->reg-1);
}
}
@@ -112,6 +124,10 @@ regalloc(struct function *fn)
for (int i = blk->ins.n - 1; i >= 0; --i) {
int hint0 = -1, hint1 = -1;
ins = &instrtab[blk->ins.p[i]];
+ if (!ins->reg && ins->skip) { /* unused */
+ *ins = mkinstr(Onop, 0,);
+ continue;
+ }
def(ins);
if (ins->op != Ocall) {
if (ins->op == Ocopy) hint0 = ins->reg - 1;
@@ -126,7 +142,7 @@ regalloc(struct function *fn)
if (reg != -1) {
assert(!bstest(taken, reg) && "nyi spill");
arg->reg = reg + 1;
- bsset(taken, reg);
+ take(reg);
}
}
use(blk, ins->op, hint0, &ins->l);
@@ -146,6 +162,7 @@ regalloc(struct function *fn)
efmt("after regalloc:\n");
irdump(fn, fn->name);
}
+ bscopy(fn->regusage, globusage, 1);
}
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/test/hello.c b/test/hello.c
index 17affa8..1e02bfc 100644
--- a/test/hello.c
+++ b/test/hello.c
@@ -1,5 +1,4 @@
-int printf(const char *, ...);
-
+printf();
int main(int argc) {
- return 42;
+ printf("hello world\n");
}
diff --git a/test/test3.c b/test/test3.c
index 6940d1b..e6599bb 100644
--- a/test/test3.c
+++ b/test/test3.c
@@ -4,6 +4,20 @@ int test1(int a, int b, int c) {
}
*/
-int t(int *p, int i) {
+int t(unsigned short *p, short i) {
return p[i];
}
+
+#if 1
+long test(long x) {
+ return x + (long)"abc";
+}
+#endif
+
+double ff(double x, double y)
+{
+ return x + y + .5;
+}
+
+long fma(long x, long y) {
+return x + (y <<1) - 2147483648;}