]> Cypherpunks repositories - gostls13.git/commitdiff
gc: special case code for single-op blocking and non-blocking selects
authorRuss Cox <rsc@golang.org>
Sun, 30 Jan 2011 21:07:57 +0000 (16:07 -0500)
committerRuss Cox <rsc@golang.org>
Sun, 30 Jan 2011 21:07:57 +0000 (16:07 -0500)
R=ken2
CC=golang-dev
https://golang.org/cl/4004045

src/cmd/gc/builtin.c.boot
src/cmd/gc/print.c
src/cmd/gc/runtime.go
src/cmd/gc/select.c
src/cmd/gc/sinit.c
src/cmd/gc/typecheck.c
src/pkg/runtime/chan.c

index af16870fe0ba81a2d85b98e021fcabc5267ba73f..421ce19552351a777aea5db29113f2f48a68cb6d 100644 (file)
@@ -72,11 +72,14 @@ char *runtimeimport =
        "func \"\".chansend2 (hchan chan<- any, elem any) bool\n"
        "func \"\".closechan (hchan any)\n"
        "func \"\".closedchan (hchan any) bool\n"
+       "func \"\".selectnbsend (hchan chan<- any, elem any) bool\n"
+       "func \"\".selectnbrecv (elem *any, hchan <-chan any) bool\n"
        "func \"\".newselect (size int) *uint8\n"
        "func \"\".selectsend (sel *uint8, hchan chan<- any, elem any) bool\n"
        "func \"\".selectrecv (sel *uint8, hchan <-chan any, elem *any) bool\n"
        "func \"\".selectdefault (sel *uint8) bool\n"
        "func \"\".selectgo (sel *uint8)\n"
+       "func \"\".block ()\n"
        "func \"\".makeslice (typ *uint8, nel int64, cap int64) []any\n"
        "func \"\".sliceslice1 (old []any, lb uint64, width uint64) []any\n"
        "func \"\".sliceslice (old []any, lb uint64, hb uint64, width uint64) []any\n"
index 25c4126397d65cb06773c03e63c6d3c731d072d2..695a5a39799182ff5c4e4b39f862fb9dc493cdec 100644 (file)
@@ -48,6 +48,7 @@ exprfmt(Fmt *f, Node *n, int prec)
        case ODOTMETH:
        case ODOTTYPE:
        case ODOTTYPE2:
+       case OXDOT:
        case OARRAYBYTESTR:
        case OCAP:
        case OCLOSE:
index 59a1171ed0d0f00ab0b5d6b9aa57b7645272dfc1..d7ab17f1ce015ed20a629f1ff75962cf05306ebc 100644 (file)
@@ -99,11 +99,15 @@ func chansend2(hchan chan<- any, elem any) (pres bool)
 func closechan(hchan any)
 func closedchan(hchan any) bool
 
+func selectnbsend(hchan chan<- any, elem any) bool
+func selectnbrecv(elem *any, hchan <-chan any) bool
+
 func newselect(size int) (sel *byte)
 func selectsend(sel *byte, hchan chan<- any, elem any) (selected bool)
 func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool)
 func selectdefault(sel *byte) (selected bool)
 func selectgo(sel *byte)
+func block()
 
 func makeslice(typ *byte, nel int64, cap int64) (ary []any)
 func sliceslice1(old []any, lb uint64, width uint64) (ary []any)
index 1a37713114a1b24ee93690fe9b0c03e3a6dca63a..5686e959952c93532123ce7d218a49504f6804b4 100644 (file)
@@ -45,27 +45,23 @@ typecheckselect(Node *sel)
                                break;
 
                        case OAS:
-                               // convert x = <-c into OSELRECV(x, c)
-                               // assignment might have introduced a
-                               // conversion.  throw it away.
-                               // it will come back when the select code
-                               // gets generated, because it always assigns
-                               // through a temporary.
+                               // convert x = <-c into OSELRECV(x, <-c).
+                               // remove implicit conversions; the eventual assignment
+                               // will reintroduce them.
                                if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit)
                                        n->right = n->right->left;
