aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--amd64/all.h2
-rw-r--r--ir.c13
-rw-r--r--ir.h21
-rw-r--r--irdump.c40
-rw-r--r--op.def1
-rw-r--r--parse.c93
-rw-r--r--regalloc.c22
-rw-r--r--test.c9
9 files changed, 150 insertions, 54 deletions
diff --git a/Makefile b/Makefile
index 90bac1b..14824e3 100644
--- a/Makefile
+++ b/Makefile
@@ -19,10 +19,11 @@ obj/%.o: %.c common.h
@mkdir -p `dirname $@`
$(CC) $(CFLAGS) -c -o $@ $<
+ir.h: op.def builtin.def
obj/main.o: parse.h
obj/parse.o: parse.h ir.h
obj/ir.o: ir.h
-obj/irdump.o: ir.h op.def
+obj/irdump.o: ir.h
obj/lex.o: parse.h
obj/eval.o: parse.h
obj/io.o: parse.h keywords.def
diff --git a/amd64/all.h b/amd64/all.h
index ae21d3d..fdd759a 100644
--- a/amd64/all.h
+++ b/amd64/all.h
@@ -8,14 +8,12 @@
_(XMM8) _(XMM9) _(XMM10) _(XMM11) _(XMM12) _(XMM13) _(XMM14) _(XMM15)
enum {
- Rxxx,
#define R(r) r,
LIST_REGS(R)
#undef R
};
const char amd64_rnames[][6] = {
- "?",
#define R(r) #r,
LIST_REGS(R)
#undef R
diff --git a/ir.c b/ir.c
index 65280f8..251990d 100644
--- a/ir.c
+++ b/ir.c
@@ -163,12 +163,11 @@ mksymref(struct function *fn, const char *s)
}
union ref
-mkcall(struct function *fn, union type fnty, uint narg, union ref *args, union irtype *typs)
+mkcallarg(struct function *fn, uint narg, int vararg, union ref *args, union irtype *typs)
{
- const struct typedata *td = &typedata[fnty.dat];
- struct call call = { narg, td->variadic ? td->nmemb : -1 };
+ struct call call = { narg, vararg };
- if (!td->kandr) assert(td->variadic ? narg >= td->nmemb : narg == td->nmemb);
+ assert((long) vararg <= narg);
if (narg) {
call.args = alloc(&fn->arena, narg*sizeof *args + narg*sizeof(union irtype), 0);
call.typs = (union irtype *)((char *)call.args + narg*sizeof *args);
@@ -176,7 +175,7 @@ mkcall(struct function *fn, union type fnty, uint narg, union ref *args, union i
memcpy(call.typs, typs, narg*sizeof *typs);
}
vpush(&calls, call);
- return mkref(REXT, calls.n-1);
+ return mkref(RMORE, calls.n-1);
}
union ref
@@ -202,7 +201,7 @@ addphi2(struct function *fn, enum irclass cls,
phi.blk[1] = b2;
phi.ref[1] = r2;
vpush(&phis, phi);
- ins.l = mkref(REXT, phis.n-1);
+ ins.l = mkref(RMORE, phis.n-1);
assert(ninstr < arraylength(instr));
assert(fn->curblk != NULL);
assert(fn->curblk->ins.n == 0);
@@ -222,7 +221,7 @@ addphi(struct function *fn, enum irclass cls, struct block **blk, union ref *ref
memcpy(phi.blk, blk, n * sizeof(struct block *));
memcpy(phi.ref, ref, n * sizeof(union ref));
vpush(&phis, phi);
- ins.l = mkref(REXT, phis.n-1);
+ ins.l = mkref(RMORE, phis.n-1);
assert(ninstr < arraylength(instr));
assert(fn->curblk != NULL);
assert(fn->curblk->ins.n == 0);
diff --git a/ir.h b/ir.h
index f1a511e..ed4854a 100644
--- a/ir.h
+++ b/ir.h
@@ -10,8 +10,8 @@ enum irclass {
#define kisflt(k) in_range((k), KF4, KF8)
union irtype {
- struct { ushort isagg : 1, cls : 15; };
- struct { ushort _ : 1, dat : 15; };
+ struct { ushort _ : 1, cls : 15; };
+ struct { ushort isagg : 1, dat : 15; };
ushort bits;
};
@@ -61,7 +61,7 @@ enum refkind {
RARG, /* function argument */
RICON, /* small integer constants */
RXCON, /* other constants (incl. external symbols) */
- REXT, /* reference to extra data for Ocall and Ophi */
+ RMORE, /* reference to extra data for Ocall and Ophi */
RREG, /* machine register */
};
@@ -78,9 +78,16 @@ enum op {
#undef _
};
+enum builtin {
+ BTxxx,
+#define _(b,...) BT##b,
+#include "builtin.def"
+#undef _
+};
+
struct instr {
uchar op, cls;
- uchar reg, hint;
+ ushort reg; /* 0 -> unallocated; else reg + 1 */
union ref l, r;
};
@@ -107,7 +114,7 @@ struct function {
bool globl;
};
-enum { MAXREGS = 32 };
+enum { MAXREGS = 64 };
struct mctarg {
short gpr0, /* first gpr */
@@ -125,7 +132,7 @@ extern const uchar siz2intcls[];
#define NOREF ((union ref) {0})
#define mkref(t, x) ((union ref) {{ (t), (x) }})
#define mkzerocon() ((union ref) {{ RICON, 0 }})
-#define mkinstr(O, C, ...) ((struct instr) { .op = (O), .cls = (C), .reg=0,.hint=0, __VA_ARGS__ })
+#define mkinstr(O, C, ...) ((struct instr) { .op = (O), .cls = (C), .reg=0, __VA_ARGS__ })
void irinit(struct function *);
void irfini(struct function *);
union irtype mkirtype(union type);
@@ -133,7 +140,7 @@ union ref mkintcon(struct function *, enum irclass, vlong);
union ref mkfltcon(struct function *, enum irclass, double);
union ref mksymref(struct function *, const char *);
void conputdat(struct irdat *, uint off, enum typetag t, const void *dat);
-union ref mkcall(struct function *, union type fnty, uint narg, union ref *, union irtype *);
+union ref mkcallarg(struct function *, uint narg, int vararg, union ref *, union irtype *);
union ref addinstr(struct function *, struct instr);
union ref addphi2(struct function *, enum irclass cls,
struct block *b1, union ref r1, struct block *b2, union ref r2);
diff --git a/irdump.c b/irdump.c
index 425c7d2..38b074e 100644
--- a/irdump.c
+++ b/irdump.c
@@ -22,6 +22,13 @@ prityp(union irtype typ)
}
}
+static const char *builtinname[] = {
+ "?\??",
+#define _(b,...) #b,
+#include "builtin.def"
+#undef _
+};
+
static void
dumpref(enum op o, union ref ref)
{
@@ -29,7 +36,10 @@ dumpref(enum op o, union ref ref)
switch (ref.t) {
case RTMP: efmt("%%%d", ref.idx); break;
case RARG: efmt("%%arg%d", ref.idx); break;
- case RICON: efmt("%d", ref.i); break;
+ case RICON:
+ if (o == Obuiltin) efmt("\"%s\"", builtinname[ref.i]);
+ else efmt("%d", ref.i);
+ break;
case RXCON:
con = &conht[ref.idx];
if (con->issym) efmt("$%s", con->sym);
@@ -42,15 +52,14 @@ dumpref(enum op o, union ref ref)
default: assert(0);
}
break;
- case REXT:
- if (o == Ocall) {
+ case RMORE:
+ if (o == Ocall || o == Obuiltin) {
extern vec_of(struct call) calls;
struct call *call = &calls.p[ref.idx];
for (int i = 0; i < call->narg; ++i) {
- if (call->vararg == i) {
- if (i > 0) efmt(", ");
+ if (i > 0) efmt(", ");
+ if (call->vararg == i)
efmt("..., ");
- }
prityp(call->typs[i]);
efmt(" ");
dumpref(0, call->args[i]);
@@ -64,7 +73,7 @@ dumpref(enum op o, union ref ref)
dumpref(0, phi->ref[i]);
efmt("]");
}
- }
+ } else assert(0);
break;
default: assert(!"ref");
}
@@ -72,7 +81,7 @@ dumpref(enum op o, union ref ref)
extern struct instr instr[];
static const char *opname[] = {
- "???",
+ "?\??",
#define _(o,...) #o,
#include "op.def"
#undef _
@@ -91,7 +100,7 @@ dumpinst(const struct instr *ins)
efmt(" ");
if (ins->cls) {
efmt("%s %%%d", clsname[ins->cls], ins - instr);
- if (ins->reg) efmt("(%ls)", mctarg->rnames[ins->reg]);
+ if (ins->reg) efmt("(%ls)", mctarg->rnames[ins->reg - 1]);
efmt(" = ");
}
efmt("%s ", opname[ins->op]);
@@ -103,9 +112,9 @@ dumpinst(const struct instr *ins)
}
static void
-dumpblk(struct block *blk)
+dumpblk(struct function *fn, struct block *blk)
{
- static const char *jnames[] = { 0, "b", "b", "ret", "rets" };
+ static const char *jnames[] = { 0, "b", "b", "ret", "ret" };
static const uchar jnarg[] = { 0, 0, 1, 0, 1 };
efmt(" .L%d:\n", blk->id);
for (int i = 0; i < blk->phi.n; ++i) {
@@ -116,6 +125,10 @@ dumpblk(struct block *blk)
}
efmt(" %s ", jnames[blk->jmp.t]);
if (jnarg[blk->jmp.t]) {
+ if (blk->jmp.t == Jrets) {
+ prityp(mkirtype(fn->retty));
+ efmt(" ");
+ }
dumpref(0, blk->jmp.arg);
if (blk->s1) efmt(", ");
}
@@ -132,9 +145,8 @@ irdump(struct function *fn, const char *fname)
efmt("function %s : %ty\n", fname, fn->fnty);
blk = fn->entry;
do {
- dumpblk(blk);
- blk = blk->lnext;
- } while (blk != fn->entry);
+ dumpblk(fn, blk);
+ } while ((blk = blk->lnext) != fn->entry);
}
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/op.def b/op.def
index 290611c..9fc7d7a 100644
--- a/op.def
+++ b/op.def
@@ -58,4 +58,5 @@ _(store2, 2)
_(store4, 2)
_(store8, 2)
_(call, 2)
+_(builtin, 2)
_(phi, 1)
diff --git a/parse.c b/parse.c
index 5b38637..006117e 100644
--- a/parse.c
+++ b/parse.c
@@ -873,6 +873,8 @@ Postfix:
case '&':
if (!islvalue(&ex))
error(&span, "operand to unary & is not an lvalue");
+ if (ex.t == EGETF && ex.fld.bitsiz)
+ error(&span, "cannot take address of bitfield");
ex = mkexpr(EADDROF, span, mkptrtype(ex.ty, ex.qual), .sub = exprdup(pr, &ex));
break;
case TKWsizeof:
@@ -947,8 +949,47 @@ commaexpr(struct parser *pr)
/* -> IR */
/*********/
+static union ref expraddr(struct function *, const struct expr *);
static union ref exprvalue(struct function *, const struct expr *);
+static bool
+exhasaddr(const struct expr *ex)
+{
+ if (ex->t == ECOND)
+ return exhasaddr(&ex->sub[1]) && exhasaddr(&ex->sub[2]);
+ if (ex->t == ESEQ)
+ return exhasaddr(&ex->sub[1]);
+ if (ex->t == ESET)
+ return exhasaddr(&ex->sub[0]);
+ if (ex->t == EGETF)
+ return !ex->fld.bitsiz;
+ return islvalue(ex);
+}
+
+static void
+structcopy(struct function *fn, union ref dst, const struct expr *src)
+{
+ if (exhasaddr(src)) {
+ union ref argsr;
+ union ref srcref = expraddr(fn, src);
+ union ref args[2] = { dst, srcref };
+ union irtype typ[2] = { mkirtype(src->ty) };
+ typ[1] = typ[0];
+ argsr = mkcallarg(fn, 2, -1, args, typ);
+ addinstr(fn, mkinstr(Obuiltin, 0, mkref(RICON, BTstructcopy), argsr));
+ }
+ assert(!"nyi");
+}
+
+static union ref
+structreturn(struct function *fn, const struct expr *src)
+{
+ if (exhasaddr(src)) {
+ return expraddr(fn, src);
+ }
+ assert(!"nyi");
+}
+
static union ref
expraddr(struct function *fn, const struct expr *ex)
{
@@ -983,7 +1024,14 @@ expraddr(struct function *fn, const struct expr *ex)
ins.l = r;
ins.r = mkintcon(fn, KI4, ex->fld.off);
return addinstr(fn, ins);
- break;
+ case ESET:
+ assert(isagg(ex->ty));
+ r = expraddr(fn, &ex->sub[0]);
+ structcopy(fn, r, &ex->sub[1]);
+ return r;
+ case ESEQ:
+ (void)exprvalue(fn, &ex->sub[0]);
+ return expraddr(fn, &ex->sub[1]);
default:
assert(!"lvalue?>");
}
@@ -1071,14 +1119,19 @@ static union ref
narrow(struct function *fn, enum irclass to, enum typetag tt, union ref ref)
{
struct instr ins = {0};
- assert(isintt(tt) || tt == TYPTR);
+ assert(isscalart(tt));
if (targ_primsizes[tt] >= cls2siz[to]) return ref;
ins.cls = to;
- switch (targ_primsizes[tt]) {
- case 1: ins.op = issignedt(tt) ? Oexts1 : Oextu1; break;
- case 2: ins.op = issignedt(tt) ? Oexts2 : Oextu2; break;
- case 4: ins.op = issignedt(tt) ? Oexts4 : Oextu4; break;
- default: assert(0);
+ if (isfltt(tt)) {
+ assert(to == KF4 && tt == TYDOUBLE);
+ ins.op = Ocvtf8f4;
+ } else {
+ switch (targ_primsizes[tt]) {
+ case 1: ins.op = issignedt(tt) ? Oexts1 : Oextu1; break;
+ case 2: ins.op = issignedt(tt) ? Oexts2 : Oextu2; break;
+ case 4: ins.op = issignedt(tt) ? Oexts4 : Oextu4; break;
+ default: assert(0);
+ }
}
ins.l = ref;
return addinstr(fn, ins);
@@ -1261,7 +1314,7 @@ exprvalue(struct function *fn, const struct expr *ex)
eval((struct expr *)ex, EVFOLD);
sub = ex->sub;
- if (ex->ty.t == TYARRAY || ex->ty.t == TYFUNC)
+ if (ex->ty.t != TYVOID && !isscalar(ex->ty))
return expraddr(fn, ex);
switch (ex->t) {
case ENUMLIT:
@@ -1404,10 +1457,12 @@ exprvalue(struct function *fn, const struct expr *ex)
ins.r = cvt(fn, ty.t, sub[1^swp].ty.t, ins.r);
ins.cls = cls;
return addinstr(fn, ins);
- break;
case ESET:
assert(isscalar(ex->ty));
- return genstore(fn, ex->ty, expraddr(fn, &sub[0]), exprvalue(fn, &sub[1]));
+ r = expraddr(fn, &sub[0]);
+ q = cvt(fn, sub[0].ty.t, sub[1].ty.t, exprvalue(fn, &sub[1]));
+ genstore(fn, ex->ty, r, q);
+ return narrow(fn, cls, sub[0].ty.t, q);
case ESETMUL:
ins.op = isunsigned(ex->ty) ? Oumul : Omul;
goto Compound;
@@ -1470,7 +1525,8 @@ exprvalue(struct function *fn, const struct expr *ex)
vpush(&args, cvt(fn, ty.t, arg->ty.t, exprvalue(fn, arg)));
vpush(&typs, mkirtype(ty));
}
- ins.r = mkcall(fn, sub[0].ty, ex->narg, args.p, typs.p);
+ ins.r = mkcallarg(fn, ex->narg, td->variadic ? td->nmemb : td->kandr ? 0 : -1,
+ args.p, typs.p);
vfree(&args);
vfree(&typs);
return addinstr(fn, ins);
@@ -1594,7 +1650,10 @@ stmt(struct parser *pr, struct function *fn)
ex.ty, fn->retty);
}
EMITS {
- r = cvt(fn, fn->retty.t, ex.ty.t, exprvalue(fn, &ex));
+ if (isscalar(fn->retty))
+ r = cvt(fn, fn->retty.t, ex.ty.t, exprvalue(fn, &ex));
+ else
+ r = structreturn(fn, &ex);
putjump(fn, Jrets, r, NULL, NULL);
}
} else {
@@ -1711,9 +1770,13 @@ function(struct parser *pr, struct function *fn, const char **pnames, const stru
siz = typesize(arg.ty);
nalloc = siz/align + ((siz&(align-1)) != 0);
EMITS {
- struct instr alloca = { op, KPTR, .l = mkintcon(fn, KI4, nalloc) };
- arg.id = addinstr(fn, alloca).idx;
- genstore(fn, arg.ty, mkref(RTMP, arg.id), mkref(RARG, i));
+ if (isscalar(arg.ty)) {
+ struct instr alloca = { op, KPTR, .l = mkintcon(fn, KI4, nalloc) };
+ arg.id = addinstr(fn, alloca).idx;
+ genstore(fn, arg.ty, mkref(RTMP, arg.id), mkref(RARG, i));
+ } else {
+ arg.id = addinstr(fn, mkinstr(Ocopy, KPTR, mkref(RARG, i))).idx;
+ }
}
putdecl(pr, &arg);
} else {
diff --git a/regalloc.c b/regalloc.c
index 4edbcb1..3b7ab74 100644
--- a/regalloc.c
+++ b/regalloc.c
@@ -6,6 +6,13 @@ extern vec_of(struct phi) phis;
static struct bitset taken[1];
+static void
+def(struct instr *ins)
+{
+ if (ins->reg)
+ bsclr(taken, ins->reg - 1);
+}
+
static int
nextreg(enum irclass cls)
{
@@ -31,7 +38,7 @@ static void
use(struct block *blk, enum op op, int hint, union ref *ref)
{
struct instr *ins;
- if (ref->t == REXT) {
+ if (ref->t == RMORE) {
if (op == Ocall) {
struct call *call = &calls.p[ref->idx];
for (int i = 0; i < call->narg; ++i)
@@ -52,17 +59,17 @@ use(struct block *blk, enum op op, int hint, union ref *ref)
/* result of comparison instr is only used to conditionally branch,
* doesn't usually need a reg (handled by isel) */
return;
- ins->reg = hint ? hint : nextreg(ins->cls);
+ ins->reg = (hint ? hint : nextreg(ins->cls)) + 1;
}
}
void
regalloc(struct function *fn)
{
- struct block *blk = fn->entry->lprev;
struct instr *ins;
+ struct block *last = fn->entry->lprev, *blk = last;
- /* a dumb linear register allocator that visits instructions backwards
+ /* a dumb linear register allocator that visits instructions physically backwards
* starting at the end of the function, when encountering a use of a new
* temporary, it allocates a register for it. when encountering the definition
* of a temporary, it frees up its register
@@ -71,18 +78,17 @@ regalloc(struct function *fn)
if (blk->jmp.arg.t) use(blk, blk->jmp.t == Jbcnd ? -1 : 0, 0, &blk->jmp.arg);
for (int i = blk->phi.n - 1; i >= 0; --i) {
ins = &instr[blk->phi.p[i]];
- bsclr(taken, ins->reg);
+ def(ins);
assert(ins->op == Ophi);
use(blk, Ophi, ins->reg, &ins->l);
}
for (int i = blk->ins.n - 1; i >= 0; --i) {
ins = &instr[blk->ins.p[i]];
- bsclr(taken, ins->reg);
+ def(ins);
if (ins->l.t) use(blk, ins->op, 0, &ins->l);
if (ins->r.t) use(blk, ins->op, 0, &ins->r);
}
- blk = blk->lprev;
- } while (blk != fn->entry->lprev);
+ } while ((blk = blk->lprev) != last);
}
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/test.c b/test.c
index 5bb4e72..83a9d8b 100644
--- a/test.c
+++ b/test.c
@@ -63,4 +63,13 @@ int hmm(float x, int a, char *p, char *q) {
return x > 1 ? a || p : p < q && a > 0;
}
+struct v2f { float x, y; };
+
+struct v2f add(struct v2f a, struct v2f b) {
+ struct v2f r;
+ r.x = a.x + b.x;
+ r.y = a.y + b.y;
+ return r;
+}
+
//