diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | amd64/all.h | 15 | ||||
| -rw-r--r-- | amd64/emit.c | 458 | ||||
| -rw-r--r-- | amd64/isel.c | 247 | ||||
| -rw-r--r-- | amd64/sysv.c | 1 | ||||
| -rw-r--r-- | common.h | 9 | ||||
| -rw-r--r-- | ir.c | 22 | ||||
| -rw-r--r-- | ir.h | 26 | ||||
| -rw-r--r-- | irdump.c | 51 | ||||
| -rw-r--r-- | main.c | 1 | ||||
| -rw-r--r-- | op.def | 10 | ||||
| -rw-r--r-- | parse.c | 21 | ||||
| -rw-r--r-- | regalloc.c | 25 | ||||
| -rw-r--r-- | test/hello.c | 5 | ||||
| -rw-r--r-- | test/test3.c | 16 |
15 files changed, 742 insertions, 167 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 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 }; @@ -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) { @@ -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); } @@ -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); @@ -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)) { @@ -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); } @@ -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) @@ -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; @@ -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;} |