qbe

Internal scc patchset buffer for QBE
Log | Files | Refs | README | LICENSE

commit fcdef10dae54d7124aca9ccbefe53baa8e67267d
parent 9858a12730717d9c5e5deec4264d7041d75fc947
Author: Quentin Carbonneaux <quentin@c9x.me>
Date:   Mon, 18 Oct 2021 21:04:10 +0200

make variadic args explicit

Some abis, like the riscv one, treat
arguments differently depending on
whether they are variadic or not.
To prepare for the upcomming riscv
target, we change the variadic call
syntax and give meaning to the
location of the '...' marker.

  # new syntax
  %ret =w call $f(w %regular, ..., w %variadic)

By nature of their abis, the change
is backwards compatible for existing
targets.

Diffstat:
Mall.h | 3+--
Mamd64/sysv.c | 34+++++++++++++++++++++-------------
Marm64/abi.c | 17+++++++++++------
Mdoc/il.txt | 6+++---
Mlive.c | 2+-
Mload.c | 2+-
Mops.h | 2+-
Mparse.c | 48+++++++++++++++++++++++++++++++-----------------
Mtest/_slow.qbe | 10+++++-----
Mtest/abi5.ssa | 18+++++++++---------
Mtest/abi6.ssa | 4++--
Mtest/echo.ssa | 2+-
Mtest/vararg2.ssa | 28++++++++++++++--------------
13 files changed, 101 insertions(+), 75 deletions(-)

