#include <libc.h>
#include "go.h"
+static Type* selecttype(int32 size);
+
void
typecheckselect(Node *sel)
{
walkselect(Node *sel)
{
int lno, i;
- Node *n, *r, *a, *var, *cas, *dflt, *ch;
+ Node *n, *r, *a, *var, *selv, *cas, *dflt, *ch;
NodeList *l, *init;
if(sel->list == nil && sel->xoffset != 0)
// generate sel-struct
setlineno(sel);
- var = temp(ptrto(types[TUINT8]));
- r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset)));
+ selv = temp(selecttype(sel->xoffset));
+ selv->esc = EscNone;
+ r = nod(OAS, selv, N);
+ typecheck(&r, Etop);
+ init = list(init, r);
+ var = conv(conv(nod(OADDR, selv, N), types[TUNSAFEPTR]), ptrto(types[TUINT8]));
+ r = mkcall("newselect", T, nil, var, nodintconst(selv->type->width), nodintconst(sel->xoffset));
typecheck(&r, Etop);
init = list(init, r);
break;
}
}
+ // selv is no longer alive after use.
+ r->nbody = list(r->nbody, nod(OVARKILL, selv, N));
r->nbody = concat(r->nbody, cas->nbody);
r->nbody = list(r->nbody, nod(OBREAK, N, N));
init = list(init, r);
walkstmtlist(sel->nbody);
lineno = lno;
}
+
+// Keep in sync with src/pkg/runtime/chan.h.
+static Type*
+selecttype(int32 size)
+{
+ Node *sel, *sudog, *scase, *arr;
+
+ // TODO(dvyukov): it's possible to generate SudoG and Scase only once
+ // and then cache; and also cache Select per size.
+ sudog = nod(OTSTRUCT, N, N);
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("g")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("selectdone")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("link")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
+ typecheck(&sudog, Etype);
+ sudog->type->noalg = 1;
+ sudog->type->local = 1;
+
+ scase = nod(OTSTRUCT, N, N);
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("sg")), sudog));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8]))));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(ptrto(types[TUINT8]))));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("kind")), typenod(types[TUINT16])));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("so")), typenod(types[TUINT16])));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(types[TUINT8]))));
+ typecheck(&scase, Etype);
+ scase->type->noalg = 1;
+ scase->type->local = 1;
+
+ sel = nod(OTSTRUCT, N, N);
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("tcase")), typenod(types[TUINT16])));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("ncase")), typenod(types[TUINT16])));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorder")), typenod(ptrto(types[TUINT8]))));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorder")), typenod(ptrto(types[TUINT8]))));
+ arr = nod(OTARRAY, nodintconst(size), scase);
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("scase")), arr));
+ arr = nod(OTARRAY, nodintconst(size), typenod(ptrto(types[TUINT8])));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorderarr")), arr));
+ arr = nod(OTARRAY, nodintconst(size), typenod(types[TUINT16]));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorderarr")), arr));
+ typecheck(&sel, Etype);
+ sel->type->noalg = 1;
+ sel->type->local = 1;
+
+ return sel->type;
+}
selected = chanrecv(t, c, elem, !nb, &received);
}
-static Select* newselect(int32);
-
-#pragma textflag NOSPLIT
-func newselect(size int32) (sel *byte) {
- sel = (byte*)newselect(size);
-}
-
-static Select*
-newselect(int32 size)
+static int64
+selectsize(int32 size)
{
- int32 n;
Select *sel;
+ int64 selsize;
- n = 0;
- if(size > 1)
- n = size-1;
-
- // allocate all the memory we need in a single allocation
- // start with Select with size cases
- // then lockorder with size entries
- // then pollorder with size entries
- sel = runtime·mal(sizeof(*sel) +
- n*sizeof(sel->scase[0]) +
+ selsize = sizeof(*sel) +
+ (size-1)*sizeof(sel->scase[0]) +
size*sizeof(sel->lockorder[0]) +
- size*sizeof(sel->pollorder[0]));
+ size*sizeof(sel->pollorder[0]);
+ return ROUND(selsize, Int64Align);
+}
+#pragma textflag NOSPLIT
+func newselect(sel *Select, selsize int64, size int32) {
+ if(selsize != selectsize(size)) {
+ runtime·printf("runtime: bad select size %D, want %D\n", selsize, selectsize(size));
+ runtime·throw("bad select size");
+ }
sel->tcase = size;
sel->ncase = 0;
sel->lockorder = (void*)(sel->scase + size);
if(debug)
runtime·printf("newselect s=%p size=%d\n", sel, size);
- return sel;
}
// cut in half to give stack a chance to split
}
if(cas->sg.releasetime > 0)
runtime·blockevent(cas->sg.releasetime - t0, 2);
- runtime·free(sel);
return pc;
sclose:
rcase = (runtimeSelect*)cases.array;
- sel = newselect(cases.len);
+ // FlagNoScan is safe here, because all objects are also referenced from cases.
+ sel = runtime·mallocgc(selectsize(cases.len), 0, FlagNoScan);
+ runtime·newselect(sel, selectsize(cases.len), cases.len);
for(i=0; i<cases.len; i++) {
rc = &rcase[i];
switch(rc->dir) {
// this used to have a spurious "live at entry to f11a: ~r0"
func f11a() *int {
- select { // ERROR "live at call to selectgo: autotmp"
+ select { // ERROR "live at call to newselect: autotmp" "live at call to selectgo: autotmp"
case <-c: // ERROR "live at call to selectrecv: autotmp"
return nil
case <-c: // ERROR "live at call to selectrecv: autotmp"
// get to the bottom of the function.
// This used to have a spurious "live at call to printint: p".
print(1) // nothing live here!
- select { // ERROR "live at call to selectgo: autotmp"
+ select { // ERROR "live at call to newselect: autotmp" "live at call to selectgo: autotmp"
case <-c: // ERROR "live at call to selectrecv: autotmp"
return nil
case <-c: // ERROR "live at call to selectrecv: autotmp"
// Unlike previous, the cases in this select fall through,
// so we can get to the println, so p is not dead.
print(1) // ERROR "live at call to printint: p"
- select { // ERROR "live at call to newselect: p" "live at call to selectgo: autotmp.* p"
+ select { // ERROR "live at call to newselect: autotmp.* p" "live at call to selectgo: autotmp.* p"
case <-c: // ERROR "live at call to selectrecv: autotmp.* p"
case <-c: // ERROR "live at call to selectrecv: autotmp.* p"
}