summaryrefslogtreecommitdiff
path: root/sys/src/cmd
diff options
context:
space:
mode:
authoraiju <devnull@localhost>2018-11-10 13:46:16 +0000
committeraiju <devnull@localhost>2018-11-10 13:46:16 +0000
commite6d99771e5c1eef3f69fc847253d4709ffaa84be (patch)
tree16161401edc25c084ad24533519e094716965635 /sys/src/cmd
parent8c097ae84a500eae9c8e4ee21b7b3ea8f8d23259 (diff)
adding dtracy (crude early version)
Diffstat (limited to 'sys/src/cmd')
-rw-r--r--sys/src/cmd/dtracy/act.c571
-rw-r--r--sys/src/cmd/dtracy/cgen.c313
-rw-r--r--sys/src/cmd/dtracy/dat.h123
-rw-r--r--sys/src/cmd/dtracy/dtracy.c224
-rw-r--r--sys/src/cmd/dtracy/fns.h35
-rw-r--r--sys/src/cmd/dtracy/lex.c390
-rwxr-xr-xsys/src/cmd/dtracy/lint.rc20
-rw-r--r--sys/src/cmd/dtracy/mkfile22
-rw-r--r--sys/src/cmd/dtracy/parse.y123
-rw-r--r--sys/src/cmd/dtracy/type.c529
10 files changed, 2350 insertions, 0 deletions
diff --git a/sys/src/cmd/dtracy/act.c b/sys/src/cmd/dtracy/act.c
new file mode 100644
index 000000000..335d1bd23
--- /dev/null
+++ b/sys/src/cmd/dtracy/act.c
@@ -0,0 +1,571 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <dtracy.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+/* this contains the code to prepare the kernel data structures and to parse records */
+
+Clause *clause;
+Clause **clauses;
+int nclauses;
+
+/* we could just rely on the types in the expression tree but i'm paranoid */
+typedef struct Val Val;
+struct Val {
+ enum {
+ VALINT,
+ VALSTR,
+ } type;
+ union {
+ vlong v;
+ char *s;
+ };
+};
+
+Val
+mkval(int type, ...)
+{
+ Val r;
+ va_list va;
+
+ r.type = type;
+ va_start(va, type);
+ switch(type){
+ case VALINT: r.v = va_arg(va, uvlong); break;
+ case VALSTR: r.s = va_arg(va, char*); break;
+ }
+ va_end(va);
+ return r;
+}
+
+void
+clausebegin(void)
+{
+ clause = emalloc(sizeof(Clause));
+ clause->id = nclauses;
+}
+
+void
+addprobe(char *s)
+{
+ clause->probs = erealloc(clause->probs, sizeof(char *) * (clause->nprob + 1));
+ clause->probs[clause->nprob++] = strdup(s);
+}
+
+void
+addstat(int type, ...)
+{
+ Stat *s;
+ va_list va;
+
+ clause->stats = erealloc(clause->stats, sizeof(Stat) * (clause->nstats + 1));
+ s = &clause->stats[clause->nstats++];
+ memset(s, 0, sizeof(Stat));
+ s->type = type;
+ va_start(va, type);
+ switch(type){
+ case STATEXPR:
+ s->n = va_arg(va, Node *);
+ break;
+ case STATPRINT:
+ case STATPRINTF:
+ break;
+ default:
+ sysfatal("addstat: unknown type %d", type);
+ }
+ va_end(va);
+}
+
+void
+addarg(Node *n)
+{
+ Stat *s;
+
+ assert(clause->nstats > 0);
+ s = &clause->stats[clause->nstats - 1];
+ s->arg = erealloc(s->arg, sizeof(Node *) * (s->narg + 1));
+ s->arg[s->narg++] = n;
+}
+
+void
+clauseend(void)
+{
+ clauses = erealloc(clauses, sizeof(Clause) * (nclauses + 1));
+ clauses[nclauses++] = clause;
+}
+
+void
+actgradd(DTActGr *a, DTAct b)
+{
+ a->acts = erealloc(a->acts, sizeof(DTAct) * (a->nact + 1));
+ a->acts[a->nact++] = b;
+}
+
+void
+addpred(DTExpr *e)
+{
+ clause->pred = e;
+}
+
+static void
+prepprintf(Node **arg, int narg, DTActGr *g, int *recoff)
+{
+ char *fmt;
+ int n;
+ Fmt f;
+
+ if(narg <= 0) sysfatal("printf() needs an argument");
+ if((*arg)->type != OSTR) sysfatal("printf() format string must be a literal");
+ fmt = (*arg)->str;
+ fmtstrinit(&f);
+ n = 1;
+ for(; *fmt != 0; fmt++){
+ fmtrune(&f, *fmt);
+ if(*fmt != '%')
+ continue;
+ fmt++;
+ again:
+ switch(*fmt){
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ case 'u': case '+': case '-': case ',': case '#': case ' ': case '.':
+ fmtrune(&f, *fmt);
+ fmt++;
+ goto again;
+ case 'x': case 'X': case 'o': case 'b': case 'd':
+ if(n >= narg) sysfatal("printf() too few arguments");
+ if(arg[n]->typ->type != TYPINT) sysfatal("print() %%%c with non-integer", *fmt);
+ arg[n] = tracegen(arg[n], g, recoff);
+ n++;
+ fmtrune(&f, 'l');
+ fmtrune(&f, 'l');
+ fmtrune(&f, *fmt);
+ break;
+ case 's':
+ if(n >= narg) sysfatal("printf() too few arguments");
+ if(arg[n]->typ->type != TYPSTRING) sysfatal("print() %%s with non-string");
+ arg[n] = tracegen(arg[n], g, recoff);
+ n++;
+ fmtrune(&f, *fmt);
+ break;
+ case 0: sysfatal("printf() missing verb");
+ default: sysfatal("printf() unknown verb %%%c", *fmt);
+ }
+ }
+ if(n < narg) sysfatal("printf() too many arguments");
+ (*arg)->str = fmtstrflush(&f);
+}
+
+DTClause *
+mkdtclause(Clause *c)
+{
+ DTClause *d;
+ Stat *s;
+ int recoff, i;
+
+ d = emalloc(sizeof(DTClause));
+ d->nprob = c->nprob;
+ d->probs = c->probs;
+ d->gr = emalloc(sizeof(DTActGr));
+ d->gr->pred = c->pred;
+ d->gr->id = c->id;
+ recoff = 12;
+ for(s = c->stats; s < c->stats + c->nstats; s++)
+ switch(s->type){
+ case STATEXPR:
+ actgradd(d->gr, (DTAct){ACTTRACE, codegen(s->n), 0});
+ break;
+ case STATPRINT:
+ for(i = 0; i < s->narg; i++)
+ s->arg[i] = tracegen(s->arg[i], d->gr, &recoff);
+ break;
+ case STATPRINTF:
+ prepprintf(s->arg, s->narg, d->gr, &recoff);
+ break;
+ }
+ return d;
+}
+
+void
+packclauses(Fmt *f)
+{
+ int i;
+ DTClause *d;
+
+ for(i = 0; i < nclauses; i++){
+ d = mkdtclause(clauses[i]);
+ dtclpack(f, d);
+ }
+}
+
+/* epid lookup table, filled with info from the kernel */
+Enab *enabtab[1024];
+
+void
+addepid(u32int epid, u32int cid, int reclen, char *p)
+{
+ Enab *e, **ep;
+
+ assert(cid < nclauses);
+ assert((uint)reclen >= 12);
+ e = emalloc(sizeof(Enab));
+ e->epid = epid;
+ e->cl = clauses[cid];
+ e->reclen = reclen;
+ e->probe = strdup(p);
+ ep = &enabtab[epid % nelem(enabtab)];
+ e->next = *ep;
+ *ep = e;
+}
+
+Enab *
+epidlookup(u32int epid)
+{
+ Enab *e;
+
+ for(e = enabtab[epid % nelem(enabtab)]; e != nil; e = e->next)
+ if(e->epid == epid)
+ return e;
+ return nil;
+}
+
+uchar *
+unpack(uchar *p, uchar *e, char *fmt, ...)
+{
+ va_list va;
+ u64int vl;
+
+ va_start(va, fmt);
+ for(;;)
+ switch(*fmt++){
+ case 'c':
+ if(p + 1 > e) return nil;
+ *va_arg(va, u8int *) = p[0];
+ p += 1;
+ break;
+ case 's':
+ if(p + 2 > e) return nil;
+ *va_arg(va, u16int *) = p[0] | p[1] << 8;
+ p += 2;
+ break;
+ case 'i':
+ if(p + 4 > e) return nil;
+ *va_arg(va, u32int *) = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+ p += 4;
+ break;
+ case 'v':
+ if(p + 8 > e) return nil;
+ vl = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+ vl |= (uvlong)p[4] << 32 | (uvlong)p[5] << 40 | (uvlong)p[6] << 48 | (uvlong)p[7] << 56;
+ *va_arg(va, u64int *) = vl;
+ p += 8;
+ break;
+ case 0:
+ return p;
+ default:
+ abort();
+ }
+}
+
+static Val
+receval(Node *n, uchar *p, uchar *e, Enab *en)
+{
+ u8int c;
+ u16int s;
+ u32int i;
+ uvlong v;
+ char *sp;
+ uchar *q;
+ Val a, b;
+
+ switch(n->type){
+ case OSYM:
+ switch(n->sym->type){
+ case SYMVAR:
+ switch(n->sym->idx){
+ case DTV_TIME:
+ q = unpack(p + 4, e, "v", &v);
+ assert(q != nil);
+ return mkval(VALINT, v);
+ case DTV_PROBE:
+ return mkval(VALSTR, en->probe);
+ default: sysfatal("receval: unknown variable %d", n->type); return mkval(VALINT, 0LL);
+ }
+ break;
+ default: sysfatal("receval: unknown symbol type %d", n->type); return mkval(VALINT, 0LL);
+ }
+ case ONUM: return mkval(VALINT, n->num);
+ case OBIN:
+ a = receval(n->n1, p, e, en);
+ b = receval(n->n2, p, e, en);
+ assert(a.type == VALINT);
+ assert(b.type == VALINT);
+ return mkval(VALINT, evalop(n->op, n->typ->sign, a.v, b.v));
+ case OLNOT:
+ a = receval(n->n1, p, e, en);
+ assert(a.type == VALINT);
+ return mkval(VALINT, (uvlong) !a.v);
+ case OTERN:
+ a = receval(n->n1, p, e, en);
+ assert(a.type == VALINT);
+ return a.v ? receval(n->n2, p, e, en) : receval(n->n3, p, e, en);
+ case ORECORD:
+ switch(n->typ->type){
+ case TYPINT:
+ switch(n->typ->size){
+ case 1: q = unpack(p + n->num, e, "c", &c); v = n->typ->sign ? (s8int)c : (u8int)c; break;
+ case 2: q = unpack(p + n->num, e, "s", &s); v = n->typ->sign ? (s16int)s : (u16int)s; break;
+ case 4: q = unpack(p + n->num, e, "i", &i); v = n->typ->sign ? (s32int)i : (u32int)i; break;
+ case 8: q = unpack(p + n->num, e, "v", &v); break;
+ default: q = nil;
+ }
+ assert(q != nil);
+ return mkval(VALINT, v);
+ case TYPSTRING:
+ assert(p + n->num + n->typ->size <= e);
+ sp = emalloc(n->typ->size + 1);
+ memcpy(sp, p + n->num, n->typ->size);
+ return mkval(VALSTR, sp); /* TODO: fix leak */
+ default:
+ sysfatal("receval: don't know how to parse record for %τ", n->typ);
+ }
+ default:
+ sysfatal("receval: unknown type %α", n->type);
+ return mkval(VALINT, 0LL);
+ }
+}
+
+static void
+execprintf(Node **arg, int narg, uchar *p, uchar *e, Enab *en)
+{
+ char *x, *xp;
+ Val v;
+ int i;
+
+ x = emalloc(sizeof(uvlong) * (narg - 1));
+ xp = x;
+ for(i = 0; i < narg - 1; i++){
+ v = receval(arg[i + 1], p, e, en);
+ switch(v.type){
+ case VALINT:
+ *(uvlong*)xp = v.v;
+ xp += sizeof(uvlong);
+ break;
+ case VALSTR:
+ *(char**)xp = v.s;
+ xp += sizeof(char*);
+ break;
+ default: abort();
+ }
+ }
+ vfprint(1, (*arg)->str, (va_list) x);
+ free(x);
+}
+
+int
+parseclause(Clause *cl, uchar *p, uchar *e, Enab *en, Biobuf *bp)
+{
+ Stat *s;
+ int i;
+ Val v;
+
+ for(s = cl->stats; s < cl->stats + cl->nstats; s++)
+ switch(s->type){
+ case STATEXPR: break;
+ case STATPRINT:
+ for(i = 0; i < s->narg; i++){
+ v = receval(s->arg[i], p, e, en);
+ switch(v.type){
+ case VALINT:
+ Bprint(bp, "%lld", v.v);
+ break;
+ case VALSTR:
+ Bprint(bp, "%s", v.s);
+ break;
+ default: sysfatal("parseclause: unknown val type %d", s->type);
+ }
+ Bprint(bp, "%c", i == s->narg - 1 ? '\n' : ' ');
+ }
+ break;
+ case STATPRINTF:
+ execprintf(s->arg, s->narg, p, e, en);
+ break;
+ default:
+ sysfatal("parseclause: unknown type %d", s->type);
+ }
+ return 0;
+}
+
+int
+parsebuf(uchar *p, int n, Biobuf *bp)
+{
+ uchar *e;
+ u32int epid;
+ u64int ts;
+ Enab *en;
+
+ e = p + n;
+ while(p < e){
+ p = unpack(p, e, "iv", &epid, &ts);
+ if(p == nil) goto err;
+ en = epidlookup(epid);
+ if(en == nil) goto err;
+ if(parseclause(en->cl, p - 12, p + en->reclen - 12, en, bp) < 0) return -1;
+ p += en->reclen - 12;
+ }
+ return 0;
+err:
+ werrstr("buffer invalid");
+ return -1;
+}
+
+static void
+dumpexpr(DTExpr *e, char *prefix)
+{
+ int i;
+
+ for(i = 0; i < e->n; i++)
+ print("%s%.8ux %I\n", prefix, e->b[i], e->b[i]);
+}
+
+#pragma varargck type "ε" Node*
+
+static void
+fmtstring(Fmt *f, char *s)
+{
+ fmtrune(f, '"');
+ for(; *s != 0; s++)
+ switch(*s){
+ case '\n': fmtprint(f, "\\n"); break;
+ case '\r': fmtprint(f, "\\r"); break;
+ case '\t': fmtprint(f, "\\t"); break;
+ case '\v': fmtprint(f, "\\v"); break;
+ case '\b': fmtprint(f, "\\b"); break;
+ case '\a': fmtprint(f, "\\a"); break;
+ case '"': fmtprint(f, "\""); break;
+ case '\\': fmtprint(f, "\\"); break;
+ default:
+ if(*s < 0x20 || *s >= 0x7f)
+ fmtprint(f, "\\%.3o", (uchar)*s);
+ else
+ fmtrune(f, *s);
+ }
+ fmtrune(f, '"');
+}
+
+typedef struct Op Op;
+struct Op {
+ char *name;
+ int pred;
+ enum { PRECRIGHT = 1 } flags;
+};
+static Op optab[] = {
+ [OPLOR] {"||", 3, 0},
+ [OPLAND] {"&&", 4, 0},
+ [OPOR] {"|", 5, 0},
+ [OPXNOR] {"~^", 6, 0},
+ [OPXOR] {"^", 6, 0},
+ [OPAND] {"&", 7, 0},
+ [OPEQ] {"==", 8, },
+ [OPNE] {"!=", 8, 0},
+ [OPLE] {"<=", 9, 0},
+ [OPLT] {"<", 9, 0},
+ [OPLSH] {"<<", 10, 0},
+ [OPRSH] {">>", 10, 0},
+ [OPADD] {"+", 11, 0},
+ [OPSUB] {"-", 11, 0},
+ [OPDIV] {"/", 12, 0},
+ [OPMOD] {"%", 12, 0},
+ [OPMUL] {"*", 12, 0},
+};
+enum { PREDUNARY = 14 };
+
+int
+nodefmt(Fmt *f)
+{
+ Node *n;
+ Op *op;
+ int p;
+
+ p = f->width;
+ n = va_arg(f->args, Node *);
+ switch(n->type){
+ case OSYM: fmtprint(f, "%s", n->sym->name); break;
+ case ONUM: fmtprint(f, "%lld", n->num); break;
+ case OSTR: fmtstring(f, n->str); break;
+ case OBIN:
+ if(n->op >= nelem(optab) || optab[n->op].name == nil)
+ fmtprint(f, "(%*ε ??op%d %*ε)", PREDUNARY, n->n1, n->op, PREDUNARY, n->n2);
+ else{
+ op = &optab[n->op];
+ if(op->pred < p) fmtrune(f, '(');
+ fmtprint(f, "%*ε %s %*ε", op->pred + (op->flags & PRECRIGHT), n->n1, op->name, op->pred + (~op->flags & PRECRIGHT), n->n2);
+ if(op->pred < p) fmtrune(f, ')');
+ }
+ break;
+ case OLNOT: fmtprint(f, "!%*ε", PREDUNARY, n->n1); break;
+ case OTERN: fmtprint(f, "%2ε ? %1ε : %1ε", n->n1, n->n2, n->n3); break;
+ case ORECORD: fmtprint(f, "record(%ε, %τ, %d)", n->n1, n->typ, (int)n->num); break;
+ case OCAST: fmtprint(f, "(%τ) %*ε", n->typ, PREDUNARY, n->n1); break;
+ default: fmtprint(f, "??? %α", n->type);
+ }
+ return 0;
+}
+
+void
+dump(void)
+{
+ int i, j;
+ Stat *s;
+ Clause *c;
+ DTClause *d;
+ DTAct *a;
+
+ for(i = 0; i < nclauses; i++){
+ c = clauses[i];
+ d = mkdtclause(c);
+ print("clause %d:\n", c->id);
+ for(j = 0; j < c->nprob; j++)
+ print("\tprobe '%s'\n", c->probs[j]);
+ print("\tkernel code:\n");
+ if(c->pred == nil)
+ print("\t\tno predicate\n");
+ else{
+ print("\t\tpredicate\n");
+ dumpexpr(c->pred, "\t\t\t");
+ }
+ for(a = d->gr->acts; a < d->gr->acts + d->gr->nact; a++)
+ switch(a->type){
+ case ACTTRACE:
+ print("\t\ttrace (%d bytes)\n", a->size);
+ dumpexpr(a->p, "\t\t\t");
+ break;
+ case ACTTRACESTR:
+ print("\t\ttrace string (%d bytes)\n", a->size);
+ dumpexpr(a->p, "\t\t\t");
+ break;
+ default:
+ print("\t\t??? %d\n", a->type);
+ }
+ print("\trecord formatting:\n");
+ for(s = c->stats; s < c->stats + c->nstats; s++)
+ switch(s->type){
+ case STATEXPR:
+ break;
+ case STATPRINT:
+ print("\t\tprint\n");
+ for(j = 0; j < s->narg; j++)
+ print("\t\t\targ %ε\n", s->arg[j]);
+ break;
+ case STATPRINTF:
+ print("\t\tprintf\n");
+ for(j = 0; j < s->narg; j++)
+ print("\t\t\targ %ε\n", s->arg[j]);
+ break;
+ default:
+ print("\t\t??? %d\n", s->type);
+ }
+ }
+}
diff --git a/sys/src/cmd/dtracy/cgen.c b/sys/src/cmd/dtracy/cgen.c
new file mode 100644
index 000000000..6b4f77d19
--- /dev/null
+++ b/sys/src/cmd/dtracy/cgen.c
@@ -0,0 +1,313 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+u16int regsused = 1;
+u32int cbuf[256];
+int ncbuf;
+int labtab[256];
+int nlab;
+
+static void
+emit(u32int x)
+{
+ assert(ncbuf < nelem(cbuf));
+ cbuf[ncbuf++] = x;
+}
+
+static int
+regalloc(void)
+{
+ u16int v;
+ int n;
+
+ if(regsused == 0xffff){
+ error("out of registers");
+ return 0;
+ }
+ v = regsused + 1 & ~regsused;
+ regsused ^= v;
+ n = 0;
+ if((u8int)v == 0) {v >>= 8; n += 8;}
+ if((v & 0xf) == 0) {v >>= 4; n += 4;}
+ if((v & 3) == 0) {v >>= 2; n += 2;}
+ return n + (v >> 1);
+}
+
+static void
+regfree(int n)
+{
+ assert((regsused & 1<<n) != 0);
+ assert(n != 0);
+ regsused &= ~(1<<n);
+}
+
+static int
+popcount(u64int s)
+{
+ s = (s & 0x5555555555555555ULL) + (s >> 1 & 0x5555555555555555ULL);
+ s = (s & 0x3333333333333333ULL) + (s >> 2 & 0x3333333333333333ULL);
+ s = (s & 0x0F0F0F0F0F0F0F0FULL) + (s >> 4 & 0x0F0F0F0F0F0F0F0FULL);
+ s = (s & 0x00FF00FF00FF00FFULL) + (s >> 8 & 0x00FF00FF00FF00FFULL);
+ s = (s & 0x0000FFFF0000FFFFULL) + (s >> 16 & 0x0000FFFF0000FFFFULL);
+ return (u32int)s + (u32int)(s >> 32);
+}
+
+static int
+constenc(s64int val)
+{
+ int i, r;
+ s64int x;
+
+ r = 0;
+ do{
+ i = popcount(val ^ val - 1) - 1;
+ x = val << 54 - i >> 54;
+ if(r == 0){
+ r = regalloc();
+ emit(DTE_LDI << 24 | (x & 0x3ff) << 14 | i << 8 | r);
+ }else
+ emit(DTE_XORI << 24 | (x & 0x3ff) << 14 | i << 8 | r);
+ val ^= x << i;
+ }while(val != 0);
+ return r;
+}
+
+static int egen(Node *);
+
+static void
+condgen(Node *n, int invert, int truelab)
+{
+ int r1, r2, l1, op;
+
+ if(n->type != OBIN) goto other;
+ switch(n->op){
+ case OPEQ: op = DTE_SEQ; goto cmp;
+ case OPNE: op = DTE_SNE; goto cmp;
+ case OPLT: op = DTE_SLT; goto cmp;
+ case OPLE: op = DTE_SLE;
+ cmp:
+ r1 = egen(n->n1);
+ r2 = egen(n->n2);
+ if(invert)
+ emit(DTE(op ^ 1, r2, r1, truelab));
+ else
+ emit(DTE(op, r1, r2, truelab));
+ regfree(r1);
+ regfree(r2);
+ break;
+ case OPLOR:
+ case OPLAND:
+ if(invert ^ n->op == OPLOR){
+ condgen(n->n1, invert, truelab);
+ condgen(n->n2, invert, truelab);
+ }else{
+ l1 = nlab++;
+ condgen(n->n1, !invert, l1);
+ condgen(n->n2, invert, truelab);
+ labtab[l1] = ncbuf;
+ }
+ break;
+ default:
+ other:
+ r1 = egen(n);
+ emit(DTE(DTE_BNE ^ invert, r1, 0, truelab));
+ regfree(r1);
+ break;
+ }
+}
+
+static int
+condvgen(Node *n, int invert)
+{
+ int r, l1, l2, op;
+
+ if(n->type == OLNOT)
+ return condvgen(n->n1, !invert);
+ if(n->type != OBIN) goto other;
+ switch(n->op){
+ case OPEQ: op = DTE_SEQ; goto cmp;
+ case OPNE: op = DTE_SNE; goto cmp;
+ case OPLT: op = DTE_SLT; goto cmp;
+ case OPLE: op = DTE_SLE;
+ cmp:
+ if(invert)
+ return egen(node(OBIN, op ^ 1, n->n2, n->n1));
+ return egen(n);
+ case OPLOR:
+ case OPLAND:
+ if(invert ^ n->op == OPLOR){
+ l1 = nlab++;
+ l2 = nlab++;
+ condgen(n->n1, invert, l1);
+ r = condvgen(n->n2, invert);
+ emit(DTE(DTE_BEQ, 0, 0, l2));
+ labtab[l1] = ncbuf;
+ emit(DTE(DTE_LDI, 0, 1<<6, r));
+ labtab[l2] = ncbuf;
+ return r;
+ }else{
+ l1 = nlab++;
+ l2 = nlab++;
+ condgen(n->n1, invert, l1);
+ r = condvgen(n->n2, invert);
+ emit(DTE(DTE_BEQ, 0, 0, l2));
+ labtab[l1] = ncbuf;
+ emit(DTE(DTE_LDI, 0, 0<<6, r));
+ labtab[l2] = ncbuf;
+ return r;
+ }
+ default:
+ other:
+ r = egen(n);
+ emit(DTE(DTE_SNE ^ invert, r, 0, r));
+ return r;
+ }
+}
+
+static int
+egen(Node *n)
+{
+ int r1, r2, rt, l1, l2, op;
+
+ switch(/*nodetype*/n->type){
+ case ONUM:
+ return constenc(n->num);
+ case OSYM:
+ switch(n->sym->type){
+ case SYMVAR:
+ rt = regalloc();
+ emit(DTE(DTE_LDV, n->sym->idx, rt, 0));
+ return rt;
+ default: sysfatal("egen: unknown symbol type %d", n->sym->type); return 0;
+ }
+ case OBIN:
+ switch(/*oper*/n->op){
+ case OPLAND:
+ case OPLOR:
+ return condvgen(n, 0);
+ case OPADD: op = DTE_ADD; break;
+ case OPSUB: op = DTE_SUB; break;
+ case OPMUL: op = DTE_MUL; break;
+ case OPDIV: op = n->typ->sign ? DTE_SDIV : DTE_UDIV; break;
+ case OPMOD: op = n->typ->sign ? DTE_SMOD : DTE_UMOD; break;
+ case OPAND: op = DTE_AND; break;
+ case OPOR: op = DTE_OR; break;
+ case OPXOR: op = DTE_XOR; break;
+ case OPLSH: op = DTE_LSL; break;
+ case OPRSH: op = n->typ->sign ? DTE_ASR : DTE_LSR; break;
+ case OPEQ: op = DTE_SEQ; break;
+ case OPNE: op = DTE_SNE; break;
+ case OPLT: op = DTE_SLT; break;
+ case OPLE: op = DTE_SLE; break;
+ case OPXNOR: op = DTE_XNOR; break;
+ default: sysfatal("egen: unknown op %d", n->op); return 0;
+ }
+ r1 = egen(n->n1);
+ r2 = egen(n->n2);
+ regfree(r1);
+ regfree(r2);
+ rt = regalloc();
+ emit(DTE(op, r1, r2, rt));
+ return rt;
+ case OTERN:
+ l1 = nlab++;
+ l2 = nlab++;
+ condgen(n->n1, 1, l1);
+ r1 = egen(n->n2);
+ emit(DTE(DTE_BEQ, 0, 0, l2));
+ labtab[l1] = ncbuf;
+ r2 = egen(n->n3);
+ if(r1 != r2)
+ emit(DTE(DTE_OR, 0, r2, r1));
+ labtab[l2] = ncbuf;
+ return r1;
+ case OLNOT:
+ return condvgen(n, 0);
+ case OCAST:
+ switch(n->typ->type){
+ case TYPINT:
+ r1 = egen(n->n1);
+ emit(DTE(n->typ->sign ? DTE_SXT : DTE_ZXT, r1, n->typ->size * 8, r1));
+ return r1;
+ case TYPSTRING:
+ return egen(n->n1);
+ default:
+ sysfatal("egen: don't know how to cast %τ to %τ", n->n1->typ, n->typ);
+ }
+ case ORECORD:
+ case OSTR:
+ default: sysfatal("egen: unknown type %α", n->type); return 0;
+ }
+}
+
+DTExpr *
+codegen(Node *n)
+{
+ int r, i, t;
+ DTExpr *ep;
+
+ regsused = 1;
+ ncbuf = 0;
+ nlab = 0;
+ r = egen(n);
+ emit(DTE(DTE_RET, r, 0, 0));
+
+ for(i = 0; i < ncbuf; i++)
+ if((cbuf[i] >> 24 & 0xf0) == 0x30){
+ t = labtab[cbuf[i] & 0xff];
+ assert((uint)(t - i - 1) < 0x100);
+ cbuf[i] = cbuf[i] & 0xffffff00 | t - i - 1;
+ }
+
+ ep = emalloc(sizeof(DTExpr) + ncbuf * sizeof(u32int));
+ ep->n = ncbuf;
+ ep->b = (void *)(ep + 1);
+ memcpy(ep->b, cbuf, ncbuf * sizeof(u32int));
+ return ep;
+}
+
+Node *
+tracegen(Node *n, DTActGr *g, int *recoff)
+{
+ switch(/*nodetype*/n->type){
+ case OSYM:
+ case ONUM:
+ case OSTR:
+ break;
+ case OBIN:
+ n->n1 = tracegen(n->n1, g, recoff);
+ n->n2 = tracegen(n->n2, g, recoff);
+ break;
+ case OLNOT:
+ n->n1 = tracegen(n->n1, g, recoff);
+ break;
+ case OTERN:
+ n->n1 = tracegen(n->n1, g, recoff);
+ n->n2 = tracegen(n->n2, g, recoff);
+ n->n3 = tracegen(n->n3, g, recoff);
+ break;
+ case OCAST:
+ n->n1 = tracegen(n->n1, g, recoff);
+ break;
+ case ORECORD:
+ switch(n->typ->type){
+ case TYPINT:
+ actgradd(g, (DTAct){ACTTRACE, codegen(n->n1), n->typ->size});
+ break;
+ case TYPSTRING:
+ actgradd(g, (DTAct){ACTTRACESTR, codegen(n->n1), n->typ->size});
+ break;
+ default:
+ sysfatal("tracegen: don't know how to record %τ", n->typ);
+ }
+ n->num = *recoff;
+ *recoff += n->typ->size;
+ return n;
+ default: sysfatal("tracegen: unknown type %α", n->type); return nil;
+ }
+ return n;
+}
diff --git a/sys/src/cmd/dtracy/dat.h b/sys/src/cmd/dtracy/dat.h
new file mode 100644
index 000000000..fc6737b4c
--- /dev/null
+++ b/sys/src/cmd/dtracy/dat.h
@@ -0,0 +1,123 @@
+typedef struct Node Node;
+typedef struct Symbol Symbol;
+typedef struct SymTab SymTab;
+typedef struct Clause Clause;
+typedef struct Enab Enab;
+typedef struct Stat Stat;
+typedef struct Type Type;
+
+enum {
+ SYMHASH = 256,
+};
+
+struct Type {
+ enum {
+ TYPINVAL,
+ TYPINT,
+ TYPPTR,
+ TYPSTRING,
+ } type;
+ int size;
+ uchar sign;
+ Type *ref;
+ Type *typenext;
+};
+
+struct Symbol {
+ enum {
+ SYMNONE,
+ SYMVAR,
+ } type;
+ char *name;
+ int idx;
+ Symbol *next;
+ Type *typ;
+};
+
+struct SymTab {
+ Symbol *sym[SYMHASH];
+};
+
+struct Node {
+ enum {
+ OINVAL,
+ OSYM,
+ ONUM,
+ OSTR,
+ OBIN,
+ OLNOT,
+ OTERN,
+ ORECORD,
+ OCAST,
+ } type;
+ enum {
+ OPINVAL,
+ OPADD,
+ OPSUB,
+ OPMUL,
+ OPDIV,
+ OPMOD,
+ OPAND,
+ OPOR,
+ OPXOR,
+ OPLSH,
+ OPRSH,
+ OPEQ,
+ OPNE,
+ OPLT,
+ OPLE,
+ OPLAND,
+ OPLOR,
+ OPXNOR,
+ } op;
+ Node *n1, *n2, *n3;
+ Symbol *sym;
+ char *str;
+ s64int num;
+
+ /* used by elidecasts() */
+ char databits;
+ enum {UPZX, UPSX} upper;
+
+ int recsize;
+
+ Type *typ;
+};
+
+struct Stat {
+ enum {
+ STATEXPR,
+ STATPRINT,
+ STATPRINTF,
+ } type;
+ Node *n;
+ int narg;
+ Node **arg;
+};
+
+struct Clause {
+ int id;
+ Stat *stats;
+ int nstats;
+ char **probs;
+ int nprob;
+ DTExpr *pred;
+};
+
+struct Enab {
+ int epid;
+ int reclen;
+ char *probe;
+ Clause *cl;
+ Enab *next;
+};
+
+extern int errors;
+
+#pragma varargck type "α" int
+#pragma varargck type "t" int
+#pragma varargck type "τ" Type *
+#pragma varargck type "ε" Node *
+#pragma varargck argpos error 1
+
+extern int dflag;
diff --git a/sys/src/cmd/dtracy/dtracy.c b/sys/src/cmd/dtracy/dtracy.c
new file mode 100644
index 000000000..8585180f2
--- /dev/null
+++ b/sys/src/cmd/dtracy/dtracy.c
@@ -0,0 +1,224 @@
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+char *dtracyroot = "#Δ";
+int dtracyno;
+int ctlfd, buffd;
+
+int
+min(int a, int b)
+{
+ return a < b ? a : b;
+}
+
+int
+max(int a, int b)
+{
+ return a < b ? b : a;
+}
+
+void *
+emalloc(ulong n)
+{
+ void *v;
+
+ v = malloc(n);
+ if(v == nil) sysfatal("malloc: %r");
+ memset(v, 0, n);
+ setmalloctag(v, getcallerpc(&n));
+ return v;
+}
+
+void *
+erealloc(void *v, ulong n)
+{
+ v = realloc(v, n);
+ if(n != 0){
+ if(v == nil) sysfatal("realloc: %r");
+ setrealloctag(v, getcallerpc(&v));
+ }
+ return v;
+}
+
+void *
+dtmalloc(ulong n)
+{
+ return emalloc(n);
+}
+
+void
+dtfree(void *v)
+{
+ free(v);
+}
+
+void
+defvar(char *name, int idx, Type *ty)
+{
+ Symbol *s;
+
+ s = getsym(name);
+ s->type = SYMVAR;
+ s->idx = idx;
+ s->typ = ty;
+}
+
+void
+globvars(void)
+{
+ defvar("arg0", DTV_ARG0, type(TYPINT, 8, 1));
+ defvar("arg1", DTV_ARG1, type(TYPINT, 8, 1));
+ defvar("arg2", DTV_ARG2, type(TYPINT, 8, 1));
+ defvar("arg3", DTV_ARG3, type(TYPINT, 8, 1));
+ defvar("arg4", DTV_ARG4, type(TYPINT, 8, 1));
+ defvar("arg5", DTV_ARG5, type(TYPINT, 8, 1));
+ defvar("arg6", DTV_ARG6, type(TYPINT, 8, 1));
+ defvar("arg7", DTV_ARG7, type(TYPINT, 8, 1));
+ defvar("arg8", DTV_ARG8, type(TYPINT, 8, 1));
+ defvar("arg9", DTV_ARG9, type(TYPINT, 8, 1));
+ defvar("pid", DTV_PID, type(TYPINT, 4, 1));
+ defvar("machno", DTV_MACHNO, type(TYPINT, 4, 1));
+ defvar("time", DTV_TIME, type(TYPINT, 8, 0));
+ defvar("probe", DTV_PROBE, type(TYPSTRING));
+}
+
+int
+setup(void)
+{
+ char buf[512];
+ char *p;
+ int n;
+
+ snprint(buf, sizeof(buf), "%s/clone", dtracyroot);
+ ctlfd = open(buf, ORDWR);
+ if(ctlfd < 0) return -1;
+ n = read(ctlfd, buf, sizeof(buf) - 1);
+ if(n < 0) return -1;
+ buf[n] = 0;
+ dtracyno = strtol(buf, &p, 10);
+ if(p == buf || *p != 0){
+ werrstr("expected number reading from ctl");
+ return -1;
+ }
+ snprint(buf, sizeof(buf), "%s/%d/buf", dtracyroot, dtracyno);
+ buffd = open(buf, OREAD);
+ if(buffd < 0) return -1;
+ return 0;
+}
+
+int
+progcopy(void)
+{
+ char buf[512];
+ int fd;
+ char *prog;
+ Fmt f;
+
+ fmtstrinit(&f);
+ packclauses(&f);
+ prog = fmtstrflush(&f);
+ snprint(buf, sizeof(buf), "%s/%d/prog", dtracyroot, dtracyno);
+ fd = open(buf, OWRITE);
+ if(fd < 0) return -1;
+ if(write(fd, prog, strlen(prog)) < 0){
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+int
+epidread(void)
+{
+ char buf[512];
+ Biobuf *bp;
+ char *s;
+ char *f[5];
+ int a, b, c;
+
+ snprint(buf, sizeof(buf), "%s/%d/epid", dtracyroot, dtracyno);
+ bp = Bopen(buf, OREAD);
+ if(bp == nil) return -1;
+ for(; s = Brdstr(bp, '\n', 1), s != nil; free(s)){
+ if(tokenize(s, f, nelem(f)) != 4)
+ goto err;
+ a = atoi(f[0]);
+ b = atoi(f[1]);
+ c = atoi(f[2]);
+ addepid(a, b, c, f[3]);
+ }
+ return 0;
+err:
+ werrstr("epidread: invalid format");
+ free(s);
+ return -1;
+
+}
+
+void
+bufread(Biobuf *bp)
+{
+ static uchar buf[65536];
+ int n;
+
+ n = read(buffd, buf, sizeof(buf));
+ if(n < 0) sysfatal("bufread: %r");
+ if(parsebuf(buf, n, bp) < 0)
+ sysfatal("parsebuf: %r");
+ Bflush(bp);
+}
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [ -cd ] script\n", argv0);
+ exits("usage");
+}
+
+int dflag;
+
+void
+main(int argc, char **argv)
+{
+ Biobuf *out;
+
+ dflag = 0;
+ ARGBEGIN {
+ case 'd': dflag = 1; break;
+ default: usage();
+ } ARGEND;
+ if(argc != 1) usage();
+
+ fmtinstall(L'α', nodetfmt);
+ fmtinstall('t', typetfmt);
+ fmtinstall(L'I', dtefmt);
+ fmtinstall(L'τ', typefmt);
+ fmtinstall(L'ε', nodefmt);
+ lexinit();
+ lexstring(argv[0]);
+ globvars();
+ yyparse();
+ if(errors != 0)
+ exits("errors");
+ if(dflag)
+ dump();
+ else{
+ if(setup() < 0)
+ sysfatal("setup: %r");
+ if(progcopy() < 0)
+ sysfatal("progcopy: %r");
+ if(epidread() < 0)
+ sysfatal("epidread: %r");
+ fprint(ctlfd, "go");
+ out = Bfdopen(1, OWRITE);
+ if(out == nil) sysfatal("Bfdopen: %r");
+ for(;;)
+ bufread(out);
+ }
+ exits(nil);
+}
diff --git a/sys/src/cmd/dtracy/fns.h b/sys/src/cmd/dtracy/fns.h
new file mode 100644
index 000000000..f418e4f34
--- /dev/null
+++ b/sys/src/cmd/dtracy/fns.h
@@ -0,0 +1,35 @@
+void yyerror(char *);
+int yylex(void);
+int yyparse(void);
+Node *node(int, ...);
+int nodetfmt(Fmt *);
+int typetfmt(Fmt *);
+int typefmt(Fmt *);
+int nodefmt(Fmt *);
+void *emalloc(ulong);
+void *erealloc(void *, ulong);
+DTAct action(int, DTExpr *);
+DTExpr *codegen(Node *);
+void error(char *, ...);
+Symbol *getsym(char *);
+void lexinit(void);
+void lexstring(char *);
+void clausebegin(void);
+void addstat(int, ...);
+void addarg(Node *);
+void addprobe(char *);
+void addpred(DTExpr *);
+void clauseend(void);
+void packclauses(Fmt *);
+void addepid(u32int, u32int, int, char*);
+int parsebuf(uchar *, int, Biobuf*);
+Node *tracegen(Node *, DTActGr *, int *);
+void actgradd(DTActGr *, DTAct);
+void needruntime(Node *);
+void dump(void);
+vlong evalop(int, int, vlong, vlong);
+Node *exprcheck(Node *, int);
+Type *type(int, ...);
+int min(int, int);
+int max(int, int);
+Node *addtype(Type *, Node *);
diff --git a/sys/src/cmd/dtracy/lex.c b/sys/src/cmd/dtracy/lex.c
new file mode 100644
index 000000000..15a959ffa
--- /dev/null
+++ b/sys/src/cmd/dtracy/lex.c
@@ -0,0 +1,390 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <dtracy.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+#include "y.tab.h"
+
+char *str, *strp;
+int lineno = 1;
+int errors;
+
+typedef struct Keyword Keyword;
+struct Keyword {
+ char *name;
+ int tok;
+};
+/* both tables must be sorted */
+Keyword kwtab[] = {
+ "if", TIF,
+ "print", TPRINT,
+ "printf", TPRINTF,
+ "s16", TS16,
+ "s32", TS32,
+ "s64", TS64,
+ "s8", TS8,
+ "string", TSTRING,
+ "u16", TU16,
+ "u32", TU32,
+ "u64", TU64,
+ "u8", TU8,
+};
+Keyword optab[] = {
+ "!=", TNE,
+ "&&", TAND,
+ "<<", TLSL,
+ "<=", TLE,
+ "==", TEQ,
+ ">=", TGE,
+ ">>", TLSR,
+ "||", TOR,
+};
+Keyword *kwchar[128], *opchar[128];
+
+void
+lexinit(void)
+{
+ Keyword *kw;
+
+ for(kw = kwtab; kw < kwtab + nelem(kwtab); kw++)
+ if(kwchar[*kw->name] == nil)
+ kwchar[*kw->name] = kw;
+ for(kw = optab; kw < optab + nelem(optab); kw++)
+ if(opchar[*kw->name] == nil)
+ opchar[*kw->name] = kw;
+}
+
+void
+lexstring(char *s)
+{
+ str = strp = s;
+}
+
+void
+error(char *fmt, ...)
+{
+ Fmt f;
+ char buf[128];
+ va_list va;
+
+ fmtfdinit(&f, 2, buf, sizeof(buf));
+ fmtprint(&f, "%d ", lineno);
+ va_start(va, fmt);
+ fmtvprint(&f, fmt, va);
+ fmtrune(&f, '\n');
+ va_end(va);
+ fmtfdflush(&f);
+ errors++;
+}
+
+void
+yyerror(char *msg)
+{
+ error("%s", msg);
+}
+
+static int
+getch(void)
+{
+ if(*strp == 0) return -1;
+ return *strp++;
+}
+
+static void
+ungetch(void)
+{
+ assert(strp > str);
+ if(*strp != 0)
+ strp--;
+}
+
+int
+yylex(void)
+{
+ int ch;
+ static char buf[512];
+ char *p;
+ Keyword *kw;
+ u64int v;
+
+again:
+ while(ch = getch(), ch >= 0 && isspace(ch)){
+ if(ch == '\n')
+ lineno++;
+ }
+ if(ch < 0)
+ return -1;
+ if(ch == '/'){
+ ch = getch();
+ if(ch == '/'){
+ while(ch = getch(), ch >= 0 && ch != '\n')
+ ;
+ if(ch == '\n')
+ lineno++;
+ goto again;
+ }
+ if(ch == '*'){
+ s1:
+ ch = getch();
+ if(ch < 0) return -1;
+ if(ch == '\n') lineno++;
+ if(ch != '*') goto s1;
+ s2:
+ ch = getch();
+ if(ch < 0) return -1;
+ if(ch == '\n') lineno++;
+ if(ch == '*') goto s2;
+ if(ch != '/') goto s1;
+ goto again;
+ }
+ ungetch();
+ return '/';
+ }
+ if(isalnum(ch) || ch == '_' || ch >= 0x80 || ch == ':'){
+ p = buf;
+ *p++ = ch;
+ while(ch = getch(), isalnum(ch) || ch == '_' || ch >= 0x80 || ch == ':')
+ if(p < buf + sizeof(buf) - 1)
+ *p++ = ch;
+ *p = 0;
+ ungetch();
+ v = strtoull(buf, &p, 0);
+ if(p != buf && *p == 0){
+ yylval.num = v;
+ return TNUM;
+ }
+ if(strcmp(buf, ":") == 0)
+ return ':';
+ if((uchar)buf[0] < 0x80 && kwchar[buf[0]] != nil)
+ for(kw = kwchar[buf[0]]; kw < kwtab + nelem(kwtab) && kw->name[0] == buf[0]; kw++)
+ if(strcmp(kw->name, buf) == 0)
+ return kw->tok;
+ yylval.sym = getsym(buf);
+ return TSYM;
+ }
+ if(ch == '"'){
+ p = buf;
+ while(ch = getch(), ch >= 0 && ch != '"'){
+ if(ch == '\n')
+ error("unterminated string");
+ if(ch == '\\')
+ switch(ch = getch()){
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'v': ch = '\v'; break;
+ case 'b': ch = '\b'; break;
+ case 'a': ch = '\a'; break;
+ case '"': case '\\': break;
+ default: error("unknown escape code \\%c", ch);
+ }
+ if(p < buf + sizeof(buf) - 1)
+ *p++ = ch;
+ }
+ if(ch < 0) error("unterminated string");
+ *p = 0;
+ yylval.str = strdup(buf);
+ return TSTR;
+ }
+ if(opchar[ch] != nil){
+ buf[0] = ch;
+ buf[1] = getch();
+ for(kw = opchar[buf[0]]; kw < optab + nelem(optab) && kw->name[0] == buf[0]; kw++)
+ if(buf[1] == kw->name[1]){
+ buf[2] = getch();
+ buf[3] = 0;
+ if(kw + 1 < optab + nelem(optab) && strcmp(kw[1].name, buf) == 0)
+ return kw[1].tok;
+ ungetch();
+ return kw->tok;
+ }
+ ungetch();
+ }
+ return ch;
+}
+
+int
+nodetfmt(Fmt *f)
+{
+ int t;
+ static char *nodestr[] = {
+ [OINVAL] "OINVAL",
+ [OBIN] "OBIN",
+ [OLNOT] "OLNOT",
+ [OSYM] "OSYM",
+ [ONUM] "ONUM",
+ [OSTR] "OSTR",
+ [OTERN] "OTERN",
+ [ORECORD] "ORECORD",
+ [OCAST] "OCAST",
+ };
+
+ t = va_arg(f->args, int);
+ if(t >= nelem(nodestr) || nodestr[t] == nil)
+ return fmtprint(f, "??? (%d)", t);
+ else
+ return fmtprint(f, "%s", nodestr[t]);
+}
+
+Node *
+node(int type, ...)
+{
+ va_list va;
+ Node *n;
+
+ n = emalloc(sizeof(Node));
+ n->type = type;
+ va_start(va, type);
+ switch(type){
+ case OBIN:
+ n->op = va_arg(va, int);
+ n->n1 = va_arg(va, Node *);
+ n->n2 = va_arg(va, Node *);
+ break;
+ case OLNOT:
+ n->n1 = va_arg(va, Node *);
+ break;
+ case OSYM:
+ n->sym = va_arg(va, Symbol *);
+ break;
+ case ONUM:
+ n->num = va_arg(va, s64int);
+ break;
+ case OTERN:
+ n->n1 = va_arg(va, Node *);
+ n->n2 = va_arg(va, Node *);
+ n->n3 = va_arg(va, Node *);
+ break;
+ case ORECORD:
+ n->n1 = va_arg(va, Node *);
+ break;
+ case OCAST:
+ n->typ = va_arg(va, Type *);
+ n->n1 = va_arg(va, Node *);
+ break;
+ case OSTR:
+ n->str = va_arg(va, char *);
+ break;
+ default:
+ sysfatal("node: unknown type %α", type);
+ }
+ va_end(va);
+ return n;
+}
+
+SymTab globals;
+
+static u64int
+hash(char *s)
+{
+ u64int h;
+
+ h = 0xcbf29ce484222325ULL;
+ for(; *s != 0; s++){
+ h ^= *s;
+ h *= 0x100000001b3ULL;
+ }
+ return h;
+}
+
+Symbol *
+getsym(char *name)
+{
+ u64int h;
+ Symbol **sp, *s;
+
+ h = hash(name);
+ for(sp = &globals.sym[h % SYMHASH]; s = *sp, s != nil; sp = &s->next)
+ if(strcmp(s->name, name) == 0)
+ return s;
+ *sp = s = emalloc(sizeof(Symbol));
+ s->name = strdup(name);
+ return s;
+}
+
+int
+typetfmt(Fmt *f)
+{
+ int t;
+ static char *tstr[] = {
+ [TYPINVAL] "TYPINVAL",
+ [TYPINT] "TYPINT",
+ [TYPPTR] "TYPPTR",
+ [TYPSTRING] "TYPSTRING",
+ };
+
+ t = va_arg(f->args, int);
+ if(t >= nelem(tstr) || tstr[t] == nil)
+ return fmtprint(f, "??? (%d)", t);
+ else
+ return fmtprint(f, "%s", tstr[t]);
+}
+
+int
+typefmt(Fmt *f)
+{
+ Type *t;
+
+ t = va_arg(f->args, Type *);
+ switch(t->type){
+ case TYPINT: return fmtprint(f, "%c%d", t->sign ? 's' : 'u', t->size * 8);
+ case TYPSTRING: return fmtprint(f, "string");
+ case TYPPTR: return fmtprint(f, "%τ*", t->ref);
+ default: return fmtprint(f, "%t", t->type);
+ }
+}
+
+static Type typu8 = {.type TYPINT, .size 1, .sign 0};
+static Type typs8 = {.type TYPINT, .size 1, .sign 1};
+static Type typu16 = {.type TYPINT, .size 2, .sign 0};
+static Type typs16 = {.type TYPINT, .size 2, .sign 1};
+static Type typu32 = {.type TYPINT, .size 4, .sign 0};
+static Type typs32 = {.type TYPINT, .size 4, .sign 1};
+static Type typu64 = {.type TYPINT, .size 8, .sign 0};
+static Type typs64 = {.type TYPINT, .size 8, .sign 1};
+static Type typstr = {.type TYPSTRING, .size DTSTRMAX };
+static Type *typereg;
+
+static Type *
+mkptr(Type *t)
+{
+ Type *s;
+
+ for(s = typereg; s != nil; s = s->typenext)
+ if(s->type == TYPPTR && s->ref == t)
+ return s;
+ s = emalloc(sizeof(Type));
+ s->type = TYPPTR;
+ s->ref = t;
+ return s;
+}
+
+Type *
+type(int typ, ...)
+{
+ int size, sign;
+ va_list va;
+
+ va_start(va, typ);
+ switch(typ){
+ case TYPINT:
+ size = va_arg(va, int);
+ sign = va_arg(va, int);
+ switch(size << 4 | sign){
+ case 0x10: return &typu8;
+ case 0x11: return &typs8;
+ case 0x20: return &typu16;
+ case 0x21: return &typs16;
+ case 0x40: return &typu32;
+ case 0x41: return &typs32;
+ case 0x80: return &typu64;
+ case 0x81: return &typs64;
+ default: sysfatal("type: invalid (size,sign) = (%d,%d)\n", size, sign); return nil;
+ }
+ case TYPSTRING: return &typstr;
+ case TYPPTR: return mkptr(va_arg(va, Type *));
+ default: sysfatal("type: unknown %t", typ); return nil;
+ }
+}
diff --git a/sys/src/cmd/dtracy/lint.rc b/sys/src/cmd/dtracy/lint.rc
new file mode 100755
index 000000000..cafbdd835
--- /dev/null
+++ b/sys/src/cmd/dtracy/lint.rc
@@ -0,0 +1,20 @@
+#!/bin/rc
+# check for full switch statements
+
+nl='
+'
+nodetypes=`''{sed -n '/OINVAL/,/}/ s/,//p' dat.h | sed 's/[ ]*//g; /^$/d' | sort | grep -v '^OINVAL$'}
+switches=`$nl{grep -n '/\*nodetype\*/' *.[ch]}
+for(l in $switches){
+ f=`:{echo $l}
+ a=`$nl{sed -n $f(2)^'s/[^ ].*//p' $f(1)}
+ comm -23 <{echo $nodetypes} <{sed -n $f(2)^',/^'^$a^'}/ s/^'^$a^'case ([^:]*):.*/\1/p' $f(1) | sort} | sed -n 's/.+/'^$f(1)^':'^$f(2)^' missing &/p'
+}
+
+oper=`''{sed -n '/OPINVAL/,/}/ s/,//p' dat.h | sed 's/[ ]*//g; /^$/d' | sort | grep -v '^OPINVAL$'}
+switches=`$nl{grep -n '/\*oper\*/' *.[ch]}
+for(l in $switches){
+ f=`:{echo $l}
+ a=`$nl{sed -n $f(2)^'s/[^ ].*//p' $f(1)}
+ comm -23 <{echo $oper} <{sed -n $f(2)^',/^'^$a^'}/ s/^'^$a^'case ([^:]*):.*/\1/p' $f(1) | sort} | sed -n 's/.+/'^$f(1)^':'^$f(2)^' missing &/p'
+}
diff --git a/sys/src/cmd/dtracy/mkfile b/sys/src/cmd/dtracy/mkfile
new file mode 100644
index 000000000..7d1b4b45c
--- /dev/null
+++ b/sys/src/cmd/dtracy/mkfile
@@ -0,0 +1,22 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=dtracy
+OFILES=\
+ dtracy.$O\
+ lex.$O\
+ y.tab.$O\
+ cgen.$O\
+ act.$O\
+ type.$O\
+
+YFILES=parse.y
+
+HFILES=\
+ dat.h\
+ fns.h\
+
+</sys/src/cmd/mkone
+
+YFLAGS=-v -d -D1
+lex.$O: y.tab.h
diff --git a/sys/src/cmd/dtracy/parse.y b/sys/src/cmd/dtracy/parse.y
new file mode 100644
index 000000000..9f231dbc9
--- /dev/null
+++ b/sys/src/cmd/dtracy/parse.y
@@ -0,0 +1,123 @@
+%{
+#include <u.h>
+#include <libc.h>
+#include <dtracy.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+%}
+
+%union{
+ Node *n;
+ Symbol *sym;
+ DTExpr *e;
+ s64int num;
+ char *str;
+ Type *t;
+}
+
+%type <n> expr
+%type <t> type
+
+%token <sym> TSYM
+%token <num> TNUM
+%token <str> TSTR
+%token TPRINT TPRINTF
+%token TIF
+%token TU8 TU16 TU32 TU64
+%token TS8 TS16 TS32 TS64
+%token TSTRING
+
+%right '?'
+%left TOR
+%left TAND
+%left '|'
+%left '^'
+%left '&'
+%left TEQ TNE
+%left '<' '>' TLE TGE
+%left TLSL TLSR
+%left '+' '-'
+%left '*' '/' '%'
+%left unary
+%right castprec
+
+%%
+
+program: | program clause
+
+clause: { clausebegin(); } probes optpredicate optaction { clauseend(); }
+
+optpredicate: | TIF expr { addpred(codegen(exprcheck($2, 1))); }
+
+optaction:
+ {
+ addstat(STATPRINT);
+ addarg(node(OSYM, getsym("probe")));
+ }
+ | action
+action: '{' stats '}'
+stats: | stats0 | stats0 ';'
+stats0: stat | stats0 ';' stat
+
+stat: expr { addstat(STATEXPR, exprcheck($1, 0)); }
+| TPRINT { addstat(STATPRINT); } pelist
+| TPRINTF { addstat(STATPRINTF); } pelist
+
+
+pelist:
+ '(' ')'
+ | '(' arg ',' ')'
+ | '(' elist2 optcomma ')'
+ | arg optcomma
+ | elist2 optcomma
+elist2: arg ',' arg | elist2 ',' arg
+arg: expr { addarg(exprcheck($1, 0)); }
+optcomma: | ','
+
+expr:
+ TSYM { $$ = node(OSYM, $1); }
+ | TNUM { $$ = node(ONUM, $1); }
+ | TSTR { $$ = node(OSTR, $1); }
+ | expr '+' expr { $$ = node(OBIN, OPADD, $1, $3); }
+ | expr '-' expr { $$ = node(OBIN, OPSUB, $1, $3); }
+ | expr '*' expr { $$ = node(OBIN, OPMUL, $1, $3); }
+ | expr '/' expr { $$ = node(OBIN, OPDIV, $1, $3); }
+ | expr '%' expr { $$ = node(OBIN, OPMOD, $1, $3); }
+ | expr '&' expr { $$ = node(OBIN, OPAND, $1, $3); }
+ | expr '|' expr { $$ = node(OBIN, OPOR, $1, $3); }
+ | expr '^' expr { $$ = node(OBIN, OPXOR, $1, $3); }
+ | expr TLSL expr { $$ = node(OBIN, OPLSH, $1, $3); }
+ | expr TLSR expr { $$ = node(OBIN, OPRSH, $1, $3); }
+ | expr TEQ expr { $$ = node(OBIN, OPEQ, $1, $3); }
+ | expr TNE expr { $$ = node(OBIN, OPNE, $1, $3); }
+ | expr '<' expr { $$ = node(OBIN, OPLT, $1, $3); }
+ | expr TLE expr { $$ = node(OBIN, OPLE, $1, $3); }
+ | expr '>' expr { $$ = node(OBIN, OPLT, $3, $1); }
+ | expr TGE expr { $$ = node(OBIN, OPLE, $3, $1); }
+ | expr TAND expr { $$ = node(OBIN, OPLAND, $1, $3); }
+ | expr TOR expr { $$ = node(OBIN, OPLOR, $1, $3); }
+ | '-' expr %prec unary { $$ = node(OBIN, OPSUB, node(ONUM, 0LL), $2); }
+ | '~' expr %prec unary { $$ = node(OBIN, OPXNOR, node(ONUM, 0LL), $2); }
+ | '!' expr %prec unary { $$ = node(OLNOT, $2); }
+ | '(' expr ')' { $$ = $2; }
+ | expr '?' expr ':' expr %prec '?' { $$ = node(OTERN, $1, $3, $5); }
+ | '(' type ')' expr %prec castprec { $$ = node(OCAST, $2, $4); }
+
+type:
+ TU8 { $$ = type(TYPINT, 1, 0); }
+ | TS8 { $$ = type(TYPINT, 1, 1); }
+ | TU16 { $$ = type(TYPINT, 2, 0); }
+ | TS16 { $$ = type(TYPINT, 2, 1); }
+ | TU32 { $$ = type(TYPINT, 4, 0); }
+ | TS32 { $$ = type(TYPINT, 4, 1); }
+ | TU64 { $$ = type(TYPINT, 8, 0); }
+ | TS64 { $$ = type(TYPINT, 8, 1); }
+ | TSTRING { $$ = type(TYPSTRING); }
+
+probes:
+ TSYM { addprobe($1->name); }
+ | probes ',' TSYM { addprobe($3->name); }
+
+
+%%
diff --git a/sys/src/cmd/dtracy/type.c b/sys/src/cmd/dtracy/type.c
new file mode 100644
index 000000000..fdf7872b6
--- /dev/null
+++ b/sys/src/cmd/dtracy/type.c
@@ -0,0 +1,529 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <dtracy.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+Node *
+icast(int sign, int size, Node *n)
+{
+ Type *t;
+
+ t = type(TYPINT, sign, size);
+ return node(OCAST, t, n);
+}
+
+/*
+ the type checker checks types.
+ the result is an expression that is correct if evaluated with 64-bit operands all the way.
+ to maintain c-like semantics, this means adding casts all over the place, which will get optimised later.
+
+ note we use kencc, NOT ansi c, semantics for unsigned.
+*/
+
+Node *
+typecheck(Node *n)
+{
+ int s1, s2, sign;
+
+ switch(/*nodetype*/n->type){
+ case OSYM:
+ switch(n->sym->type){
+ case SYMNONE: error("undeclared '%s'", n->sym->name); break;
+ case SYMVAR: n->typ = n->sym->typ; break;
+ default: sysfatal("typecheck: unknown symbol type %d", n->sym->type);
+ }
+ break;
+ case ONUM:
+ if((vlong)n->num >= -0x80000000LL && (vlong)n->num <= 0x7fffffffLL)
+ n->typ = type(TYPINT, 4, 1);
+ else
+ n->typ = type(TYPINT, 8, 1);
+ break;
+ case OSTR:
+ n->typ = type(TYPSTRING);
+ break;
+ case OBIN:
+ n->n1 = typecheck(n->n1);
+ n->n2 = typecheck(n->n2);
+ if(n->n1->typ == nil || n->n2->typ == nil)
+ break;
+ if(n->n1->typ->type != TYPINT){
+ error("%τ not allowed in operation", n->n1->typ);
+ break;
+ }
+ if(n->n2->typ->type != TYPINT){
+ error("%τ not allowed in operation", n->n2->typ);
+ break;
+ }
+ s1 = n->n1->typ->size;
+ s2 = n->n2->typ->size;
+ sign = n->n1->typ->sign && n->n2->typ->sign;
+ switch(n->op){
+ case OPADD:
+ case OPSUB:
+ case OPMUL:
+ case OPDIV:
+ case OPMOD:
+ case OPAND:
+ case OPOR:
+ case OPXOR:
+ case OPXNOR:
+ n->typ = type(TYPINT, 8, sign);
+ if(s1 > 4 || s2 > 4){
+ n->n1 = icast(8, sign, n->n1);
+ n->n2 = icast(8, sign, n->n2);
+ return n;
+ }else{
+ n->n1 = icast(4, sign, n->n1);
+ n->n2 = icast(4, sign, n->n2);
+ return icast(4, sign, n);
+ }
+ case OPEQ:
+ case OPNE:
+ case OPLT:
+ case OPLE:
+ n->typ = type(TYPINT, 4, sign);
+ if(s1 > 4 || s2 > 4){
+ n->n1 = icast(8, sign, n->n1);
+ n->n2 = icast(8, sign, n->n2);
+ return n;
+ }else{
+ n->n1 = icast(4, sign, n->n1);
+ n->n2 = icast(4, sign, n->n2);
+ return n;
+ }
+ case OPLAND:
+ case OPLOR:
+ n->typ = type(TYPINT, 4, sign);
+ return n;
+ case OPLSH:
+ case OPRSH:
+ if(n->n1->typ->size <= 4)
+ n->n1 = icast(4, n->n1->typ->sign, n->n1);
+ n->typ = n->n1->typ;
+ return icast(n->typ->size, n->typ->sign, n);
+ default:
+ sysfatal("typecheck: unknown op %d", n->op);
+ }
+ break;
+ case OCAST:
+ n->n1 = typecheck(n->n1);
+ if(n->n1->typ == nil)
+ break;
+ if(n->typ->type == TYPINT && n->n1->typ->type == TYPINT){
+ }else if(n->typ == n->n1->typ){
+ }else if(n->typ->type == TYPSTRING && n->n1->typ->type == TYPINT){
+ }else
+ error("can't cast from %τ to %τ", n->n1->typ, n->typ);
+ break;
+ case OLNOT:
+ n->n1 = typecheck(n->n1);
+ if(n->n1->typ == nil)
+ break;
+ if(n->n1->typ->type != TYPINT){
+ error("%τ not allowed in operation", n->n1->typ);
+ break;
+ }
+ n->typ = type(TYPINT, 4, 1);
+ break;
+ case OTERN:
+ n->n1 = typecheck(n->n1);
+ n->n2 = typecheck(n->n2);
+ n->n3 = typecheck(n->n3);
+ if(n->n1->typ == nil || n->n2->typ == nil || n->n3->typ == nil)
+ break;
+ if(n->n1->typ->type != TYPINT){
+ error("%τ not allowed in operation", n->n1->typ);
+ break;
+ }
+ if(n->n2->typ->type == TYPINT || n->n3->typ->type == TYPINT){
+ sign = n->n2->typ->sign && n->n3->typ->sign;
+ s1 = n->n2->typ->size;
+ s2 = n->n3->typ->size;
+ if(s1 > 4 || s2 > 4){
+ n->n2 = icast(8, sign, n->n2);
+ n->n3 = icast(8, sign, n->n3);
+ n->typ = type(TYPINT, 8, sign);
+ return n;
+ }else{
+ n->n2 = icast(4, sign, n->n2);
+ n->n3 = icast(4, sign, n->n3);
+ n->typ = type(TYPINT, 4, sign);
+ return n;
+ }
+ }else if(n->n2->typ == n->n3->typ){
+ n->typ = n->n2->typ;
+ }else
+ error("don't know how to do ternary with %τ and %τ", n->n2->typ, n->n3->typ);
+ break;
+ case ORECORD:
+ default: sysfatal("typecheck: unknown node type %α", n->type);
+ }
+ return n;
+}
+
+vlong
+evalop(int op, int sign, vlong v1, vlong v2)
+{
+ switch(/*oper*/op){
+ case OPADD: return v1 + v2; break;
+ case OPSUB: return v1 - v2; break;
+ case OPMUL: return v1 * v2; break;
+ case OPDIV: if(v2 == 0) sysfatal("division by zero"); return sign ? v1 / v2 : (uvlong)v1 / (uvlong)v2; break;
+ case OPMOD: if(v2 == 0) sysfatal("division by zero"); return sign ? v1 % v2 : (uvlong)v1 % (uvlong)v2; break;
+ case OPAND: return v1 & v2; break;
+ case OPOR: return v1 | v2; break;
+ case OPXOR: return v1 ^ v2; break;
+ case OPXNOR: return ~(v1 ^ v2); break;
+ case OPLSH:
+ if((u64int)v2 >= 64)
+ return 0;
+ else
+ return v1 << v2;
+ break;
+ case OPRSH:
+ if(sign){
+ if((u64int)v2 >= 64)
+ return v1 >> 63;
+ else
+ return v1 >> v2;
+ }else{
+ if((u64int)v2 >= 64)
+ return 0;
+ else
+ return (u64int)v1 >> v2;
+ }
+ break;
+ case OPEQ: return v1 == v2; break;
+ case OPNE: return v1 != v2; break;
+ case OPLT: return v1 < v2; break;
+ case OPLE: return v1 <= v2; break;
+ case OPLAND: return v1 && v2; break;
+ case OPLOR: return v1 || v2; break;
+ default:
+ sysfatal("cfold: unknown op %.2x", op);
+ return 0;
+ }
+
+}
+
+Node *
+addtype(Type *t, Node *n)
+{
+ n->typ = t;
+ return n;
+}
+
+/* fold constants */
+
+static Node *
+cfold(Node *n)
+{
+ switch(/*nodetype*/n->type){
+ case ONUM:
+ case OSYM:
+ case OSTR:
+ return n;
+ case OBIN:
+ n->n1 = cfold(n->n1);
+ n->n2 = cfold(n->n2);
+ if(n->n1->type != ONUM || n->n2->type != ONUM)
+ return n;
+ return addtype(n->typ, node(ONUM, evalop(n->op, n->typ->sign, n->n1->num, n->n2->num)));
+ case OLNOT:
+ n->n1 = cfold(n->n1);
+ if(n->n1->type == ONUM)
+ return addtype(n->typ, node(ONUM, !n->n1->num));
+ return n;
+ case OTERN:
+ n->n1 = cfold(n->n1);
+ n->n2 = cfold(n->n2);
+ n->n3 = cfold(n->n3);
+ if(n->n1->type == ONUM)
+ return n->n1->num ? n->n2 : n->n3;
+ return n;
+ case OCAST:
+ n->n1 = cfold(n->n1);
+ if(n->n1->type != ONUM || n->typ->type != TYPINT)
+ return n;
+ switch(n->typ->size << 4 | n->typ->sign){
+ case 0x10: return addtype(n->typ, node(ONUM, (vlong)(u8int)n->n1->num));
+ case 0x11: return addtype(n->typ, node(ONUM, (vlong)(s8int)n->n1->num));
+ case 0x20: return addtype(n->typ, node(ONUM, (vlong)(u16int)n->n1->num));
+ case 0x21: return addtype(n->typ, node(ONUM, (vlong)(s16int)n->n1->num));
+ case 0x40: return addtype(n->typ, node(ONUM, (vlong)(u32int)n->n1->num));
+ case 0x41: return addtype(n->typ, node(ONUM, (vlong)(s32int)n->n1->num));
+ case 0x80: return addtype(n->typ, node(ONUM, n->n1->num));
+ case 0x81: return addtype(n->typ, node(ONUM, n->n1->num));
+ }
+ return n;
+ case ORECORD:
+ default:
+ fprint(2, "cfold: unknown type %α\n", n->type);
+ return n;
+ }
+}
+
+/* calculate the minimum record size for each node of the expression */
+static Node *
+calcrecsize(Node *n)
+{
+ switch(/*nodetype*/n->type){
+ case ONUM:
+ case OSTR:
+ n->recsize = 0;
+ break;
+ case OSYM:
+ switch(n->sym->type){
+ case SYMVAR:
+ switch(n->sym->idx){
+ case DTV_TIME:
+ case DTV_PROBE:
+ n->recsize = 0;
+ break;
+ default:
+ n->recsize = n->typ->size;
+ break;
+ }
+ break;
+ default: sysfatal("calcrecsize: unknown symbol type %d", n->sym->type); return nil;
+ }
+ break;
+ case OBIN:
+ n->n1 = calcrecsize(n->n1);
+ n->n2 = calcrecsize(n->n2);
+ n->recsize = min(n->typ->size, n->n1->recsize + n->n2->recsize);
+ break;
+ case OLNOT:
+ n->n1 = calcrecsize(n->n1);
+ n->recsize = min(n->typ->size, n->n1->recsize);
+ break;
+ case OCAST:
+ n->n1 = calcrecsize(n->n1);
+ if(n->typ->type == TYPSTRING)
+ n->recsize = n->typ->size;
+ else
+ n->recsize = min(n->typ->size, n->n1->recsize);
+ break;
+ case OTERN:
+ n->n1 = calcrecsize(n->n1);
+ n->n2 = calcrecsize(n->n2);
+ n->n3 = calcrecsize(n->n3);
+ n->recsize = min(n->typ->size, n->n1->recsize + n->n2->recsize + n->n3->recsize);
+ break;
+ case ORECORD:
+ default: sysfatal("calcrecsize: unknown type %α", n->type); return nil;
+ }
+ return n;
+}
+
+/* insert ORECORD nodes to mark the subexpression that we will pass to the kernel */
+static Node *
+insrecord(Node *n)
+{
+ if(n->recsize == 0)
+ return n;
+ if(n->typ->size == n->recsize)
+ return addtype(n->typ, node(ORECORD, n));
+ switch(/*nodetype*/n->type){
+ case ONUM:
+ case OSTR:
+ case OSYM:
+ break;
+ case OBIN:
+ n->n1 = insrecord(n->n1);
+ n->n2 = insrecord(n->n2);
+ break;
+ case OLNOT:
+ case OCAST:
+ n->n1 = insrecord(n->n1);
+ break;
+ case OTERN:
+ n->n1 = insrecord(n->n1);
+ n->n2 = insrecord(n->n2);
+ n->n3 = insrecord(n->n3);
+ break;
+ case ORECORD:
+ default: sysfatal("insrecord: unknown type %α", n->type); return nil;
+ }
+ return n;
+}
+
+/*
+ delete useless casts.
+ going down we determine the number of bits (m) needed to be correct at each stage.
+ going back up we determine the number of bits (n->databits) which can be either 0 or 1.
+ all other bits are either zero (n->upper == UPZX) or sign-extended (n->upper == UPSX).
+ note that by number of bits we always mean a consecutive block starting from the LSB.
+
+ we can delete a cast if it either affects only bits not needed (according to m) or
+ if it's a no-op (according to databits, upper).
+*/
+static Node *
+elidecasts(Node *n, int m)
+{
+ switch(/*nodetype*/n->type){
+ case OSTR:
+ return n;
+ case ONUM:
+ n->databits = n->typ->size * 8;
+ n->upper = n->typ->sign ? UPSX : UPZX;
+ break;
+ case OSYM:
+ /* TODO: make less pessimistic */
+ n->databits = 64;
+ break;
+ case OBIN:
+ switch(/*oper*/n->op){
+ case OPADD:
+ case OPSUB:
+ n->n1 = elidecasts(n->n1, m);
+ n->n2 = elidecasts(n->n2, m);
+ n->databits = min(64, max(n->n1->databits, n->n2->databits) + 1);
+ n->upper = n->n1->upper | n->n2->upper;
+ break;
+ case OPMUL:
+ n->n1 = elidecasts(n->n1, m);
+ n->n2 = elidecasts(n->n2, m);
+ n->databits = min(64, n->n1->databits + n->n2->databits);
+ n->upper = n->n1->upper | n->n2->upper;
+ break;
+ case OPAND:
+ case OPOR:
+ case OPXOR:
+ case OPXNOR:
+ n->n1 = elidecasts(n->n1, m);
+ n->n2 = elidecasts(n->n2, m);
+ if(n->op == OPAND && (n->n1->upper == UPZX || n->n2->upper == UPZX)){
+ n->upper = UPZX;
+ if(n->n1->upper == UPZX && n->n2->upper == UPZX)
+ n->databits = min(n->n1->databits, n->n2->databits);
+ else if(n->n1->upper == UPZX)
+ n->databits = n->n1->databits;
+ else
+ n->databits = n->n2->databits;
+ }else{
+ n->databits = max(n->n1->databits, n->n2->databits);
+ n->upper = n->n1->upper | n->n2->upper;
+ }
+ break;
+ case OPLSH:
+ n->n1 = elidecasts(n->n1, m);
+ n->n2 = elidecasts(n->n2, 64);
+ if(n->n2->type == ONUM && n->n2->num >= 0 && n->n1->databits + (uvlong)n->n2->num <= 64)
+ n->databits = n->n1->databits + n->n2->num;
+ else
+ n->databits = 64;
+ n->upper = n->n1->upper;
+ break;
+ case OPRSH:
+ n->n1 = elidecasts(n->n1, 64);
+ n->n2 = elidecasts(n->n2, 64);
+ if(n->n1->upper == n->typ->sign){
+ n->databits = n->n1->databits;
+ n->upper = n->n1->upper;
+ }else{
+ n->databits = 64;
+ n->upper = UPZX;
+ }
+ break;
+ case OPEQ:
+ case OPNE:
+ case OPLT:
+ case OPLE:
+ case OPLAND:
+ case OPLOR:
+ n->n1 = elidecasts(n->n1, 64);
+ n->n2 = elidecasts(n->n2, 64);
+ n->databits = 1;
+ n->upper = UPZX;
+ break;
+ case OPDIV:
+ case OPMOD:
+ default:
+ n->n1 = elidecasts(n->n1, 64);
+ n->n2 = elidecasts(n->n2, 64);
+ n->databits = 64;
+ n->upper = UPZX;
+ break;
+ }
+ break;
+ case OLNOT:
+ n->n1 = elidecasts(n->n1, 64);
+ n->databits = 1;
+ n->upper = UPZX;
+ break;
+ case OCAST:
+ switch(n->typ->type){
+ case TYPINT:
+ n->n1 = elidecasts(n->n1, min(n->typ->size * 8, m));
+ if(n->n1->databits < n->typ->size * 8 && n->n1->upper == n->typ->sign){
+ n->databits = n->n1->databits;
+ n->upper = n->n1->upper;
+ }else{
+ n->databits = n->typ->size * 8;
+ n->upper = n->typ->sign ? UPSX : UPZX;
+ }
+ if(n->typ->size * 8 >= m) return n->n1;
+ if(n->typ->size * 8 >= n->n1->databits && n->typ->sign == n->n1->upper) return n->n1;
+ if(n->typ->size * 8 > n->n1->databits && n->typ->sign && !n->n1->upper) return n->n1;
+ break;
+ case TYPSTRING:
+ n->n1 = elidecasts(n->n1, 64);
+ break;
+ default:
+ sysfatal("elidecasts: don't know how to cast %τ to %τ", n->n1->typ, n->typ);
+ }
+ break;
+ case ORECORD:
+ n->n1 = elidecasts(n->n1, min(n->typ->size * 8, m));
+ if(n->n1->databits < n->typ->size * 8 && n->n1->upper == n->typ->sign){
+ n->databits = n->n1->databits;
+ n->upper = n->n1->upper;
+ }else{
+ n->databits = n->typ->size * 8;
+ n->upper = n->typ->sign ? UPSX : UPZX;
+ }
+ break;
+ case OTERN:
+ n->n1 = elidecasts(n->n1, 64);
+ n->n2 = elidecasts(n->n2, m);
+ n->n3 = elidecasts(n->n3, m);
+ if(n->n2->upper == n->n3->upper){
+ n->databits = max(n->n2->databits, n->n3->databits);
+ n->upper = n->n2->upper;
+ }else{
+ if(n->n3->upper == UPSX)
+ n->databits = max(min(64, n->n2->databits + 1), n->n3->databits);
+ else
+ n->databits = max(min(64, n->n3->databits + 1), n->n2->databits);
+ n->upper = UPSX;
+ }
+ break;
+ default: sysfatal("elidecasts: unknown type %α", n->type);
+ }
+// print("need %d got %d%c %ε\n", n->needbits, n->databits, "ZS"[n->upper], n);
+ return n;
+}
+
+
+Node *
+exprcheck(Node *n, int pred)
+{
+ if(dflag) print("start %ε\n", n);
+ n = typecheck(n);
+ if(errors) return n;
+ if(dflag) print("typecheck %ε\n", n);
+ n = cfold(n);
+ if(dflag) print("cfold %ε\n", n);
+ if(!pred){
+ n = insrecord(calcrecsize(n));
+ if(dflag) print("insrecord %ε\n", n);
+ }
+ n = elidecasts(n, 64);
+ if(dflag) print("elidecasts %ε\n", n);
+ return n;
+}