diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | amd64/isel.c | 3 | ||||
| -rw-r--r-- | amd64/sysv.c | 44 | ||||
| -rw-r--r-- | c/c.c | 198 | ||||
| -rw-r--r-- | c/eval.c | 6 | ||||
| -rw-r--r-- | common.h | 6 | ||||
| -rw-r--r-- | ir/builder.c | 217 | ||||
| -rw-r--r-- | ir/fold.c | 119 | ||||
| -rw-r--r-- | ir/ir.c | 95 | ||||
| -rw-r--r-- | ir/ir.h | 12 | ||||
| -rw-r--r-- | ir/optmem.c | 14 | ||||
| -rw-r--r-- | ir/regalloc.c | 19 |
12 files changed, 522 insertions, 213 deletions
@@ -1,4 +1,4 @@ -SRC=main.c io.c mem.c c/c.c c/lex.c c/eval.c type.c targ.c ir/ir.c ir/dump.c ir/ssa.c ir/cfg.c \ +SRC=main.c io.c mem.c c/c.c c/lex.c c/eval.c type.c targ.c ir/ir.c ir/builder.c ir/fold.c ir/dump.c ir/ssa.c ir/cfg.c \ ir/intrin.c ir/abi0.c ir/optmem.c ir/regalloc.c amd64/sysv.c amd64/isel.c amd64/emit.c obj/obj.c obj/elf.c \ embedfilesdir.c CFLAGS=-Wall -std=c11 -pedantic diff --git a/amd64/isel.c b/amd64/isel.c index 133868e..c250ec0 100644 --- a/amd64/isel.c +++ b/amd64/isel.c @@ -137,7 +137,6 @@ selcall(struct function *fn, struct instr *ins, struct block *blk, int *curi) } #define isimm32(r) (concls(r) == KI4) -#define rswap(a,b) do { union ref _t = (a); (a) = (b); (b) = _t; } while (0) static bool acon(struct addr *addr, union ref r) @@ -248,7 +247,7 @@ fuseaddr(union ref *r, struct block *blk, int *curi) if (!addr.base.bits) { /* absolute int address in disp */ - assert(!addr.index.bits); + if (addr.index.bits) return 0; addr.base = mkintcon(KPTR, addr.disp); addr.disp = 0; } diff --git a/amd64/sysv.c b/amd64/sysv.c index 43ecd3c..334be26 100644 --- a/amd64/sysv.c +++ b/amd64/sysv.c @@ -1,6 +1,24 @@ #include "all.h" static int classify(uchar cls[2], const struct typedata *td, uint off); + +static void +clsscalar(uchar cls[2], uint off, union type ty) +{ + enum irclass k = type2cls[scalartypet(ty)]; + uchar *fcls = &cls[off/8]; + if (isflt(ty)) { /* SSE */ + if (!*fcls || (*fcls == KF4 && k > *fcls)) + *fcls = k; + } else { /* INTEGER */ + assert(isint(ty) || ty.t == TYPTR); + if (cls2siz[*fcls] < cls2siz[k]) + *fcls = k; + } + if (off % 8 >= 4 && cls2siz[*fcls] < 8) + *fcls = kisint(*fcls) ? KI8 : KF8; +} + static int classifyarr(uchar cls[2], union type ty, uint off) { @@ -15,12 +33,8 @@ classifyarr(uchar cls[2], union type ty, uint off) } else if (chld.t == TYARRAY) { if (!classifyarr(cls, chld, offx)) return cls[0] = cls[1] = 0; - } else if (isflt(chld)) { /* SSE */ - if (!cls[offx/8]) - cls[offx/8] = KF8; - } else { /* INTEGER */ - assert(isint(chld) || chld.t == TYPTR); - cls[offx/8] = KI8; + } else { + clsscalar(cls, offx, chld); } } return !!cls[0] + !!cls[1]; @@ -28,7 +42,7 @@ classifyarr(uchar cls[2], union type ty, uint off) static int classify(uchar cls[2], const struct typedata *td, uint off) { - uint siz = alignup(td->siz, 8); + uint siz = alignup(td->siz, 4); if (siz > 16) /* MEMORY */ return 0; for (int i = 0; i < td->nmemb; ++i) { @@ -43,12 +57,8 @@ classify(uchar cls[2], const struct typedata *td, uint off) if (isincomplete(fld->t)) continue; if (!classifyarr(cls, fld->t, off + fld->off)) return cls[0] = cls[1] = 0; - } else if (isflt(fld->t)) { /* SSE */ - if (!cls[(fld->off + off)/8]) - cls[(fld->off + off)/8] = KF8; - } else { /* INTEGER */ - assert(isint(fld->t) || fld->t.t == TYPTR); - cls[(fld->off + off)/8] = KI8; + } else { + clsscalar(cls, fld->off + off, fld->t); } } return !!cls[0] + !!cls[1]; @@ -84,9 +94,9 @@ abiarg(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype typ) ni_save = *ni, nf_save = *nf; for (int i = 0; i < ret; ++i) { assert(cls[i]); - if (cls[i] == KF8 && *nf < NFLT) + if (kisflt(cls[i]) && *nf < NFLT) r[i] = XMM0 + (*nf)++; - else if (cls[i] == KI8 && *ni < NINT) + else if (kisint(cls[i]) && *ni < NINT) r[i] = intregs[(*ni)++]; else { /* MEMORY */ *ni = ni_save, *nf = nf_save; @@ -121,9 +131,9 @@ abiret(short r[2], uchar cls[2], int *ni, union irtype typ) assert(ret <= 2); for (int i = 0, ni = 0, nf = 0; i < ret; ++i) { assert(cls[i]); - if (cls[i] == KF8) /* SSE (XMM0, XMM1) */ + if (kisflt(cls[i])) /* SSE (XMM0, XMM1) */ r[i] = XMM0 + nf++; - else if (cls[i] == KI8) /* INTEGER (RAX, RDX) */ + else if (kisint(cls[i])) /* INTEGER (RAX, RDX) */ r[i] = ni++ == 0 ? RAX : RDX; else assert(0); } @@ -1980,7 +1980,7 @@ ptypeof(struct comp *cm) return ty; } -static void +static bool declspec(struct declstate *st, struct comp *cm) { struct token tk; @@ -2139,11 +2139,16 @@ End: else goto Bad; st->base = mktype(t ? t : TYINT); - } else if (!st->base.t && ccopt.cstd < STDC23) { - warn(&span, "type implicitly declared as int"); - st->base = mktype(TYINT); - } else if (!st->base.t) - fatal(&span, "expected declaration type specifier"); + } else if (!st->base.t) { + if (ccopt.cstd < STDC23) { + warn(&span, "type implicitly declared as int"); + st->base = mktype(TYINT); + } else { + fatal(&span, "expected declaration type specifier"); + } + return 0; + } + return 1; } /* circular doubly linked list used to parse declarators */ @@ -2493,7 +2498,11 @@ pdecl(struct declstate *st, struct comp *cm) { error(&tk.span, "this storage class is not allowed in this context"); st->scls &= allowed; } - declspec(st, cm); + peek(cm, &tk); + if (!declspec(st, cm) && st->kind != DTOPLEVEL) { + lex(cm, &tk); + error(&tk.span, "unknown type name %'s", tk.s); + } } if (st->scls == SCTYPEDEF) iniallowed = 0; @@ -2584,7 +2593,6 @@ expraddr(struct function *fn, const struct expr *ex) { struct decl *decl; union ref r; - struct instr ins = {0}; switch (ex->t) { case ESYM: @@ -2613,12 +2621,7 @@ expraddr(struct function *fn, const struct expr *ex) case EGETF: r = expraddr(fn, ex->sub); assert(ex->fld.bitsiz == 0); - if (ex->fld.off == 0) return r; - ins.cls = KPTR; - ins.op = Oadd; - ins.l = r; - ins.r = mkintcon(KI4, ex->fld.off); - return addinstr(fn, ins); + return irbinop(fn, Oadd, KPTR, r, mkintcon(KI4, ex->fld.off)); case ESET: assert(isagg(ex->ty)); r = expraddr(fn, &ex->sub[1]); @@ -2711,7 +2714,7 @@ geninit(struct function *fn, union type t, union ref dst, const struct expr *src if (bscount(azero, arraylength(azero)) < 32) { /* write individual zeros at non initialized gaps */ for (uint i = 0; bsiter(&i, azero, arraylength(azero)) && i < siz; i += align) { - adr = i == 0 ? dst : addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, i))); + adr = irbinop(fn, Oadd, KPTR, dst, mkref(RICON, i)); addinstr(fn, mkinstr(Ostore1 + ilog2(align), 0, .l = adr, .r = ZEROREF)); } } else { @@ -2731,7 +2734,7 @@ geninit(struct function *fn, union type t, union ref dst, const struct expr *src for (struct initval *val = ini->vals; val; val = val->next) { uint off = val->off; struct expr *ex = &val->ex; - adr = off == 0 ? dst : addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, off))); + adr = irbinop(fn, Oadd, KPTR, dst, mkref(RICON, off)); if (ex->t == EINIT || ex->t == ESTRLIT) { geninit(fn, ex->ty, adr, ex); } else if (isagg(ex->ty)) { @@ -2754,83 +2757,115 @@ geninit(struct function *fn, union type t, union ref dst, const struct expr *src genstore(fn, ctyp, adr, mkref(RICON, src->s.w16[i])); else genstore(fn, ctyp, adr, mkintcon(KI4, src->s.w32[i])); - adr = addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, (i+1)*csiz))); + adr = irbinop(fn, Oadd, KPTR, dst, mkref(RICON, (i+1)*csiz)); } genstore(fn, ctyp, adr, ZEROREF); /* null term */ } else assert(0); } +static bool +isboollike(struct function *fn, union ref r) +{ + struct instr *ins; + if (r.t == RICON && (r.i == 0 || r.i == 1)) return 1; + if (r.t != RTMP) return 0; + ins = &instrtab[r.i]; + if (oiscmp(ins->op)) /* these instrs already have output range of [0,1] */ + return 1; + if (ins->op == Ophi) { /* check if all the phi args are boollike */ + struct block *blk; + union ref *phi = NULL; + for (blk = fn->curblk; phi == NULL; blk = blk->lprev) { + /* find blk that defines phi */ + assert(blk != fn->entry); + for (int i = 0; i < blk->phi.n; ++i){ + if (blk->phi.p[i] == r.i) { + phi = phitab.p[ins->l.i]; + break; + } + } + } + for (int i = 0; i < blk->npred; ++i) { + if (!isboollike(fn, phi[i])) { + return 0; + } + } + return 1; + } + if (in_range(ins->op, Oand, Oxor)) + return isboollike(fn, ins->l) && isboollike(fn, ins->r); + if (ins->op == Ocopy || in_range(ins->op, Oexts1, Oextu4)) + return isboollike(fn, ins->l); + if (ins->op == Oparam) + return typedata[fn->fnty.dat].param[ins->l.i].t == TYBOOL; + return 0; +} + static union ref cvt(struct function *fn, union type to, union type from, union ref ref) { enum irclass kto = type2cls[scalartypet(to)], kfrom = type2cls[scalartypet(from)]; - struct instr ins = {0}; + enum op op; if (to.bits == from.bits) return ref; assert(kto && kfrom); if (kto == kfrom && to.t != TYBOOL) return ref; if (ref.t == RICON && kto < KF4) return ref; - ins.cls = kto; - ins.l = ref; if (kisflt(kto) || kisflt(kfrom)) { if (ref.t == RICON) { assert(kisflt(kto) && kisint(kfrom)); return mkfltcon(kto, kto == KF4 ? (float)ref.i : (double)ref.i); } - if (kisflt(kto) && kfrom == KI4) ins.op = issigned(from) ? Ocvts4f : Ocvtu4f; - else if (to.t == TYBOOL && kisflt(kfrom)) ins.op = Oneq, ins.r = mkfltcon(kfrom, 0.0); - else if (kisflt(kto) && kfrom == KI8) ins.op = issigned(from) ? Ocvts8f : Ocvtu8f; - else if (kto == KF8 && kfrom == KF4) ins.op = Ocvtf4f8; - else if (kto == KF4 && kfrom == KF8) ins.op = Ocvtf8f4; - else if (kfrom == KF4) ins.op = issigned(to) ? Ocvtf4s : Ocvtf4u; - else if (kfrom == KF8) ins.op = issigned(to) ? Ocvtf8s : Ocvtf8u; + if (kisflt(kto) && kfrom == KI4) op = issigned(from) ? Ocvts4f : Ocvtu4f; + else if (to.t == TYBOOL && kisflt(kfrom)) return irbinop(fn, Oneq, kfrom, ref, mkfltcon(kfrom, 0.0)); + else if (kisflt(kto) && kfrom == KI8) op = issigned(from) ? Ocvts8f : Ocvtu8f; + else if (kto == KF8 && kfrom == KF4) op = Ocvtf4f8; + else if (kto == KF4 && kfrom == KF8) op = Ocvtf8f4; + else if (kfrom == KF4) op = issigned(to) ? Ocvtf4s : Ocvtf4u; + else if (kfrom == KF8) op = issigned(to) ? Ocvtf8s : Ocvtf8u; else assert(0); } else { if (to.t == TYBOOL) { if (from.t == TYBOOL) return ref; - if (ref.t == RTMP) - /* these instrs already have output range of [0,1] */ - if (oiscmp(instrtab[ref.i].op)) - return ref; - ins.op = Oneq, ins.r = ZEROREF; + if (isboollike(fn, ref)) + return kfrom == KI4 ? ref : cvt(fn, mktype(TYINT), from, ref); + return irbinop(fn, Oneq, kfrom, ref, ZEROREF); } - else if (kfrom == KI4 && issigned(from)) ins.op = Oexts4; - else if (kfrom == KI4) ins.op = Oextu4; + else if (kfrom == KI4 && issigned(from)) op = Oexts4; + else if (kfrom == KI4) op = Oextu4; else if (kto == KI4 && isintcon(ref)) return issigned(to) ? mkintcon(kto, (int)intconval(ref)) : mkintcon(kto, (uint)intconval(ref)); - else ins.op = Ocopy; + else op = Ocopy; } - return addinstr(fn, ins); + return irunop(fn, op, kto, ref); } static union ref narrow(struct function *fn, enum irclass to, union type t, union ref ref, uint bitsiz) { - struct instr ins = {0}; enum typetag tt = scalartypet(t); assert(isscalar(t)); if (targ_primsizes[tt] < cls2siz[to]) { - ins.cls = to; + enum op op; if (isfltt(tt)) { assert(to == KF4 && tt >= TYDOUBLE); - ins.op = Ocvtf8f4; + op = Ocvtf8f4; } else { static const enum op ext[5][2] = { [1] = {Oextu1, Oexts1}, [2] = {Oextu2, Oexts2}, [4] = {Oextu4, Oexts4} }; - ins.op = ext[targ_primsizes[tt]][issignedt(tt)]; + op = ext[targ_primsizes[tt]][issignedt(tt)]; } - ins.l = ref; - ref = addinstr(fn, ins); + ref = irunop(fn, op, to, ref); } if (bitsiz) { assert(kisint(to) && isintt(tt) && bitsiz < 8*targ_primsizes[tt]); if (!issignedt(tt)) { - ref = addinstr(fn, mkinstr(Oand, to, .l = ref, .r = mkintcon(to, (1ull<<bitsiz)-1))); + ref = irbinop(fn, Oand, to, ref, mkintcon(to, (1ull<<bitsiz)-1)); } else { uint sh = 8*cls2siz[to] - bitsiz; - ref = addinstr(fn, mkinstr(Oshl, to, .l = ref, .r = mkref(RICON, sh))); - ref = addinstr(fn, mkinstr(Osar, to, .l = ref, .r = mkref(RICON, sh))); + ref = irbinop(fn, Oshl, to, ref, mkref(RICON, sh)); + ref = irbinop(fn, Osar, to, ref, mkref(RICON, sh)); } } return ref; @@ -2849,14 +2884,11 @@ genptroff(struct function *fn, enum op op, uint siz, union ref ptr, else if (idx.t == RICON) { if (op == Osub) op = Oadd, idx.i = -idx.i; off = mkintcon(cls, idx.i * (int)siz); - } else if (ispo2(siz)) - off = addinstr(fn, - mkinstr(Oshl, cls, .l = idx, .r = mkintcon(cls, ilog2(siz)))); - else - off = addinstr(fn, - mkinstr(Omul, cls, .l = idx, .r = mkintcon(cls, siz))); + } else { + off = irbinop(fn, Omul, cls, idx, mkintcon(cls, siz)); + } assert(in_range(op, Oadd, Osub)); - return addinstr(fn, mkinstr(op, KPTR, .l = ptr, .r = off)); + return irbinop(fn, op, KPTR, ptr, off); } union ref @@ -2864,12 +2896,12 @@ genptrdiff(struct function *fn, uint siz, union ref a, union ref b) { uint cls = type2cls[targ_ptrdifftype]; assert(siz > 0); - a = addinstr(fn, mkinstr(Osub, cls, .l = a, .r = b)); + a = irbinop(fn, Osub, cls, a, b); if (siz == 1) return a; - else if ((siz & (siz-1)) == 0) /* is power of 2 */ - return addinstr(fn, mkinstr(Osar, cls, a, mkintcon(cls, ilog2(siz)))); + else if (ispo2(siz)) + return irbinop(fn, Osar, cls, a, mkintcon(cls, ilog2(siz))); else - return addinstr(fn, mkinstr(Odiv, cls, a, mkintcon(cls, siz))); + return irbinop(fn, Odiv, cls, a, mkintcon(cls, siz)); } /* used to emit the jumps in an in if (), while (), etc condition */ @@ -3042,25 +3074,21 @@ genbitfload(struct function *fn, const union type ty, union ref *addr, const str uvlong mask; assert(k); - if (off > 0) - *addr = addinstr(fn, mkinstr(Oadd, KPTR, .l = *addr, .r = mkintcon(KI4, off))); + *addr = irbinop(fn, Oadd, KPTR, *addr, mkintcon(KI4, off)); tmp = genload(fn, ty, *addr, volatyl); if (!issigned(ty)) { /* shift right and mask */ - if (bitoff > 0) - tmp = addinstr(fn, mkinstr(Oslr, k, .l = tmp, .r = mkref(RICON, bitoff))); + tmp = irbinop(fn, Oslr, k, tmp, mkref(RICON, bitoff)); if (bitsiz < 8*typesize(ty)) { mask = bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1; - tmp = addinstr(fn, mkinstr(Oand, k, .l = tmp, .r = mkintcon(k, mask))); + tmp = irbinop(fn, Oand, k, tmp, mkintcon(k, mask)); } } else { /* shift left and shift right arithmetic to propagate sign bit */ int sh = 8*cls2siz[k] - bitsiz - bitoff; - if (sh) - tmp = addinstr(fn, mkinstr(Oshl, k, .l = tmp, .r = mkref(RICON, sh))); + tmp = irbinop(fn, Oshl, k, tmp, mkref(RICON, sh)); sh += bitoff; - if (sh) - tmp = addinstr(fn, mkinstr(Osar, k, .l = tmp, .r = mkref(RICON, sh))); + tmp = irbinop(fn, Osar, k, tmp, mkref(RICON, sh)); } return tmp; } @@ -3076,27 +3104,25 @@ genbitfstore(struct function *fn, const union type ty, union ref addr, assert(k); if (!tmp.bits) { - if (off > 0) - addr = addinstr(fn, mkinstr(Oadd, KPTR, .l = addr, .r = mkintcon(KI4, off))); + addr = irbinop(fn, Oadd, KPTR, addr, mkintcon(KPTR, off)); tmp = genload(fn, ty, addr, 0); } mask = (bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1) << bitoff; /* mask out bits in existing container */ - tmp = addinstr(fn, mkinstr(Oand, k, .l = tmp, .r = mkintcon(k, ~mask))); + tmp = irbinop(fn, Oand, k, tmp, mkintcon(k, ~mask)); /* shift and mask source value */ if (isintcon(val)) { val = mkintcon(k, ((uvlong)intconval(val) << bitoff) & mask); } else { - if (bitoff) - val = addinstr(fn, mkinstr(Oshl, k, .l = val, .r = mkref(RICON, bitoff))); + val = irbinop(fn, Oshl, k, val, mkref(RICON, bitoff)); if (bitsiz < bittypesize) - val = addinstr(fn, mkinstr(Oand, k, .l = val, .r = mkintcon(k, mask))); + val = irbinop(fn, Oand, k, val, mkintcon(k, mask)); } /* combine and write */ if (bitsiz < bittypesize) - val = addinstr(fn, mkinstr(Oior, k, .l = tmp, .r = val)); + val = irbinop(fn, Oior, k, tmp, val); genstore(fn, ty, addr, val); } @@ -3111,7 +3137,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) int swp = 0; struct expr *sub; - eval((struct expr *)ex, EVFOLD); + //eval((struct expr *)ex, EVFOLD); sub = ex->sub; if (ex->ty.t != TYVOID && !isscalar(ex->ty)) @@ -3155,7 +3181,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) if (discard) return NOREF; ins.l = cvt(fn, ex->ty, sub->ty, ins.l); ins.cls = cls; - return addinstr(fn, ins); + return irunop(fn, ins.op, ins.cls, ins.l); case ELOGNOT: for (; sub->t == ELOGNOT; ex = sub, sub = sub->sub) swp ^= 1; @@ -3165,7 +3191,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) ins.l = cvt(fn, ex->ty, sub->ty, ins.l); ins.r = mkintcon(cls, 0); ins.cls = cls; - return addinstr(fn, ins); + return irbinop(fn, ins.op, ins.cls, ins.l, ins.r); case EDEREF: discard &= (ex->qual & QVOLATILE) == 0; r = compileexpr(fn, sub, discard); @@ -3219,7 +3245,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) return genptroff(fn, ins.op, typesize(typechild(sub[0].ty)), ins.l, sub[1].ty, ins.r); } ins.cls = cls; - return addinstr(fn, ins); + return irbinop(fn, ins.op, ins.cls, ins.l, ins.r); case EPOSTINC: case EPOSTDEC: ins.op = ex->t == EPOSTINC ? Oadd : Osub; @@ -3230,7 +3256,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) ins.r = mkintcon(KI4, typesize(typechild(ex->ty))); else ins.r = mkref(RICON, 1); - genstore(fn, sub->ty, r, addinstr(fn, ins)); + genstore(fn, sub->ty, r, irbinop(fn, ins.op, ins.cls, ins.l, ins.r)); return ins.l; case EPREINC: case EPREDEC: @@ -3242,7 +3268,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) ins.r = mkintcon(KI4, typesize(typechild(ex->ty))); else ins.r = mkref(RICON, 1); - q = addinstr(fn, ins); + q = irbinop(fn, ins.op, ins.cls, ins.l, ins.r); genstore(fn, sub->ty, r, q); if (discard) return NOREF; return narrow(fn, cls, ex->ty, q, 0); @@ -3274,7 +3300,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) ins.l = cvt(fn, ty, sub[0].ty, ins.l); ins.r = cvt(fn, ty, sub[1].ty, ins.r); ins.cls = type2cls[ty.t]; - return addinstr(fn, ins); + return irbinop(fn, ins.op, ins.cls, ins.l, ins.r); case ESET: assert(isscalar(ex->ty)); q = cvt(fn, sub[0].ty, sub[1].ty, exprvalue(fn, &sub[1])); @@ -3327,7 +3353,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) /* bit-field */ r = expraddr(fn, &sub[0].sub[0]); ins.l = genbitfload(fn, sub[0].ty, &r, &sub[0].fld, sub[0].qual & QVOLATILE); - q = addinstr(fn, ins); + q = irbinop(fn, ins.op, ins.cls, ins.l, ins.r); genbitfstore(fn, sub[0].ty, r, &sub[0].fld, ins.l, q); } else { bitsiz = 0; @@ -3336,7 +3362,7 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) if ((ins.op != Oadd && ins.op != Osub) || cls != KPTR) { ins.l = cvt(fn, ty, sub[0].ty, ins.l); ins.r = cvt(fn, ex->ty, sub[1].ty, ins.r); - q = addinstr(fn, ins); + q = irbinop(fn, ins.op, ins.cls, ins.l, ins.r); } else { q = genptroff(fn, ins.op, typesize(typechild(ex->ty)), ins.l, sub[1].ty, ins.r); } @@ -3513,7 +3539,7 @@ genswitch(struct comp *cm, struct function *fn, const struct expr *ex) struct swcase c = st.cases.p[i]; EMITS { struct block *next = i < st.cases.n - 1 ? newblk(fn) : st.bdefault; - putcondbranch(fn, addinstr(fn, mkinstr(Oequ, k, .l = sel, .r = mkintcon(k, c.val))), c.blk, next); + putcondbranch(fn, irbinop(fn, Oequ, k, sel, mkintcon(k, c.val)), c.blk, next); if (next != st.bdefault) useblk(fn, next); } } @@ -4101,11 +4127,9 @@ docomp(struct comp *cm) int nerr = nerror; struct decl decl = pdecl(&st, cm); - if (nerror != nerr) { - if (st.varini) { - (void)expr(cm); - pdecl(&st, cm); - } + if (nerror != nerr && st.varini) { + (void)expr(cm); + pdecl(&st, cm); continue; } if (st.empty) break; @@ -29,6 +29,7 @@ numcast(union type ty, struct expr *dst, const struct expr *src) struct expr tmp; enum typetag td = targ2hosttype(ty.t); enum typetag ts = targ2hosttype(src->ty.t == TYENUM ? src->ty.backing : src->ty.t); + vlong isrc; if (src == dst) tmp = *src, src = &tmp; assert(src->t == ENUMLIT); @@ -45,11 +46,12 @@ numcast(union type ty, struct expr *dst, const struct expr *src) else if (td == TYDOUBLE) dst->f = (double) src->i; else if (TF(TYUVLONG)) dst->u = src->f; else if (TF(TYBOOL)) dst->i = (bool) src->f; - else if (isfltt(ts)) { dst->i = src->f; goto Narrow; } + else if (isfltt(ts)) { isrc = src->f; goto Narrow; } else { + isrc = src->i; Narrow: switch (td) { -#define I(Ty, Tag) case Tag: dst->i = (Ty) src->i; break; +#define I(Ty, Tag) case Tag: dst->i = (Ty) isrc; break; I(bool, TYBOOL) I(signed char, TYSCHAR) I(unsigned char, TYUCHAR) @@ -78,7 +78,7 @@ ispo2(uvlong x) { return (x != 0) & ((x & (x - 1)) == 0); } static inline uint -ilog2(uint x) { /* assumes x is a power of 2 */ +ilog2(uvlong x) { /* assumes x is a power of 2 */ #if HAS_BUILTIN(ctz) return __builtin_ctz(x); #else @@ -280,12 +280,12 @@ extern const char *ttypenames[/*id*/]; #define tdqualsiz(nmemb) ((nmemb)/4 + ((nmemb)%4 != 0)) static inline int -tdgetqual(const uchar *pqual, int idx) +tdgetqual(const uchar *pqual, uint idx) { return pqual ? pqual[idx/4] >> 2*(idx%4) & 3 : 0; } static inline void -tdsetqual(uchar *pqual, int idx, int qual) +tdsetqual(uchar *pqual, uint idx, int qual) { assert(pqual); pqual[idx/4] &= ~(3 << (2*(idx%4))); diff --git a/ir/builder.c b/ir/builder.c new file mode 100644 index 0000000..213a491 --- /dev/null +++ b/ir/builder.c @@ -0,0 +1,217 @@ +#include "ir.h" + +/* binary arithmetic builder with peephole optimizations */ +union ref +irbinop(struct function *fn, enum op op, enum irclass k, union ref l, union ref r) +{ + static const union ref ONE = {.t=RICON, .i=1}; + vlong iv; + union ref c; + + if (foldbinop(&c, op, k, l, r)) + return c; + + switch (op) { + case Oadd: + if (l.bits == ZEROREF.bits) return r; /* 0 + x ==> x */ + /* fallthru */ + case Osub: + if (r.bits == ZEROREF.bits) return l; /* x +/- 0 ==> x */ + break; + case Omul: case Oumul: + if (iscon(l)) rswap(l, r); /* put const in rhs */ + if (r.bits == ZEROREF.bits) /* x * 0 ==> 0 */ + return ZEROREF; + if (r.bits == ONE.bits) /* x * 1 ==> x */ + return l; + if (isintcon(r) && ispo2(iv = intconval(r))) { + /* x * 2^y ==> x << y */ + op = Oshl; + r = mkintcon(k, ilog2(iv)); + } + break; + case Odiv: + break; + case Oudiv: + if (isintcon(r) && ispo2(iv = intconval(r))) { + /* x / 2^y ==> x >> y */ + op = Oslr; + r = mkintcon(k, ilog2(iv)); + } + break; + case Orem: + break; + case Ourem: + if (isintcon(r) && ispo2(iv = intconval(r))) { + /* x % 2^y ==> x & 2^y-1 */ + op = Oand; + r = mkintcon(k, iv - 1); + } + break; + case Oand: + if (iscon(l)) rswap(l, r); /* put const in rhs */ + if (r.bits == ZEROREF.bits) /* x & 0 ==> 0 */ + return ZEROREF; + break; + case Oior: + if (iscon(l)) rswap(l, r); /* put const in rhs */ + if (r.bits == ZEROREF.bits) /* x | 0 ==> x */ + return l; + case Oxor: + if (iscon(l)) rswap(l, r); /* put const in rhs */ + if (r.bits == ZEROREF.bits) /* x ^ 0 ==> x */ + return l; + case Oshl: case Osar: case Oslr: + if (r.bits == ZEROREF.bits) /* x shift 0 ==> x */ + return l; + break; + case Oequ: + if (r.bits == l.bits && kisint(k)) + return ONE; + break; + case Oneq: + if (r.bits == l.bits && kisint(k)) + return ZEROREF; + break; + case Olth: + break; + case Ogth: + break; + case Olte: + break; + case Ogte: + break; + case Oulth: + break; + case Ougth: + break; + case Oulte: + break; + case Ougte: + break; + default: + assert(!"binop?"); + } + return addinstr(fn, mkinstr(op, k, l, r)); +} + +union ref +irunop(struct function *fn, enum op op, enum irclass k, union ref a) +{ + union ref c; + struct instr *ins = NULL; + if (foldunop(&c, op, k, a)) + return c; + if (a.t == RTMP) ins = &instrtab[a.i]; + switch (op) { + case Oneg: + if (ins && ins->op == Oneg) /* -(-x) ==> x */ + return ins->l; + break; + case Onot: + if (ins && ins->op == Onot) /* ~(~x) ==> x */ + return ins->l; + break; + case Ocvtf4s: case Ocvtf4u: case Ocvtf4f8: case Ocvtf8s: + case Ocvtf8u: case Ocvtf8f4: case Ocvts4f: case Ocvtu4f: + case Ocvts8f: case Ocvtu8f: + case Oexts1: case Oextu1: case Oexts2: case Oextu2: + case Oexts4: case Oextu4: + case Ocopy: + break; + default: assert(!"unop?"); + } + return addinstr(fn, mkinstr(op, k, a)); +} + +int newinstr(void); + +union ref +addinstr(struct function *fn, struct instr ins) +{ + int new = newinstr(); + assert(fn->curblk != NULL); + instrtab[new] = ins; + adduse(fn->curblk, new, ins.l); + adduse(fn->curblk, new, ins.r); + vpush(&fn->curblk->ins, new); + return mkref(RTMP, new); +} + +void +useblk(struct function *fn, struct block *blk) +{ + extern int nerror; + if (fn->curblk && nerror == 0) assert(fn->curblk->jmp.t && "never finished block"); + if (blk) assert(!blk->jmp.t && "reusing built block"); + if (!blk->lprev) { /* initialize */ + blk->lnext = fn->entry; + blk->lprev = fn->entry->lprev; + blk->lprev->lnext = blk; + blk->id = blk->lprev->id + 1; + ++fn->nblk; + fn->entry->lprev = blk; + } + fn->curblk = blk; +} + +union ref +addphi(struct function *fn, enum irclass cls, union ref *r) +{ + int new; + struct instr ins = { Ophi, cls }; + union ref *refs = NULL; + + xbgrow(&refs, fn->curblk->npred); + memcpy(refs, r, fn->curblk->npred * sizeof *r); + vpush(&phitab, refs); + ins.l = mkref(RXXX, phitab.n-1); + + assert(fn->curblk != NULL); + assert(fn->curblk->ins.n == 0); + new = newinstr(); + instrtab[new] = ins; + for (int i = 0; i < fn->curblk->npred; ++i) { + adduse(fn->curblk, new, r[i]); + } + vpush(&fn->curblk->phi, new); + return mkref(RTMP, new); +} + +#define putjump(fn, j, arg0, arg1, T, F) \ + fn->curblk->jmp.t = j; \ + fn->curblk->jmp.arg[0] = arg0; \ + fn->curblk->jmp.arg[1] = arg1; \ + fn->curblk->s1 = T; \ + fn->curblk->s2 = F; \ + fn->curblk = NULL; + +void +putbranch(struct function *fn, struct block *blk) +{ + assert(fn->curblk && blk); + addpred(blk, fn->curblk); + putjump(fn, Jb, NOREF, NOREF, blk, NULL); +} + +void +putcondbranch(struct function *fn, union ref arg, struct block *t, struct block *f) +{ + assert(fn->curblk && t && f); + adduse(fn->curblk, USERJUMP, arg); + addpred(t, fn->curblk); + addpred(f, fn->curblk); + putjump(fn, Jb, arg, NOREF, t, f); +} + +void +putreturn(struct function *fn, union ref r0, union ref r1) +{ + adduse(fn->curblk, USERJUMP, r0); + adduse(fn->curblk, USERJUMP, r1); + putjump(fn, Jret, r0, r1, NULL, NULL); +} + +#undef putjump + +/* vim:set ts=3 sw=3 expandtab: */ diff --git a/ir/fold.c b/ir/fold.c new file mode 100644 index 0000000..fd6076e --- /dev/null +++ b/ir/fold.c @@ -0,0 +1,119 @@ +#include "ir.h" + +static union ref +foldint(enum op op, enum irclass k, union ref lr, union ref rr) +{ + vlong x; + union { + vlong s; + uvlong u; + } l = {.s = intconval(lr)}, r = {.s = intconval(rr)}; + bool w = cls2siz[k] == 8; + if (in_range(op, Odiv, Ourem)) assert(r.u != 0); + switch (op) { + case Ocopy: x = l.s; break; + case Oneg: x = -l.s; break; + case Onot: x = ~l.s; break; + case Ocvtf4s: x = (int)(float)fltconval(lr); break; + case Ocvtf4u: x = (uint)(float)fltconval(lr); break; + case Ocvtf8s: x = (vlong)fltconval(lr); break; + case Ocvtf8u: x = (uvlong)fltconval(lr); break; + case Oexts1: x = (schar)l.s; break; + case Oextu1: x = (uchar)l.s; break; + case Oexts2: x = (short)l.s; break; + case Oextu2: x = (ushort)l.s; break; + case Oexts4: x = (int)l.s; break; + case Oextu4: x = (uint)l.s; break; + case Oadd: x = l.u + r.u; break; + case Osub: x = l.u - r.u; break; + case Omul: x = l.u * r.u; break; + case Oumul: x = l.u * r.u; break; + case Odiv: x = w ? l.s / r.s : (int)l.s / (int)r.s; break; + case Oudiv: x = w ? l.u / r.u : (uint)l.u / (uint)r.u; break; + case Orem: x = w ? l.s % r.s : (int)l.s % (int)r.s; break; + case Ourem: x = w ? l.u % r.u : (uint)l.u % (uint)r.u; break; + case Oand: x = l.u & r.u; break; + case Oior: x = l.u | r.u; break; + case Oxor: x = l.u ^ r.u; break; + case Oshl: x = l.u << (r.u & (w ? 63 : 31)); break; + case Oslr: x = w ? l.u >> (r.u&63) : (int)l.s >> (r.u&31); break; + case Osar: x = w ? l.s >> (r.u&63) : (int)l.s >> (r.u&31); break; + case Oequ: x = l.s == r.s; break; + case Oneq: x = l.s != r.s; break; + case Olth: x = l.s < r.s; break; + case Ogth: x = l.s > r.s; break; + case Olte: x = l.s <= r.s; break; + case Ogte: x = l.s >= r.s; break; + case Oulth: x = l.u < r.u; break; + case Ougth: x = l.u > r.u; break; + case Oulte: x = l.u <= r.u; break; + case Ougte: x = l.u >= r.u; break; + default: assert(0); + } + return mkintcon(k, x); +} + +static union ref +foldflt(enum op op, enum irclass k, union ref lr, union ref rr) +{ + int xi; + double x, l = fltconval(lr), r = fltconval(rr); + bool w = k == KF8; + if (in_range(op, Odiv, Ourem)) assert(r != 0.0); + switch (op) { + case Ocopy: x = l; break; + case Oneg: x = -l; break; + case Ocvtf4f8: x = (float)l; break; + case Ocvtf8f4: x = (float)l; break; + case Ocvts4f: x = (int)intconval(lr); break; + case Ocvtu4f: x = (int)intconval(lr); break; + case Ocvts8f: x = (vlong)intconval(lr); break; + case Ocvtu8f: x = (uvlong)intconval(lr); break; + case Oadd: x = l + r; break; + case Osub: x = l - r; break; + case Omul: x = l * r; break; + case Odiv: x = l / r; break; + case Oequ: xi = l == r; break; + case Oneq: xi = l != r; break; + case Olth: xi = l < r; break; + case Ogth: xi = l > r; break; + case Olte: xi = l <= r; break; + case Ogte: xi = l >= r; break; + default: assert(0); + } + if (oiscmp(op)) return mkref(RICON, xi); + if (!w) x = (float)x; + return mkfltcon(k, x); +} + +bool +foldbinop(union ref *to, enum op op, enum irclass k, union ref l, union ref r) +{ + if (!iscon(l) || !iscon(r)) return 0; + if (in_range(op, Odiv, Ourem) && (kisint(k) ? intconval(r) == 0 : fltconval(r) == 0)) + return 0; + if (!oisarith(op)) + return 0; + to->t = oiscmp(op) ? KI4 : k; + if (kisint(k)) + *to = foldint(op, k, l, r); + else + *to = foldflt(op, k, l, r); + return 1; +} + +bool +foldunop(union ref *to, enum op op, enum irclass k, union ref a) +{ + if (!iscon(a)) return 0; + if (op != Ocopy && !oisarith(op)) + return 0; + to->t = k; + if (kisint(k)) + *to = foldint(op, k, a, ZEROREF); + else + *to = foldflt(op, k, a, ZEROREF); + return 1; +} + +/* vim:set ts=3 sw=3 expandtab: */ @@ -297,7 +297,7 @@ insertblk(struct function *fn, struct block *pred, struct block *subst) assert(0); } -static int +int newinstr(void) { int t; @@ -383,8 +383,7 @@ insertphi(struct block *blk, enum irclass cls) int new = newinstr(); union ref *refs = NULL; assert(blk->npred > 0); - xbgrow(&refs, blk->npred); - memset(refs, 0, blk->npred * sizeof *refs); + xbgrowz(&refs, blk->npred); vpush(&phitab, refs); instrtab[new] = mkinstr(Ophi, cls, mkref(RXXX, phitab.n - 1)); vpush(&blk->phi, new); @@ -506,96 +505,6 @@ fillblkids(struct function *fn) do blk->id = i++; while ((blk = blk->lnext) != fn->entry); } -/** IR builders **/ - -void -useblk(struct function *fn, struct block *blk) -{ - extern int nerror; - if (fn->curblk && nerror == 0) assert(fn->curblk->jmp.t && "never finished block"); - if (blk) assert(!blk->jmp.t && "reusing built block"); - if (!blk->lprev) { /* initialize */ - blk->lnext = fn->entry; - blk->lprev = fn->entry->lprev; - blk->lprev->lnext = blk; - blk->id = blk->lprev->id + 1; - ++fn->nblk; - fn->entry->lprev = blk; - } - fn->curblk = blk; -} - -union ref -addinstr(struct function *fn, struct instr ins) -{ - int new = newinstr(); - assert(fn->curblk != NULL); - instrtab[new] = ins; - adduse(fn->curblk, new, ins.l); - adduse(fn->curblk, new, ins.r); - vpush(&fn->curblk->ins, new); - return mkref(RTMP, new); -} - -union ref -addphi(struct function *fn, enum irclass cls, union ref *r) -{ - int new; - struct instr ins = { Ophi, cls }; - union ref *refs = NULL; - - xbgrow(&refs, fn->curblk->npred); - memcpy(refs, r, fn->curblk->npred * sizeof *r); - vpush(&phitab, refs); - ins.l = mkref(RXXX, phitab.n-1); - - assert(fn->curblk != NULL); - assert(fn->curblk->ins.n == 0); - new = newinstr(); - instrtab[new] = ins; - for (int i = 0; i < fn->curblk->npred; ++i) { - adduse(fn->curblk, new, r[i]); - } - vpush(&fn->curblk->phi, new); - return mkref(RTMP, new); -} - -#define putjump(fn, j, arg0, arg1, T, F) \ - fn->curblk->jmp.t = j; \ - fn->curblk->jmp.arg[0] = arg0; \ - fn->curblk->jmp.arg[1] = arg1; \ - fn->curblk->s1 = T; \ - fn->curblk->s2 = F; \ - fn->curblk = NULL; - -void -putbranch(struct function *fn, struct block *blk) -{ - assert(fn->curblk && blk); - addpred(blk, fn->curblk); - putjump(fn, Jb, NOREF, NOREF, blk, NULL); -} - -void -putcondbranch(struct function *fn, union ref arg, struct block *t, struct block *f) -{ - assert(fn->curblk && t && f); - adduse(fn->curblk, USERJUMP, arg); - addpred(t, fn->curblk); - addpred(f, fn->curblk); - putjump(fn, Jb, arg, NOREF, t, f); -} - -void -putreturn(struct function *fn, union ref r0, union ref r1) -{ - adduse(fn->curblk, USERJUMP, r0); - adduse(fn->curblk, USERJUMP, r1); - putjump(fn, Jret, r0, r1, NULL, NULL); -} - -#undef putjump - /** Misc **/ static void @@ -77,6 +77,7 @@ struct addr { #define mkref(t, x) ((union ref) {{ (t), (x) }}) #define mktyperef(t) ((union ref) {{ RTYPE, (t).bits }}) #define ref2type(r) ((union irtype) {.bits = (r).i}) +#define rswap(a,b) do { union ref _t = (a); (a) = (b); (b) = _t; } while (0) enum op { Oxxx, @@ -86,6 +87,7 @@ enum op { }; #define oiscmp(o) in_range(o, Oequ, Ougte) +#define oisarith(o) in_range(o, Oneg, Ougte) #define oisalloca(o) in_range(o, Oalloca1, Oalloca16) #define oisstore(o) in_range(o, Ostore1, Ostore8) #define oisload(o) in_range(o, Oloads1, Oloadf8) @@ -219,7 +221,7 @@ union ref mkfltcon(enum irclass, double); #define isnumcon(r) ((r).t == RICON || ((r).t == RXCON && conht[(r).i].cls)) #define isaddrcon(r) ((r).t == RXCON && !conht[(r).i].cls && !conht[(r).i].deref) #define intconval(r) ((r).t == RICON ? (r).i : conht[(r).i].i) -#define fltconval(r) (conht[(r).i].f) +#define fltconval(r) ((r).t == RICON ? (r).i : conht[(r).i].f) union ref mksymref(const char *); union ref mkdatref(const char *name, uint siz, uint align, const void *, uint n, bool deref); const char *xcon2sym(int ref); @@ -247,7 +249,9 @@ void fillblkids(struct function *); #define markvisited(blk) ((blk)->visit = visitmark) void numberinstrs(struct function *); -/* IR builder functions */ +/** builder.c **/ +union ref irbinop(struct function *, enum op, enum irclass, union ref lhs, union ref rhs); +union ref irunop(struct function *, enum op, enum irclass, union ref); union ref addinstr(struct function *, struct instr); union ref addphi(struct function *, enum irclass, union ref []); void useblk(struct function *, struct block *); @@ -255,6 +259,10 @@ void putbranch(struct function *, struct block *); void putcondbranch(struct function *, union ref arg, struct block *t, struct block *f); void putreturn(struct function *, union ref r0, union ref r1); +/** fold.c **/ +bool foldbinop(union ref *to, enum op, enum irclass, union ref l, union ref r); +bool foldunop(union ref *to, enum op, enum irclass, union ref); + /** irdump.c **/ void irdump(struct function *); diff --git a/ir/optmem.c b/ir/optmem.c index 471f11c..9b71c8d 100644 --- a/ir/optmem.c +++ b/ir/optmem.c @@ -246,6 +246,20 @@ mem2reg(struct function *fn) k, mkref(RREG, mctarg->bpr)); } else { adduse(use->blk, use->u, val); + if (isintcon(val) && ext != Ocopy) { + vlong x = intconval(val); + switch (ext) { + case Oexts1: x = (schar)x; break; + case Oextu1: x = (uchar)x; break; + case Oexts2: x = (short)x; break; + case Oextu2: x = (ushort)x; break; + case Oexts4: x = (int)x; break; + case Oextu4: x = (uint)x; break; + default: assert(0); + } + val = mkintcon(k, x); + ext = Ocopy; + } *m = mkinstr(ext, k, val); } } diff --git a/ir/regalloc.c b/ir/regalloc.c index fae1a61..f43bcc7 100644 --- a/ir/regalloc.c +++ b/ir/regalloc.c @@ -2,7 +2,7 @@ /** Implements linear scan register allocation **/ -#if 0 +#if 1 #define DBG(...) if(ccopt.dbg.r) efmt(__VA_ARGS__) #else #define DBG(...) ((void)0) @@ -1000,12 +1000,19 @@ devirt(struct rega *ra, struct block *blk) bool dosave; /* pick scratch register, or any register that doesn't conflict with this instr's srcs/dst */ if (nspill > 0) { - for (reg = kisflt(ld.cls) ? mctarg->fpr0 : mctarg->gpr0;; ++reg) { - if (reg == ins->reg-1) continue; - for (int j = 0; j < i; ++j) - if (argref[j]->t == RREG && argref[j]->i == reg) continue; - break; + regset avail = kisflt(ld.cls) ? fpregset : gpregset; + if (ins->reg) rsclr(&avail, ins->reg-1); + for (int j = 0; j < nargref; ++j) { + struct interval *it; + if (argref[j]->t == RREG) rsclr(&avail, argref[j]->i); + else if (argref[j]->t == RTMP) { + it = &ra->intervals.temps[argref[j]->i]; + if (it->alloc.t == AREG) rsclr(&avail, it->alloc.a); + } } + assert(avail != 0); + if (reg &~ (fn->regusage | mctarg->rcallee)) reg &= ~(fn->regusage | mctarg->rcallee); + reg = lowestsetbit(avail); /* if not the designated scratch register, we need to save+restore */ if ((dosave = rstest(fn->regusage, reg) || rstest(mctarg->rcallee, reg))) { insertinstr(blk, curi++, mkinstr(Oxsave, 0, .l = mkref(RREG, reg))); |