+
                                if(n->right->op != ORECV) {
                                        yyerror("select assignment must have receive on right hand side");
                                        break;
                                }
                                n->op = OSELRECV;
-                               n->right = n->right->left;
                                break;
 
                        case ORECV:
-                               // convert <-c into OSELRECV(N, c)
-                               n->op = OSELRECV;
-                               n->right = n->left;
-                               n->left = N;
+                               // convert <-c into OSELRECV(N, <-c)
+                               n = nod(OSELRECV, N, n);
+                               ncase->left = n;
                                break;
 
                        case OSEND:
@@ -81,11 +77,149 @@ typecheckselect(Node *sel)
 void
 walkselect(Node *sel)
 {
-       int lno;
-       Node *n, *ncase, *r, *a, *tmp, *var;
+       int lno, i;
+       Node *n, *r, *a, *tmp, *var, *cas, *dflt, *ch;
        NodeList *l, *init;
-
+       
+       if(sel->list == nil && sel->xoffset != 0)
+               fatal("double walkselect");     // already rewrote
+       
        lno = setlineno(sel);
+       i = count(sel->list);
+       
+       // optimization: zero-case select
+       if(i == 0) {
+               sel->nbody = list1(mkcall("block", nil, nil));
+               goto out;
+       }
+
+       // optimization: one-case select: single op.
+       if(i == 1) {
+               cas = sel->list->n;
+               l = cas->ninit;
+               if(cas->left != N) {  // not default:
+                       n = cas->left;
+                       l = concat(l, n->ninit);
+                       n->ninit = nil;
+                       switch(n->op) {
+                       default:
+                               fatal("select %O", n->op);
+
+                       case OSEND:
+                               ch = cheapexpr(n->left, &l);
+                               n->left = ch;
+                               break;
+
+                       case OSELRECV:
+                               r = n->right;
+                               ch = cheapexpr(r->left, &l);
+                               r->left = ch;
+
+                               if(n->left == N)
+                                       n = r;
+                               else {
+                                       n = nod(OAS, n->left, r);
+                                       typecheck(&n, Etop);
+                               }
+                               break;
+                       }
+
+                       // if ch == nil { block() }; n;
+                       a = nod(OIF, N, N);
+                       a->ntest = nod(OEQ, ch, nodnil());
+                       a->nbody = list1(mkcall("block", nil, &l));
+                       typecheck(&a, Etop);
+                       l = list(l, a);
+                       l = list(l, n);
+               }
+               l = concat(l, cas->nbody);
+               sel->nbody = l;
+               goto out;
+       }
+
+       // introduce temporary variables for OSELRECV where needed.
+       // this rewrite is used by both the general code and the next optimization.
+       for(l=sel->list; l; l=l->next) {
+               cas = l->n;
+               n = cas->left;
+               if(n == N)
+                       continue;
+               switch(n->op) {
+               case OSELRECV:
+                       ch = n->right->left;
+
+                       // If we can use the address of the target without
+                       // violating addressability or order of operations, do so.
+                       // Otherwise introduce a temporary.
+                       // Also introduce a temporary for := variables that escape,
+                       // so that we can delay the heap allocation until the case
+                       // is selected.
+                       if(n->left == N || isblank(n->left))
+                               n->left = nodnil();
+                       else if(n->left->op == ONAME &&
+                                       (!n->colas || (n->class&PHEAP) == 0) &&
+                                       convertop(ch->type->type, n->left->type, nil) == OCONVNOP) {
+                               n->left = nod(OADDR, n->left, N);
+                               n->left->etype = 1;  // pointer does not escape
+                               typecheck(&n->left, Erv);
+                       } else {
+                               tmp = nod(OXXX, N, N);
+                               tempname(tmp, ch->type->type);
+                               a = nod(OADDR, tmp, N);
+                               a->etype = 1;  // pointer does not escape
+                               typecheck(&a, Erv);
+                               r = nod(OAS, n->left, tmp);
+                               typecheck(&r, Etop);
+                               cas->nbody = concat(n->ninit, cas->nbody);
+                               n->ninit = nil;
+                               cas->nbody = concat(list1(r), cas->nbody);
+                               n->left = a;
+                       }
+               }
+       }
+
+       // optimization: two-case select but one is default: single non-blocking op.
+       if(i == 2 && (sel->list->n->left == nil || sel->list->next->n->left == nil)) {
+               if(sel->list->n->left == nil) {
+                       cas = sel->list->next->n;
+                       dflt = sel->list->n;
+               } else {
+                       dflt = sel->list->next->n;
+                       cas = sel->list->n;
+               }
+               
+               n = cas->left;
+               r = nod(OIF, N, N);
+               r->ninit = cas->ninit;
+               switch(n->op) {
+               default:
+                       fatal("select %O", n->op);
+
+               case OSEND:
+                       // if c != nil && selectnbsend(c, v) { body } else { default body }
+                       ch = cheapexpr(n->left, &r->ninit);
+                       r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()),
+                               mkcall1(chanfn("selectnbsend", 2, ch->type),
+                                       types[TBOOL], &r->ninit, ch, n->right));
+                       break;
+                       
+               case OSELRECV:
+                       // if c != nil && selectnbrecv(&v, c) { body } else { default body }
+                       r = nod(OIF, N, N);
+                       r->ninit = cas->ninit;
+                       ch = cheapexpr(n->right->left, &r->ninit);
+                       r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()),
+                               mkcall1(chanfn("selectnbrecv", 2, ch->type),
+                                       types[TBOOL], &r->ninit, n->left, ch));
+                       break;
+               }
+               typecheck(&r->ntest, Erv);
+               r->nbody = cas->nbody;
+               r->nelse = concat(dflt->ninit, dflt->nbody);
+               sel->nbody = list1(r);
+               goto out;
+       }               
+
        init = sel->ninit;
        sel->ninit = nil;
 
