shithub: mc

Download patch

ref: 6e6db2755b917179743b1f7649aa8b71a7dac8fc
parent: 8addbdbc991cb4718e15bf16ce7f203faa1227f6
parent: 56a192ced907bcc27405b85e120abb3005f9d981
author: Ori Bernstein <[email protected]>
date: Fri Oct 10 00:04:09 EDT 2014

Merge branch 'master' of git+ssh://mimir.eigenstate.org/git/ori/mc

--- a/6/Makefile
+++ b/6/Makefile
@@ -1,9 +1,12 @@
 INSTBIN=6m
-OBJ=isel.o \
-    locs.o \
-    main.o \
-    ra.o \
-    simp.o \
+OBJ= \
+	gen.o \
+	isel.o \
+	locs.o \
+	main.o \
+	ra.o \
+	simp.o \
+	typeinfo.o \
 
 DEPS=../parse/libparse.a ../opt/libmi.a
 
--- a/6/asm.h
+++ b/6/asm.h
@@ -185,15 +185,18 @@
     Bitset *initial;    /* initial set of locations used by this fn */
 };
 
+/* globals */
 extern char *modenames[];
+extern Type *tyintptr;
+extern Type *tyword;
+extern Type *tyvoid;
+extern Node *abortoob;
 
 /* options */
 extern int extracheck;
 
-/* entry points */
-void genblob(FILE *fd, Node *blob, Htab *globls, Htab *strtab);
-void genasm(FILE *fd, Func *fn, Htab *globls, Htab *strtab);
-void genstrings(FILE *fd, Htab *strtab);
+void simpglobl(Node *dcl, Htab *globls, Func ***fn, size_t *nfn, Node ***blob, size_t *nblob);
+void selfunc(Isel *is, Func *fn, Htab *globls, Htab *strtab);
 void gen(Node *file, char *out);
 
 /* location generation */
@@ -212,15 +215,15 @@
 Loc *locmemls(char *disp, Loc *base, Loc *idx, int scale, Mode mode);
 Loc *loclit(long val, Mode m);
 Loc *loclitl(char *lbl);
+char *asmname(Node *n);
 Loc *coreg(Reg r, Mode m);
 int isfloatmode(Mode m);
 int isintmode(Mode m);
 
-void locprint(FILE *fd, Loc *l, char spec);
-void iprintf(FILE *fd, Insn *insn);
-
 /* emitting instructions */
 Insn *mkinsn(AsmOp op, ...);
+void iprintf(FILE *fd, Insn *insn);
+void locprint(FILE *fd, Loc *l, char spec);
 
 /* register allocation */
 extern char *regnames[]; /* name table */
@@ -233,6 +236,8 @@
 /* useful functions */
 size_t tysize(Type *t);
 size_t size(Node *n);
+size_t tyoffset(Type *ty, Node *memb);
+size_t offset(Node *aggr, Node *memb);
 int stacktype(Type *t);
 int floattype(Type *t);
 int stacknode(Node *n);
