diff options
author | Quentin Carbonneaux <quentin.carbonneaux@yale.edu> | 2017-02-07 23:01:24 -0500 |
---|---|---|
committer | Quentin Carbonneaux <quentin.carbonneaux@yale.edu> | 2017-02-10 11:05:54 -0500 |
commit | b99a8b0d07d43b89d5e27883ee5a9a67c2645809 (patch) | |
tree | 9a3f4ebcc0bb971a7e361115b8d9b19529902cb7 | |
parent | 8799dc30ac472545bc93957c22f070590ff44cb3 (diff) | |
download | roux-b99a8b0d07d43b89d5e27883ee5a9a67c2645809.tar.gz |
support variable argument lists
This change is backward compatible, calls to "variadic" functions (like printf) must now be annotated (with ...).
-rw-r--r-- | all.h | 5 | ||||
-rw-r--r-- | emit.c | 22 | ||||
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | parse.c | 35 | ||||
-rw-r--r-- | sysv.c | 219 | ||||
-rw-r--r-- | test/abi5.ssa | 18 | ||||
-rw-r--r-- | test/echo.ssa | 2 | ||||
-rw-r--r-- | tools/lexh.c | 1 |
8 files changed, 256 insertions, 47 deletions
diff --git a/all.h b/all.h index 1ccf053..d13ef5c 100644 --- a/all.h +++ b/all.h @@ -256,6 +256,9 @@ enum Op { Oalloc, Oalloc1 = Oalloc + NAlign-1, + Ovastart, + Ovaarg, + Ocopy, NPubOp, @@ -265,6 +268,7 @@ enum Op { Oarg, Oargc, Ocall, + Ovacall, /* reserved instructions */ Onop, @@ -442,6 +446,7 @@ struct Fn { bits reg; int slot; char export; + char vararg; char name[NString]; }; diff --git a/emit.c b/emit.c index 2c0b3cc..ccbd516 100644 --- a/emit.c +++ b/emit.c @@ -137,13 +137,14 @@ slot(int s, Fn *fn) /* sign extend s using a bitfield */ x.i = s; + assert(x.i <= fn->slot); /* specific to NAlign == 3 */ if (x.i < 0) return -4 * x.i; - else { - assert(fn->slot >= x.i); + else if (fn->vararg) + return -176 + -4 * (fn->slot - x.i); + else return -4 * (fn->slot - x.i); - } } static void @@ -481,7 +482,7 @@ framesz(Fn *fn) o ^= 1 & (fn->reg >> rclob[i]); f = fn->slot; f = (f + 3) & -4; - return 4*f + 8*o; + return 4*f + 8*o + 176*fn->vararg; } void @@ -504,20 +505,27 @@ emitfn(Fn *fn, FILE *f) static int id0; Blk *b, *s; Ins *i, itmp; - int *r, c, fs; + int *r, c, fs, o, n; fprintf(f, ".text\n"); if (fn->export) fprintf(f, ".globl %s%s\n", symprefix, fn->name); fprintf(f, "%s%s:\n" - "\tpush %%rbp\n" - "\tmov %%rsp, %%rbp\n", + "\tpushq %%rbp\n" + "\tmovq %%rsp, %%rbp\n", symprefix, fn->name ); fs = framesz(fn); if (fs) fprintf(f, "\tsub $%d, %%rsp\n", fs); + if (fn->vararg) { + o = -176; + for (r=rsave; r-rsave<6; ++r, o+=8) + fprintf(f, "\tmovq %%%s, %d(%%rbp)\n", rname[*r][0], o); + for (n=0; n<8; ++n, o+=16) + fprintf(f, "\tmovaps %%xmm%d, %d(%%rbp)\n", n, o); + } for (r=rclob; r-rclob < NRClob; r++) if (fn->reg & BIT(*r)) { itmp.arg[0] = TMP(*r); diff --git a/main.c b/main.c index b60ded6..f93af37 100644 --- a/main.c +++ b/main.c @@ -63,6 +63,7 @@ func(Fn *fn) filluse(fn); fold(fn); abi(fn); + fillpreds(fn); filluse(fn); isel(fn); fillrpo(fn); diff --git a/parse.c b/parse.c index f8fd705..913fc07 100644 --- a/parse.c +++ b/parse.c @@ -65,11 +65,14 @@ OpDesc opdesc[NOp] = { [Oarg] = { "arg", 0, {A(w,l,s,d), A(x,x,x,x)}, 0, 0, 0 }, [Oargc] = { "argc", 0, {A(e,x,e,e), A(e,l,e,e)}, 0, 0, 0 }, [Ocall] = { "call", 0, {A(m,m,m,m), A(x,x,x,x)}, 0, 0, 0 }, + [Ovacall] = { "vacall", 0, {A(m,m,m,m), A(x,x,x,x)}, 0, 0, 0 }, [Oxsetnp] = { "xsetnp", 0, {A(x,x,e,e), A(x,x,e,e)}, 0, 0, 0 }, [Oxsetp] = { "xsetp", 0, {A(x,x,e,e), A(x,x,e,e)}, 0, 0, 0 }, [Oalloc] = { "alloc4", 1, {A(e,l,e,e), A(e,x,e,e)}, 0, 0, 0 }, [Oalloc+1] = { "alloc8", 1, {A(e,l,e,e), A(e,x,e,e)}, 0, 0, 0 }, [Oalloc+2] = { "alloc16", 1, {A(e,l,e,e), A(e,x,e,e)}, 0, 0, 0 }, + [Ovaarg] = { "vaarg", 0, {A(m,m,m,m), A(x,x,x,x)}, 0, 0, 0 }, + [Ovastart] = { "vastart", 0, {A(m,m,m,m), A(x,x,x,x)}, 0, 0, 0 }, #define X(c) \ [Ocmpw+IC##c] = { "c" #c "w", 0, {A(w,w,e,e), A(w,w,e,e)}, 1, 0, 1 }, \ [Ocmpl+IC##c] = { "c" #c "l", 0, {A(l,l,e,e), A(l,l,e,e)}, 1, 0, 1 }, \ @@ -139,6 +142,7 @@ enum { Tlbrace, Trbrace, Tnl, + Tdots, Teof, Ntok @@ -168,13 +172,14 @@ static char *kwmap[Ntok] = { [Td] = "d", [Ts] = "s", [Tz] = "z", + [Tdots] = "...", }; enum { BMask = 8191, /* for blocks hash */ - K = 2047061843, /* found using tools/lexh.c */ - M = 24, + K = 3233235, /* found using tools/lexh.c */ + M = 23, }; static char lexh[1 << (32-M)]; @@ -326,7 +331,7 @@ lex() if (0) Alpha: c = fgetc(inf); if (!isalpha(c) && c != '.' && c != '_') - err("lexing failure: invalid character %c (%d)", c, c); + err("invalid character %c (%d)", c, c); i = 0; do { if (i >= NString-1) @@ -485,14 +490,14 @@ parsecls(int *tyn) } } -static void +static int parserefl(int arg) { int k, ty; Ref r; expect(Tlparen); - while (peek() != Trparen) { + while (peek() != Trparen && peek() != Tdots) { if (curi - insb >= NIns) err("too many instructions (1)"); k = parsecls(&ty); @@ -516,7 +521,11 @@ parserefl(int arg) break; expect(Tcomma); } - next(); + if (next() == Tdots) { + expect(Trparen); + return 1; + } + return 0; } static Blk * @@ -561,7 +570,9 @@ parseline(PState ps) err("label or } expected"); switch (t) { default: - if (isstore(t) || t == Tcall) { + if (isstore(t)) { + case Tcall: + case Ovastart: /* operations without result */ r = R; k = Kw; @@ -639,9 +650,11 @@ DoOp: } if (op == Tcall) { arg[0] = parseref(); - parserefl(1); + if (parserefl(1)) + op = Ovacall; + else + op = Ocall; expect(Tnl); - op = Ocall; if (k == 4) { k = Kl; arg[1] = TYPE(ty); @@ -825,7 +838,7 @@ parsefn(int export) if (next() != Tglo) err("function name expected"); strcpy(curf->name, tokval.str); - parserefl(0); + curf->vararg = parserefl(0); if (nextnl() != Tlbrace) err("function body must start with {"); ps = PLbl; @@ -1142,7 +1155,7 @@ printref(Ref r, Fn *fn, FILE *f) fprintf(f, "S%d", (r.val&(1<<28)) ? r.val-(1<<29) : r.val); break; case RCall: - fprintf(f, "%03x", r.val); + fprintf(f, "%04x", r.val); break; case RType: fprintf(f, ":%s", typ[r.val].name); diff --git a/sysv.c b/sysv.c index b05510c..f4b167b 100644 --- a/sysv.c +++ b/sysv.c @@ -228,6 +228,17 @@ int rclob[] = {RBX, R12, R13, R14, R15}; MAKESURE(rsave_has_correct_size, sizeof rsave == NRSave * sizeof(int)); MAKESURE(rclob_has_correct_size, sizeof rclob == NRClob * sizeof(int)); +/* layout of call's second argument (RCall) + * + * 29 12 8 4 3 0 + * |0...00|x|xxxx|xxxx|xx|xx| range + * | | | | ` gp regs returned (0..2) + * | | | ` sse regs returned (0..2) + * | | ` gp regs passed (0..6) + * | ` sse regs passed (0..8) + * ` 1 if calling a vararg function (0..1) + */ + bits retregs(Ref r, int p[2]) { @@ -257,21 +268,22 @@ bits argregs(Ref r, int p[2]) { bits b; - int j, ni, nf; + int j, ni, nf, va; assert(rtype(r) == RCall); b = 0; ni = (r.val >> 4) & 15; nf = (r.val >> 8) & 15; + va = (r.val >> 12) & 1; for (j=0; j<ni; j++) b |= BIT(rsave[j]); for (j=0; j<nf; j++) b |= BIT(XMM0+j); if (p) { - p[0] = ni + 1; + p[0] = ni + va; p[1] = nf; } - return b | BIT(RAX); + return b | (va ? BIT(RAX) : 0); } static Ref @@ -288,7 +300,7 @@ selcall(Fn *fn, Ins *i0, Ins *i1, RAlloc **rap) { Ins *i; AClass *ac, *a, aret; - int ca, ni, ns, al; + int ca, ni, ns, al, va; uint stk, off; Ref r, r1, r2, reg[2]; RAlloc *ra; @@ -354,8 +366,11 @@ selcall(Fn *fn, Ins *i0, Ins *i1, RAlloc **rap) ca += 1 << 2; } } + va = i1->op == Ovacall; + ca |= va << 12; emit(Ocall, i1->cls, R, i1->arg[0], CALL(ca)); - emit(Ocopy, Kw, TMP(RAX), getcon((ca >> 8) & 15, fn), R); + if (va) + emit(Ocopy, Kw, TMP(RAX), getcon((ca >> 8) & 15, fn), R); ni = ns = 0; if (ra && aret.inmem) @@ -397,12 +412,12 @@ selcall(Fn *fn, Ins *i0, Ins *i1, RAlloc **rap) emit(Osalloc, Kl, r, getcon(stk, fn), R); } -static void +static int selpar(Fn *fn, Ins *i0, Ins *i1) { AClass *ac, *a, aret; Ins *i; - int ni, ns, s, al; + int ni, ns, s, al, fa; Ref r; ac = alloc((i1-i0) * sizeof ac[0]); @@ -411,9 +426,9 @@ selpar(Fn *fn, Ins *i0, Ins *i1) if (fn->retty >= 0) { typclass(&aret, &typ[fn->retty]); - argsclass(i0, i1, ac, Opar, &aret); + fa = argsclass(i0, i1, ac, Opar, &aret); } else - argsclass(i0, i1, ac, Opar, 0); + fa = argsclass(i0, i1, ac, Opar, 0); for (i=i0, a=ac; i<i1; i++, a++) { if (i->op != Oparc || a->inmem) @@ -462,6 +477,156 @@ selpar(Fn *fn, Ins *i0, Ins *i1) } else emit(Ocopy, i->cls, i->to, r, R); } + + return fa | (s*4)<<12; +} + +static Blk * +split(Fn *fn, Blk *b) +{ + Blk *bn; + + ++fn->nblk; + bn = blknew(); + bn->nins = &insb[NIns] - curi; + idup(&bn->ins, curi, bn->nins); + curi = &insb[NIns]; + bn->visit = ++b->visit; + snprintf(bn->name, NString, "%s.%d", b->name, b->visit); + bn->loop = b->loop; + bn->link = b->link; + b->link = bn; + return bn; +} + +static void +chpred(Blk *b, Blk *bp, Blk *bp1) +{ + Phi *p; + uint a; + + for (p=b->phi; p; p=p->link) { + for (a=0; p->blk[a]!=bp; a++) + assert(a+1<p->narg); + p->blk[a] = bp1; + } +} + +void +selvaarg(Fn *fn, Blk *b, Ins *i) +{ + Ref loc, lreg, lstk, nr, r0, r1, c4, c8, c16, c, ap; + Blk *b0, *bstk, *breg; + int isint; + + c4 = getcon(4, fn); + c8 = getcon(8, fn); + c16 = getcon(16, fn); + ap = i->arg[0]; + isint = KBASE(i->cls) == 0; + + /* @b [...] + r0 =l add ap, (0 or 4) + nr =l loadsw r0 + r1 =w cultw nr, (48 or 176) + jnz r1, @breg, @bstk + @breg + r0 =l add ap, 16 + r1 =l loadl r0 + lreg =l add r1, nr + r0 =w add nr, (8 or 16) + r1 =l add ap, (0 or 4) + storew r0, r1 + @bstk + r0 =l add ap, 8 + lstk =l loadl r0 + r1 =l add lstk, 8 + storel r1, r0 + @b0 + %loc =l phi @breg %lreg, @bstk %lstk + i->to =(i->cls) load %loc + */ + + loc = newtmp("abi", Kl, fn); + emit(Oload, i->cls, i->to, loc, R); + b0 = split(fn, b); + b0->jmp = b->jmp; + b0->s1 = b->s1; + b0->s2 = b->s2; + if (b->s1) + chpred(b->s1, b, b0); + if (b->s2 && b->s2 != b->s1) + chpred(b->s2, b, b0); + + lreg = newtmp("abi", Kl, fn); + nr = newtmp("abi", Kl, fn); + r0 = newtmp("abi", Kw, fn); + r1 = newtmp("abi", Kl, fn); + emit(Ostorew, Kw, R, r0, r1); + emit(Oadd, Kl, r1, ap, isint ? CON_Z : c4); + emit(Oadd, Kw, r0, nr, isint ? c8 : c16); + r0 = newtmp("abi", Kl, fn); + r1 = newtmp("abi", Kl, fn); + emit(Oadd, Kl, lreg, r1, nr); + emit(Oload, Kl, r1, r0, R); + emit(Oadd, Kl, r0, ap, c16); + breg = split(fn, b); + breg->jmp.type = Jjmp; + breg->s1 = b0; + + lstk = newtmp("abi", Kl, fn); + r0 = newtmp("abi", Kl, fn); + r1 = newtmp("abi", Kl, fn); + emit(Ostorel, Kw, R, r1, r0); + emit(Oadd, Kl, r1, lstk, c8); + emit(Oload, Kl, lstk, r0, R); + emit(Oadd, Kl, r0, ap, c8); + bstk = split(fn, b); + bstk->jmp.type = Jjmp; + bstk->s1 = b0; + + b0->phi = alloc(sizeof *b0->phi); + *b0->phi = (Phi){ + .cls = Kl, .to = loc, + .narg = 2, + .blk = {bstk, breg}, + .arg = {lstk, lreg}, + }; + r0 = newtmp("abi", Kl, fn); + r1 = newtmp("abi", Kw, fn); + b->jmp.type = Jjnz; + b->jmp.arg = r1; + b->s1 = breg; + b->s2 = bstk; + c = getcon(isint ? 48 : 176, fn); + emit(Ocmpw+ICult, Kw, r1, nr, c); + emit(Oloadsw, Kl, nr, r0, R); + emit(Oadd, Kl, r0, ap, isint ? CON_Z : c4); +} + +void +selvastart(Fn *fn, int fa, Ref ap) +{ + Ref r0, r1; + int gp, fp, sp; + + gp = ((fa >> 4) & 15) * 8; + fp = 48 + ((fa >> 8) & 15) * 16; + sp = fa >> 12; + r0 = newtmp("abi", Kl, fn); + r1 = newtmp("abi", Kl, fn); + emit(Ostorel, Kw, R, r1, r0); + emit(Oadd, Kl, r1, TMP(RBP), getcon(-176, fn)); + emit(Oadd, Kl, r0, ap, getcon(16, fn)); + r0 = newtmp("abi", Kl, fn); + r1 = newtmp("abi", Kl, fn); + emit(Ostorel, Kw, R, r1, r0); + emit(Oadd, Kl, r1, TMP(RBP), getcon(sp, fn)); + emit(Oadd, Kl, r0, ap, getcon(8, fn)); + r0 = newtmp("abi", Kl, fn); + emit(Ostorew, Kw, R, getcon(fp, fn), r0); + emit(Oadd, Kl, r0, ap, getcon(4, fn)); + emit(Ostorew, Kw, R, getcon(gp, fn), ap); } void @@ -470,13 +635,16 @@ abi(Fn *fn) Blk *b; Ins *i, *i0, *ip; RAlloc *ral; - int n; + int n, fa; + + for (b=fn->start; b; b=b->link) + b->visit = 0; - /* lower arguments */ + /* lower parameters */ for (b=fn->start, i=b->ins; i-b->ins < b->nins; i++) if (i->op != Opar && i->op != Oparc) break; - selpar(fn, b->ins, i); + fa = selpar(fn, b->ins, i); n = b->nins - (i - b->ins) + (&insb[NIns] - curi); i0 = alloc(n * sizeof(Ins)); ip = icpy(ip = i0, curi, &insb[NIns] - curi); @@ -484,27 +652,40 @@ abi(Fn *fn) b->nins = n; b->ins = i0; - /* lower calls and returns */ + /* lower calls, returns, and vararg instructions */ ral = 0; b = fn->start; do { if (!(b = b->link)) b = fn->start; /* do it last */ + if (b->visit) + continue; curi = &insb[NIns]; selret(b, fn); - for (i=&b->ins[b->nins]; i!=b->ins;) { - if ((--i)->op == Ocall) { + for (i=&b->ins[b->nins]; i!=b->ins;) + switch ((--i)->op) { + default: + emiti(*i); + break; + case Ocall: + case Ovacall: for (i0=i; i0>b->ins; i0--) if ((i0-1)->op != Oarg) if ((i0-1)->op != Oargc) break; selcall(fn, i0, i, &ral); i = i0; - continue; + break; + case Ovastart: + selvastart(fn, fa, i->arg[0]); + break; + case Ovaarg: + selvaarg(fn, b, i); + break; + case Oarg: + case Oargc: + die("unreachable"); } - assert(i->op != Oarg && i->op != Oargc); - emiti(*i); - } if (b == fn->start) for (; ral; ral=ral->link) emiti(ral->i); diff --git a/test/abi5.ssa b/test/abi5.ssa index c3d9046..edfda4e 100644 --- a/test/abi5.ssa +++ b/test/abi5.ssa @@ -25,41 +25,41 @@ export function $test() { @start %r1 =:st1 call $t1() - %i1 =w call $printf(l $fmt1, l %r1) + %i1 =w call $printf(l $fmt1, l %r1, ...) %r2 =:st2 call $t2() %w2 =w loadw %r2 - %i2 =w call $printf(l $fmt2, w %w2) + %i2 =w call $printf(l $fmt2, w %w2, ...) %r3 =:st3 call $t3() %s3 =s loads %r3 %r34 =l add %r3, 4 %w3 =w loadw %r34 %p3 =d exts %s3 - %i3 =w call $printf(l $fmt3, d %p3, w %w3) + %i3 =w call $printf(l $fmt3, d %p3, w %w3, ...) %r4 =:st4 call $t4() %w4 =w loadw %r4 %r48 =l add 8, %r4 %d4 =d loadd %r48 - %i4 =w call $printf(l $fmt4, w %w4, d %d4) + %i4 =w call $printf(l $fmt4, w %w4, d %d4, ...) %r5 =:st5 call $t5() %s5 =s loads %r5 %d5 =d exts %s5 %r58 =l add %r5, 8 %l5 =l loadl %r58 - %i5 =w call $printf(l $fmt5, d %d5, l %l5) + %i5 =w call $printf(l $fmt5, d %d5, l %l5, ...) %r6 =:st6 call $t6() - %i6 =w call $printf(l $fmt6, l %r6) + %i6 =w call $printf(l $fmt6, l %r6, ...) %r7 =:st7 call $t7() %s7 =s loads %r7 %d71 =d exts %s7 %r78 =l add %r7, 8 %d72 =d loadd %r78 - %i7 =w call $printf(l $fmt7, d %d71, d %d72) + %i7 =w call $printf(l $fmt7, d %d71, d %d72, ...) %r8 =:st8 call $t8() %r84 =l add 4, %r8 @@ -69,14 +69,14 @@ function $test() { %w82 =w loadw %r84 %w83 =w loadw %r88 %w84 =w loadw %r812 - %i8 =w call $printf(l $fmt8, w %w81, w %w82, w %w83, w %w84) + %i8 =w call $printf(l $fmt8, w %w81, w %w82, w %w83, w %w84, ...) %r9 =:st9 call $t9() %r94 =l add 4, %r9 %w9 =w loadw %r9 %s9 =s loads %r94 %d9 =d exts %s9 - %i9 =w call $printf(l $fmt9, w %w9, d %d9) + %i9 =w call $printf(l $fmt9, w %w9, d %d9, ...) ret } diff --git a/test/echo.ssa b/test/echo.ssa index 6671a6a..6010986 100644 --- a/test/echo.ssa +++ b/test/echo.ssa @@ -20,7 +20,7 @@ function w $main(w %argc, l %argv) { @loop2 %sep =w phi @last 10, @nolast 32 %arg =l loadl %av - %r =w call $printf(l %fmt, l %arg, w %sep) + %r =w call $printf(l %fmt, l %arg, w %sep, ...) %av1 =l add %av, 8 %ac1 =w sub %ac, 1 jmp @loop diff --git a/tools/lexh.c b/tools/lexh.c index a4ca937..db27f8f 100644 --- a/tools/lexh.c +++ b/tools/lexh.c @@ -22,6 +22,7 @@ char *tok[] = { "ceql", "cnel", "cles", "clts", "cgts", "cges", "cnes", "ceqs", "cos", "cuos", "cled", "cltd", "cgtd", "cged", "cned", "ceqd", "cod", "cuod", + "vaarg", "vastart", "...", "call", "phi", "jmp", "jnz", "ret", "export", "function", "type", "data", "align", "l", "w", |