@@ -96,16 +230,13 @@ walkselect(Node *sel)
        typecheck(&r, Etop);
        init = list(init, r);
 
-       if(sel->list == nil && sel->xoffset != 0)
-               fatal("double walkselect");     // already rewrote
-
        // register cases
        for(l=sel->list; l; l=l->next) {
-               ncase = l->n;
-               n = ncase->left;
+               cas = l->n;
+               n = cas->left;
                r = nod(OIF, N, N);
-               r->nbody = ncase->ninit;
-               ncase->ninit = nil;
+               r->nbody = cas->ninit;
+               cas->ninit = nil;
                if(n != nil) {
                        r->nbody = concat(r->nbody, n->ninit);
                        n->ninit = nil;
@@ -113,29 +244,24 @@ walkselect(Node *sel)
                if(n == nil) {
                        // selectdefault(sel *byte);
                        r->ntest = mkcall("selectdefault", types[TBOOL], &init, var);
-               } else if(n->op == OSEND) {
-                       // selectsend(sel *byte, hchan *chan any, elem any) (selected bool);
-                       r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], &init, var, n->left, n->right);
-               } else if(n->op == OSELRECV) {
-                       tmp = N;
-                       if(n->left == N)
-                               a = nodnil();
-                       else {
-                               // introduce temporary until we're sure this will succeed.
-                               tmp = nod(OXXX, N, N);
-                               tempname(tmp, n->right->type->type);
-                               a = nod(OADDR, tmp, N);
-                       }
-                       // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
-                       r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->type), types[TBOOL], &init, var, n->right, a);
-                       if(tmp != N) {
-                               a = nod(OAS, n->left, tmp);
-                               typecheck(&a, Etop);
-                               r->nbody = list(r->nbody, a);
+               } else {
+                       switch(n->op) {
+                       default:
+                               fatal("select %O", n->op);
+       
+                       case OSEND:
+                               // selectsend(sel *byte, hchan *chan any, elem any) (selected bool);
+                               r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
+                                       &init, var, n->left, n->right);
+                               break;
+                       case OSELRECV:
+                               // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
+                               r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL],
+                                       &init, var, n->right->left, n->left);
+                               break;
                        }
-               } else
-                       fatal("select %O", n->op);
-               r->nbody = concat(r->nbody, ncase->nbody);
+               }
+               r->nbody = concat(r->nbody, cas->nbody);
                r->nbody = list(r->nbody, nod(OBREAK, N, N));
                init = list(init, r);
        }
