diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | amd64/all.h | 4 | ||||
| -rw-r--r-- | amd64/emit.c | 323 | ||||
| -rw-r--r-- | amd64/sysv.c | 1 | ||||
| -rw-r--r-- | common.h | 2 | ||||
| -rw-r--r-- | io.c | 23 | ||||
| -rw-r--r-- | ir.c | 3 | ||||
| -rw-r--r-- | ir.h | 5 | ||||
| -rw-r--r-- | irdump.c | 9 | ||||
| -rw-r--r-- | main.c | 3 | ||||
| -rw-r--r-- | obj.c | 27 | ||||
| -rw-r--r-- | obj.h | 12 | ||||
| -rw-r--r-- | op.def | 8 | ||||
| -rw-r--r-- | test/hello.c | 4 | ||||
| -rw-r--r-- | test/test3.c | 9 |
15 files changed, 417 insertions, 18 deletions
@@ -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: */ @@ -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); @@ -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; @@ -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); @@ -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[]; @@ -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"); } @@ -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(); } @@ -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: */ @@ -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: */ @@ -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]; +} |