aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--amd64/all.h4
-rw-r--r--amd64/emit.c323
-rw-r--r--amd64/sysv.c1
-rw-r--r--common.h2
-rw-r--r--io.c23
-rw-r--r--ir.c3
-rw-r--r--ir.h5
-rw-r--r--irdump.c9
-rw-r--r--main.c3
-rw-r--r--obj.c27
-rw-r--r--obj.h12
-rw-r--r--op.def8
-rw-r--r--test/hello.c4
-rw-r--r--test/test3.c9
15 files changed, 417 insertions, 18 deletions
diff --git a/Makefile b/Makefile
index 3379e44..b9fe7fe 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
+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
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 915d93a..b8bb699 100644
--- a/amd64/all.h
+++ b/amd64/all.h
@@ -6,10 +6,12 @@
_(XMM0) _(XMM1) _(XMM2) _(XMM3) _(XMM4) _(XMM5) _(XMM6) _(XMM7) \
_(XMM8) _(XMM9) _(XMM10) _(XMM11) _(XMM12) _(XMM13) _(XMM14) _(XMM15)
-enum {
+enum reg {
#define R(r) r,
LIST_REGS(R)
#undef R
};
+void amd64_emit(struct function *);
+
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/amd64/emit.c b/amd64/emit.c
new file mode 100644
index 0000000..1dd6797
--- /dev/null
+++ b/amd64/emit.c
@@ -0,0 +1,323 @@
+#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 };
+static struct oper {
+ uchar siz : 4, t : 4;
+ struct { uchar shift, index, base; }; /* OMEM */
+ union {
+ uchar reg; /* OGPR/FPR */
+ int disp; /* OMEM */
+ int imm; /* OIMM */
+ };
+} 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};
+
+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 RXCON:
+ assert(conht[r.i].cls == KI4);
+ return mkoper(OIMM, Z32, .imm = conht[r.i].i4);
+ default: assert(0);
+ }
+}
+
+enum { NOBASE = RBP, NOINDEX = RSP };
+
+static struct oper
+addmemoper(struct oper mem, struct oper add)
+{
+ assert(mem.t == OMEM);
+ if (add.t == OIMM) {
+ mem.disp += add.imm;
+ } else if (add.t == OGPR) {
+ assert(mem.index == NOINDEX);
+ mem.index = add.reg;
+ }
+ return mem;
+}
+
+enum operenc {
+ EN_RR = 1, /* reg, reg with /r */
+ EN_MR, /* mem, reg with /r */
+ EN_RM, /* reg, mem with /r */
+ EN_RI32, /* reg, imm32 with /0 */
+ EN_OI, /* reg, imm32 with op + reg */
+};
+struct desc {
+ uchar siz;
+ uchar td, ts;
+ const char *opc;
+ uchar ext;
+ uchar operenc;
+};
+
+static void
+encode(uchar **pcode, const struct desc *tab, int ntab, struct oper dst, struct oper src)
+{
+ const char *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) {
+ en = &tab[i];
+ break;
+ }
+ }
+ assert(en && "no match for instr");
+
+ rex = (en->siz == Z64) << 3; /* REX.W */
+ opc = en->opc;
+ nopc = strlen(opc);
+ 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);
+ D(opc, nopc);
+ B(0300 | (dst.reg & 7) << 3 | (src.reg & 7));
+ break;
+ case EN_MR:
+ mem = dst;
+ reg = src.reg;
+ goto Mem;
+ case EN_RM:
+ mem = src;
+ reg = dst.reg;
+ Mem:
+ 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);
+ if (mem.index == NOINDEX && mem.shift == 0) sib = 0;
+ 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;
+ 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);
+ break;
+ case EN_RI32:
+ rex |= (dst.reg >> 3) << 0; /* REX.B */
+ if (rex) B(0x40 | rex);
+ D(opc, nopc);
+ B(0300 | en->ext << 3 | (dst.reg & 7));
+ I32(src.imm);
+ break;
+ case EN_OI:
+ rex |= (dst.reg >> 3) << 0; /* REX.B */
+ if (rex) B(0x40 | rex);
+ B(*opc++ + (dst.reg & 7));
+ D(opc, nopc - 1);
+ I32(src.imm);
+ break;
+ }
+}
+
+#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); \
+ }
+
+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 */
+)
+DEFINSTR2(Xlea,
+ {Z32, OGPR, OMEM, "\x8D", 0, EN_RM}, /* LEA r32,m32 */
+ {Z64, OGPR, OMEM, "\x8D", 0, EN_RM}, /* LEA r64,m64 */
+)
+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},
+)
+
+static bool /* stack frame size <= 128? */
+smallstack(struct function *fn)
+{
+ uint stktop = 0;
+ struct block *blk = fn->entry;
+ do {
+ for (int i = 0; i < blk->ins.n; ++i) {
+ struct instr *ins = &instrtab[blk->ins.p[i]];
+ if (oisalloca(ins->op)) {
+ uint align = 1 << (ins->op - Oalloca1);
+ uint siz = ins->l.i * align;
+ stktop = alignup(stktop + siz, align);
+ if (alignup(stktop, 16) > 128) return 0;
+ }
+ }
+ } while ((blk = blk->lnext) != fn->entry);
+ return 1;
+}
+
+static void
+aligncode(uchar **pcode, int align)
+{
+ int rem;
+ while ((rem = (*pcode - objout.textbegin) & (align - 1)) != 0) {
+ switch (align - rem) {
+ case 15: case 14: case 13: case 12: case 11: case 10:
+ case 9: B(0x66);
+ case 8: DS("\x0f\x1f\x84\x00\x00\x00\x00\x00"); break;
+ case 7: DS("\x0f\x1f\x80\x00\x00\x00\x00"); break;
+ case 6: B(0x66);
+ case 5: DS("\x0f\x1f\x44\x00\x00"); break;
+ case 4: DS("\x0f\x1f\x40\x00"); break;
+ case 3: DS("\x0f\1f\00"); break;
+ case 2: B(0x66);
+ case 1: B(0x90); break;
+ }
+ }
+}
+
+#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);
+ uchar **pcode = &objout.code;
+
+ aligncode(pcode, 16);
+
+ /** prologue **/
+ /* push rbp; mov rbp, rsp */
+ DS("\x55\x48\x89\xE5");
+ /* sub rsp, <stack size> */
+ if (stack8)
+ DS("\x48\x83\xEC"), rspdisp = *pcode, DS("\xAA");
+ else
+ DS("\x48\x81\xEC"), rspdisp = *pcode, DS("\xAA\xAA\xAA\xAA");
+
+ 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]);
+ }
+ 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 */
+ DS("\xC9\xC3"); /* leave; ret */
+ }
+ } while ((blk = blk->lnext) != fn->entry);
+
+ stktop = alignup(stktop, 16);
+ if (stack8) {
+ assert(stktop <= 128);
+ if (stktop < 128) *rspdisp = stktop;
+ else {
+ /* cannot encode `sub rsp, 128` with 8bit imm, turn into `add rsp, -128` */
+ rspdisp[-1] = 0xC4;
+ *rspdisp = -128;
+ }
+ } else {
+ wr32le(rspdisp, alignup(stktop, 16));
+ }
+}
+
+void
+amd64_emit(struct function *fn)
+{
+ emitbin(fn);
+}
+
+/* vim:set ts=3 sw=3 expandtab: */
diff --git a/amd64/sysv.c b/amd64/sysv.c
index f909067..da7469a 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,
+ .emit = amd64_emit
};
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/common.h b/common.h
index 5ebbcd8..f45ce62 100644
--- a/common.h
+++ b/common.h
@@ -398,6 +398,8 @@ int bfmt(struct wbuf *, const char *, ...);
#define efmt(...) bfmt(&bstderr, __VA_ARGS__)
struct memfile mapopen(const char **err, const char *path);
void mapclose(struct memfile *);
+void *mapzeros(uint);
+int munmap(void *, size_t);
int openfile(const char **err, struct memfile **, const char *path);
const char *getfilename(int id);
struct memfile *getfile(int id);
diff --git a/io.c b/io.c
index 9156d27..cc94a08 100644
--- a/io.c
+++ b/io.c
@@ -671,14 +671,6 @@ Err:
}
void
-_assertfmt(const char *file, int line, const char *func, const char *expr)
-{
- ioflush(&bstdout);
- efmt("%s:%d: %s: Assertion `%s' failed.\n", file, line, func, expr);
- ioflush(&bstderr);
-}
-
-void
mapclose(struct memfile *f)
{
assert(f->p);
@@ -686,6 +678,21 @@ mapclose(struct memfile *f)
memset(f, 0, sizeof *f);
}
+void *
+mapzeros(uint N)
+{
+ void *p = mmap(NULL, N, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ return p == MAP_FAILED ? NULL : p;
+}
+
+void
+_assertfmt(const char *file, int line, const char *func, const char *expr)
+{
+ ioflush(&bstdout);
+ efmt("%s:%d: %s: Assertion `%s' failed.\n", file, line, func, expr);
+ ioflush(&bstderr);
+}
+
static struct file {
const char *path;
struct memfile f;
diff --git a/ir.c b/ir.c
index 1d937cf..0074040 100644
--- a/ir.c
+++ b/ir.c
@@ -4,7 +4,7 @@ uchar type2cls[NTYPETAG];
uchar cls2siz[KF8+1];
const uchar siz2intcls[] = { [1] = KI4, [2] = KI4, [4] = KI4, [8] = KI8 };
-struct instr instrtab[1<<14];
+struct instr instrtab[MAXINSTR];
static int ninstr;
static int instrfreelist;
struct calltab calltab;
@@ -375,6 +375,7 @@ irfini(struct function *fn)
if (!nerror) {
abi0(fn);
regalloc(fn);
+ mctarg->emit(fn);
}
freefn(fn);
diff --git a/ir.h b/ir.h
index 2b6267a..1b5e20f 100644
--- a/ir.h
+++ b/ir.h
@@ -87,6 +87,7 @@ enum op {
#define oiscmp(o) in_range(o, Oequ, Oulte)
#define oisalloca(o) in_range(o, Oalloca1, Oalloca16)
#define oisstore(o) in_range(o, Ostore1, Ostore8)
+#define oisload(o) in_range(o, Oloads1, Oloadf8)
enum intrin {
INxxx,
@@ -153,8 +154,12 @@ struct mctarg {
* if passed by pointer cls[0] == KPTR, r[0] contains integer register or -1 if stack
*/
int (*abiarg)(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype);
+
+ void (*emit)(struct function *);
};
+enum { MAXINSTR = 1<<14 };
+
extern uchar type2cls[];
extern uchar cls2siz[];
extern const uchar siz2intcls[];
diff --git a/irdump.c b/irdump.c
index 0b6424b..c51ed63 100644
--- a/irdump.c
+++ b/irdump.c
@@ -106,7 +106,14 @@ dumpref(enum op o, union ref ref)
prityp(ref2type(ref));
break;
case RMORE:
- assert(0);
+ if (o == Ophi) {
+ struct phi *phi = &phitab.p[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);
break;
default: assert(!"ref");
}
diff --git a/main.c b/main.c
index d91188f..0e91510 100644
--- a/main.c
+++ b/main.c
@@ -1,5 +1,6 @@
#include "common.h"
#include "parse.h"
+#include "obj.h"
#include <stdlib.h>
#include <unistd.h>
@@ -80,6 +81,8 @@ main(int argc, char **argv)
}
targ_init(targ ? targ : "amd64-sysv");
+ objini("a.out");
initparser(&pr, file);
parse(&pr);
+ if (!nerror) objfini();
}
diff --git a/obj.c b/obj.c
new file mode 100644
index 0000000..78aab14
--- /dev/null
+++ b/obj.c
@@ -0,0 +1,27 @@
+#include "obj.h"
+#include "common.h"
+
+struct objfile objout;
+
+void
+objini(const char *file)
+{
+ enum { NTEXT = 4<<20 /* 4MiB */ };
+ assert(!objout.file);
+ objout.file = file;
+ objout.code = objout.textbegin = mapzeros(NTEXT);
+ objout.textend = objout.textbegin + NTEXT;
+}
+
+void
+objfini(void)
+{
+ void *popen(char *, char *), pclose(void *);
+ long fwrite(void *, size_t, size_t, void *);
+ void *cmd = popen("ndisasm -b64 -", "w");
+ ioflush(&bstderr); ioflush(&bstdout);
+ fwrite(objout.textbegin, 1, objout.code - objout.textbegin, cmd);
+ pclose(cmd);
+}
+
+/* vim:set ts=3 sw=3 expandtab: */
diff --git a/obj.h b/obj.h
new file mode 100644
index 0000000..0258119
--- /dev/null
+++ b/obj.h
@@ -0,0 +1,12 @@
+#include "common.h"
+
+extern struct objfile {
+ const char *file;
+ uchar *textbegin, *textend;
+ uchar *code;
+} objout;
+
+void objini(const char *);
+void objfini(void);
+
+/* vim:set ts=3 sw=3 expandtab: */
diff --git a/op.def b/op.def
index dd559e5..752d43e 100644
--- a/op.def
+++ b/op.def
@@ -13,12 +13,12 @@ _(cvts4f, 1)
_(cvtu4f, 1)
_(cvts8f, 1)
_(cvtu8f, 1)
-_(extu1, 1)
-_(extu2, 1)
-_(extu4, 1)
_(exts1, 1)
+_(extu1, 1)
_(exts2, 1)
+_(extu2, 1)
_(exts4, 1)
+_(extu4, 1)
_(add, 2)
_(sub, 2)
_(mul, 2)
@@ -51,8 +51,8 @@ _(loads2, 1)
_(loadu2, 1)
_(loads4, 1)
_(loadu4, 1)
-_(loadf4, 1)
_(loadi8, 1)
+_(loadf4, 1)
_(loadf8, 1)
_(store1, 2)
_(store2, 2)
diff --git a/test/hello.c b/test/hello.c
index 98a93a0..17affa8 100644
--- a/test/hello.c
+++ b/test/hello.c
@@ -1,5 +1,5 @@
int printf(const char *, ...);
-int main() {
- printf("hello world\n");
+int main(int argc) {
+ return 42;
}
diff --git a/test/test3.c b/test/test3.c
new file mode 100644
index 0000000..6940d1b
--- /dev/null
+++ b/test/test3.c
@@ -0,0 +1,9 @@
+/*
+int test1(int a, int b, int c) {
+ return a && b ? c - b ? c - b : a+b : 7;
+}
+*/
+
+int t(int *p, int i) {
+ return p[i];
+}