diff options
| -rw-r--r-- | amd64/emit.c | 243 | ||||
| -rw-r--r-- | amd64/isel.c | 118 | ||||
| -rw-r--r-- | c.c | 5 | ||||
| -rw-r--r-- | ir.c | 9 | ||||
| -rw-r--r-- | ir.h | 1 | ||||
| -rw-r--r-- | irdump.c | 8 | ||||
| -rw-r--r-- | regalloc.c | 7 | ||||
| -rw-r--r-- | test/fib.c | 19 |
8 files changed, 370 insertions, 40 deletions
diff --git a/amd64/emit.c b/amd64/emit.c index 955d59d..e9561d3 100644 --- a/amd64/emit.c +++ b/amd64/emit.c @@ -168,7 +168,7 @@ enum operenc { EN_RI8, /* reg, imm8 with /0 */ EN_RI32, /* reg, imm32 with /0 */ EN_MI8, /* mem, imm8 with /x */ - EN_MI16, /* mem, imm16 with /x */ + EN_MI16, /* mem, imm16 with /x */ EN_MI32, /* mem, imm32 with /x */ EN_OI, /* reg, imm32 with op + reg */ EN_I32, /* imm32 */ @@ -361,6 +361,7 @@ static void Xmov(uchar **pcode, enum irclass k, struct oper dst, struct oper src {4|8, PGPR, PGPR, "\x8B", 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, PMEM, PI32, "\xC7", EN_MI32}, /* MOV m32/64, imm */ { 8, PGPR, PU32, "\xB8", EN_OI, .norexw=1}, /* MOV r64, uimm */ { 8, PGPR, PI32, "\xC7", EN_RI32}, /* MOV r64, imm */ {4, PFPR, PFPR, "\xF3\x0F\x10", EN_RR}, /* MOVSS xmm, xmm */ @@ -373,8 +374,8 @@ static void Xmov(uchar **pcode, enum irclass k, struct oper dst, struct oper src static const uchar k2off[] = { [KI4] = 0, [KI8] = 1, [KPTR] = 1, - [KF4] = 6, - [KF8] = 9, + [KF4] = 7, + [KF8] = 10, }; encode(pcode, all + k2off[k], arraylength(all) - k2off[k], k, dst, src); } @@ -457,6 +458,89 @@ DEFINSTR1(Xcall, {-1, PGPR, 0, "\xFF", EN_R, .ext=2, .norexw=1}, /* CALL r64 */ {-1, PMEM, 0, "\xFF", EN_M, .ext=2, .norexw=1}, /* CALL m64 */ ) +DEFINSTR2(Xcmp, + {4|8, PGPR, PGPR, "\x3B", EN_RR}, /* CMP r32/64, r32/64 */ + {4|8, PGPR, PI8, "\x83", EN_RI8, .ext=7}, /* CMP r32/64, imm8 */ + {4|8, PRAX, PI32, "\x3D", EN_I32}, /* CMP eax/rax, imm */ + {4|8, PGPR, PI32, "\x81", EN_RI32, .ext=7}, /* CMP r32/64, imm */ + { 8, PGPR, PMEM, "\x3B", EN_RM}, /* CMP r64, m64 */ +) +DEFINSTR2(Xtest, + {4|8, PGPR, PGPR, "\x85", EN_RR}, /* TEST r32/64, r32/64 */ +) + +enum cc { + CCO = 0x0, /* OF = 1*/ + CCNO = 0x1, /* OF = 0*/ + CCB = 0x2, CCC = 0x2, CCNAE = 0x2, /* below; CF = 1; not above or equal */ + CCAE = 0x3, CCNB = 0x3, CCNC = 0x3, /* above or equal; not below; CF = 0 */ + CCE = 0x4, CCZ = 0x4, /* equal; ZF = 1 */ + CCNE = 0x5, CCNZ = 0x5, /* not equal; ZF = 0 */ + CCBE = 0x6, CCNA = 0x6, /* below or equal; not above; CF=1 or ZF=1 */ + CCA = 0x7, CCNBE = 0x7, /* above; not below or equal; CF=0 and ZF=0 */ + CCS = 0x8, /* ZS = 1; negative */ + CCNS = 0x9, /* ZS = 0; non-negative */ + CCP = 0xA, CCPE = 0xA, /* PF = 1; parity even */ + CCNP = 0xB, CCPO = 0xB, /* PF = 0; parity odd */ + CCL = 0xC, CCNGE = 0xC, /* lower; not greater or equal; SF != OF */ + CCGE = 0xD, CCNL = 0xD, /* greater or equal; not lower; SF == OF */ + CCLE = 0xE, CCNG = 0xE, /* less or equal; not greater; ZF=1 or SF != OF */ + CCG = 0xF, CCNLE = 0xF, /* greater; not less or equal; ZF=0 and SF = OF*/ + ALWAYS, +}; + +/* maps blk -> address when resolved; or to linked list of jump displacement + * relocations */ +static struct blkaddr { + bool resolved; + union { + uint addr; + uint relreloc; + }; +} *blkaddr; +static uint nblkaddr; + +static void +Xjcc(uchar **pcode, enum cc cc, struct block *dst) +{ + int disp, insaddr = *pcode - objout.textbegin; + bool rel8 = 0; + + if (blkaddr[dst->id].resolved) { + disp = blkaddr[dst->id].addr - (insaddr + 2); + if ((uint)(disp + 128) < 256) /* can use 1-byte displacement? */ + rel8 = 1; + else { /* otherwise 4-byte displacement */ + disp -= 3; + disp -= cc != ALWAYS; /* 'Jcc rel32' has 2 opcode bytes */ + } + } else { + disp = blkaddr[dst->id].relreloc; + blkaddr[dst->id].relreloc = insaddr + 1 + (cc != ALWAYS); + } + if (cc == ALWAYS) { + B(rel8 ? 0xEB : 0xE9); /* JMP rel8/rel32 */ + } else { + assert(in_range(cc, 0, 0xF)); + if (rel8) B(0x70 + cc); /* Jcc rel8 */ + else B(0x0F), B(0x80 + cc); /* Jcc rel32 */ + } + if (rel8) B(disp); else I32(disp); +} + +static void +Xsetcc(uchar **pcode, enum cc cc, int reg) +{ + int rex = 0; + assert(in_range(cc, 0x0, 0xF)); + + if (in_range(reg, RSP, RDI)) rex = 0x40; + rex |= (reg >> 3); /* REX.B */ + if (rex) B(rex); + B(0x0F), B(0x90+cc); /* SETcc */ + B(0xC0 + (reg & 7)); /* ModR/M with mod=11, rm=reg */ + +} static void Xpush(uchar **pcode, enum reg reg) @@ -474,13 +558,33 @@ Xpop(uchar **pcode, enum reg reg) B(0x58 + (reg & 7)); } +/* are flags live at given instruction? */ +static bool +flagslivep(struct block *blk, int curi) +{ + int cmpi; + /* conditional branch that references a previous comparison instruction? */ + if (blk->jmp.t != Jb || !blk->jmp.arg[0].t) + return 0; + assert(blk->jmp.arg[0].t == RTMP); + cmpi = blk->jmp.arg[1].i; + for (int i = blk->ins.n - 1; i > curi; --i) { + if (blk->ins.p[i] == cmpi) + /* flags defined after given instruction, dead here */ + return 0; + } + /* flags defined before given instruction, live here */ + return 1; +} + /* Copy dst = val, with some peephole optimizations */ static void -gencopy(uchar **pcode, enum irclass cls, struct oper dst, union ref val) +gencopy(uchar **pcode, enum irclass cls, struct block *blk, int curi, 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 */ + if (val.t == RMORE && !flagslivep(blk, curi)) { + /* this is a LEA, but maybe it can be lowered to a 2-address instruction, + * which may clobber flags */ const struct addr *addr = &addrht[val.i]; if (addr->base.t && dst.reg == mkregoper(addr->base).reg) { /* base = dst */ if (addr->index.t && !addr->disp && !addr->shift){ @@ -514,9 +618,9 @@ gencopy(uchar **pcode, enum irclass cls, struct oper dst, union ref val) /* normal (not 2-address) case */ Lea: Xlea(pcode, cls, dst, ref2oper(val)); - } else if (val.t == RICON && val.i == 0 && dst.t == OREG) { - /* dst = 0 -> xor dst, dst */ - Xxor(pcode, cls, dst, dst); + } else if (val.bits == ZEROREF.bits && dst.t == OREG && !flagslivep(blk, curi)) { + /* dst = 0 -> xor dst, dst; but only if it is ok to clobber flags */ + Xxor(pcode, kisint(cls) ? KI4 : cls, dst, dst); } else if (val.t == RXCON && conht[val.i].isdat && !conht[val.i].deref) { Xlea(pcode, cls, dst, mkoper(OCONR, .con = val.i)); } else { @@ -526,16 +630,33 @@ gencopy(uchar **pcode, enum irclass cls, struct oper dst, union ref val) } } +/* condition code for CMP */ +static const uchar icmpop2cc[] = { + [Oequ] = CCE, [Oneq] = CCNE, + [Olth] = CCL, [Ogth] = CCG, [Olte] = CCLE, [Ogte] = CCGE, + [Oulth] = CCB, [Ougth] = CCA, [Oulte] = CCBE, [Ougte] = CCGE, +}; +/* condition code for TEST reg,reg (compare with zero) */ +static const uchar icmpzero2cc[] = { + [Oequ] = CCE, [Oulte] = CCE, + [Oneq] = CCNE, [Ougth] = CCNE, + [Olth] = CCS, [Ogte] = CCNS, + [Olte] = CCLE, [Ogth] = CCG, + [Oulth] = CCB, [Ougte] = CCAE, /* actually constants */ +}; + static void -emitinstr(uchar **pcode, struct function *fn, struct block *blk, int ii, struct instr *ins) +emitinstr(uchar **pcode, struct function *fn, struct block *blk, int curi, struct instr *ins) { struct oper dst, src; + bool regzeroed; enum irclass cls = ins->cls; void (*X)(uchar **, enum irclass, struct oper, struct oper) = NULL; void (*X1)(uchar **, enum irclass, struct oper) = NULL; switch (ins->op) { - default: assert(!"nyi ins"); + default: + fatal(NULL, "amd64: in %y; unimplemented instr '%s'", fn->name, opnames[ins->op]); case Onop: break; case Ostore1: cls = KI4, X = Xmovb; goto Store; case Ostore2: cls = KI4, X = Xmovw; goto Store; @@ -577,6 +698,7 @@ emitinstr(uchar **pcode, struct function *fn, struct block *blk, int ii, struct break; case Osub: X = kisint(cls) ? Xsub : Xsubf; goto ALU2; case Oshl: X = Xshl; goto ALU2; + case Oxor: X = Xxor; goto ALU2; ALU2: dst = mkregoper(ins->l); assert(ins->reg-1 == dst.reg); @@ -601,13 +723,44 @@ emitinstr(uchar **pcode, struct function *fn, struct block *blk, int ii, struct case KF4: case KF8: assert(!"nyi"); } break; + case Oequ: case Oneq: + case Olth: case Ogth: case Olte: case Ogte: + case Oulth: case Ougth: case Oulte: case Ougte: + dst = mkregoper(ins->l); + /* TODO handle float cmps */ + src = mkimmdatregoper(ins->r); + regzeroed = 0; + if (ins->reg && dst.reg != ins->reg-1 && (src.t != OREG || src.reg != ins->reg-1)) { + /* can zero output reg before test instruction (differs from both inputs) */ + /* XXX this doesn't check if a source operand is an addr containing the register */ + struct oper dst = reg2oper(ins->reg-1); + Xxor(pcode, KI4, dst, dst); + regzeroed = 1; + } + if (ins->r.bits != ZEROREF.bits) + Xcmp(pcode, cls, dst, src); + else + Xtest(pcode, cls, dst, dst); + if (ins->reg) { + enum cc cc; + dst = reg2oper(ins->reg-1); + if (ins->r.bits != ZEROREF.bits) { /* CMP */ + cc = icmpop2cc[ins->op]; + } else { /* TEST r,r (CMP r, 0) */ + cc = icmpzero2cc[ins->op]; + } + Xsetcc(pcode, cc, dst.reg); + if (!regzeroed) + Xmovzxb(pcode, KI4, dst, dst); + } + break; case Omove: dst = ref2oper(ins->l); - gencopy(pcode, cls, dst, ins->r); + gencopy(pcode, cls, blk, curi, dst, ins->r); break; case Ocopy: dst = reg2oper(ins->reg-1); - gencopy(pcode, cls, dst, ins->l); + gencopy(pcode, cls, blk, curi, dst, ins->l); break; case Ocall: Xcall(pcode, KPTR, ref2oper(ins->l)); @@ -617,6 +770,47 @@ emitinstr(uchar **pcode, struct function *fn, struct block *blk, int ii, struct } static void +emitbranch(uchar **pcode, struct block *blk) +{ + enum cc cc = ALWAYS; + assert(blk->s1); + if (blk->s2) { + /* conditional branch.. */ + union ref arg = blk->jmp.arg[0]; + + if (!arg.t) /* implicit by ZF */ + cc = CCNZ; + else { + struct instr *ins; + assert(arg.t == RTMP); + ins = &instrtab[arg.i]; + assert(oiscmp(ins->op)); + /* TODO handle float cmps */ + if (ins->r.bits != ZEROREF.bits) { + /* for CMP instr */ + cc = icmpop2cc[ins->op]; + } else { + /* for TEST instr, which modifies ZF and SF and sets CF = OF = 0 */ + cc = icmpzero2cc[ins->op]; + } + } + if (blk->s1 == blk->lnext) { + /* if s1 is next adjacent block, swap s1,s2 and flip condition to emit a + * single jump */ + struct block *tmp = blk->s1; + blk->s1 = blk->s2; + blk->s2 = tmp; + cc ^= 1; + } + } + /* make sure to fallthru if jumping to next adjacent block */ + if (blk->s2 || blk->s1 != blk->lnext) + Xjcc(pcode, cc, blk->s1); + if (blk->s2 && blk->s2 != blk->lnext) + Xjcc(pcode, ALWAYS, blk->s2); +} + +static void calleesave(uchar **pcode, struct function *fn) { if (bstest(fn->regusage, RBX)) Xpush(pcode, RBX); @@ -662,6 +856,13 @@ emitbin(struct function *fn) uchar **pcode = &objout.code; uchar *start; + + if (nblkaddr < fn->nblk) { + blkaddr = xrealloc(blkaddr, fn->nblk * sizeof *blkaddr); + nblkaddr = fn->nblk; + } + memset(blkaddr, 0, nblkaddr * sizeof *blkaddr); + aligncode(pcode, 16); start = *pcode; @@ -682,6 +883,20 @@ emitbin(struct function *fn) blk = fn->entry; do { + struct blkaddr *bb = &blkaddr[blk->id]; + uint bbaddr = *pcode - objout.textbegin; + assert(!bb->resolved); + while (bb->relreloc) { + uint next; + int disp = bbaddr - bb->relreloc - 4; + + memcpy(&next, objout.textbegin + bb->relreloc, 4); + wr32le(objout.textbegin + bb->relreloc, disp); + bb->relreloc = next; + } + bb->resolved = 1; + bb->addr = bbaddr; + for (int i = 0; i < blk->ins.n; ++i) { emitinstr(pcode, fn, blk, i, &instrtab[blk->ins.p[i]]); } @@ -690,7 +905,7 @@ emitbin(struct function *fn) calleerestore(pcode, fn); if (fn->stksiz) B(0xC9); /* leave */ B(0xC3); /* ret */ - } + } else emitbranch(pcode, blk); } while ((blk = blk->lnext) != fn->entry); objdeffunc(fn->name, fn->globl, start - objout.textbegin, *pcode - start); } diff --git a/amd64/isel.c b/amd64/isel.c index 43e3aa7..a613f53 100644 --- a/amd64/isel.c +++ b/amd64/isel.c @@ -1,8 +1,58 @@ #include "all.h" #include "../endian.h" +enum flag { + ZF = 1 << 0, + SF = 1 << 1, + CF = 1 << 2, + OF = 1 << 3, + CLOBF = 1 << 4, +}; + +/* flags read by integer cmp ops */ +static const uchar intcmpflags[] = { + [Oequ] = ZF, [Oneq] = ZF, + [Olth] = SF|OF, [Olte] = ZF|SF|OF, + [Ogte] = SF|OF, [Ogth] = ZF|SF|OF, + [Oulth] = CF, [Oulte] = ZF|CF, + [Ougte] = CF, [Ougth] = ZF|CF, +}; + +static const uchar opflags[] = { + [Oneg] = ZF|CLOBF, + [Oadd] = ZF|CLOBF, + [Osub] = ZF|CLOBF, + [Omul] = CLOBF, + [Oumul] = CLOBF, + [Odiv] = CLOBF, + [Oudiv] = CLOBF, + [Orem] = CLOBF, + [Ourem] = CLOBF, + [Oand] = ZF|CLOBF, + [Oior] = ZF|CLOBF, + [Oxor] = ZF|CLOBF, + [Oshl] = ZF|CLOBF, + [Osar] = ZF|CLOBF, + [Oslr] = ZF|CLOBF, + [Oequ] = ZF|CLOBF, + [Oneq] = ZF|CLOBF, + [Olth] = ZF|CLOBF, + [Ogth] = ZF|CLOBF, + [Olte] = ZF|CLOBF, + [Ogte] = ZF|CLOBF, + [Oulth] = ZF|CLOBF, + [Ougth] = ZF|CLOBF, + [Oulte] = ZF|CLOBF, + [Ougte] = ZF|CLOBF, + [Ocall] = CLOBF, + [Oxinc] = ZF|CLOBF, + [Oxdec] = ZF|CLOBF, +}; + +static int iflagsrc = -1; + static void -fixarg(struct function *fn, union ref *r, struct instr *ins, struct block *blk, int *curi) +fixarg(union ref *r, struct instr *ins, struct block *blk, int *curi) { int sh; enum op op = ins ? ins->op : 0; @@ -114,7 +164,7 @@ aadd(struct addr *addr, union ref r) } static bool -fuseaddr(struct function *fn, union ref *r) +fuseaddr(union ref *r) { struct addr addr = { 0 }; @@ -163,14 +213,19 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) ins->r = mkref(RREG, RCX); } goto ALU; + case Oequ: case Oneq: 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 = ((op - Olth) ^ 1) + Olth; + if (!in_range(ins->op, Oequ, Oneq)) + ins->op = ((op - Olth) ^ 1) + Olth; rswap(ins->l, ins->r); } - goto ALU; + if (ins->l.t != RTMP && ins->l.t != RREG) + ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l)); + fixarg(&ins->r, ins, blk, curi); + break; case Odiv: case Oudiv: case Orem: case Ourem: if (kisflt(ins->cls)) goto ALU; /* TODO fuse div/rem pair */ @@ -179,7 +234,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) insertinstr(blk, (*curi)++, mkinstr(Omove, ins->cls, mkref(RREG, RAX), ins->l)); /* mark RDX as clobbered. sign/zero-extending RAX into RDX is handled in emit() */ insertinstr(blk, (*curi)++, mkinstr(Omove, ins->cls, mkref(RREG, RDX), mkref(RREG, RDX))); - fixarg(fn, &ins->r, ins, blk, curi); /* make sure rhs is memory or reg */ + fixarg(&ins->r, ins, blk, curi); /* make sure rhs is memory or reg */ ins->l = mkref(RREG, RAX); if (op == Orem) ins->op = Odiv; else if (op == Ourem) ins->op = Oudiv; @@ -207,7 +262,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) temp.op = Ocopy; temp.cls = ins->cls; temp.l = mkref(RTMP, ins - instrtab); - if (fuseaddr(fn, &temp.l)) { + if (fuseaddr(&temp.l)) { *ins = temp; break; } @@ -226,7 +281,6 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) /* fallthru */ 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); @@ -240,24 +294,47 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l)); if (ins->r.t) case Omove: - fixarg(fn, &ins->r, ins, blk, curi); + fixarg(&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 (!fuseaddr(fn, &ins->l) && ins->l.t != RTMP && ins->l.t != RREG) + if (!fuseaddr(&ins->l) && ins->l.t != RTMP && ins->l.t != RREG) ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, ins->l)); break; case Ostore1: case Ostore2: case Ostore4: case Ostore8: - if (!fuseaddr(fn, &ins->l) && ins->l.t != RTMP && ins->l.t != RREG) + if (!fuseaddr(&ins->l) && ins->l.t != RTMP && ins->l.t != RREG) ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, ins->l)); - fixarg(fn, &ins->r, ins, blk, curi); + fixarg(&ins->r, ins, blk, curi); break; case Ocopy: - fixarg(fn, &ins->l, ins, blk, curi); + fixarg(&ins->l, ins, blk, curi); break; } } +static void +seljmp(struct function *fn, struct block *blk) +{ + if (blk->jmp.t == Jb && blk->jmp.arg[0].t) { + union ref c = blk->jmp.arg[0]; + if (c.t != RTMP) { + enum irclass cls = c.t == RICON ? KI4 : c.t == RXCON && conht[c.i].cls ? conht[c.i].cls : KPTR; + int curi = blk->ins.n; + + c = insertinstr(blk, blk->ins.n, mkinstr(Ocopy, cls, c)); + sel(fn, &instrtab[c.i], blk, &curi); + } + if (iflagsrc == c.i) { + if (!oiscmp(instrtab[c.i].op)) + blk->jmp.arg[0] = NOREF; /* implicit by zero flag */ + } else { + if (!(opflags[instrtab[c.i].op] & ZF) || c.i != blk->ins.p[blk->ins.n - 1]) { + blk->jmp.arg[0] = insertinstr(blk, blk->ins.n, mkinstr(Oneq, instrtab[c.i].cls, c, ZEROREF)); + } + } + } +} + void amd64_isel(struct function *fn) { @@ -266,15 +343,24 @@ amd64_isel(struct function *fn) fn->stksiz = 0; do { - for (int i = 0; i < blk->phi.n; ++i) { + int i; + for (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); + int curi = phi->blk[i]->ins.n; + fixarg(&phi->ref[i], NULL, phi->blk[i], &curi); } } - for (int i = 0; i < blk->ins.n; ++i) { - sel(fn, &instrtab[blk->ins.p[i]], blk, &i); + iflagsrc = -1; + for (i = 0; i < blk->ins.n; ++i) { + struct instr *ins = &instrtab[blk->ins.p[i]]; + sel(fn, ins, blk, &i); + if (ins->op < arraylength(opflags) && kisint(ins->cls)) { + if (opflags[ins->op] & ZF) iflagsrc = ins - instrtab; + else if (opflags[ins->op] & CLOBF) iflagsrc = -1; + } } + seljmp(fn, blk); } while ((blk = blk->lnext) != fn->entry); if (ccopt.dbg.i) { @@ -1513,7 +1513,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) Cmp: ty = cvtarith(sub[0].ty, sub[1].ty); if (!ty.t) ty.t = TYPTR; - if (isunsigned(ty) && in_range(ins.op, Olth, Olte)) + if (isunsigned(ty) && in_range(ins.op, Olth, Ogte)) ins.op += Oulth - Olth; ins.l = compileexpr(fn, &sub[0], discard); ins.r = compileexpr(fn, &sub[1], discard); @@ -1928,7 +1928,8 @@ localdecl(struct comp *cm, struct function *fn, bool forini) } Err: if (!put) putdecl(cm, &decl); - } + } else if (forini) + error(&decl.span, "non-variable declaration in 'for' loop initializer"); } while (st.more); } @@ -5,6 +5,13 @@ uchar type2cls[NTYPETAG]; uchar cls2siz[KF8+1]; const uchar siz2intcls[] = { [1] = KI4, [2] = KI4, [4] = KI4, [8] = KI8 }; +const char *opnames[] = { + "?\??", +#define _(o,...) #o, +#include "op.def" +#undef _ +}; + struct instr instrtab[MAXINSTR]; int ninstr; static int instrfreelist; @@ -46,6 +53,7 @@ irinit(struct function *fn) cls2siz[KPTR] = targ_primsizes[TYPTR]; } fn->entry = fn->curblk = alloc(&fn->arena, sizeof(struct block), 0); + fn->nblk = 1; memset(fn->entry, 0, sizeof *fn->entry); fn->entry->lprev = fn->entry->lnext = fn->entry; } @@ -325,6 +333,7 @@ useblk(struct function *fn, struct block *blk) blk->lprev = fn->entry->lprev; blk->lprev->lnext = blk; blk->id = blk->lprev->id + 1; + ++fn->nblk; fn->entry->lprev = blk; } fn->curblk = blk; @@ -95,6 +95,7 @@ enum op { #define oisalloca(o) in_range(o, Oalloca1, Oalloca16) #define oisstore(o) in_range(o, Ostore1, Ostore8) #define oisload(o) in_range(o, Oloads1, Oloadf8) +extern const char *opnames[]; enum intrin { INxxx, @@ -151,12 +151,6 @@ dumpcall(struct call *call) } } -static const char *opname[] = { - "?\??", -#define _(o,...) #o, -#include "op.def" -#undef _ -}; static const uchar opnarg[] = { 0, #define _(o,n) n, @@ -180,7 +174,7 @@ dumpinst(const struct instr *ins) efmt("%s %%%d", clsname[ins->cls], ins - instrtab); efmt(" = "); } - efmt("%s ", opname[ins->op]); + efmt("%s ", opnames[ins->op]); } for (i = 0; i < opnarg[ins->op]; ++i) { if (i) efmt(", "); @@ -242,7 +242,9 @@ use(struct rega *ra, struct block *blk, int curi, enum op op, int hint, union re vpush(&ra->freestk, s); } } - *ref = mkref(RREG, ins->reg-1); + /* do not patch ref if it's cond branch arg, emit() wants to know what instr it is */ + if (ref != blk->jmp.arg || blk->jmp.t != Jb) + *ref = mkref(RREG, ins->reg-1); } void @@ -273,6 +275,9 @@ regalloc(struct function *fn) do { for (int i = 0; i < 2; ++i) { if (!blk->jmp.arg[i].t) break; + /* do not allocate a reg for a cmp op used a branch argument, since it's a pseudo op */ + if (blk->jmp.t == Jb && blk->jmp.arg[i].t == RTMP && oiscmp(instrtab[blk->jmp.arg[i].i].op)) + break; use(&ra, blk, blk->ins.n-1, (blk->jmp.t != Jb) - 1, blk->jmp.t == Jret ? fn->abiret[i].reg : -1, &blk->jmp.arg[i], blk->jmp.arg[!i]); diff --git a/test/fib.c b/test/fib.c new file mode 100644 index 0000000..6254a5f --- /dev/null +++ b/test/fib.c @@ -0,0 +1,19 @@ +unsigned fib(unsigned x) { + unsigned r = 0, q = 1; + while (x--) { + unsigned s = r + q; + r = q; + q = s; + } + return q; +} + +int atoi(const char *); +int printf(const char *, ...); + +int main(int argc, char **argv) { + unsigned n = argv[1] ? atoi(argv[1]) : 10; + printf("fib(%u) = %u\n", n, fib(n)); +} + +/* vim:set ts=3 sw=3 expandtab: */ |