@@ -143,8 +269,9 @@ walkselect(Node *sel)
        // run the select
        init = list(init, mkcall("selectgo", T, nil, var));
        sel->nbody = init;
-       sel->list = nil;
-       walkstmtlist(init);
 
+out:
+       sel->list = nil;
+       walkstmtlist(sel->nbody);
        lineno = lno;
 }
index be96a1477a25d6619e35ce131b3add2e455d3f93..44e33dae90f861864d4f6a2a3bd153eab3f57888 100644 (file)
@@ -95,6 +95,7 @@ init1(Node *n, NodeList **out)
                case OAS2MAPR:
                case OAS2DOTTYPE:
                case OAS2RECV:
+               case OAS2RECVCLOSED:
                        if(n->defn->initorder)
                                break;
                        n->defn->initorder = 1;
index 6711f69f5c6c816ce8885c48a41ea942449bfebc..8e8f8da29c936b57f5f627d369f1d71adb20ea9d 100644 (file)
@@ -18,7 +18,7 @@ static int    onearg(Node*, char*, ...);
 static int     twoarg(Node*);
 static int     lookdot(Node*, Type*, int);
 static int     looktypedot(Node*, Type*, int);
-static void    typecheckaste(int, int, Type*, NodeList*, char*);
+static void    typecheckaste(int, Node*, int, Type*, NodeList*, char*);
 static Type*   lookdot1(Sym *s, Type *t, Type *f, int);
 static int     nokeys(NodeList*);
 static void    typecheckcomplit(Node**);
@@ -504,7 +504,7 @@ reswitch:
                l = n->left;
                if((t = l->type) == T)
                        goto error;
-               if(!(top & Eindir))
+               if(!(top & Eindir) && !n->etype)
                        addrescapes(n->left);
                n->type = ptrto(t);
                goto ret;
@@ -668,6 +668,13 @@ reswitch:
                goto ret;
 
        case OSEND:
+               if(0 && top == Erv) {
+                       // can happen because grammar for if header accepts
+                       // simple_stmt for condition.  Falling through would give
+                       // an error "c <- v used as value" but we can do better.
+                       yyerror("send statement %#N used as value; use select for non-blocking send", n);
+                       goto error;
+               }
                ok |= Etop | Erv;
                l = typecheck(&n->left, Erv);
                typecheck(&n->right, Erv);
@@ -801,7 +808,7 @@ reswitch:
 
                case ODOTMETH:
                        n->op = OCALLMETH;
-                       typecheckaste(OCALL, 0, getthisx(t), list1(l->left), "method receiver");
+                       typecheckaste(OCALL, n->left, 0, getthisx(t), list1(l->left), "method receiver");
                        break;
 
                default:
@@ -812,7 +819,7 @@ reswitch:
                        }
                        break;
                }
-               typecheckaste(OCALL, n->isddd, getinargx(t), n->list, "function argument");
+               typecheckaste(OCALL, n->left, n->isddd, getinargx(t), n->list, "function argument");
                ok |= Etop;
                if(t->outtuple == 0)
                        goto ret;
@@ -1246,7 +1253,7 @@ reswitch:
                }
                if(curfn->type->outnamed && n->list == nil)
                        goto ret;
-               typecheckaste(ORETURN, 0, getoutargx(curfn->type), n->list, "return argument");
+               typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument");
                goto ret;
 
        case OSELECT:
@@ -1591,7 +1598,7 @@ nokeys(NodeList *l)
  * typecheck assignment: type list = expression list
  */
 static void
