summaryrefslogtreecommitdiff
path: root/src/cmd/gc/racewalk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/racewalk.c')
-rw-r--r--src/cmd/gc/racewalk.c122
1 files changed, 76 insertions, 46 deletions
diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
index 5d4f62e76..d6a5b3cce 100644
--- a/src/cmd/gc/racewalk.c
+++ b/src/cmd/gc/racewalk.c
@@ -23,6 +23,7 @@ static void racewalklist(NodeList *l, NodeList **init);
static void racewalknode(Node **np, NodeList **init, int wr, int skip);
static int callinstr(Node **n, NodeList **init, int wr, int skip);
static Node* uintptraddr(Node *n);
+static void makeaddable(Node *n);
static Node* basenod(Node *n);
static void foreach(Node *n, void(*f)(Node*, void*), void *c);
static void hascallspred(Node *n, void *c);
@@ -50,6 +51,18 @@ ispkgin(const char **pkgs, int n)
return 0;
}
+static int
+isforkfunc(Node *fn)
+{
+ // Special case for syscall.forkAndExecInChild.
+ // In the child, this function must not acquire any locks, because
+ // they might have been locked at the time of the fork. This means
+ // no rescheduling, no malloc calls, and no new stack segments.
+ // Race instrumentation does all of the above.
+ return myimportpath != nil && strcmp(myimportpath, "syscall") == 0 &&
+ strcmp(fn->nname->sym->name, "forkAndExecInChild") == 0;
+}
+
void
racewalk(Node *fn)
{
@@ -57,7 +70,7 @@ racewalk(Node *fn)
Node *nodpc;
char s[1024];
- if(ispkgin(omit_pkgs, nelem(omit_pkgs)))
+ if(ispkgin(omit_pkgs, nelem(omit_pkgs)) || isforkfunc(fn))
return;
if(!ispkgin(noinst_pkgs, nelem(noinst_pkgs))) {
@@ -121,8 +134,20 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
if(debug['w'] > 1)
dump("racewalk-before", n);
setlineno(n);
- if(init == nil || init == &n->ninit)
+ 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);
@@ -213,6 +238,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
callinstr(&n, init, wr, skip);
goto ret;
+ case OSPTR:
case OLEN:
case OCAP:
racewalknode(&n->left, init, 0, 0);
@@ -254,9 +280,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
// side effects are safe.
// n->right may not be executed,
// so instrumentation goes to n->right->ninit, not init.
- l = nil;
- racewalknode(&n->right, &l, wr, 0);
- appendinit(&n->right, l);
+ racewalknode(&n->right, &n->right->ninit, wr, 0);
goto ret;
case ONAME:
@@ -293,6 +317,8 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OSLICE:
case OSLICEARR:
+ case OSLICE3:
+ case OSLICE3ARR:
// Seems to only lead to double instrumentation.
//racewalknode(&n->left, init, 0, 0);
goto ret;
@@ -310,10 +336,6 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
racewalknode(&n->left, init, 0, 0);
goto ret;
- case OTYPESW:
- racewalknode(&n->right, init, 0, 0);
- goto ret;
-
// should not appear in AST by now
case OSEND:
case ORECV:
@@ -363,6 +385,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
case OIF:
case OCALLMETH:
case ORETURN:
+ case ORETJMP:
case OSWITCH:
case OSELECT:
case OEMPTY:
@@ -376,7 +399,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
// does not require instrumentation
case OPRINT: // don't bother instrumenting it
case OPRINTN: // don't bother instrumenting it
- case OCHECKNOTNIL: // always followed by a read.
+ 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)
@@ -388,18 +411,15 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
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);
- l = nil;
- racewalknode(&n->ntest, &l, 0, 0);
- n->ninit = concat(n->ninit, l);
- l = nil;
- racewalknode(&n->nincr, &l, 0, 0);
- n->ninit = concat(n->ninit, l);
+ racewalknode(&n->ntest, &n->ntest->ninit, 0, 0);
+ racewalknode(&n->nincr, &n->nincr->ninit, 0, 0);
racewalklist(n->nbody, nil);
racewalklist(n->nelse, nil);
racewalklist(n->rlist, nil);
@@ -431,8 +451,8 @@ static int
callinstr(Node **np, NodeList **init, int wr, int skip)
{
Node *f, *b, *n;
- Type *t, *t1;
- int class, res, hascalls;
+ Type *t;
+ int class, hascalls;
n = *np;
//print("callinstr for %+N [ %O ] etype=%E class=%d\n",
@@ -443,33 +463,6 @@ callinstr(Node **np, NodeList **init, int wr, int skip)
t = n->type;
if(isartificial(n))
return 0;
- if(t->etype == TSTRUCT) {
- // TODO: instrument arrays similarly.
- // PARAMs w/o PHEAP are not interesting.
- if(n->class == PPARAM || n->class == PPARAMOUT)
- return 0;
- res = 0;
- hascalls = 0;
- foreach(n, hascallspred, &hascalls);
- if(hascalls) {
- n = detachexpr(n, init);
- *np = n;
- }
- for(t1=t->type; t1; t1=t1->down) {
- if(t1->sym && strcmp(t1->sym->name, "_")) {
- n = treecopy(n);
- f = nod(OXDOT, n, newname(t1->sym));
- f->type = t1;
- if(f->type->etype == TFIELD)
- f->type = f->type->type;
- if(callinstr(&f, init, wr, 0)) {
- typecheck(&f, Erv);
- res = 1;
- }
- }
- }
- return res;
- }
b = basenod(n);
// it skips e.g. stores to ... parameter array
@@ -489,19 +482,56 @@ callinstr(Node **np, NodeList **init, int wr, int skip)
*np = n;
}
n = treecopy(n);
- f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(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;
}
+// 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;
+ }
+}
+
static Node*
uintptraddr(Node *n)
{
Node *r;
r = nod(OADDR, n, N);
+ r->bounded = 1;
r = conv(r, types[TUNSAFEPTR]);
r = conv(r, types[TUINTPTR]);
return r;