--- /dev/null
+++ b/6/gen.c
@@ -1,0 +1,524 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "parse.h"
+#include "opt.h"
+#include "asm.h"
+#include "../config.h"
+
+/* string tables */
+char *insnfmts[] = {
+#define Insn(val, fmt, use, def) fmt,
+#include "insns.def"
+#undef Insn
+};
+
+char* modenames[] = {
+  [ModeB] = "b",
+  [ModeW] = "w",
+  [ModeL] = "l",
+  [ModeQ] = "q",
+  [ModeF] = "s",
+  [ModeD] = "d"
+};
+
+
+static size_t writeblob(FILE *fd, Htab *globls, Htab *strtab, Node *blob);
+
+static void fillglobls(Stab *st, Htab *globls)
+{
+    void **k;
+    size_t i, nk;
+    Stab *stab;
+    Node *s;
+
+    k = htkeys(st->dcl, &nk);
+    for (i = 0; i < nk; i++) {
+        s = htget(st->dcl, k[i]);
+        htput(globls, s, asmname(s->decl.name));
+    }
+    free(k);
+
+    k = htkeys(st->ns, &nk);
+    for (i = 0; i < nk; i++) {
+        stab = htget(st->ns, k[i]);
+        fillglobls(stab, globls);
+    }
+    free(k);
+}
+
+static void initconsts(Htab *globls)
+{
+    Type *ty;
+    Node *name;
+    Node *dcl;
+
+    tyintptr = mktype(Zloc, Tyuint64);
+    tyword = mktype(Zloc, Tyuint);
+    tyvoid = mktype(Zloc, Tyvoid);
+
+    ty = mktyfunc(Zloc, NULL, 0, mktype(Zloc, Tyvoid));
+    name = mknsname(Zloc, "_rt", "abort_oob");
+    dcl = mkdecl(Zloc, name, ty);
+    dcl->decl.isconst = 1;
+    dcl->decl.isextern = 1;
+    htput(globls, dcl, asmname(dcl->decl.name));
+
+    abortoob = mkexpr(Zloc, Ovar, name, NULL);
+    abortoob->expr.type = ty;
+    abortoob->expr.did = dcl->decl.did;
+    abortoob->expr.isconst = 1;
+}
+
+void locprint(FILE *fd, Loc *l, char spec)
+{
+    assert(l->mode);
+    switch (l->type) {
+        case Loclitl:
+            assert(spec == 'i' || spec == 'x' || spec == 'u');
+            fprintf(fd, "$%s", l->lbl);
+            break;
+        case Loclbl:
+            assert(spec == 'm' || spec == 'v' || spec == 'x');
+            fprintf(fd, "%s", l->lbl);
+            break;
+        case Locreg:
+            assert((spec == 'r' && isintmode(l->mode)) || 
+                   (spec == 'f' && isfloatmode(l->mode)) ||
+                   spec == 'v' ||
+                   spec == 'x' ||
+                   spec == 'u');
+            if (l->reg.colour == Rnone)
+                fprintf(fd, "%%P.%zd%s", l->reg.id, modenames[l->mode]);
+            else
+                fprintf(fd, "%s", regnames[l->reg.colour]);
+            break;
+        case Locmem:
+        case Locmeml:
+            assert(spec == 'm' || spec == 'v' || spec == 'x');
+            if (l->type == Locmem) {
+                if (l->mem.constdisp)
+                    fprintf(fd, "%ld", l->mem.constdisp);
+            } else {
+                if (l->mem.lbldisp)
+                    fprintf(fd, "%s", l->mem.lbldisp);
+            }
+            if (l->mem.base) {
+                fprintf(fd, "(");
+                locprint(fd, l->mem.base, 'r');
+                if (l->mem.idx) {
+                    fprintf(fd, ",");
+                    locprint(fd, l->mem.idx, 'r');
+                }
+                if (l->mem.scale > 1)
+                    fprintf(fd, ",%d", l->mem.scale);
+                if (l->mem.base)
+                    fprintf(fd, ")");
+            } else if (l->type != Locmeml) {
+                die("Only locmeml can have unspecified base reg");
+            }
+            break;
+        case Loclit:
+            assert(spec == 'i' || spec == 'x' || spec == 'u');
+            fprintf(fd, "$%ld", l->lit);
+            break;
+        case Locnone:
+            die("Bad location in locprint()");
+            break;
+    }
+}
+
+static int issubreg(Loc *a, Loc *b)
+{
+    return rclass(a) == rclass(b) && a->mode != b->mode;
+}
+
+void iprintf(FILE *fd, Insn *insn)
+{
+    char *p;
+    int i;
+    int modeidx;
+
+    /* x64 has a quirk; it has no movzlq because mov zero extends. This
+     * means that we need to do a movl when we really want a movzlq. Since
+     * we don't know the name of the reg to use, we need to sub it in when
+     * writing... */
+    switch (insn->op) {
+        case Imovzx:
+            if (insn->args[0]->mode == ModeL && insn->args[1]->mode == ModeQ) {
+                if (insn->args[1]->reg.colour) {
+                    insn->op = Imov;
+                    insn->args[1] = coreg(insn->args[1]->reg.colour, ModeL);
+                }
+            }
+            break;
+        case Imovs:
+            if (insn->args[0]->reg.colour == Rnone || insn->args[1]->reg.colour == Rnone)
+                break;
+            /* moving a reg to itself is dumb. */
+            if (insn->args[0]->reg.colour == insn->args[1]->reg.colour)
+                return;
+            break;
+        case Imov:
+            assert(!isfloatmode(insn->args[0]->mode));
+            if (insn->args[0]->type != Locreg || insn->args[1]->type != Locreg)
+                break;
+            if (insn->args[0]->reg.colour == Rnone || insn->args[1]->reg.colour == Rnone)
+                break;
+            /* if one reg is a subreg of another, we can just use the right
+             * mode to move between them. */
+            if (issubreg(insn->args[0], insn->args[1]))
+                insn->args[0] = coreg(insn->args[0]->reg.colour, insn->args[1]->mode);
+            /* moving a reg to itself is dumb. */
+            if (insn->args[0]->reg.colour == insn->args[1]->reg.colour)
+                return;
+            break;
+        default:
+            break;
+    }
+    p = insnfmts[insn->op];
+    i = 0;
+    modeidx = 0;
+    for (; *p; p++) {
+        if (*p !=  '%') {
+            fputc(*p, fd);
+            continue;
+        }
+
+        /* %-formating */
+        p++;
+        switch (*p) {
+            case '\0':
+                goto done; /* skip the final p++ */
+            case 'r': /* int register */
+            case 'f': /* float register */
+            case 'm': /* memory */
+            case 'i': /* imm */
+            case 'v': /* reg/mem */
+            case 'u': /* reg/imm */
+            case 'x': /* reg/mem/imm */
+                locprint(fd, insn->args[i], *p);
+                i++;
+                break;
+            case 't':
+            default:
+                /* the  asm description uses 1-based indexing, so that 0
+                 * can be used as a sentinel. */
+                if (isdigit(*p))
+                    modeidx = strtol(p, &p, 10) - 1;
+
+                if (*p == 't')
+                    fputs(modenames[insn->args[modeidx]->mode], fd);
+                else
+                    die("Invalid %%-specifier '%c'", *p);
+                break;
+        }
+    }
+done:
+    return;
+}
+
+
+static void writebytes(FILE *fd, char *p, size_t sz)
+{
+    size_t i;
+
+    for (i = 0; i < sz; i++) {
+        if (i % 60 == 0)
+            fprintf(fd, "\t.ascii \"");
+        if (p[i] == '"' || p[i] == '\\')
+            fprintf(fd, "\\");
+        if (isprint(p[i]))
+            fprintf(fd, "%c", p[i]);
+        else
+            fprintf(fd, "\\%03o", (uint8_t)p[i] & 0xff);
+        /* line wrapping for readability */
+        if (i % 60 == 59 || i == sz - 1)
+            fprintf(fd, "\"\n");
+    }
+}
+
+static size_t writelit(FILE *fd, Htab *strtab, Node *v, Type *ty)
+{
+    char buf[128];
+    char *lbl;
+    size_t sz;
+    char *intsz[] = {
+        [1] = ".byte",
+        [2] = ".short",
+        [4] = ".long",
+        [8] = ".quad"
+    };
+    union {
+        float fv;
+        double dv;
+        uint64_t qv;
+        uint32_t lv;
+    } u;
+
+    assert(v->type == Nlit);
+    sz = tysize(ty);
+    switch (v->lit.littype) {
+        case Lint:      fprintf(fd, "\t%s %lld\n", intsz[sz], v->lit.intval);    break;
+        case Lbool:     fprintf(fd, "\t.byte %d\n", v->lit.boolval);     break;
+        case Lchr:      fprintf(fd, "\t.long %d\n",  v->lit.chrval);     break;
+        case Lflt:
+                if (tybase(v->lit.type)->type == Tyflt32) {
+                    u.fv = v->lit.fltval;
+                    fprintf(fd, "\t.long 0x%" PRIx32 "\n", u.lv);
+                } else if (tybase(v->lit.type)->type == Tyflt64) {
+                    u.dv = v->lit.fltval;
+                    fprintf(fd, "\t.quad 0x%" PRIx64 "\n", u.qv);
+                }
+                break;
+        case Lstr:
+           if (hthas(strtab, v->lit.strval)) {
+               lbl = htget(strtab, v->lit.strval);
+           } else {
+               lbl = genlblstr(buf, sizeof buf);
+               htput(strtab, v->lit.strval, strdup(lbl));
+           }
+           fprintf(fd, "\t.quad %s\n", lbl);
+           fprintf(fd, "\t.quad %zd\n", strlen(v->lit.strval));
+           break;
+        case Lfunc:
+            die("Generating this shit ain't ready yet ");
+            break;
+        case Llbl:
+            die("Can't generate literal labels, ffs. They're not data.");
+            break;
+    }
+    return sz;
+}
+
+static size_t writepad(FILE *fd, size_t sz)
+{
+    assert((ssize_t)sz >= 0);
+    if (sz > 0)
+        fprintf(fd, "\t.fill %zd,1,0\n", sz);
+    return sz;
+}
+
+static size_t getintlit(Node *n, char *failmsg)
+{
+    if (exprop(n) != Olit)
+        fatal(n, "%s", failmsg);
+    n = n->expr.args[0];
+    if (n->lit.littype != Lint)
+        fatal(n, "%s", failmsg);
+    return n->lit.intval;
+}
+
+static size_t writeslice(FILE *fd, Htab *globls, Htab *strtab, Node *n)
+{
+    Node *base, *lo, *hi;
+    ssize_t loval, hival, sz;
+    char *lbl;
+
+    base = n->expr.args[0];
+    lo = n->expr.args[1];
+    hi = n->expr.args[2];
+
+    /* by this point, all slicing operations should have had their bases
+     * pulled out, and we should have vars with their pseudo-decls in their
+     * place */
+    if (exprop(base) != Ovar || !base->expr.isconst)
+        fatal(base, "slice base is not a constant value");
+    loval = getintlit(lo, "lower bound in slice is not constant literal");
+    hival = getintlit(hi, "upper bound in slice is not constant literal");
+    sz = tysize(tybase(exprtype(base))->sub[0]);
+
+    lbl = htget(globls, base);
+    fprintf(fd, "\t.quad %s + (%zd*%zd)\n", lbl, loval, sz);
+    fprintf(fd, "\t.quad %zd\n", (hival - loval));
+    return size(n);
+}
+
+static size_t writestruct(FILE *fd, Htab *globls, Htab *strtab, Node *n)
+{
+    Type *t;
+    Node **dcl;
+    int found;
+    size_t i, j;
+    size_t sz, pad, end;
+    size_t ndcl;
+
+    sz = 0;
+    t = tybase(exprtype(n));
+    assert(t->type == Tystruct);
+    dcl = t->sdecls;
+    ndcl = t->nmemb;
+    for (i = 0; i < ndcl; i++) {
+        pad = alignto(sz, decltype(dcl[i]));
+        sz += writepad(fd, pad - sz);
+        found = 0;
+        for (j = 0; j < n->expr.nargs; j++)
+            if (!strcmp(namestr(n->expr.args[j]->expr.idx), declname(dcl[i]))) {
+                found = 1;
+                sz += writeblob(fd, globls, strtab, n->expr.args[j]);
+            }
+        if (!found)
+            sz += writepad(fd, size(dcl[i]));
+    }
+    end = alignto(sz, t);
+    sz += writepad(fd, end - sz);
+    return sz;
+}
+static size_t writeblob(FILE *fd, Htab *globls, Htab *strtab, Node *n)
+{
+    size_t i, sz;
+
+    switch(exprop(n)) {
+        case Otup:
+        case Oarr:
+            sz = 0;
+            for (i = 0; i < n->expr.nargs; i++)
+                sz += writeblob(fd, globls, strtab, n->expr.args[i]);
+            break;
+        case Ostruct:
+            sz = writestruct(fd, globls, strtab, n);
+            break;
+        case Olit:
+            sz = writelit(fd, strtab, n->expr.args[0], exprtype(n));
+            break;
+        case Oslice:
+            sz = writeslice(fd, globls, strtab, n);
+            break;
+        default:
+            dump(n, stdout);
+            die("Nonliteral initializer for global");
+            break;
+    }
+    return sz;
+}
+
+void genstrings(FILE *fd, Htab *strtab)
+{
+    void **k;
+    size_t i, nk;
+
+    k = htkeys(strtab, &nk);
+    for (i = 0; i < nk; i++) {
+        fprintf(fd, "%s:\n", (char*)htget(strtab, k[i]));
+        writebytes(fd, k[i], strlen(k[i]));
+    }
+}
+
+
+static void writeasm(FILE *fd, Isel *s, Func *fn)
+{
+    size_t i, j;
+
+    if (fn->isexport || !strcmp(fn->name, Symprefix "main"))
+        fprintf(fd, ".globl %s\n", fn->name);
+    fprintf(fd, "%s:\n", fn->name);
+    for (j = 0; j < s->cfg->nbb; j++) {
+        if (!s->bb[j])
+            continue;
+        for (i = 0; i < s->bb[j]->nlbls; i++)
+            fprintf(fd, "%s:\n", s->bb[j]->lbls[i]);
+        for (i = 0; i < s->bb[j]->ni; i++)
+            iprintf(fd, s->bb[j]->il[i]);
+    }
+}
+
+void genblob(FILE *fd, Node *blob, Htab *globls, Htab *strtab)
+{
+    char *lbl;
+
+    /* lits and such also get wrapped in decls */
+    assert(blob->type == Ndecl);
+
+    lbl = htget(globls, blob);
+    if (blob->decl.vis != Visintern)
+        fprintf(fd, ".globl %s\n", lbl);
+    fprintf(fd, "%s:\n", lbl);
+    if (blob->decl.init)
+        writeblob(fd, globls, strtab, blob->decl.init);
+    else
+        writepad(fd, size(blob));
+}
+
+/* genfunc requires all nodes in 'nl' to map cleanly to operations that are
+ * natively supported, as promised in the output of reduce().  No 64-bit
+ * operations on x32, no structures, and so on. */
+void genfunc(FILE *fd, Func *fn, Htab *globls, Htab *strtab)
+{
+    Isel is = {0,};
+
+    is.reglocs = mkht(varhash, vareq);
+    is.stkoff = fn->stkoff;
+    is.globls = globls;
+    is.ret = fn->ret;
+    is.cfg = fn->cfg;
+
+    selfunc(&is, fn, globls, strtab);
+    if (debugopt['i'])
+        writeasm(stdout, &is, fn);
+    writeasm(fd, &is, fn);
+}
+
+void gen(Node *file, char *out)
+{
+    Htab *globls, *strtab;
+    Node *n, **blob;
+    Func **fn;
+    size_t nfn, nblob;
+    size_t i;
+    FILE *fd;
+
+    /* ensure that all physical registers have a loc created before any
+     * other locs, so that locmap[Physreg] maps to the Loc for the physreg
+     * in question */
+    for (i = 0; i < Nreg; i++)
+        locphysreg(i);
+
+    fn = NULL;
+    nfn = 0;
+    blob = NULL;
+    nblob = 0;
+    globls = mkht(varhash, vareq);
+    initconsts(globls);
+
+    /* We need to define all global variables before use */
+    fillglobls(file->file.globls, globls);
+
+    pushstab(file->file.globls);
+    for (i = 0; i < file->file.nstmts; i++) {
+        n = file->file.stmts[i];
+        switch (n->type) {
+            case Nuse: /* nothing to do */ 
+            case Nimpl:
+                break;
+            case Ndecl:
+                simpglobl(n, globls, &fn, &nfn, &blob, &nblob);
+                break;
+            default:
+                die("Bad node %s in toplevel", nodestr(n->type));
+                break;
+        }
+    }
+    popstab();
+
+    fd = fopen(out, "w");
+    if (!fd)
+        die("Couldn't open fd %s", out);
+
+    strtab = mkht(strhash, streq);
+    fprintf(fd, ".data\n");
+    for (i = 0; i < nblob; i++)
+        genblob(fd, blob[i], globls, strtab);
+    fprintf(fd, ".text\n");
+    for (i = 0; i < nfn; i++)
+        genfunc(fd, fn[i], globls, strtab);
+    genstrings(fd, strtab);
+    fclose(fd);
+}
--- a/6/isel.c
+++ b/6/isel.c
@@ -16,25 +16,8 @@
 #include "asm.h"
 #include "../config.h"
 