-typecheckaste(int op, int isddd, Type *tstruct, NodeList *nl, char *desc)
+typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *desc)
 {
        Type *t, *tl, *tn;
        Node *n;
@@ -1610,16 +1617,24 @@ typecheckaste(int op, int isddd, Type *tstruct, NodeList *nl, char *desc)
                        if(tl->isddd) {
                                for(; tn; tn=tn->down) {
                                        exportassignok(tn->type, desc);
-                                       if(assignop(tn->type, tl->type->type, &why) == 0)
-                                               yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why);
+                                       if(assignop(tn->type, tl->type->type, &why) == 0) {
+                                               if(call != N)
+                                                       yyerror("cannot use %T as type %T in argument to %#N%s", tn->type, tl->type->type, desc, call, why);
+                                               else
+                                                       yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why);
+                                       }
                                }
                                goto out;
                        }
                        if(tn == T)
                                goto notenough;
                        exportassignok(tn->type, desc);
-                       if(assignop(tn->type, tl->type, &why) == 0)
-                               yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
+                       if(assignop(tn->type, tl->type, &why) == 0) {
+                               if(call != N)
+                                       yyerror("cannot use %T as type %T in argument to %#N%s", tn->type, tl->type, desc, call, why);
+                               else
+                                       yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
+                       }
                        tn = tn->down;
                }
                if(tn != T)
@@ -1664,19 +1679,29 @@ typecheckaste(int op, int isddd, Type *tstruct, NodeList *nl, char *desc)
        }
        if(nl != nil)
                goto toomany;
-       if(isddd)
-               yyerror("invalid use of ... in %#O", op);
+       if(isddd) {
+               if(call != N)
+                       yyerror("invalid use of ... in call to %#N", call);
+               else
+                       yyerror("invalid use of ... in %#O", op);
+       }
 
 out:
        lineno = lno;
        return;
 
 notenough:
-       yyerror("not enough arguments to %#O", op);
+       if(call != N)
+               yyerror("not enough arguments in call to %#N", call);
+       else
+               yyerror("not enough arguments to %#O", op);
        goto out;
 
 toomany:
-       yyerror("too many arguments to %#O", op);
+       if(call != N)
+               yyerror("too many arguments in call to %#N", call);
+       else
+               yyerror("too many arguments to %#O", op);
        goto out;
 }
 
@@ -2360,6 +2385,8 @@ typecheckas2(Node *n)
                case ORECV:
                        n->op = OAS2RECV;
                        goto common;
+                       yyerror("cannot use multiple-value assignment for non-blocking receive; use select");
+                       goto out;
                case ODOTTYPE:
                        n->op = OAS2DOTTYPE;
                        r->op = ODOTTYPE2;
index 6f9f16826c8563c80d06182f84c42b4df82febca..f3b804df447d8faf6aad986355d5f941c2b8acbc 100644 (file)
@@ -296,7 +296,8 @@ loop:
 
        sg = dequeue(&c->sendq, c);
        if(sg != nil) {
-               c->elemalg->copy(c->elemsize, ep, sg->elem);
+               if(ep != nil)
+                       c->elemalg->copy(c->elemsize, ep, sg->elem);
                c->elemalg->copy(c->elemsize, sg->elem, nil);
 
                gp = sg->g;
@@ -311,7 +312,6 @@ loop:
 
        if(pres != nil) {
                runtime·unlock(c);
-               c->elemalg->copy(c->elemsize, ep, nil);
                *pres = false;
                return;
        }
@@ -328,7 +328,8 @@ loop:
        if(sg == nil)
                goto loop;
 
-       c->elemalg->copy(c->elemsize, ep, sg->elem);
+       if(ep != nil)
+               c->elemalg->copy(c->elemsize, ep, sg->elem);
        c->elemalg->copy(c->elemsize, sg->elem, nil);
        freesg(c, sg);
        runtime·unlock(c);
@@ -341,7 +342,6 @@ asynch:
 
                if(pres != nil) {
                        runtime·unlock(c);
-                       c->elemalg->copy(c->elemsize, ep, nil);
                        *pres = false;
                        return;
                }
@@ -354,7 +354,8 @@ asynch:
                runtime·lock(c);
                goto asynch;
        }
-       c->elemalg->copy(c->elemsize, ep, c->recvdataq->elem);
+       if(ep != nil)
+               c->elemalg->copy(c->elemsize, ep, c->recvdataq->elem);
        c->elemalg->copy(c->elemsize, c->recvdataq->elem, nil);
        c->recvdataq = c->recvdataq->link;
        c->qcount--;
