commit b99a8b0d07d43b89d5e27883ee5a9a67c2645809
parent 8799dc30ac472545bc93957c22f070590ff44cb3
Author: Quentin Carbonneaux <quentin.carbonneaux@yale.edu>
Date: Tue, 7 Feb 2017 23:01:24 -0500
support variable argument lists
This change is backward compatible, calls to
"variadic" functions (like printf) must now be
annotated (with ...).
Diffstat:
8 files changed, 256 insertions(+), 47 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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",