-/* string tables */
-char *insnfmts[] = {
-#define Insn(val, fmt, use, def) fmt,
-#include "insns.def"
-#undef Insn
-};
-
-char* modenames[] = {
-  [ModeB] = "b",
-  [ModeW] = "w",
-  [ModeL] = "l",
-  [ModeQ] = "q",
-  [ModeF] = "s",
-  [ModeD] = "d"
-};
-
 /* forward decls */
 Loc *selexpr(Isel *s, Node *n);
-static size_t writeblob(FILE *fd, Htab *globls, Htab *strtab, Node *blob);
 
 /* used to decide which operator is appropriate
  * for implementing various conditional operators */
@@ -157,7 +140,7 @@
     return i;
 }
 
-void g(Isel *s, AsmOp op, ...)
+static void g(Isel *s, AsmOp op, ...)
 {
     va_list ap;
     Insn *i;
@@ -768,154 +751,6 @@
     return r;
 }
 
-void locprint(FILE *fd, Loc *l, char spec)
-{
-    assert(l->mode);
-    switch (l->type) {
-        case Loclitl:
-            assert(spec == 'i' || spec == 'x' || spec == 'u');
-            fprintf(fd, "$%s", l->lbl);
-            break;
-        case Loclbl:
-            assert(spec == 'm' || spec == 'v' || spec == 'x');
-            fprintf(fd, "%s", l->lbl);
-            break;
-        case Locreg:
-            assert((spec == 'r' && isintmode(l->mode)) || 
-                   (spec == 'f' && isfloatmode(l->mode)) ||
-                   spec == 'v' ||
-                   spec == 'x' ||
-                   spec == 'u');
-            if (l->reg.colour == Rnone)
-                fprintf(fd, "%%P.%zd%s", l->reg.id, modenames[l->mode]);
-            else
-                fprintf(fd, "%s", regnames[l->reg.colour]);
-            break;
-        case Locmem:
-        case Locmeml:
-            assert(spec == 'm' || spec == 'v' || spec == 'x');
-            if (l->type == Locmem) {
-                if (l->mem.constdisp)
-                    fprintf(fd, "%ld", l->mem.constdisp);
-            } else {
-                if (l->mem.lbldisp)
-                    fprintf(fd, "%s", l->mem.lbldisp);
-            }
-            if (l->mem.base) {
-                fprintf(fd, "(");
-                locprint(fd, l->mem.base, 'r');
-                if (l->mem.idx) {
-                    fprintf(fd, ",");
-                    locprint(fd, l->mem.idx, 'r');
-                }
-                if (l->mem.scale > 1)
-                    fprintf(fd, ",%d", l->mem.scale);
-                if (l->mem.base)
-                    fprintf(fd, ")");
-            } else if (l->type != Locmeml) {
-                die("Only locmeml can have unspecified base reg");
-            }
-            break;
-        case Loclit:
-            assert(spec == 'i' || spec == 'x' || spec == 'u');
-            fprintf(fd, "$%ld", l->lit);
-            break;
-        case Locnone:
-            die("Bad location in locprint()");
-            break;
-    }
-}
-
-int subreg(Loc *a, Loc *b)
-{
-    return rclass(a) == rclass(b) && a->mode != b->mode;
-}
-
-void iprintf(FILE *fd, Insn *insn)
-{
-    char *p;
-    int i;
-    int modeidx;
-
-    /* x64 has a quirk; it has no movzlq because mov zero extends. This
-     * means that we need to do a movl when we really want a movzlq. Since
-     * we don't know the name of the reg to use, we need to sub it in when
-     * writing... */
-    switch (insn->op) {
-        case Imovzx:
-            if (insn->args[0]->mode == ModeL && insn->args[1]->mode == ModeQ) {
-                if (insn->args[1]->reg.colour) {
-                    insn->op = Imov;
-                    insn->args[1] = coreg(insn->args[1]->reg.colour, ModeL);
-                }
-            }
-            break;
-        case Imovs:
-            if (insn->args[0]->reg.colour == Rnone || insn->args[1]->reg.colour == Rnone)
-                break;
-            /* moving a reg to itself is dumb. */
-            if (insn->args[0]->reg.colour == insn->args[1]->reg.colour)
-                return;
-            break;
-        case Imov:
-            assert(!isfloatmode(insn->args[0]->mode));
-            if (insn->args[0]->type != Locreg || insn->args[1]->type != Locreg)
-                break;
-            if (insn->args[0]->reg.colour == Rnone || insn->args[1]->reg.colour == Rnone)
-                break;
-            /* if one reg is a subreg of another, we can just use the right
-             * mode to move between them. */
-            if (subreg(insn->args[0], insn->args[1]))
-                insn->args[0] = coreg(insn->args[0]->reg.colour, insn->args[1]->mode);
-            /* moving a reg to itself is dumb. */
-            if (insn->args[0]->reg.colour == insn->args[1]->reg.colour)
-                return;
-            break;
-        default:
-            break;
-    }
-    p = insnfmts[insn->op];
-    i = 0;
-    modeidx = 0;
-    for (; *p; p++) {
-        if (*p !=  '%') {
-            fputc(*p, fd);
-            continue;
-        }
-
-        /* %-formating */
-        p++;
-        switch (*p) {
-            case '\0':
-                goto done; /* skip the final p++ */
-            case 'r': /* int register */
-            case 'f': /* float register */
-            case 'm': /* memory */
-            case 'i': /* imm */
-            case 'v': /* reg/mem */
-            case 'u': /* reg/imm */
-            case 'x': /* reg/mem/imm */
-                locprint(fd, insn->args[i], *p);
-                i++;
-                break;
-            case 't':
-            default:
-                /* the  asm description uses 1-based indexing, so that 0
-                 * can be used as a sentinel. */
-                if (isdigit(*p))
-                    modeidx = strtol(p, &p, 10) - 1;
-
-                if (*p == 't')
-                    fputs(modenames[insn->args[modeidx]->mode], fd);
-                else
-                    die("Invalid %%-specifier '%c'", *p);
-                break;
-        }
-    }
-done:
-    return;
-}
-
 static void isel(Isel *s, Node *n)
 {
     switch (n->type) {
@@ -996,23 +831,6 @@
     g(s, Iret, NULL);
 }
 
-static void writeasm(FILE *fd, Isel *s, Func *fn)
-{
-    size_t i, j;
-
-    if (fn->isexport || !strcmp(fn->name, Symprefix "main"))
-        fprintf(fd, ".globl %s\n", fn->name);
-    fprintf(fd, "%s:\n", fn->name);
-    for (j = 0; j < s->cfg->nbb; j++) {
-        if (!s->bb[j])
-            continue;
-        for (i = 0; i < s->bb[j]->nlbls; i++)
-            fprintf(fd, "%s:\n", s->bb[j]->lbls[i]);
-        for (i = 0; i < s->bb[j]->ni; i++)
-            iprintf(fd, s->bb[j]->il[i]);
-    }
-}
-
 static Asmbb *mkasmbb(Bb *bb)
 {
     Asmbb *as;
@@ -1028,226 +846,22 @@
     return as;
 }
 
-static void writebytes(FILE *fd, char *p, size_t sz)
+void selfunc(Isel *is, Func *fn, Htab *globls, Htab *strtab)
 {
-    size_t i;
-
-    for (i = 0; i < sz; i++) {
-        if (i % 60 == 0)
-            fprintf(fd, "\t.ascii \"");
-        if (p[i] == '"' || p[i] == '\\')
-            fprintf(fd, "\\");
-        if (isprint(p[i]))
-            fprintf(fd, "%c", p[i]);
-        else
-            fprintf(fd, "\\%03o", (uint8_t)p[i] & 0xff);
-        /* line wrapping for readability */
-        if (i % 60 == 59 || i == sz - 1)
-            fprintf(fd, "\"\n");
-    }
-}
-
-static size_t writelit(FILE *fd, Htab *strtab, Node *v, Type *ty)
-{
-    char buf[128];
-    char *lbl;
-    size_t sz;
-    char *intsz[] = {
-        [1] = ".byte",
-        [2] = ".short",
-        [4] = ".long",
-        [8] = ".quad"
-    };
-    union {
-        float fv;
-        double dv;
-        uint64_t qv;
-        uint32_t lv;
-    } u;
-
-    assert(v->type == Nlit);
-    sz = tysize(ty);
-    switch (v->lit.littype) {
-        case Lint:      fprintf(fd, "\t%s %lld\n", intsz[sz], v->lit.intval);    break;
-        case Lbool:     fprintf(fd, "\t.byte %d\n", v->lit.boolval);     break;
-        case Lchr:      fprintf(fd, "\t.long %d\n",  v->lit.chrval);     break;
-        case Lflt:
-                if (tybase(v->lit.type)->type == Tyflt32) {
-                    u.fv = v->lit.fltval;
-                    fprintf(fd, "\t.long 0x%" PRIx32 "\n", u.lv);
-                } else if (tybase(v->lit.type)->type == Tyflt64) {
-                    u.dv = v->lit.fltval;
-                    fprintf(fd, "\t.quad 0x%" PRIx64 "\n", u.qv);
-                }
-                break;
-        case Lstr:
-           if (hthas(strtab, v->lit.strval)) {
-               lbl = htget(strtab, v->lit.strval);
-           } else {
-               lbl = genlblstr(buf, sizeof buf);
-               htput(strtab, v->lit.strval, strdup(lbl));
-           }
-           fprintf(fd, "\t.quad %s\n", lbl);
-           fprintf(fd, "\t.quad %zd\n", strlen(v->lit.strval));
-           break;
-        case Lfunc:
-            die("Generating this shit ain't ready yet ");
-            break;
-        case Llbl:
-            die("Can't generate literal labels, ffs. They're not data.");
-            break;
-    }
-    return sz;
-}
-
-static size_t writepad(FILE *fd, size_t sz)
-{
-    assert((ssize_t)sz >= 0);
-    if (sz > 0)
-        fprintf(fd, "\t.fill %zd,1,0\n", sz);
-    return sz;
-}
-
-static size_t getintlit(Node *n, char *failmsg)
-{
-    if (exprop(n) != Olit)
-        fatal(n, "%s", failmsg);
-    n = n->expr.args[0];
-    if (n->lit.littype != Lint)
-        fatal(n, "%s", failmsg);
-    return n->lit.intval;
-}
-
-static size_t writeslice(FILE *fd, Htab *globls, Htab *strtab, Node *n)
-{
-    Node *base, *lo, *hi;
-    ssize_t loval, hival, sz;
-    char *lbl;
-
-    base = n->expr.args[0];
-    lo = n->expr.args[1];
-    hi = n->expr.args[2];
-
-    /* by this point, all slicing operations should have had their bases
-     * pulled out, and we should have vars with their pseudo-decls in their
-     * place */
-    if (exprop(base) != Ovar || !base->expr.isconst)
-        fatal(base, "slice base is not a constant value");
-    loval = getintlit(lo, "lower bound in slice is not constant literal");
-    hival = getintlit(hi, "upper bound in slice is not constant literal");
-    sz = tysize(tybase(exprtype(base))->sub[0]);
-
-    lbl = htget(globls, base);
-    fprintf(fd, "\t.quad %s + (%zd*%zd)\n", lbl, loval, sz);
-    fprintf(fd, "\t.quad %zd\n", (hival - loval));
-    return size(n);
-}
-
-static size_t writestruct(FILE *fd, Htab *globls, Htab *strtab, Node *n)
-{
-    Type *t;
-    Node **dcl;
-    int found;
-    size_t i, j;
-    size_t sz, pad, end;
-    size_t ndcl;
-
-    sz = 0;
-    t = tybase(exprtype(n));
-    assert(t->type == Tystruct);
-    dcl = t->sdecls;
-    ndcl = t->nmemb;
-    for (i = 0; i < ndcl; i++) {
-        pad = alignto(sz, decltype(dcl[i]));
-        sz += writepad(fd, pad - sz);
-        found = 0;
-        for (j = 0; j < n->expr.nargs; j++)
-            if (!strcmp(namestr(n->expr.args[j]->expr.idx), declname(dcl[i]))) {
-                found = 1;
-                sz += writeblob(fd, globls, strtab, n->expr.args[j]);
-            }
-        if (!found)
-            sz += writepad(fd, size(dcl[i]));
-    }
-    end = alignto(sz, t);
-    sz += writepad(fd, end - sz);
-    return sz;
-}
-
-static size_t writeblob(FILE *fd, Htab *globls, Htab *strtab, Node *n)
-{
-    size_t i, sz;
-
-    switch(exprop(n)) {
-        case Otup:
-        case Oarr:
-            sz = 0;
-            for (i = 0; i < n->expr.nargs; i++)
-                sz += writeblob(fd, globls, strtab, n->expr.args[i]);
-            break;
-        case Ostruct:
-            sz = writestruct(fd, globls, strtab, n);
-            break;
-        case Olit:
-            sz = writelit(fd, strtab, n->expr.args[0], exprtype(n));
-            break;
-        case Oslice:
-            sz = writeslice(fd, globls, strtab, n);
-            break;
-        default:
-            dump(n, stdout);
-            die("Nonliteral initializer for global");
-            break;
-    }
-    return sz;
-}
-
-void genblob(FILE *fd, Node *blob, Htab *globls, Htab *strtab)
-{
-    char *lbl;
-
-    /* lits and such also get wrapped in decls */
-    assert(blob->type == Ndecl);
-
-    lbl = htget(globls, blob);
-    if (blob->decl.vis != Visintern)
-        fprintf(fd, ".globl %s\n", lbl);
-    fprintf(fd, "%s:\n", lbl);
-    if (blob->decl.init)
-        writeblob(fd, globls, strtab, blob->decl.init);
-    else
-        writepad(fd, size(blob));
-}
-
-/* genasm requires all nodes in 'nl' to map cleanly to operations that are
- * natively supported, as promised in the output of reduce().  No 64-bit
- * operations on x32, no structures, and so on. */
-void genasm(FILE *fd, Func *fn, Htab *globls, Htab *strtab)
-{
-    Isel is = {0,};
     Node *n;
     Bb *bb;
     size_t i, j;
     char buf[128];
 
-    is.reglocs = mkht(varhash, vareq);
-    is.stkoff = fn->stkoff;
-    is.globls = globls;
-    is.ret = fn->ret;
-    is.cfg = fn->cfg;
-    /* ensure that all physical registers have a loc created, so we
-     * don't get any surprises referring to them in the allocator */
-    for (i = 0; i < Nreg; i++)
-        locphysreg(i);
 
     for (i = 0; i < fn->cfg->nbb; i++)
-        lappend(&is.bb, &is.nbb, mkasmbb(fn->cfg->bb[i]));
+        lappend(&is->bb, &is->nbb, mkasmbb(fn->cfg->bb[i]));
 
-    is.curbb = is.bb[0];
-    prologue(&is, fn->stksz);
+    is->curbb = is->bb[0];
+    prologue(is, fn->stksz);
     for (j = 0; j < fn->cfg->nbb - 1; j++) {
-        is.curbb = is.bb[j];
-        if (!is.bb[j])
+        is->curbb = is->bb[j];
+        if (!is->bb[j])
             continue;
         bb = fn->cfg->bb[j];
         for (i = 0; i < bb->nnl; i++) {
@@ -1255,27 +869,11 @@
             n = bb->nl[i];
             snprintf(buf, sizeof buf, "\n\t# bb = %ld, bbidx = %ld, %s:%d",
                      j, i, file->file.files[n->loc.file], n->loc.line);
-            g(&is, Ilbl, locstrlbl(buf), NULL);
-            isel(&is, fn->cfg->bb[j]->nl[i]);
+            g(is, Ilbl, locstrlbl(buf), NULL);
+            isel(is, fn->cfg->bb[j]->nl[i]);
         }
     }
-    is.curbb = is.bb[is.nbb - 1];
-    epilogue(&is);
-    regalloc(&is);
-
-    if (debugopt['i'])
-        writeasm(stdout, &is, fn);
-    writeasm(fd, &is, fn);
-}
-
-void genstrings(FILE *fd, Htab *strtab)
-{
-    void **k;
-    size_t i, nk;
-
-    k = htkeys(strtab, &nk);
-    for (i = 0; i < nk; i++) {
-        fprintf(fd, "%s:\n", (char*)htget(strtab, k[i]));
-        writebytes(fd, k[i], strlen(k[i]));
-    }
+    is->curbb = is->bb[is->nbb - 1];
+    epilogue(is);
+    regalloc(is);
 }
--- a/6/locs.c
+++ b/6/locs.c
@@ -13,6 +13,7 @@
 #include "parse.h"
 #include "opt.h"
 #include "asm.h"
+#include "../config.h"
 
 Mode regmodes[] = {
 #define Reg(r, name, mode) mode,
@@ -72,6 +73,33 @@
 {
     return m == ModeF || m == ModeD;
 }
+
+/* For x86, the assembly names are generated as follows:
+ *      local symbols: .name
+ *      un-namespaced symbols: <symprefix>name
+ *      namespaced symbols: <symprefix>namespace$name
+ */
+char *asmname(Node *n)
+{
+    char *s;
+    int len;
+
+    len = strlen(Symprefix);
+    if (n->name.ns)
+        len += strlen(n->name.ns) + 1; /* +1 for separator */
+    len += strlen(n->name.name) + 1;
+
+    s = xalloc(len + 1);
+    s[0] = '\0';
+    if (n->name.ns)
+        snprintf(s, len, "%s%s$%s", Symprefix, n->name.ns, n->name.name);
+    else if (n->name.name[0] == '.')
+        snprintf(s, len, "%s", n->name.name);
+    else
+        snprintf(s, len, "%s%s", Symprefix, n->name.name);
+    return s;
+}
+
 char *genlblstr(char *buf, size_t sz)
 {
     static int nextlbl;
--- a/6/simp.c
+++ b/6/simp.c
@@ -52,7 +52,6 @@
     Htab *stkoff;
 };
 
-static char *asmname(Node *n);
 static Node *simp(Simp *s, Node *n);
 static Node *rval(Simp *s, Node *n, Node *dst);
 static Node *lval(Simp *s, Node *n);
@@ -65,10 +64,10 @@
 static void matchpattern(Simp *s, Node *pat, Node *val, Type *t, Node *iftrue, Node *iffalse);
 
 /* useful constants */
-static Type *tyintptr;
-static Type *tyword;
-static Type *tyvoid;
-static Node *abortfunc;
+Type *tyintptr;
+Type *tyword;
+Type *tyvoid;
+Node *abortoob;
 
 size_t alignto(size_t sz, Type *t)
 {
@@ -286,139 +285,6 @@
     return s->decl.isconst && decltype(s)->type == Tyfunc;
 }
 
-/* For x86, the assembly names are generated as follows:
- *      local symbols: .name
- *      un-namespaced symbols: <symprefix>name
- *      namespaced symbols: <symprefix>namespace$name
- */
-static char *asmname(Node *n)
-{
-    char *s;
-    int len;
-
-    len = strlen(Symprefix);
-    if (n->name.ns)
-        len += strlen(n->name.ns) + 1; /* +1 for separator */
-    len += strlen(n->name.name) + 1;
-
-    s = xalloc(len + 1);
-    s[0] = '\0';
-    if (n->name.ns)
-        snprintf(s, len, "%s%s$%s", Symprefix, n->name.ns, n->name.name);
-    else if (n->name.name[0] == '.')
-        snprintf(s, len, "%s", n->name.name);
-    else
-        snprintf(s, len, "%s%s", Symprefix, n->name.name);
-    return s;
-}
-
-size_t tysize(Type *t)
-{
-    size_t sz;
-    size_t i;
-
-    sz = 0;
-    if (!t)
-        die("size of empty type => bailing.");
-    switch (t->type) {
-        case Tyvoid:
-            die("void has no size");
-            return 1;
-        case Tybool: case Tyint8:
-        case Tybyte: case Tyuint8:
-            return 1;
-        case Tyint16: case Tyuint16:
-            return 2;
-        case Tyint: case Tyint32:
-        case Tyuint: case Tyuint32:
-        case Tychar:  /* utf32 */
-            return 4;
-
-        case Typtr: case Tyfunc:
-        case Tyvalist: /* ptr to first element of valist */
-            return Ptrsz;
-
-        case Tyint64: case Tylong:
-        case Tyuint64: case Tyulong:
-            return 8;
-
-            /*end integer types*/
-        case Tyflt32:
-            return 4;
-        case Tyflt64:
-            return 8;
-
-        case Tyslice:
-            return 2*Ptrsz; /* len; ptr */
-        case Tyname:
-            return tysize(t->sub[0]);
-        case Tyarray:
-            t->asize = fold(t->asize, 1);
-            assert(exprop(t->asize) == Olit);
-            return t->asize->expr.args[0]->lit.intval * tysize(t->sub[0]);
-        case Tytuple:
-            for (i = 0; i < t->nsub; i++) {
-                sz = alignto(sz, t->sub[i]);
-                sz += tysize(t->sub[i]);
-            }
-            sz = alignto(sz, t);
-            return sz;
-            break;
-        case Tystruct:
-            for (i = 0; i < t->nmemb; i++) {
-                sz = alignto(sz, decltype(t->sdecls[i]));
-                sz += size(t->sdecls[i]);
-            }
-            sz = alignto(sz, t);
-            return sz;
-            break;
-        case Tyunion:
-            sz = Wordsz;
-            for (i = 0; i < t->nmemb; i++)
-                if (t->udecls[i]->etype)
-                    sz = max(sz, tysize(t->udecls[i]->etype) + Wordsz);
-            return align(sz, Ptrsz);
-            break;
-        case Tybad: case Tyvar: case Typaram: case Tyunres: case Ntypes:
-            die("Type %s does not have size; why did it get down to here?", tystr(t));
-            break;
-    }
-    return -1;
-}
-
-size_t size(Node *n)
-{
-    Type *t;
-
-    if (n->type == Nexpr)
-        t = n->expr.type;
-    else
-        t = n->decl.type;
-    return tysize(t);
-}
-
-/* gets the byte offset of 'memb' within the aggregate type 'aggr' */
-static size_t offset(Node *aggr, Node *memb)
-{
-    Type *ty;
-    size_t i;
-    size_t off;
-
-    ty = tybase(exprtype(aggr));
-    if (ty->type == Typtr)
-        ty = tybase(ty->sub[0]);
-
-    assert(ty->type == Tystruct);
-    off = 0;
-    for (i = 0; i < ty->nmemb; i++) {
-        off = alignto(off, decltype(ty->sdecls[i]));
-        if (!strcmp(namestr(memb), declname(ty->sdecls[i])))
-            return off;
-        off += size(ty->sdecls[i]);
-    }
-    die("Could not find member %s in struct", namestr(memb));
-    return -1;
-}
 static Node *gentemp(Simp *simp, Node *e, Type *ty, Node **dcl)
 {
     char buf[128];
@@ -879,7 +745,7 @@
     cmp->expr.type = mktype(len->loc, Tybool);
     ok = genlbl(len->loc);
     fail = genlbl(len->loc);
-    die = mkexpr(idx->loc, Ocall, abortfunc, NULL);
+    die = mkexpr(idx->loc, Ocall, abortoob, NULL);
     die->expr.type = mktype(len->loc, Tyvoid);
 
     /* insert them */
@@ -1764,28 +1630,6 @@
     return fn;
 }
 
-static void fillglobls(Stab *st, Htab *globls)
-{
-    void **k;
-    size_t i, nk;
-    Stab *stab;
-    Node *s;
-
-    k = htkeys(st->dcl, &nk);
-    for (i = 0; i < nk; i++) {
-        s = htget(st->dcl, k[i]);
-        htput(globls, s, asmname(s->decl.name));
-    }
-    free(k);
-
-    k = htkeys(st->ns, &nk);
-    for (i = 0; i < nk; i++) {
-        stab = htget(st->ns, k[i]);
-        fillglobls(stab, globls);
-    }
-    free(k);
-}
-
 static void extractsub(Simp *s, Node ***blobs, size_t *nblobs, Node *e)
 {
     size_t i;
@@ -1836,7 +1680,7 @@
     }
 }
 
-static void simpglobl(Node *dcl, Htab *globls, Func ***fn, size_t *nfn, Node ***blob, size_t *nblob)
+void simpglobl(Node *dcl, Htab *globls, Func ***fn, size_t *nfn, Node ***blob, size_t *nblob)
 {
     Simp s = {0,};
     char *name;
@@ -1859,78 +1703,4 @@
     *blob = s.blobs;
     *nblob = s.nblobs;
     free(name);
-}
-
-static void initconsts(Htab *globls)
-{
-    Type *ty;
-    Node *name;
-    Node *dcl;
-
-    tyintptr = mktype(Zloc, Tyuint64);
-    tyword = mktype(Zloc, Tyuint);
-    tyvoid = mktype(Zloc, Tyvoid);
-
-    ty = mktyfunc(Zloc, NULL, 0, mktype(Zloc, Tyvoid));
-    name = mknsname(Zloc, "_rt", "abort_oob");
-    dcl = mkdecl(Zloc, name, ty);
-    dcl->decl.isconst = 1;
-    dcl->decl.isextern = 1;
-    htput(globls, dcl, asmname(dcl->decl.name));
-
-    abortfunc = mkexpr(Zloc, Ovar, name, NULL);
-    abortfunc->expr.type = ty;
-    abortfunc->expr.did = dcl->decl.did;
-    abortfunc->expr.isconst = 1;
-}
-
-void gen(Node *file, char *out)
-{
-    Htab *globls, *strtab;
-    Node *n, **blob;
-    Func **fn;
-    size_t nfn, nblob;
-    size_t i;
-    FILE *fd;
-
-    fn = NULL;
-    nfn = 0;
-    blob = NULL;
-    nblob = 0;
-    globls = mkht(varhash, vareq);
-    initconsts(globls);
-
-    /* We need to define all global variables before use */
-    fillglobls(file->file.globls, globls);
-
-    pushstab(file->file.globls);
-    for (i = 0; i < file->file.nstmts; i++) {
-        n = file->file.stmts[i];
-        switch (n->type) {
-            case Nuse: /* nothing to do */ 
-            case Nimpl:
-                break;
-            case Ndecl:
-                simpglobl(n, globls, &fn, &nfn, &blob, &nblob);
-                break;
-            default:
-                die("Bad node %s in toplevel", nodestr(n->type));
-                break;
-        }
-    }
-    popstab();
-
-    fd = fopen(out, "w");
-    if (!fd)
-        die("Couldn't open fd %s", out);
-
-    strtab = mkht(strhash, streq);
-    fprintf(fd, ".data\n");
-    for (i = 0; i < nblob; i++)
-        genblob(fd, blob[i], globls, strtab);
-    fprintf(fd, ".text\n");
-    for (i = 0; i < nfn; i++)
-        genasm(fd, fn[i], globls, strtab);
-    genstrings(fd, strtab);
-    fclose(fd);
 }
--- /dev/null
+++ b/6/typeinfo.c
@@ -1,0 +1,129 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "parse.h"
+#include "opt.h"
+#include "asm.h"
+#include "../config.h"
+
+
+size_t tysize(Type *t)
+{
+    size_t sz;
+    size_t i;
+
+    sz = 0;
+    if (!t)
+        die("size of empty type => bailing.");
+    switch (t->type) {
+        case Tyvoid:
+            die("void has no size");
+            return 1;
+        case Tybool: case Tyint8:
+        case Tybyte: case Tyuint8:
+            return 1;
+        case Tyint16: case Tyuint16:
+            return 2;
+        case Tyint: case Tyint32:
+        case Tyuint: case Tyuint32:
+        case Tychar:  /* utf32 */
+            return 4;
+
+        case Typtr: case Tyfunc:
+        case Tyvalist: /* ptr to first element of valist */
+            return Ptrsz;
+
+        case Tyint64: case Tylong:
+        case Tyuint64: case Tyulong:
+            return 8;
+
+            /*end integer types*/
+        case Tyflt32:
+            return 4;
+        case Tyflt64:
+            return 8;
+
+        case Tyslice:
+            return 2*Ptrsz; /* len; ptr */
+        case Tyname:
+            return tysize(t->sub[0]);
+        case Tyarray:
+            t->asize = fold(t->asize, 1);
+            assert(exprop(t->asize) == Olit);
+            return t->asize->expr.args[0]->lit.intval * tysize(t->sub[0]);
+        case Tytuple:
+            for (i = 0; i < t->nsub; i++) {
+                sz = alignto(sz, t->sub[i]);
+                sz += tysize(t->sub[i]);
+            }
+            sz = alignto(sz, t);
+            return sz;
+            break;
+        case Tystruct:
+            for (i = 0; i < t->nmemb; i++) {
+                sz = alignto(sz, decltype(t->sdecls[i]));
+                sz += size(t->sdecls[i]);
+            }
+            sz = alignto(sz, t);
+            return sz;
+            break;
+        case Tyunion:
+            sz = Wordsz;
+            for (i = 0; i < t->nmemb; i++)
+                if (t->udecls[i]->etype)
+                    sz = max(sz, tysize(t->udecls[i]->etype) + Wordsz);
+            return align(sz, Ptrsz);
+            break;
+        case Tybad: case Tyvar: case Typaram: case Tyunres: case Ntypes:
+            die("Type %s does not have size; why did it get down to here?", tystr(t));
+            break;
+    }
+    return -1;
+}
+
+/* gets the byte offset of 'memb' within the aggregate type 'aggr' */
+size_t tyoffset(Type *ty, Node *memb)
+{
+    size_t i;
+    size_t off;
+
+    ty = tybase(ty);
+    if (ty->type == Typtr)
+        ty = tybase(ty->sub[0]);
+
+    assert(ty->type == Tystruct);
+    off = 0;
+    for (i = 0; i < ty->nmemb; i++) {
+        off = alignto(off, decltype(ty->sdecls[i]));
+        if (!strcmp(namestr(memb), declname(ty->sdecls[i])))
+            return off;
+        off += size(ty->sdecls[i]);
+    }
+    die("Could not find member %s in struct", namestr(memb));
+    return -1;
+}
+
+size_t size(Node *n)
+{
+    Type *t;
+
+    if (n->type == Nexpr)
+        t = n->expr.type;
+    else
+        t = n->decl.type;
+    return tysize(t);
+}
+
+size_t offset(Node *aggr, Node *memb)
+{
+    return tyoffset(exprtype(aggr), memb);
+}
+
--- a/libstd/dial.myr
+++ b/libstd/dial.myr
@@ -10,6 +10,7 @@
 use "resolve.use"
 use "result.use"
 use "sleq.use"
+use "strfind.use"
 use "sys.use"
 use "syswrap.use"
 use "utf.use"
@@ -30,10 +31,42 @@
 /* takes a plan 9 style dial string */
 const dial = {str
 	var proto, host, port
-	var socktype, portnum
 	var sa : sys.sockaddr_in	/* we only support inet sockets right now.. ugh. */
 	var sock
 
+	match parsedial(str)
+	| `Ok val:	(proto, host, port) = val
+	| `Fail m:	-> `Fail m
+	;;
+
+	match getaddr(host)
+	| `Ipv4 bits:
+		sa.fam = sys.Afinet
+		sa.addr = bits
+		sa.port = hosttonet(port)
+	| `Ipv6 bits:
+		-> `Fail "ipv6 not yet supported"
+	;;
+
+	sock = sys.socket(sa.fam, proto, 0)
+	if sock < 0
+		-> `Fail "failed to connect to socket"
+	;;
+	var err
+	err = sys.connect(sock, (&sa) castto(sys.sockaddr#), sizeof(sys.sockaddr_in))
+	if err < 0
+		put("Errno %i\n", -err)
+		sys.close(sock)
+		-> `Fail "Failed to bind socket"
+	;;
+
+	-> `Ok (sock castto(fd))
+}
+
+const parsedial = {str
+	var proto, host, port
+	var socktype, portnum
+
 	(proto, str) = nameseg(str)
 	(host, str) = nameseg(str)
 	(port, str) = nameseg(str)
@@ -61,28 +94,7 @@
 	| `None:	-> `Fail "bad port"
 	;;
 
-	match getaddr(host)
-	| `Ipv4 bits:
-		sa.fam = sys.Afinet
-		sa.addr = bits
-		sa.port = hosttonet(portnum)
-	| `Ipv6 bits:
-		-> `Fail "ipv6 not yet supported"
-	;;
-
-	sock = sys.socket(sa.fam, socktype, 0)
-	if sock < 0
-		-> `Fail "failed to connect to socket"
-	;;
-	var err
-	err = sys.connect(sock, (&sa) castto(sys.sockaddr#), sizeof(sys.sockaddr_in))
-	if err < 0
-		put("Errno %i\n", -err)
-		sys.close(sock)
-		-> `Fail "Failed to bind socket"
-	;;
-
-	-> `Ok (sock castto(fd))
+	-> `Ok (socktype, host, portnum)
 }
 
 const parseport = {port
@@ -120,13 +132,11 @@
 }
 
 const nameseg = {str
-	var len
-
-	for len = 0; len < str.len; len++
-		if str[len] == '!' castto(byte)
-			-> (str[:len], str[len+1:])
-		;;
+	match strfind(str, "!")
+	| `Some idx:
+		-> (str[:idx], str[idx:])
+	| `None:
+		-> (str, "")
 	;;
-	-> (str[:], str[len:])
 }