@@ -377,7 +378,8 @@ asynch:
 closed:
        if(closed != nil)
                *closed = true;
-       c->elemalg->copy(c->elemsize, ep, nil);
+       if(ep != nil)
+               c->elemalg->copy(c->elemsize, ep, nil);
        c->closed |= Rclosed;
        if(pres != nil)
                *pres = true;
@@ -441,12 +443,18 @@ runtime·chanrecv2(Hchan* c, ...)
        int32 o;
        byte *ae, *ap;
 
+       if(c == nil)
+               runtime·panicstring("receive from nil channel");
+
        o = runtime·rnd(sizeof(c), Structrnd);
        ae = (byte*)&c + o;
        o = runtime·rnd(o+c->elemsize, 1);
        ap = (byte*)&c + o;
 
        runtime·chanrecv(c, ae, ap, nil);
+       
+       if(!*ap)
+               c->elemalg->copy(c->elemsize, ae, nil);
 }
 
 // chanrecv3(hchan *chan any) (elem any, closed bool);
@@ -456,6 +464,9 @@ runtime·chanrecv3(Hchan* c, ...)
 {
        int32 o;
        byte *ae, *ac;
+       
+       if(c == nil)
+               runtime·panicstring("range over nil channel");
 
        o = runtime·rnd(sizeof(c), Structrnd);
        ae = (byte*)&c + o;
@@ -465,6 +476,66 @@ runtime·chanrecv3(Hchan* c, ...)
        runtime·chanrecv(c, ae, nil, ac);
 }
 
+// func selectnbsend(c chan any, elem any) bool
+//
+// compiler implements
+//
+//     select {
+//     case c <- v:
+//             ... foo
+//     default:
+//             ... bar
+//     }
+//
+// as
+//
+//     if c != nil && selectnbsend(c, v) {
+//             ... foo
+//     } else {
+//             ... bar
+//     }
+//
+#pragma textflag 7
+void
+runtime·selectnbsend(Hchan *c, ...)
+{
+       int32 o;
+       byte *ae, *ap;
+
+       o = runtime·rnd(sizeof(c), c->elemalign);
+       ae = (byte*)&c + o;
+       o = runtime·rnd(o+c->elemsize, Structrnd);
+       ap = (byte*)&c + o;
+
+       runtime·chansend(c, ae, ap);
+}
+
+// func selectnbrecv(elem *any, c chan any) bool
+//
+// compiler implements
+//
+//     select {
+//     case v = <-c:
+//             ... foo
+//     default:
+//             ... bar
+//     }
+//
+// as
+//
+//     if c != nil && selectnbrecv(&v, c) {
+//             ... foo
+//     } else {
+//             ... bar
+//     }
+//
+#pragma textflag 7
+void
+runtime·selectnbrecv(byte *v, Hchan *c, bool ok)
+{
+       runtime·chanrecv(c, v, &ok, nil);
+}      
+
 // newselect(size uint32) (sel *byte);
 #pragma textflag 7
 void
@@ -625,6 +696,13 @@ selunlock(Select *sel)
        }
 }
 
+void
+runtime·block(void)
+{
+       g->status = Gwaiting;   // forever
+       runtime·gosched();
+}
+
 // selectgo(sel *byte);
 //
 // overwrites return pc on stack to signal which case of the select
@@ -648,13 +726,13 @@ runtime·selectgo(Select *sel)
        if(debug)
                runtime·printf("select: sel=%p\n", sel);
 
-       if(sel->ncase < 2) {
-               if(sel->ncase < 1) {
-                       g->status = Gwaiting;   // forever
-                       runtime·gosched();
-               }
-               // TODO: make special case of one.
-       }
+       // The compiler rewrites selects that statically have
+       // only 0 or 1 cases plus default into simpler constructs.
+       // The only way we can end up with such small sel->ncase
+       // values here is for a larger select in which most channels
+       // have been nilled out.  The general code handles those
+       // cases correctly, and they are rare enough not to bother
+       // optimizing (and needing to test).
 
        // generate permuted order
        for(i=0; i<sel->ncase; i++)