// makeaddable returns a node whose memory location is the // same as n, but which is addressable in the Go language // sense. // This is different from functions like cheapexpr that may make // a copy of their argument. static void makeaddable(Node *n) { // The arguments to uintptraddr technically have an address but // may not be addressable in the Go sense: for example, in the case // of T(v).Field where T is a struct type and v is // an addressable value. switch(n->op) { case OINDEX: if(isfixedarray(n->left->type)) makeaddable(n->left); break; case ODOT: case OXDOT: // Turn T(v).Field into v.Field if(n->left->op == OCONVNOP) n->left = n->left->left; makeaddable(n->left); break; case ODOTPTR: default: // nothing to do break; } }
// basenod returns the simplest child node of n pointing to the same // memory area. static Node* basenod(Node *n) { for(;;) { if(n->op == ODOT || n->op == OXDOT || n->op == OCONVNOP || n->op == OCONV || n->op == OPAREN) { n = n->left; continue; } if(n->op == OINDEX && isfixedarray(n->type)) { n = n->left; continue; } break; } return n; }
static int callinstr(Node **np, NodeList **init, int wr, int skip) { Node *f, *b, *n; Type *t; int class, hascalls; n = *np; //print("callinstr for %+N [ %O ] etype=%E class=%d\n", // n, n->op, n->type ? n->type->etype : -1, n->class); if(skip || n->type == T || n->type->etype >= TIDEAL) return 0; t = n->type; if(isartificial(n)) return 0; b = basenod(n); // it skips e.g. stores to ... parameter array if(isartificial(b)) return 0; class = b->class; // BUG: we _may_ want to instrument PAUTO sometimes // e.g. if we've got a local variable/method receiver // that has got a pointer inside. Whether it points to // the heap or not is impossible to know at compile time if((class&PHEAP) || class == PPARAMREF || class == PEXTERN || b->op == OINDEX || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) { hascalls = 0; foreach(n, hascallspred, &hascalls); if(hascalls) { n = detachexpr(n, init); *np = n; } n = treecopy(n); makeaddable(n); if(t->etype == TSTRUCT || isfixedarray(t)) { f = mkcall(wr ? "racewriterange" : "racereadrange", T, init, uintptraddr(n), nodintconst(t->width)); } else f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n)); *init = list(*init, f); return 1; } return 0; }
/* * type check switch statement */ void typecheckswitch(Node *n) { int top, lno, ptr; char *nilonly; Type *t, *badtype, *missing, *have; NodeList *l, *ll; Node *ncase, *nvar; Node *def; lno = lineno; typechecklist(n->ninit, Etop); nilonly = nil; if(n->ntest != N && n->ntest->op == OTYPESW) { // type switch top = Etype; typecheck(&n->ntest->right, Erv); t = n->ntest->right->type; if(t != T && t->etype != TINTER) yyerror("cannot type switch on non-interface value %lN", n->ntest->right); } else { // value switch top = Erv; if(n->ntest) { typecheck(&n->ntest, Erv); defaultlit(&n->ntest, T); t = n->ntest->type; } else t = types[TBOOL]; if(t) { if(!okforeq[t->etype]) yyerror("cannot switch on %lN", n->ntest); else if(t->etype == TARRAY && !isfixedarray(t)) nilonly = "slice"; else if(t->etype == TARRAY && isfixedarray(t) && algtype1(t, nil) == ANOEQ) yyerror("cannot switch on %lN", n->ntest); else if(t->etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ) yyerror("cannot switch on %lN (struct containing %T cannot be compared)", n->ntest, badtype); else if(t->etype == TFUNC) nilonly = "func"; else if(t->etype == TMAP) nilonly = "map"; } } n->type = t; def = N; for(l=n->list; l; l=l->next) { ncase = l->n; setlineno(n); if(ncase->list == nil) { // default if(def != N) yyerror("multiple defaults in switch (first at %L)", def->lineno); else def = ncase; } else { for(ll=ncase->list; ll; ll=ll->next) { setlineno(ll->n); typecheck(&ll->n, Erv | Etype); if(ll->n->type == T || t == T) continue; setlineno(ncase); switch(top) { case Erv: // expression switch defaultlit(&ll->n, t); if(ll->n->op == OTYPE) yyerror("type %T is not an expression", ll->n->type); else if(ll->n->type != T && !assignop(ll->n->type, t, nil) && !assignop(t, ll->n->type, nil)) { if(n->ntest) yyerror("invalid case %N in switch on %N (mismatched types %T and %T)", ll->n, n->ntest, ll->n->type, t); else yyerror("invalid case %N in switch (mismatched types %T and bool)", ll->n, ll->n->type); } else if(nilonly && !isconst(ll->n, CTNIL)) { yyerror("invalid case %N in switch (can only compare %s %N to nil)", ll->n, nilonly, n->ntest); } break; case Etype: // type switch if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL)) { ; } else if(ll->n->op != OTYPE && ll->n->type != T) { // should this be ||? yyerror("%lN is not a type", ll->n); // reset to original type ll->n = n->ntest->right; } else if(ll->n->type->etype != TINTER && t->etype == TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) { if(have && !missing->broke && !have->broke) yyerror("impossible type switch case: %lN cannot have dynamic type %T" " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT", n->ntest->right, ll->n->type, missing->sym, have->sym, have->type, missing->sym, missing->type); else if(!missing->broke) yyerror("impossible type switch case: %lN cannot have dynamic type %T" " (missing %S method)", n->ntest->right, ll->n->type, missing->sym); } break; } } } if(top == Etype && n->type != T) { ll = ncase->list; nvar = ncase->nname; if(nvar != N) { if(ll && ll->next == nil && ll->n->type != T && !istype(ll->n->type, TNIL)) { // single entry type switch nvar->ntype = typenod(ll->n->type); } else { // multiple entry type switch or default nvar->ntype = typenod(n->type); } } } typechecklist(ncase->nbody, Etop); } lineno = lno; }
void typecheckrange(Node *n) { char *why; Type *t, *t1, *t2; Node *v1, *v2; NodeList *ll; // delicate little dance. see typecheckas2 for(ll=n->list; ll; ll=ll->next) if(ll->n->defn != n) typecheck(&ll->n, Erv | Easgn); typecheck(&n->right, Erv); if((t = n->right->type) == T) goto out; if(isptr[t->etype] && isfixedarray(t->type)) t = t->type; n->type = t; switch(t->etype) { default: yyerror("cannot range over %lN", n->right); goto out; case TARRAY: t1 = types[TINT]; t2 = t->type; break; case TMAP: t1 = t->down; t2 = t->type; break; case TCHAN: if(!(t->chan & Crecv)) { yyerror("invalid operation: range %N (receive from send-only type %T)", n->right, n->right->type); goto out; } t1 = t->type; t2 = nil; if(count(n->list) == 2) goto toomany; break; case TSTRING: t1 = types[TINT]; t2 = runetype; break; } if(count(n->list) > 2) { toomany: yyerror("too many variables in range"); } v1 = n->list->n; v2 = N; if(n->list->next) v2 = n->list->next->n; if(v1->defn == n) v1->type = t1; else if(v1->type != T && assignop(t1, v1->type, &why) == 0) yyerror("cannot assign type %T to %lN in range%s", t1, v1, why); if(v2) { if(v2->defn == n) v2->type = t2; else if(v2->type != T && assignop(t2, v2->type, &why) == 0) yyerror("cannot assign type %T to %lN in range%s", t2, v2, why); } out: typechecklist(n->nbody, Etop); // second half of dance n->typecheck = 1; for(ll=n->list; ll; ll=ll->next) if(ll->n->typecheck == 0) typecheck(&ll->n, Erv | Easgn); }
// walkexpr and walkstmt combined // walks the tree and adds calls to the // instrumentation code to top-level (statement) nodes' init static void racewalknode(Node **np, NodeList **init, int wr, int skip) { Node *n, *n1; NodeList *l; NodeList *fini; n = *np; if(n == N) return; if(debug['w'] > 1) dump("racewalk-before", n); setlineno(n); if(init == nil) fatal("racewalk: bad init list"); if(init == &n->ninit) { // If init == &n->ninit and n->ninit is non-nil, // racewalknode might append it to itself. // nil it out and handle it separately before putting it back. l = n->ninit; n->ninit = nil; racewalklist(l, nil); racewalknode(&n, &l, wr, skip); // recurse with nil n->ninit appendinit(&n, l); *np = n; return; } racewalklist(n->ninit, nil); switch(n->op) { default: fatal("racewalk: unknown node type %O", n->op); case OASOP: case OAS: case OAS2: case OAS2RECV: case OAS2FUNC: case OAS2MAPR: racewalknode(&n->left, init, 1, 0); racewalknode(&n->right, init, 0, 0); goto ret; case OCFUNC: case OVARKILL: // can't matter goto ret; case OBLOCK: if(n->list == nil) goto ret; switch(n->list->n->op) { case OCALLFUNC: case OCALLMETH: case OCALLINTER: // Blocks are used for multiple return function calls. // x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]} // We don't want to instrument between the statements because it will // smash the results. racewalknode(&n->list->n, &n->list->n->ninit, 0, 0); fini = nil; racewalklist(n->list->next, &fini); n->list = concat(n->list, fini); break; default: // Ordinary block, for loop initialization or inlined bodies. racewalklist(n->list, nil); break; } goto ret; case ODEFER: racewalknode(&n->left, init, 0, 0); goto ret; case OPROC: racewalknode(&n->left, init, 0, 0); goto ret; case OCALLINTER: racewalknode(&n->left, init, 0, 0); goto ret; case OCALLFUNC: // Instrument dst argument of runtime.writebarrier* calls // as we do not instrument runtime code. if(n->left->sym != S && n->left->sym->pkg == runtimepkg && strncmp(n->left->sym->name, "writebarrier", 12) == 0) { // Find the dst argument. // The list can be reordered, so it's not necessary just the first or the second element. for(l = n->list; l; l = l->next) { if(strcmp(n->left->sym->name, "writebarrierfat") == 0) { if(l->n->left->xoffset == widthptr) break; } else { if(l->n->left->xoffset == 0) break; } } if(l == nil) fatal("racewalk: writebarrier no arg"); if(l->n->right->op != OADDR) fatal("racewalk: writebarrier bad arg"); callinstr(&l->n->right->left, init, 1, 0); } racewalknode(&n->left, init, 0, 0); goto ret; case ONOT: case OMINUS: case OPLUS: case OREAL: case OIMAG: case OCOM: racewalknode(&n->left, init, wr, 0); goto ret; case ODOTINTER: racewalknode(&n->left, init, 0, 0); goto ret; case ODOT: racewalknode(&n->left, init, 0, 1); callinstr(&n, init, wr, skip); goto ret; case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND racewalknode(&n->left, init, 0, 0); callinstr(&n, init, wr, skip); goto ret; case OIND: // *p racewalknode(&n->left, init, 0, 0); callinstr(&n, init, wr, skip); goto ret; case OSPTR: case OLEN: case OCAP: racewalknode(&n->left, init, 0, 0); if(istype(n->left->type, TMAP)) { n1 = nod(OCONVNOP, n->left, N); n1->type = ptrto(types[TUINT8]); n1 = nod(OIND, n1, N); typecheck(&n1, Erv); callinstr(&n1, init, 0, skip); } goto ret; case OLSH: case ORSH: case OLROT: case OAND: case OANDNOT: case OOR: case OXOR: case OSUB: case OMUL: case OHMUL: case OEQ: case ONE: case OLT: case OLE: case OGE: case OGT: case OADD: case OCOMPLEX: racewalknode(&n->left, init, wr, 0); racewalknode(&n->right, init, wr, 0); goto ret; case OANDAND: case OOROR: racewalknode(&n->left, init, wr, 0); // walk has ensured the node has moved to a location where // side effects are safe. // n->right may not be executed, // so instrumentation goes to n->right->ninit, not init. racewalknode(&n->right, &n->right->ninit, wr, 0); goto ret; case ONAME: callinstr(&n, init, wr, skip); goto ret; case OCONV: racewalknode(&n->left, init, wr, 0); goto ret; case OCONVNOP: racewalknode(&n->left, init, wr, 0); goto ret; case ODIV: case OMOD: racewalknode(&n->left, init, wr, 0); racewalknode(&n->right, init, wr, 0); goto ret; case OINDEX: if(!isfixedarray(n->left->type)) racewalknode(&n->left, init, 0, 0); else if(!islvalue(n->left)) { // index of unaddressable array, like Map[k][i]. racewalknode(&n->left, init, wr, 0); racewalknode(&n->right, init, 0, 0); goto ret; } racewalknode(&n->right, init, 0, 0); if(n->left->type->etype != TSTRING) callinstr(&n, init, wr, skip); goto ret; case OSLICE: case OSLICEARR: case OSLICE3: case OSLICE3ARR: // Seems to only lead to double instrumentation. //racewalknode(&n->left, init, 0, 0); goto ret; case OADDR: racewalknode(&n->left, init, 0, 1); goto ret; case OEFACE: racewalknode(&n->left, init, 0, 0); racewalknode(&n->right, init, 0, 0); goto ret; case OITAB: racewalknode(&n->left, init, 0, 0); goto ret; // should not appear in AST by now case OSEND: case ORECV: case OCLOSE: case ONEW: case OXCASE: case OXFALL: case OCASE: case OPANIC: case ORECOVER: case OCONVIFACE: case OCMPIFACE: case OMAKECHAN: case OMAKEMAP: case OMAKESLICE: case OCALL: case OCOPY: case OAPPEND: case ORUNESTR: case OARRAYBYTESTR: case OARRAYRUNESTR: case OSTRARRAYBYTE: case OSTRARRAYRUNE: case OINDEXMAP: // lowered to call case OCMPSTR: case OADDSTR: case ODOTTYPE: case ODOTTYPE2: case OAS2DOTTYPE: case OCALLPART: // lowered to PTRLIT case OCLOSURE: // lowered to PTRLIT case ORANGE: // lowered to ordinary for loop case OARRAYLIT: // lowered to assignments case OMAPLIT: case OSTRUCTLIT: yyerror("racewalk: %O must be lowered by now", n->op); goto ret; // impossible nodes: only appear in backend. case ORROTC: case OEXTEND: yyerror("racewalk: %O cannot exist now", n->op); goto ret; // just do generic traversal case OFOR: case OIF: case OCALLMETH: case ORETURN: case ORETJMP: case OSWITCH: case OSELECT: case OEMPTY: case OBREAK: case OCONTINUE: case OFALL: case OGOTO: case OLABEL: goto ret; // does not require instrumentation case OPRINT: // don't bother instrumenting it case OPRINTN: // don't bother instrumenting it case OCHECKNIL: // always followed by a read. case OPARAM: // it appears only in fn->exit to copy heap params back case OCLOSUREVAR:// immutable pointer to captured variable case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT) case OINDREG: // at this stage, only n(SP) nodes from nodarg case ODCL: // declarations (without value) cannot be races case ODCLCONST: case ODCLTYPE: case OTYPE: case ONONAME: case OLITERAL: case OSLICESTR: // always preceded by bounds checking, avoid double instrumentation. case OTYPESW: // ignored by code generation, do not instrument. goto ret; } ret: if(n->op != OBLOCK) // OBLOCK is handled above in a special way. racewalklist(n->list, init); if(n->ntest != N) racewalknode(&n->ntest, &n->ntest->ninit, 0, 0); if(n->nincr != N) racewalknode(&n->nincr, &n->nincr->ninit, 0, 0); racewalklist(n->nbody, nil); racewalklist(n->nelse, nil); racewalklist(n->rlist, nil); *np = n; }