summary refs log tree commit diff
path: root/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'parse.c')
-rw-r--r--parse.c1099
1 files changed, 1099 insertions, 0 deletions
diff --git a/parse.c b/parse.c
new file mode 100644
index 0000000..2590971
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,1099 @@
+#include "all.h"
+#include <ctype.h>
+#include <stdarg.h>
+
+enum {
+	Kx = -1, /* Invalid operand */
+	Km = Kl, /* Memory pointer (for x64) */
+};
+
+OpDesc opdesc[NOp] = {
+#define A(a,b,c,d) {[Kw]=K##a, [Kl]=K##b, [Ks]=K##c, [Kd]=K##d}
+
+	/*            NAME       NM      ARGCLS0     ARGCLS1  SF LF */
+	[OAdd]    = { "add",      2, {A(w,l,s,d), A(w,l,s,d)}, 1, 0 },
+	[OSub]    = { "sub",      2, {A(w,l,s,d), A(w,l,s,d)}, 1, 0 },
+	[ODiv]    = { "div",      2, {A(w,l,s,d), A(w,l,s,d)}, 0, 0 },
+	[ORem]    = { "rem",      2, {A(w,l,x,x), A(w,l,x,x)}, 0, 0 },
+	[OUDiv]   = { "udiv",     2, {A(w,l,s,d), A(w,l,s,d)}, 0, 0 },
+	[OURem]   = { "urem",     2, {A(w,l,x,x), A(w,l,x,x)}, 0, 0 },
+	[OMul]    = { "mul",      2, {A(w,l,s,d), A(w,l,s,d)}, 0, 0 },
+	[OAnd]    = { "and",      2, {A(w,l,s,d), A(w,l,s,d)}, 1, 0 },
+	[OOr]     = { "or",       2, {A(w,l,s,d), A(w,l,s,d)}, 1, 0 },
+	[OXor]    = { "xor",      2, {A(w,l,s,d), A(w,l,s,d)}, 1, 0 },
+	[OSar]    = { "sar",      1, {A(w,l,x,x), A(w,w,x,x)}, 1, 0 },
+	[OShr]    = { "shr",      1, {A(w,l,x,x), A(w,w,x,x)}, 1, 0 },
+	[OShl]    = { "shl",      1, {A(w,l,x,x), A(w,w,x,x)}, 1, 0 },
+	[OStored] = { "stored",   0, {A(d,d,d,d), A(m,m,m,m)}, 0, 1 },
+	[OStores] = { "stores",   0, {A(s,s,s,s), A(m,m,m,m)}, 0, 1 },
+	[OStorel] = { "storel",   0, {A(l,l,l,l), A(m,m,m,m)}, 0, 1 },
+	[OStorew] = { "storew",   0, {A(w,w,w,w), A(m,m,m,m)}, 0, 1 },
+	[OStoreh] = { "storeh",   0, {A(w,w,w,w), A(m,m,m,m)}, 0, 1 },
+	[OStoreb] = { "storeb",   0, {A(w,w,w,w), A(m,m,m,m)}, 0, 1 },
+	[OLoad]   = { "load",     0, {A(m,m,m,m), A(x,x,x,x)}, 0, 1 },
+	[OLoadsw] = { "loadsw",   0, {A(m,m,x,x), A(x,x,x,x)}, 0, 1 },
+	[OLoaduw] = { "loaduw",   0, {A(m,m,x,x), A(x,x,x,x)}, 0, 1 },
+	[OLoadsh] = { "loadsh",   0, {A(m,m,x,x), A(x,x,x,x)}, 0, 1 },
+	[OLoaduh] = { "loaduh",   0, {A(m,m,x,x), A(x,x,x,x)}, 0, 1 },
+	[OLoadsb] = { "loadsb",   0, {A(m,m,x,x), A(x,x,x,x)}, 0, 1 },
+	[OLoadub] = { "loadub",   0, {A(m,m,x,x), A(x,x,x,x)}, 0, 1 },
+	[OExtsw]  = { "extsw",    0, {A(w,w,w,w), A(x,x,x,x)}, 0, 1 },
+	[OExtuw]  = { "extuw",    0, {A(w,w,w,w), A(x,x,x,x)}, 0, 1 },
+	[OExtsh]  = { "extsh",    0, {A(w,w,w,w), A(x,x,x,x)}, 0, 1 },
+	[OExtuh]  = { "extuh",    0, {A(w,w,w,w), A(x,x,x,x)}, 0, 1 },
+	[OExtsb]  = { "extsb",    0, {A(w,w,w,w), A(x,x,x,x)}, 0, 1 },
+	[OExtub]  = { "extub",    0, {A(w,w,w,w), A(x,x,x,x)}, 0, 1 },
+	[OExts]   = { "exts",     0, {A(w,w,w,w), A(x,x,x,x)}, 0, 1 },
+	[OTruncd] = { "truncd",   0, {A(d,d,d,d), A(x,x,x,x)}, 0, 1 },
+	[OFtosi]  = { "ftosi",    0, {A(s,d,x,x), A(x,x,x,x)}, 0, 1 },
+	[OSitof]  = { "sitof",    0, {A(x,x,w,l), A(x,x,x,x)}, 0, 1 },
+	[OCast]   = { "cast",     0, {A(s,d,w,l), A(x,x,x,x)}, 0, 1 },
+	[OCopy]   = { "copy",     1, {A(w,l,s,d), A(x,x,x,x)}, 0, 1 },
+	[ONop]    = { "nop",      0, {A(x,x,x,x), A(x,x,x,x)}, 0, 1 },
+	[OSwap]   = { "swap",     2, {A(w,l,s,d), A(w,l,s,d)}, 0, 0 },
+	[OSign]   = { "sign",     0, {A(w,l,x,x), A(x,x,x,x)}, 0, 0 },
+	[OSAlloc] = { "salloc",   0, {A(x,l,x,x), A(x,x,x,x)}, 0, 0 },
+	[OXDiv]   = { "xdiv",     1, {A(w,l,x,x), A(x,x,x,x)}, 0, 0 },
+	[OXCmp]   = { "xcmp",     1, {A(w,l,s,d), A(w,l,s,d)}, 1, 0 },
+	[OXTest]  = { "xtest",    1, {A(w,l,x,x), A(w,l,x,x)}, 1, 0 },
+	[OAddr]   = { "addr",     0, {A(m,m,x,x), A(x,x,x,x)}, 0, 1 },
+	[OPar]    = { "parn",     0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0 },
+	[OParc]   = { "parc",     0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0 },
+	[OArg]    = { "arg",      0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0 },
+	[OArgc]   = { "argc",     0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0 },
+	[OCall]   = { "call",     0, {A(m,m,m,m), A(x,x,x,x)}, 0, 0 },
+	[OXSetnp] = { "xsetnp",   0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0 },
+	[OXSetp]  = { "xsetp",    0, {A(x,x,x,x), A(x,x,x,x)}, 0, 0 },
+	[OAlloc]   = { "alloc4",  1, {A(l,l,l,l), A(x,x,x,x)}, 0, 0 },
+	[OAlloc+1] = { "alloc8",  1, {A(l,l,l,l), A(x,x,x,x)}, 0, 0 },
+	[OAlloc+2] = { "alloc16", 1, {A(l,l,l,l), A(x,x,x,x)}, 0, 0 },
+#define X(c) \
+	[OCmpw+IC##c] = { "c"    #c "w", 0, {A(w,w,x,x), A(w,w,x,x)}, 1, 0 }, \
+	[OCmpl+IC##c] = { "c"    #c "l", 0, {A(l,l,x,x), A(l,l,x,x)}, 1, 0 }, \
+	[OXSet+IC##c] = { "xset" #c,     0, {A(x,x,x,x), A(x,x,x,x)}, 0, 1 },
+	ICMPS(X)
+#undef X
+#define X(c) \
+	[OCmps+FC##c] = { "c"    #c "s", 0, {A(s,s,x,x), A(s,s,x,x)}, 1, 0 }, \
+	[OCmpd+FC##c] = { "c"    #c "d", 0, {A(d,d,x,x), A(d,d,x,x)}, 1, 0 },
+	FCMPS(X)
+#undef X
+
+};
+#undef A
+
+typedef enum {
+	PXXX,
+	PLbl,
+	PPhi,
+	PIns,
+	PEnd,
+} PState;
+
+enum {
+	TXXX = NPubOp,
+	TCall,
+	TPhi,
+	TJmp,
+	TJnz,
+	TRet,
+	TExport,
+	TFunc,
+	TType,
+	TData,
+	TAlign,
+	TL,
+	TW,
+	TH,
+	TB,
+	TD,
+	TS,
+	TZ,
+
+	TInt,
+	TFlts,
+	TFltd,
+	TTmp,
+	TLbl,
+	TGlo,
+	TTyp,
+	TStr,
+
+	TPlus,
+	TEq,
+	TComma,
+	TLParen,
+	TRParen,
+	TLBrace,
+	TRBrace,
+	TNL,
+	TEOF,
+};
+
+
+static FILE *inf;
+static char *inpath;
+static int thead;
+static struct {
+	char chr;
+	double fltd;
+	float flts;
+	int64_t num;
+	char *str;
+} tokval;
+static int lnum;
+
+static Tmp *tmp;
+static Con *con;
+static int ntmp;
+static int ncon;
+static Phi **plink;
+static Blk **bmap;
+static Blk *curb;
+static Blk **blink;
+static int nblk;
+static int rcls;
+static int ntyp;
+
+
+void
+err(char *s, ...)
+{
+	char buf[100], *p, *end;
+	va_list ap;
+
+	p = buf;
+	end = buf + sizeof(buf);
+
+	va_start(ap, s);
+	p += snprintf(p, end - p, "%s:%d: ", inpath, lnum);
+	p += vsnprintf(p, end - p, s, ap);
+	va_end(ap);
+
+	diag(buf);
+}
+
+static int
+lex()
+{
+	static struct {
+		char *str;
+		int tok;
+	} tmap[] = {
+		{ "call", TCall },
+		{ "phi", TPhi },
+		{ "jmp", TJmp },
+		{ "jnz", TJnz },
+		{ "ret", TRet },
+		{ "export", TExport },
+		{ "function", TFunc },
+		{ "type", TType },
+		{ "data", TData },
+		{ "align", TAlign },
+		{ "l", TL },
+		{ "w", TW },
+		{ "h", TH },
+		{ "b", TB },
+		{ "d", TD },
+		{ "s", TS },
+		{ "z", TZ },
+		{ "loadw", OLoad }, /* for convenience */
+		{ "loadl", OLoad },
+		{ "loads", OLoad },
+		{ "loadd", OLoad },
+		{ "alloc1", OAlloc },
+		{ "alloc2", OAlloc },
+		{ 0, TXXX }
+	};
+	static char tok[NString];
+	int c, i;
+	int t;
+
+	do
+		c = fgetc(inf);
+	while (isblank(c));
+	t = TXXX;
+	tokval.chr = c;
+	switch (c) {
+	case EOF:
+		return TEOF;
+	case ',':
+		return TComma;
+	case '(':
+		return TLParen;
+	case ')':
+		return TRParen;
+	case '{':
+		return TLBrace;
+	case '}':
+		return TRBrace;
+	case '=':
+		return TEq;
+	case '+':
+		return TPlus;
+	case 's':
+		if (fscanf(inf, "_%f", &tokval.flts) != 1)
+			break;
+		return TFlts;
+	case 'd':
+		if (fscanf(inf, "_%lf", &tokval.fltd) != 1)
+			break;
+		return TFltd;
+	case '%':
+		t = TTmp;
+		goto Alpha;
+	case '@':
+		t = TLbl;
+		goto Alpha;
+	case '$':
+		t = TGlo;
+		goto Alpha;
+	case ':':
+		t = TTyp;
+		goto Alpha;
+	case '#':
+		while (fgetc(inf) != '\n')
+			;
+	case '\n':
+		lnum++;
+		return TNL;
+	}
+	if (isdigit(c) || c == '-' || c == '+') {
+		ungetc(c, inf);
+		if (fscanf(inf, "%"SCNd64, &tokval.num) != 1)
+			err("invalid integer literal");
+		return TInt;
+	}
+	if (c == '"') {
+		tokval.str = vnew(0, 1);
+		for (i=0;; i++) {
+			c = fgetc(inf);
+			vgrow(&tokval.str, i+1);
+			if (c == '"')
+			if (!i || tokval.str[i-1] != '\\') {
+				tokval.str[i] = 0;
+				return TStr;
+			}
+			tokval.str[i] = c;
+		}
+	}
+	if (0)
+Alpha:		c = fgetc(inf);
+	if (!isalpha(c) && c != '.' && c != '_')
+		err("lexing failure: invalid character %c (%d)", c, c);
+	i = 0;
+	do {
+		if (i >= NString-1)
+			err("identifier too long");
+		tok[i++] = c;
+		c = fgetc(inf);
+	} while (isalpha(c) || c == '$' || c == '.' || c == '_' || isdigit(c));
+	tok[i] = 0;
+	ungetc(c, inf);
+	tokval.str = tok;
+	if (t != TXXX) {
+		return t;
+	}
+	for (i=0; i<NPubOp; i++)
+		if (opdesc[i].name)
+		if (strcmp(tok, opdesc[i].name) == 0)
+			return i;
+	for (i=0; tmap[i].str; i++)
+		if (strcmp(tok, tmap[i].str) == 0)
+			return tmap[i].tok;
+	err("unknown keyword %s", tokval.str);
+	return TXXX;
+}
+
+static int
+peek()
+{
+	if (thead == TXXX)
+		thead = lex();
+	return thead;
+}
+
+static int
+next()
+{
+	int t;
+
+	t = peek();
+	thead = TXXX;
+	return t;
+}
+
+static int
+nextnl()
+{
+	int t;
+
+	while ((t = next()) == TNL)
+		;
+	return t;
+}
+
+static void
+expect(int t)
+{
+	static char *ttoa[] = {
+		[TLbl] = "label",
+		[TComma] = ",",
+		[TEq] = "=",
+		[TNL] = "newline",
+		[TLParen] = "(",
+		[TRParen] = ")",
+		[TLBrace] = "{",
+		[TRBrace] = "}",
+		[TEOF] = 0,
+	};
+	char buf[128], *s1, *s2;
+	int t1;
+
+	t1 = next();
+	if (t == t1)
+		return;
+	s1 = ttoa[t] ? ttoa[t] : "??";
+	s2 = ttoa[t1] ? ttoa[t1] : "??";
+	sprintf(buf, "%s expected, got %s instead", s1, s2);
+	err(buf);
+}
+
+static Ref
+tmpref(char *v)
+{
+	int t;
+
+	for (t=Tmp0; t<ntmp; t++)
+		if (strcmp(v, tmp[t].name) == 0)
+			return TMP(t);
+	vgrow(&tmp, ++ntmp);
+	strcpy(tmp[t].name, v);
+	return TMP(t);
+}
+
+static Ref
+parseref()
+{
+	Con c;
+	int i;
+
+	memset(&c, 0, sizeof c);
+	switch (next()) {
+	case TTmp:
+		return tmpref(tokval.str);
+	case TInt:
+		c.type = CBits;
+		c.bits.i = tokval.num;
+		goto Look;
+	case TFlts:
+		c.type = CBits;
+		c.bits.s = tokval.flts;
+		c.flt = 1;
+		goto Look;
+	case TFltd:
+		c.type = CBits;
+		c.bits.d = tokval.fltd;
+		c.flt = 2;
+		goto Look;
+	case TGlo:
+		c.type = CAddr;
+		strcpy(c.label, tokval.str);
+	Look:
+		for (i=0; i<ncon; i++)
+			if (con[i].type == c.type
+			&& con[i].bits.i == c.bits.i
+			&& strcmp(con[i].label, c.label) == 0)
+				return CON(i);
+		vgrow(&con, ++ncon);
+		con[i] = c;
+		return CON(i);
+	default:
+		return R;
+	}
+}
+
+static int
+parsecls(int *tyn)
+{
+	int i;
+
+	switch (next()) {
+	default:
+		err("invalid class specifier");
+	case TTyp:
+		for (i=0; i<ntyp; i++)
+			if (strcmp(tokval.str, typ[i].name) == 0) {
+				*tyn = i;
+				return 4;
+			}
+		err("undefined type");
+	case TW:
+		return Kw;
+	case TL:
+		return Kl;
+	case TS:
+		return Ks;
+	case TD:
+		return Kd;
+	}
+}
+
+static void
+parserefl(int arg)
+{
+	int k, t, ty;
+	Ref r;
+
+	expect(TLParen);
+	if (peek() == TRParen) {
+		next();
+		return;
+	}
+	for (;;) {
+		if (curi - insb >= NIns)
+			err("too many instructions (1)");
+		k = parsecls(&ty);
+		r = parseref();
+		if (req(r, R))
+			err("invalid reference argument");
+		if (!arg && rtype(r) != RTmp)
+			err("invalid function parameter");
+		if (k == 4)
+			if (arg)
+				*curi = (Ins){OArgc, R, {TYPE(ty), r}, Kl};
+			else
+				*curi = (Ins){OParc, r, {TYPE(ty)}, Kl};
+		else
+			if (arg)
+				*curi = (Ins){OArg, R, {r}, k};
+			else
+				*curi = (Ins){OPar, r, {R}, k};
+		curi++;
+		t = next();
+		if (t == TRParen)
+			break;
+		if (t != TComma)
+			err(", or ) expected");
+	}
+}
+
+static Blk *
+findblk(char *name)
+{
+	int i;
+
+	for (i=0; i<nblk; i++)
+		if (strcmp(bmap[i]->name, name) == 0)
+			return bmap[i];
+	vgrow(&bmap, ++nblk);
+	bmap[i] = blknew();
+	strcpy(bmap[i]->name, name);
+	return bmap[i];
+}
+
+static void
+closeblk()
+{
+	curb->nins = curi - insb;
+	idup(&curb->ins, insb, curb->nins);
+	blink = &curb->link;
+	curi = insb;
+}
+
+static PState
+parseline(PState ps)
+{
+	Ref arg[NPred] = {R};
+	Blk *blk[NPred];
+	Phi *phi;
+	Ref r;
+	Blk *b;
+	int t, op, i, k, ty;
+
+	t = nextnl();
+	if (ps == PLbl && t != TLbl && t != TRBrace)
+		err("label or } expected");
+	switch (t) {
+	default:
+		if (isstore(t)) {
+			/* operations without result */
+			r = R;
+			k = 0;
+			op = t;
+			goto DoOp;
+		}
+		err("label, instruction or jump expected");
+	case TRBrace:
+		return PEnd;
+	case TTmp:
+		break;
+	case TLbl:
+		b = findblk(tokval.str);
+		if (b->jmp.type != JXXX)
+			err("multiple definitions of block");
+		if (curb && curb->jmp.type == JXXX) {
+			closeblk();
+			curb->jmp.type = JJmp;
+			curb->s1 = b;
+		}
+		*blink = b;
+		curb = b;
+		plink = &curb->phi;
+		expect(TNL);
+		return PPhi;
+	case TRet:
+		curb->jmp.type = (int[]){
+			JRetw, JRetl,
+			JRets, JRetd,
+			JRetc, JRet0
+		}[rcls];
+		if (rcls < 5) {
+			r = parseref();
+			if (req(r, R))
+				err("return value expected");
+			curb->jmp.arg = r;
+		}
+		goto Close;
+	case TJmp:
+		curb->jmp.type = JJmp;
+		goto Jump;
+	case TJnz:
+		curb->jmp.type = JJnz;
+		r = parseref();
+		if (req(r, R))
+			err("invalid argument for jnz jump");
+		curb->jmp.arg = r;
+		expect(TComma);
+	Jump:
+		expect(TLbl);
+		curb->s1 = findblk(tokval.str);
+		if (curb->jmp.type != JJmp) {
+			expect(TComma);
+			expect(TLbl);
+			curb->s2 = findblk(tokval.str);
+		}
+	Close:
+		expect(TNL);
+		closeblk();
+		return PLbl;
+	}
+	r = tmpref(tokval.str);
+	expect(TEq);
+	k = parsecls(&ty);
+	op = next();
+DoOp:
+	if (op == TPhi) {
+		if (ps != PPhi)
+			err("unexpected phi instruction");
+		op = -1;
+	}
+	if (op == TCall) {
+		arg[0] = parseref();
+		parserefl(1);
+		expect(TNL);
+		op = OCall;
+		if (k == 4) {
+			k = Kl;
+			arg[1] = TYPE(ty);
+		} else
+			arg[1] = R;
+		goto Ins;
+	}
+	if (k == 4)
+		err("size class must be w, l, s, or d");
+	if (op >= NPubOp)
+		err("invalid instruction");
+	i = 0;
+	if (peek() != TNL)
+		for (;;) {
+			if (i == NPred)
+				err("too many arguments");
+			if (op == -1) {
+				expect(TLbl);
+				blk[i] = findblk(tokval.str);
+			}
+			arg[i] = parseref();
+			if (req(arg[i], R))
+				err("invalid instruction argument");
+			i++;
+			t = peek();
+			if (t == TNL)
+				break;
+			if (t != TComma)
+				err(", or end of line expected");
+			next();
+		}
+	next();
+	if (op != -1) {
+	Ins:
+		if (curi - insb >= NIns)
+			err("too many instructions (2)");
+		curi->op = op;
+		curi->cls = k;
+		curi->to = r;
+		curi->arg[0] = arg[0];
+		curi->arg[1] = arg[1];
+		curi++;
+		return PIns;
+	} else {
+		phi = alloc(sizeof *phi);
+		phi->to = r;
+		phi->cls = k;
+		memcpy(phi->arg, arg, i * sizeof arg[0]);
+		memcpy(phi->blk, blk, i * sizeof blk[0]);
+		phi->narg = i;
+		*plink = phi;
+		plink = &phi->link;
+		return PPhi;
+	}
+}
+
+static Fn *
+parsefn(int export)
+{
+	PState ps;
+	Fn *fn;
+
+	ntmp = Tmp0;
+	ncon = 1; /* first constant must be 0 */
+	curb = 0;
+	nblk = 0;
+	curi = insb;
+	tmp = vnew(ntmp, sizeof tmp[0]);
+	con = vnew(ncon, sizeof con[0]);
+	bmap = vnew(nblk, sizeof bmap[0]);
+	con[0].type = CBits;
+	fn = alloc(sizeof *fn);
+	fn->export = export;
+	blink = &fn->start;
+	fn->retty = -1;
+	if (peek() != TGlo)
+		rcls = parsecls(&fn->retty);
+	else
+		rcls = 5;
+	if (next() != TGlo)
+		err("function name expected");
+	strcpy(fn->name, tokval.str);
+	parserefl(0);
+	if (nextnl() != TLBrace)
+		err("function body must start with {");
+	ps = PLbl;
+	do
+		ps = parseline(ps);
+	while (ps != PEnd);
+	if (!curb)
+		err("empty file");
+	if (curb->jmp.type == JXXX)
+		err("last block misses jump");
+	fn->tmp = tmp;
+	fn->con = con;
+	fn->mem = vnew(0, sizeof fn->mem[0]);
+	fn->ntmp = ntmp;
+	fn->ncon = ncon;
+	fn->nmem = 0;
+	fn->nblk = nblk;
+	fn->rpo = 0;
+	return fn;
+}
+
+static void
+parsetyp()
+{
+	Typ *ty;
+	int t, n, sz, al, s, a, c, flt;
+
+	if (ntyp >= NTyp)
+		err("too many type definitions");
+	ty = &typ[ntyp++];
+	ty->align = -1;
+	if (nextnl() != TTyp ||  nextnl() != TEq)
+		err("type name, then = expected");
+	strcpy(ty->name, tokval.str);
+	t = nextnl();
+	if (t == TAlign) {
+		if (nextnl() != TInt)
+			err("alignment expected");
+		for (al=0; tokval.num /= 2; al++)
+			;
+		ty->align = al;
+		t = nextnl();
+	}
+	if (t != TLBrace)
+		err("type body must start with {");
+	t = nextnl();
+	if (t == TInt) {
+		ty->dark = 1;
+		ty->size = tokval.num;
+		if (ty->align == -1)
+			err("dark types need alignment");
+		t = nextnl();
+	} else {
+		ty->dark = 0;
+		n = -1;
+		sz = 0;
+		al = 0;
+		for (;;) {
+			flt = 0;
+			switch (t) {
+			default: err("invalid size specifier %c", tokval.chr);
+			case TD: flt = 1;
+			case TL: s = 8; a = 3; break;
+			case TS: flt = 1;
+			case TW: s = 4; a = 2; break;
+			case TH: s = 2; a = 1; break;
+			case TB: s = 1; a = 0; break;
+			}
+			if (a > al)
+				al = a;
+			if ((a = sz & (s-1))) {
+				a = s - a;
+				if (++n < NSeg) {
+					/* padding segment */
+					ty->seg[n].ispad = 1;
+					ty->seg[n].len = a;
+				}
+			}
+			t = nextnl();
+			if (t == TInt) {
+				c = tokval.num;
+				t = nextnl();
+			} else
+				c = 1;
+			while (c-- > 0) {
+				if (++n < NSeg) {
+					ty->seg[n].isflt = flt;
+					ty->seg[n].ispad = 0;
+					ty->seg[n].len = s;
+				}
+				sz += a + s;
+			}
+			if (t != TComma)
+				break;
+			t = nextnl();
+		}
+		if (++n >= NSeg)
+			ty->dark = 1;
+		else
+			ty->seg[n].len = 0;
+		if (ty->align == -1)
+			ty->align = al;
+		else
+			al = ty->align;
+		a = (1 << al) - 1;
+		ty->size = (sz + a) & ~a;
+	}
+	if (t != TRBrace)
+		err("expected closing }");
+}
+
+static void
+parsedatref(Dat *d)
+{
+	int t;
+
+	d->isref = 1;
+	d->u.ref.nam = tokval.str;
+	d->u.ref.off = 0;
+	t = peek();
+	if (t == TPlus) {
+		next();
+		if (next() != TInt)
+			err("invalid token after offset in ref");
+		d->u.ref.off = tokval.num;
+	}
+}
+
+static void
+parsedatstr(Dat *d)
+{
+	d->isstr = 1;
+	d->u.str = tokval.str;
+}
+
+static void
+parsedat(void cb(Dat *), int export)
+{
+	char s[NString];
+	int t;
+	Dat d;
+
+	d.type = DStart;
+	d.isstr = 0;
+	d.isref = 0;
+	d.export = export;
+	cb(&d);
+	if (nextnl() != TGlo || nextnl() != TEq)
+		err("data name, then = expected");
+	strcpy(s, tokval.str);
+	t = nextnl();
+	if (t == TAlign) {
+		if (nextnl() != TInt)
+			err("alignment expected");
+		d.type = DAlign;
+		d.u.num = tokval.num;
+		cb(&d);
+		t = nextnl();
+	}
+	d.type = DName;
+	d.u.str = s;
+	cb(&d);
+
+	if (t != TLBrace)
+		err("expected data contents in { .. }");
+	for (;;) {
+		switch (nextnl()) {
+		default: err("invalid size specifier %c in data", tokval.chr);
+		case TRBrace: goto Done;
+		case TL: d.type = DL; break;
+		case TW: d.type = DW; break;
+		case TH: d.type = DH; break;
+		case TB: d.type = DB; break;
+		case TS: d.type = DW; break;
+		case TD: d.type = DL; break;
+		case TZ: d.type = DZ; break;
+		}
+		t = nextnl();
+		do {
+			d.isref = 0;
+			d.isstr = 0;
+			memset(&d.u, 0, sizeof d.u);
+			if (t == TFlts)
+				d.u.flts = tokval.flts;
+			else if (t == TFltd)
+				d.u.fltd = tokval.fltd;
+			else if (t == TInt)
+				d.u.num = tokval.num;
+			else if (t == TGlo)
+				parsedatref(&d);
+			else if (t == TStr)
+				parsedatstr(&d);
+			else
+				err("constant literal expected");
+			cb(&d);
+			t = nextnl();
+		} while (t == TInt || t == TFlts || t == TFltd);
+		if (t == TRBrace)
+			break;
+		if (t != TComma)
+			err(", or } expected");
+	}
+Done:
+	d.type = DEnd;
+	cb(&d);
+}
+
+void
+parse(FILE *f, char *path, void data(Dat *), void func(Fn *))
+{
+	int t, export;
+
+	inf = f;
+	inpath = path;
+	lnum = 1;
+	thead = TXXX;
+	ntyp = 0;
+	for (;;) {
+		export = 0;
+		switch (nextnl()) {
+		default:
+			err("top-level definition expected");
+		case TExport:
+			export = 1;
+			t = nextnl();
+			if (t == TFunc) {
+		case TFunc:
+				func(parsefn(export));
+				break;
+			}
+			else if (t == TData) {
+		case TData:
+				parsedat(data, export);
+				break;
+			}
+			else
+				err("export can only qualify data and function");
+		case TType:
+			parsetyp();
+			break;
+		case TEOF:
+			return;
+		}
+	}
+}
+
+static void
+printcon(Con *c, FILE *f)
+{
+	switch (c->type) {
+	case CUndef:
+		break;
+	case CAddr:
+		fprintf(f, "$%s", c->label);
+		if (c->bits.i)
+			fprintf(f, "%+"PRIi64, c->bits.i);
+		break;
+	case CBits:
+		if (c->flt == 1)
+			fprintf(f, "s_%f", c->bits.s);
+		else if (c->flt == 2)
+			fprintf(f, "d_%lf", c->bits.d);
+		else
+			fprintf(f, "%"PRIi64, c->bits.i);
+		break;
+	}
+}
+
+void
+printref(Ref r, Fn *fn, FILE *f)
+{
+	int i;
+	Mem *m;
+
+	switch (rtype(r)) {
+	case RTmp:
+		if (r.val < Tmp0)
+			fprintf(f, "R%d", r.val);
+		else
+			fprintf(f, "%%%s", fn->tmp[r.val].name);
+		break;
+	case RCon:
+		printcon(&fn->con[r.val], f);
+		break;
+	case RSlot:
+		fprintf(f, "S%d", r.val);
+		break;
+	case RACall:
+		fprintf(f, "%03x", r.val & AMask);
+		break;
+	case RAType:
+		fprintf(f, ":%s", typ[r.val & AMask].name);
+		break;
+	case RAMem:
+		i = 0;
+		m = &fn->mem[r.val & AMask];
+		fputc('[', f);
+		if (m->offset.type != CUndef) {
+			printcon(&m->offset, f);
+			i = 1;
+		}
+		if (!req(m->base, R)) {
+			if (i)
+				fprintf(f, " + ");
+			printref(m->base, fn, f);
+			i = 1;
+		}
+		if (!req(m->index, R)) {
+			if (i)
+				fprintf(f, " + ");
+			fprintf(f, "%d * ", m->scale);
+			printref(m->index, fn, f);
+		}
+		fputc(']', f);
+		break;
+	}
+}
+
+void
+printfn(Fn *fn, FILE *f)
+{
+	static char *jtoa[NJmp] = {
+		[JRet0]     = "ret",
+		[JRetw]     = "retw",
+		[JRetl]     = "retl",
+		[JRetc]     = "retc",
+		[JRets]     = "rets",
+		[JRetd]     = "retd",
+		[JJnz]      = "jnz",
+		[JXJnp]     = "xjnp",
+		[JXJp]      = "xjp",
+	#define X(c) [JXJc+IC##c] = "xj" #c,
+		ICMPS(X)
+	#undef X
+	};
+	static char prcls[NOp] = {
+		[OArg] = 1,
+		[OSwap] = 1,
+		[OXCmp] = 1,
+		[OXTest] = 1,
+		[OXDiv] = 1,
+		[OXIDiv] = 1,
+	};
+	static char ktoc[] = "wlsd";
+	Blk *b;
+	Phi *p;
+	Ins *i;
+	uint n;
+
+	if (fn->export)
+		fprintf(f, "export ");
+	fprintf(f, "function $%s() {\n", fn->name);
+	for (b=fn->start; b; b=b->link) {
+		fprintf(f, "@%s\n", b->name);
+		for (p=b->phi; p; p=p->link) {
+			fprintf(f, "\t");
+			printref(p->to, fn, f);
+			fprintf(f, " =%c phi ", ktoc[p->cls]);
+			assert(p->narg);
+			for (n=0;; n++) {
+				fprintf(f, "@%s ", p->blk[n]->name);
+				printref(p->arg[n], fn, f);
+				if (n == p->narg-1) {
+					fprintf(f, "\n");
+					break;
+				} else
+					fprintf(f, ", ");
+			}
+		}
+		for (i=b->ins; i-b->ins < b->nins; i++) {
+			fprintf(f, "\t");
+			if (!req(i->to, R)) {
+				printref(i->to, fn, f);
+				fprintf(f, " =%c ", ktoc[i->cls]);
+			}
+			assert(opdesc[i->op].name);
+			fprintf(f, "%s", opdesc[i->op].name);
+			if (req(i->to, R) && prcls[i->op])
+				fputc(ktoc[i->cls], f);
+			if (!req(i->arg[0], R)) {
+				fprintf(f, " ");
+				printref(i->arg[0], fn, f);
+			}
+			if (!req(i->arg[1], R)) {
+				fprintf(f, ", ");
+				printref(i->arg[1], fn, f);
+			}
+			fprintf(f, "\n");
+		}
+		switch (b->jmp.type) {
+		case JRet0:
+		case JRetw:
+		case JRetl:
+		case JRets:
+		case JRetd:
+		case JRetc:
+			fprintf(f, "\t%s", jtoa[b->jmp.type]);
+			if (b->jmp.type != JRet0 || !req(b->jmp.arg, R)) {
+				fprintf(f, " ");
+				printref(b->jmp.arg, fn, f);
+			}
+			if (b->jmp.type == JRetc)
+				fprintf(f, ", :%s", typ[fn->retty].name);
+			fprintf(f, "\n");
+			break;
+		case JJmp:
+			if (b->s1 != b->link)
+				fprintf(f, "\tjmp @%s\n", b->s1->name);
+			break;
+		default:
+			fprintf(f, "\t%s ", jtoa[b->jmp.type]);
+			if (b->jmp.type == JJnz) {
+				printref(b->jmp.arg, fn, f);
+				fprintf(f, ", ");
+			}
+			fprintf(f, "@%s, @%s\n", b->s1->name, b->s2->name);
+			break;
+		}
+	}
+	fprintf(f, "}\n");
+}