diff --git a/all.h b/all.h @@ -171,12 +171,11 @@ enum { }; #define INRANGE(x, l, u) ((unsigned)(x) - l <= u - l) /* linear in x */ -#define iscall(o) INRANGE(o, Ocall, Ovacall) #define isstore(o) INRANGE(o, Ostoreb, Ostored) #define isload(o) INRANGE(o, Oloadsb, Oload) #define isext(o) INRANGE(o, Oextsb, Oextuw) #define ispar(o) INRANGE(o, Opar, Opare) -#define isarg(o) INRANGE(o, Oarg, Oarge) +#define isarg(o) INRANGE(o, Oarg, Oargv) #define isret(j) INRANGE(j, Jret0, Jretc) enum Class { diff --git a/amd64/sysv.c b/amd64/sysv.c @@ -153,7 +153,7 @@ selret(Blk *b, Fn *fn) static int argsclass(Ins *i0, Ins *i1, AClass *ac, int op, AClass *aret, Ref *env) { - int nint, ni, nsse, ns, n, *pn; + int varc, envc, nint, ni, nsse, ns, n, *pn; AClass *a; Ins *i; @@ -162,6 +162,8 @@ argsclass(Ins *i0, Ins *i1, AClass *ac, int op, AClass *aret, Ref *env) else nint = 6; nsse = 8; + varc = 0; + envc = 0; for (i=i0, a=ac; i<i1; i++, a++) switch (i->op - op + Oarg) { case Oarg: @@ -196,14 +198,23 @@ argsclass(Ins *i0, Ins *i1, AClass *ac, int op, AClass *aret, Ref *env) a->inmem = 1; break; case Oarge: + envc = 1; if (op == Opar) *env = i->to; else *env = i->arg[0]; break; + case Oargv: + varc = 1; + break; + default: + die("unreachable"); } - return (!req(R, *env) << 12) | ((6-nint) << 4) | ((8-nsse) << 8); + if (varc && envc) + err("sysv abi does not support variadic env calls"); + + return ((varc|envc) << 12) | ((6-nint) << 4) | ((8-nsse) << 8); } int amd64_sysv_rsave[] = { @@ -290,7 +301,7 @@ selcall(Fn *fn, Ins *i0, Ins *i1, RAlloc **rap) { Ins *i; AClass *ac, *a, aret; - int ca, ni, ns, al, varc, envc; + int ca, ni, ns, al; uint stk, off; Ref r, r1, r2, reg[2], env; RAlloc *ra; @@ -358,22 +369,20 @@ selcall(Fn *fn, Ins *i0, Ins *i1, RAlloc **rap) ca += 1 << 2; } } - envc = !req(R, env); - varc = i1->op == Ovacall; - if (varc && envc) - err("sysv abi does not support variadic env calls"); - ca |= varc << 12; /* envc set in argsclass() */ + emit(Ocall, i1->cls, R, i1->arg[0], CALL(ca)); - if (envc) + + if (!req(R, env)) emit(Ocopy, Kl, TMP(RAX), env, R); - if (varc) + else if ((ca >> 12) & 1) /* vararg call */ emit(Ocopy, Kw, TMP(RAX), getcon((ca >> 8) & 15, fn), R); ni = ns = 0; if (ra && aret.inmem) emit(Ocopy, Kl, rarg(Kl, &ni, &ns), ra->i.to, R); /* pass hidden argument */ + for (i=i0, a=ac; i<i1; i++, a++) { - if (a->inmem || i->op == Oarge) + if (i->op >= Oarge || a->inmem) continue; r1 = rarg(a->cls[0], &ni, &ns); if (i->op == Oargc) { @@ -393,7 +402,7 @@ selcall(Fn *fn, Ins *i0, Ins *i1, RAlloc **rap) r = newtmp("abi", Kl, fn); for (i=i0, a=ac, off=0; i<i1; i++, a++) { - if (!a->inmem) + if (i->op >= Oarge || !a->inmem) continue; if (i->op == Oargc) { if (a->align == 4) @@ -676,7 +685,6 @@ amd64_sysv_abi(Fn *fn) emiti(*i); break; case Ocall: - case Ovacall: for (i0=i; i0>b->ins; i0--) if (!isarg((i0-1)->op)) break; diff --git a/arm64/abi.c b/arm64/abi.c @@ -255,6 +255,10 @@ argsclass(Ins *i0, Ins *i1, Class *carg, Ref *env) case Oarge: *env = i->arg[0]; break; + case Oargv: + break; + default: + die("unreachable"); } return ((gp-gpreg) << 5) | ((fp-fpreg) << 9); @@ -371,10 +375,11 @@ selcall(Fn *fn, Ins *i0, Ins *i1, Insl **ilp) } } + emit(Ocall, 0, R, i1->arg[0], CALL(cty)); + envc = !req(R, env); if (envc) die("todo (arm abi): env calls"); - emit(Ocall, 0, R, i1->arg[0], CALL(cty)); if (cty & (1 << 13)) /* struct return argument */ @@ -383,9 +388,9 @@ selcall(Fn *fn, Ins *i0, Ins *i1, Insl **ilp) for (i=i0, c=ca; i<i1; i++, c++) { if ((c->class & Cstk) != 0) continue; - if (i->op != Oargc) + if (i->op == Oarg) emit(Ocopy, *c->cls, TMP(*c->reg), i->arg[0], R); - else + if (i->op == Oargc) ldregs(c->reg, c->cls, c->nreg, i->arg[1], fn); } @@ -393,11 +398,12 @@ selcall(Fn *fn, Ins *i0, Ins *i1, Insl **ilp) for (i=i0, c=ca; i<i1; i++, c++) { if ((c->class & Cstk) == 0) continue; - if (i->op != Oargc) { + if (i->op == Oarg) { r = newtmp("abi", Kl, fn); emit(Ostorel, 0, R, i->arg[0], r); emit(Oadd, Kl, r, TMP(SP), getcon(off, fn)); - } else + } + if (i->op == Oargc) blit(TMP(SP), off, i->arg[1], c->size, fn); off += c->size; } @@ -675,7 +681,6 @@ arm64_abi(Fn *fn) emiti(*i); break; case Ocall: - case Ovacall: for (i0=i; i0>b->ins; i0--) if (!isarg((i0-1)->op)) break; diff --git a/doc/il.txt b/doc/il.txt @@ -751,7 +751,7 @@ single-precision floating point number `%f` into `%rs`. ARG := ABITY VAL # Regular argument | 'env' VAL # Environment argument (first) - | '...' # Variadic marker (last) + | '...' # Variadic marker ABITY := BASETY | :IDENT @@ -778,8 +778,8 @@ integer. If the called function does not expect an environment parameter, it will be safely discarded. See the <@ Functions > section for more information about environment parameters. -When the called function is variadic, the last argument -must be `...`. +When the called function is variadic, there must be a `...` +marker separating the named and variadic arguments. ~ Variadic ~~~~~~~~~~ diff --git a/live.c b/live.c @@ -82,7 +82,7 @@ Again: for (k=0; k<2; k++) b->nlive[k] = nlv[k]; for (i=&b->ins[b->nins]; i!=b->ins;) { - if (iscall((--i)->op) && rtype(i->arg[1]) == RCall) { + if ((--i)->op == Ocall && rtype(i->arg[1]) == RCall) { b->in->t[0] &= ~T.retregs(i->arg[1], m); for (k=0; k<2; k++) { nlv[k] -= m[k]; diff --git a/load.c b/load.c @@ -234,7 +234,7 @@ def(Slice sl, bits msk, Blk *b, Ins *i, Loc *il) while (i > b->ins) { --i; if (killsl(i->to, sl) - || (iscall(i->op) && escapes(sl.ref, curf))) + || (i->op == Ocall && escapes(sl.ref, curf))) goto Load; ld = isload(i->op); if (ld) { diff --git a/ops.h b/ops.h @@ -137,8 +137,8 @@ O(pare, T(e,x,e,e, e,x,e,e), 0) X(0, 0, 0) O(arg, T(w,l,s,d, x,x,x,x), 0) X(0, 0, 0) O(argc, T(e,x,e,e, e,l,e,e), 0) X(0, 0, 0) O(arge, T(e,l,e,e, e,x,e,e), 0) X(0, 0, 0) +O(argv, T(x,x,x,x, x,x,x,x), 0) X(0, 0, 0) O(call, T(m,m,m,m, x,x,x,x), 0) X(0, 0, 0) -O(vacall, T(m,m,m,m, x,x,x,x), 0) X(0, 0, 0) /* Flags Setting */ O(flagieq, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) diff --git a/parse.c b/parse.c @@ -450,25 +450,44 @@ parsecls(int *tyn) static int parserefl(int arg) { - int k, ty, env, hasenv; + int k, ty, env, hasenv, vararg; Ref r; hasenv = 0; + vararg = 0; expect(Tlparen); - while (peek() != Trparen && peek() != Tdots) { + while (peek() != Trparen) { if (curi - insb >= NIns) err("too many instructions (1)"); - env = peek() == Tenv; - if (env) { + if (!arg && vararg) + err("no parameters allowed after '...'"); + switch (peek()) { + case Tdots: + if (vararg) + err("only one '...' allowed"); + vararg = 1; + if (arg) { + *curi = (Ins){.op = Oargv}; + curi++; + } + next(); + goto Next; + case Tenv: + if (hasenv) + err("only one environment allowed"); + hasenv = 1; + env = 1; next(); k = Kl; - } else + break; + default: + env = 0; k = parsecls(&ty); + break; + } r = parseref(); if (req(r, R)) err("invalid argument"); - if (hasenv && env) - err("only one environment allowed"); if (!arg && rtype(r) != RTmp) err("invalid function parameter"); if (k == 4) @@ -487,16 +506,13 @@ parserefl(int arg) else *curi = (Ins){Opar, k, r, {R}}; curi++; - hasenv |= env; + Next: if (peek() == Trparen) break; expect(Tcomma); } - if (next() == Tdots) { - expect(Trparen); - return 1; - } - return 0; + expect(Trparen); + return vararg; } static Blk * @@ -621,10 +637,8 @@ DoOp: } if (op == Tcall) { arg[0] = parseref(); - if (parserefl(1)) - op = Ovacall; - else - op = Ocall; + parserefl(1); + op = Ocall; expect(Tnl); if (k == 4) { k = Kl; diff --git a/test/_slow.qbe b/test/_slow.qbe @@ -3408,7 +3408,7 @@ function $transparent_crc(l %.1, l %.3, w %.5) { %.9 =l copy $.Lstring.93 %.10 =l loadl %.4 %.11 =l loadl %.2 - %.12 =w call $printf(l %.9, l %.10, l %.11, ...) + %.12 =w call $printf(l %.9, ..., l %.10, l %.11) @if_false.679 %.13 =l loadl $crc32_context %.14 =l loadl %.2 @@ -3461,7 +3461,7 @@ function $transparent_crc_bytes(l %.1, w %.3, l %.5, w %.7) { %.28 =l loadl $crc32_context %.29 =l copy 4294967295 %.30 =l xor %.28, %.29 - %.31 =w call $printf(l %.26, l %.27, l %.30, ...) + %.31 =w call $printf(l %.26, ..., l %.27, l %.30) @if_false.687 ret } @@ -3480,7 +3480,7 @@ function $platform_main_end(l %.1, w %.3) { @if_true.690 %.8 =l copy $.Lstring.97 %.9 =l loadl %.2 - %.10 =w call $printf(l %.8, l %.9, ...) + %.10 =w call $printf(l %.8, ..., l %.9) @if_false.691 ret } @@ -35219,7 +35219,7 @@ function w $main(w %.1, l %.3) { %.53 =w loadsw %.5 %.54 =w loadsw %.6 %.55 =w loadsw %.7 - %.56 =w call $printf(l %.52, w %.53, w %.54, w %.55, ...) + %.56 =w call $printf(l %.52, ..., w %.53, w %.54, w %.55) @if_false.1519 @for_cont.1516 %.57 =w loadsw %.7 @@ -35338,7 +35338,7 @@ function w $main(w %.1, l %.3) { @if_true.1524 %.138 =l copy $.Lstring.129 %.139 =w loadsw %.5 - %.140 =w call $printf(l %.138, w %.139, ...) + %.140 =w call $printf(l %.138, ..., w %.139) @if_false.1525 @for_cont.1522 %.141 =w loadsw %.5 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/abi6.ssa b/test/abi6.ssa @@ -13,8 +13,8 @@ function $f(:hfa3 %h1, :hfa3 %h2, d %d1, :hfa3 %h3, d %d2) { call $phfa3(:hfa3 %h1) call $phfa3(:hfa3 %h2) call $phfa3(:hfa3 %h3) - call $printf(l $dfmt, d %d1, ...) - call $printf(l $dfmt, d %d2, ...) + call $printf(l $dfmt, ..., d %d1) + call $printf(l $dfmt, ..., d %d2) 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/test/vararg2.ssa b/test/vararg2.ssa @@ -19,11 +19,11 @@ export function $qbeprint0(l %fmt, ...) { jnz %isg, @casef, @cased @casef %dbl =d vaarg %vp - %r =w call $printf(l %fmtdbl, d %dbl, ...) + %r =w call $printf(l %fmtdbl, ..., d %dbl) jmp @loop @cased %int =w vaarg %vp - %r =w call $printf(l %fmtint, w %int, ...) + %r =w call $printf(l %fmtint, ..., w %int) jmp @loop @end %r =w call $puts(l %emptys) @@ -59,11 +59,11 @@ export function $qbeprint1(w %argw0, l %fmt, ...) { jnz %isg, @casef, @cased @casef %dbl =d vaarg %vp - %r =w call $printf(l %fmtdbl, d %dbl, ...) + %r =w call $printf(l %fmtdbl, ..., d %dbl) jmp @loop @cased %int =w vaarg %vp - %r =w call $printf(l %fmtint, w %int, ...) + %r =w call $printf(l %fmtint, ..., w %int) jmp @loop @end %r =w call $puts(l %emptys) @@ -99,11 +99,11 @@ export function $qbeprint2(d %argd0, l %fmt, ...) { jnz %isg, @casef, @cased @casef %dbl =d vaarg %vp - %r =w call $printf(l %fmtdbl, d %dbl, ...) + %r =w call $printf(l %fmtdbl, ..., d %dbl) jmp @loop @cased %int =w vaarg %vp - %r =w call $printf(l %fmtint, w %int, ...) + %r =w call $printf(l %fmtint, ..., w %int) jmp @loop @end %r =w call $puts(l %emptys) @@ -139,11 +139,11 @@ export function $qbeprint3(w %argw0, w %argw1, w %argw2, w %argw3, l %fmt, ...) jnz %isg, @casef, @cased @casef %dbl =d vaarg %vp - %r =w call $printf(l %fmtdbl, d %dbl, ...) + %r =w call $printf(l %fmtdbl, ..., d %dbl) jmp @loop @cased %int =w vaarg %vp - %r =w call $printf(l %fmtint, w %int, ...) + %r =w call $printf(l %fmtint, ..., w %int) jmp @loop @end %r =w call $puts(l %emptys) @@ -179,11 +179,11 @@ export function $qbeprint4(d %argd0, d %argd1, d %argd2, d %argd3, d %argd4, d % jnz %isg, @casef, @cased @casef %dbl =d vaarg %vp - %r =w call $printf(l %fmtdbl, d %dbl, ...) + %r =w call $printf(l %fmtdbl, ..., d %dbl) jmp @loop @cased %int =w vaarg %vp - %r =w call $printf(l %fmtint, w %int, ...) + %r =w call $printf(l %fmtint, ..., w %int) jmp @loop @end %r =w call $puts(l %emptys) @@ -219,11 +219,11 @@ export function $qbeprint5(w %argw0, w %argw1, w %argw2, w %argw3, w %argw4, d % jnz %isg, @casef, @cased @casef %dbl =d vaarg %vp - %r =w call $printf(l %fmtdbl, d %dbl, ...) + %r =w call $printf(l %fmtdbl, ..., d %dbl) jmp @loop @cased %int =w vaarg %vp - %r =w call $printf(l %fmtint, w %int, ...) + %r =w call $printf(l %fmtint, ..., w %int) jmp @loop @end %r =w call $puts(l %emptys) @@ -259,11 +259,11 @@ export function $qbeprint6(w %argw0, w %argw1, w %argw2, w %argw3, w %argw4, w % jnz %isg, @casef, @cased @casef %dbl =d vaarg %vp - %r =w call $printf(l %fmtdbl, d %dbl, ...) + %r =w call $printf(l %fmtdbl, ..., d %dbl) jmp @loop @cased %int =w vaarg %vp - %r =w call $printf(l %fmtint, w %int, ...) + %r =w call $printf(l %fmtint, ..., w %int) jmp @loop @end %r =w call $puts(l %emptys)