new switch implementation

in preparation of type switch.
no functional change (yet).

R=r
OCL=25784
CL=25788
This commit is contained in:
Ken Thompson 2009-03-05 15:49:34 -08:00
parent c93da7c70a
commit bf983477a2
7 changed files with 394 additions and 441 deletions

View File

@ -260,8 +260,8 @@ loop:
break;
case OFOR:
p1 = gbranch(AJMP, T); // goto test
sbreak = breakpc;
p1 = gbranch(AJMP, T); // goto test
breakpc = gbranch(AJMP, T); // break: goto done
scontin = continpc;
continpc = pc;
@ -299,15 +299,15 @@ loop:
break;
case OSWITCH:
p1 = gbranch(AJMP, T); // goto test
sbreak = breakpc;
p1 = gbranch(AJMP, T); // goto test
breakpc = gbranch(AJMP, T); // break: goto done
patch(p1, pc); // test:
if(labloop != L) {
labloop->op = OFOR;
labloop->breakpc = breakpc;
}
swgen(n); // switch(test) body
gen(n->nbody, L); // switch(test) body
patch(breakpc, pc); // done:
breakpc = sbreak;
break;
@ -367,279 +367,6 @@ ret:
lineno = lno;
}
Case*
csort(Case *l, int(*f)(Case*, Case*))
{
Case *l1, *l2, *le;
if(l == 0 || l->slink == 0)
return l;
l1 = l;
l2 = l;
for(;;) {
l2 = l2->slink;
if(l2 == 0)
break;
l2 = l2->slink;
if(l2 == 0)
break;
l1 = l1->slink;
}
l2 = l1->slink;
l1->slink = 0;
l1 = csort(l, f);
l2 = csort(l2, f);
/* set up lead element */
if((*f)(l1, l2) < 0) {
l = l1;
l1 = l1->slink;
} else {
l = l2;
l2 = l2->slink;
}
le = l;
for(;;) {
if(l1 == 0) {
while(l2) {
le->slink = l2;
le = l2;
l2 = l2->slink;
}
le->slink = 0;
break;
}
if(l2 == 0) {
while(l1) {
le->slink = l1;
le = l1;
l1 = l1->slink;
}
break;
}
if((*f)(l1, l2) < 0) {
le->slink = l1;
le = l1;
l1 = l1->slink;
} else {
le->slink = l2;
le = l2;
l2 = l2->slink;
}
}
le->slink = 0;
return l;
}
int
casecmp(Case *c1, Case *c2)
{
int w;
w = whatis(c1->scase);
if(w != whatis(c2->scase))
fatal("casecmp1");
switch(w) {
case Wlitfloat:
return mpcmpfltflt(c1->scase->val.u.fval, c2->scase->val.u.fval);
case Wlitint:
return mpcmpfixfix(c1->scase->val.u.xval, c2->scase->val.u.xval);
case Wlitstr:
return cmpslit(c1->scase, c2->scase);
// case Wlitbool:
// case Wlitnil:
}
fatal("casecmp2");
return 0;
}
void
swconst(Case *sa, int nc, Node *n1, Node *tmp)
{
Case *s, *sb;
Prog *p1, *p2, *p3;
int n;
// small number of cases --
// test them sequentially
if(nc < 4) {
for(s=sa; s!=C; s=s->slink) {
setlineno(s->scase);
memset(n1, 0, sizeof(*n1));
n1->op = OEQ;
n1->left = tmp;
n1->right = s->scase;
walktype(n1, Erv);
bgen(n1, 1, s->sprog);
}
return;
}
// large number of cases --
// find the middle and recur on each half
n = nc/2;
for(s=sa; s!=C; s=s->slink) {
n--;
if(n == 0)
break;
}
n = nc/2;
sb = s->slink;
s->slink = C;
p1 = gbranch(AJMP, T); // goto midcmp
p2 = pc; // low half of switch
swconst(sa, n, n1, tmp);
p3 = gbranch(AJMP, T); // goto end
patch(p1, pc);
setlineno(s->scase);
memset(n1, 0, sizeof(*n1));
n1->op = OLE;
n1->left = tmp;
n1->right = s->scase;
walktype(n1, Erv);
bgen(n1, 1, p2);
swconst(sb, nc-n, n1, tmp); // high half of switch
patch(p3, pc);
}
void
swgen(Node *n)
{
Node *c1, *c2;
Node n1, tmp;
Case *s0, *se, *s, *sa;
Prog *p1, *dflt;
int32 lno;
int any, nc;
Iter save1, save2;
// botch - put most of this code in
// walk. gen binary search for
// sequence of constant cases
lno = setlineno(n);
p1 = gbranch(AJMP, T);
s0 = C;
se = C;
// walk thru the body placing breaks
// and labels into the case statements
any = 0;
dflt = P;
c1 = listfirst(&save1, &n->nbody);
while(c1 != N) {
setlineno(c1);
if(c1->op == OEMPTY)
break;
if(c1->op != OCASE) {
if(s0 == C && dflt == P)
yyerror("unreachable statements in a switch");
gen(c1, L);
any = 1;
if(c1->op == OFALL)
any = 0;
c1 = listnext(&save1);
continue;
}
// put in the break between cases
if(any)
patch(gbranch(AJMP, T), breakpc);
any = 1;
// loop over case expressions
c2 = listfirst(&save2, &c1->left);
if(c2 == N)
dflt = pc;
while(c2 != N) {
s = mal(sizeof(*s));
if(s0 == C)
s0 = s;
else
se->slink = s;
se = s;
s->scase = c2; // case expression
s->sprog = pc; // where to go
c2 = listnext(&save2);
}
c1 = listnext(&save1);
}
lineno = lno;
if(any)
patch(gbranch(AJMP, T), breakpc);
patch(p1, pc);
if(n->ntest != N)
if(n->ntest->ninit != N)
gen(n->ntest->ninit, L);
tempname(&tmp, n->ntest->type);
cgen(n->ntest, &tmp);
sa = C; // base of constant cases
nc = 0;
for(s=s0; s!=C; s=s->slink) {
switch(whatis(s->scase)) {
case Wlitfloat:
case Wlitint:
case Wlitstr:
// case Wlitbool:
// case Wlitnil:
nc++;
if(sa == C)
sa = s;
se = s;
continue;
}
if(sa != C) {
se->slink = C;
sa = csort(sa, casecmp);
swconst(sa, nc, &n1, &tmp);
nc = 0;
sa = C;
}
setlineno(s->scase);
memset(&n1, 0, sizeof(n1));
n1.op = OEQ;
n1.left = &tmp;
n1.right = s->scase;
walktype(&n1, Erv);
bgen(&n1, 1, s->sprog);
}
if(sa != C) {
se->slink = C;
sa = csort(sa, casecmp);
swconst(sa, nc, &n1, &tmp);
}
if(dflt != P) {
patch(gbranch(AJMP, T), dflt);
goto ret;
}
patch(gbranch(AJMP, T), breakpc);
ret:
lineno = lno;
}
void
inarggen(void)
{

View File

@ -64,15 +64,6 @@ struct Sig
Sig* link;
};
typedef struct Case Case;
struct Case
{
Prog* sprog;
Node* scase;
Case* slink;
};
#define C ((Case*)0)
typedef struct Pool Pool;
struct Pool
{
@ -143,8 +134,6 @@ EXTERN int sizeof_Array; // runtime sizeof(Array)
void compile(Node*);
void proglist(void);
void gen(Node*, Label*);
void swgen(Node*);
void selgen(Node*);
Node* lookdot(Node*, Node*, int);
void inarggen(void);
void cgen_as(Node*, Node*);

View File

@ -21,6 +21,7 @@ OFILES=\
dcl.$O\
export.$O\
walk.$O\
swt.$O\
const.$O\
mparith1.$O\
mparith2.$O\

View File

@ -812,8 +812,7 @@ void walktype(Node*, int);
void walkconv(Node*);
void walkas(Node*);
void walkbool(Node*);
Type* walkswitch(Node*, Type*(*)(Node*, Type*));
int casebody(Node*);
void walkswitch(Node*);
void walkselect(Node*);
int whatis(Node*);
void walkdot(Node*);

View File

@ -547,8 +547,11 @@ loop:
return;
case OCASE:
// the right side points to the next case
print("%O%J\n", n->op, n);
// the right side points to label of the body
if(n->right != N && n->right->op == OGOTO && n->right->left->op == ONAME)
print("%O%J GOTO %N\n", n->op, n, n->right->left);
else
print("%O%J\n", n->op, n);
dodump(n->left, dep+1);
return;
}
@ -654,8 +657,8 @@ opnames[] =
[OCMP] = "CMP",
[OFALL] = "FALL",
[OCOMPOS] = "COMPOS",
[ODOTTYPE] = "DOTTYPE",
[OCONV] = "CONV",
[ODOTTYPE] = "DOTTYPE",
[OCONV] = "CONV",
[OCOM] = "COM",
[OCONST] = "CONST",
[OCONTINUE] = "CONTINUE",
@ -724,7 +727,7 @@ opnames[] =
[OPRINT] = "PRINT",
[OPRINTN] = "PRINTN",
[OPARAM] = "PARAM",
[ODCL] = "DCL",
[ODCL] = "DCL",
[OXXX] = "XXX",
};

333
src/cmd/gc/swt.c Normal file
View File

@ -0,0 +1,333 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go.h"
/*
* walktype
*/
Type*
sw0(Node *c, Type *place)
{
Node *r;
if(c == N)
return T;
if(c->op != OAS) {
walktype(c, Erv);
return T;
}
walktype(c->left, Elv);
r = c->right;
if(c == N)
return T;
switch(r->op) {
default:
goto bad;
case ORECV:
// <-chan
walktype(r->left, Erv);
if(!istype(r->left->type, TCHAN))
goto bad;
break;
case OINDEX:
// map[e]
walktype(r->left, Erv);
if(!istype(r->left->type, TMAP))
goto bad;
break;
case ODOTTYPE:
// interface.(type)
walktype(r->left, Erv);
if(!istype(r->left->type, TINTER))
goto bad;
break;
}
c->type = types[TBOOL];
return T;
bad:
yyerror("inappropriate assignment in a case statement");
return T;
}
/*
* return the first type
*/
Type*
sw1(Node *c, Type *place)
{
if(place == T)
return c->type;
return place;
}
/*
* return a suitable type
*/
Type*
sw2(Node *c, Type *place)
{
return types[TINT]; // botch
}
/*
* check that switch type
* is compat with all the cases
*/
Type*
sw3(Node *c, Type *place)
{
if(place == T)
return c->type;
if(c->type == T)
c->type = place;
convlit(c, place);
if(!ascompat(place, c->type))
badtype(OSWITCH, place, c->type);
return place;
}
/*
* over all cases, call paramenter function.
* four passes of these are used to allocate
* types to cases and switch
*/
Type*
walkcases(Node *sw, Type*(*call)(Node*, Type*))
{
Iter save;
Node *n;
Type *place;
int32 lno;
lno = setlineno(sw);
place = call(sw->ntest, T);
n = listfirst(&save, &sw->nbody->left);
if(n->op == OEMPTY)
return T;
loop:
if(n == N) {
lineno = lno;
return place;
}
if(n->op != OCASE)
fatal("walkcases: not case %O\n", n->op);
if(n->left != N) {
setlineno(n->left);
place = call(n->left, place);
}
n = listnext(&save);
goto loop;
}
Node*
newlabel()
{
static int label;
label++;
snprint(namebuf, sizeof(namebuf), "%.6d", label);
return newname(lookup(namebuf));
}
/*
* build separate list of statements and cases
* make labels between cases and statements
* deal with fallthrough, break, unreachable statements
*/
void
casebody(Node *sw)
{
Iter save;
Node *os, *oc, *t, *c;
Node *cas, *stat, *def;
Node *go, *br;
int32 lno;
lno = setlineno(sw);
t = listfirst(&save, &sw->nbody);
if(t == N || t->op == OEMPTY) {
sw->nbody = nod(OLIST, N, N);
return;
}
cas = N; // cases
stat = N; // statements
def = N; // defaults
os = N; // last statement
oc = N; // last case
br = nod(OBREAK, N, N);
loop:
if(t == N) {
if(oc == N && os != N)
yyerror("first switch statement must be a case");
stat = list(stat, br);
cas = list(cas, def);
sw->nbody = nod(OLIST, rev(cas), rev(stat));
//dump("case", sw->nbody->left);
//dump("stat", sw->nbody->right);
lineno = lno;
return;
}
lno = setlineno(t);
switch(t->op) {
case OXCASE:
t->op = OCASE;
if(oc == N && os != N)
yyerror("first switch statement must be a case");
if(os != N && os->op == OXFALL)
os->op = OFALL;
else
stat = list(stat, br);
go = nod(OGOTO, newlabel(), N);
c = t->left;
if(c == N) {
if(def != N)
yyerror("more than one default case");
// reuse original default case
t->right = go;
def = t;
}
// expand multi-valued cases
for(; c!=N; c=c->right) {
if(c->op != OLIST) {
// reuse original case
t->left = c;
t->right = go;
cas = list(cas, t);
break;
}
cas = list(cas, nod(OCASE, c->left, go));
}
stat = list(stat, nod(OLABEL, go->left, N));
oc = t;
os = N;
break;
default:
stat = list(stat, t);
os = t;
break;
}
t = listnext(&save);
goto loop;
}
/*
* rebulid case statements into if .. goto
*/
void
prepsw(Node *sw)
{
Iter save;
Node *name, *cas;
Node *t, *a;
int bool;
bool = 0;
if(whatis(sw->ntest) == Wlitbool) {
bool = 1; // true
if(sw->ntest->val.u.xval == 0)
bool = 2; // false
}
cas = N;
name = N;
if(bool == 0) {
name = nod(OXXX, N, N);
tempname(name, sw->ntest->type);
cas = nod(OAS, name, sw->ntest);
}
t = listfirst(&save, &sw->nbody->left);
loop:
if(t == N) {
sw->nbody->left = rev(cas);
walkstate(sw->nbody->left);
//dump("case", sw->nbody->left);
return;
}
if(t->left == N) {
cas = list(cas, t->right); // goto default
t = listnext(&save);
goto loop;
}
a = nod(OIF, N, N);
a->nbody = t->right; // then goto l
switch(bool) {
default:
// not bool const
a->ntest = nod(OEQ, name, t->left); // if name == val
break;
case 1:
// bool true
a->ntest = t->left; // if val
break;
case 2:
// bool false
a->ntest = nod(ONOT, t->left, N); // if !val
break;
}
cas = list(cas, a);
t = listnext(&save);
goto loop;
}
void
walkswitch(Node *n)
{
Type *t;
casebody(n);
if(n->ntest == N)
n->ntest = booltrue;
walkstate(n->ninit);
walktype(n->ntest, Erv);
walkstate(n->nbody);
// walktype
walkcases(n, sw0);
// find common type
t = n->ntest->type;
if(t == T)
t = walkcases(n, sw1);
// if that fails pick a type
if(t == T)
t = walkcases(n, sw2);
// set the type on all literals
if(t != T) {
walkcases(n, sw3);
convlit(n->ntest, t);
prepsw(n);
}
}

View File

@ -4,9 +4,6 @@
#include "go.h"
static Type* sw1(Node*, Type*);
static Type* sw2(Node*, Type*);
static Type* sw3(Node*, Type*);
static Node* curfn;
enum
@ -318,26 +315,7 @@ loop:
if(top != Etop)
goto nottop;
casebody(n->nbody);
if(n->ntest == N)
n->ntest = booltrue;
walkstate(n->ninit);
walktype(n->ntest, Erv);
walkstate(n->nbody);
// find common type
if(n->ntest->type == T)
n->ntest->type = walkswitch(n, sw1);
// if that fails pick a type
if(n->ntest->type == T)
n->ntest->type = walkswitch(n, sw2);
// set the type on all literals
if(n->ntest->type != T)
walkswitch(n, sw3);
walktype(n->ntest, Erv); // BOTCH is this right
walktype(n->nincr, Erv);
walkswitch(n);
goto ret;
case OSELECT:
@ -577,7 +555,6 @@ loop:
case OCASE:
if(top != Etop)
goto nottop;
walktype(n->left, Erv);
walkstate(n->right);
goto ret;
@ -1251,124 +1228,11 @@ walkconv(Node *n)
bad:
if(l->type != T)
yyerror("invalid conversion: %T to %T", l->type, t);
else if(n->left->op == OLIST)
else
if(n->left->op == OLIST)
yyerror("invalid type for composite literal: %T", t);
}
/*
* return the first type
*/
Type*
sw1(Node *c, Type *place)
{
if(place == T)
return c->type;
return place;
}
/*
* return a suitable type
*/
Type*
sw2(Node *c, Type *place)
{
return types[TINT]; // botch
}
/*
* check that switch type
* is compat with all the cases
*/
Type*
sw3(Node *c, Type *place)
{
if(place == T)
return c->type;
if(c->type == T)
c->type = place;
convlit(c, place);
if(!ascompat(place, c->type))
badtype(OSWITCH, place, c->type);
return place;
}
Type*
walkswitch(Node *sw, Type*(*call)(Node*, Type*))
{
Node *n, *c;
Type *place;
place = call(sw->ntest, T);
setlineno(sw);
n = sw->nbody;
if(n->op == OLIST)
n = n->left;
if(n->op == OEMPTY)
return T;
for(; n!=N; n=n->right) {
if(n->op != OCASE)
fatal("walkswitch: not case %O\n", n->op);
for(c=n->left; c!=N; c=c->right) {
if(c->op != OLIST) {
setlineno(c);
place = call(c, place);
break;
}
setlineno(c);
place = call(c->left, place);
}
}
return place;
}
int
casebody(Node *n)
{
Node *oc, *ot, *t;
Iter save;
/*
* look to see if statements at top level have
* case labels attached to them. convert the illegal
* ops XFALL and XCASE into legal ops FALL and CASE.
* all unconverted ops will thus be caught as illegal
*/
oc = N; // last case statement
ot = N; // last statement (look for XFALL)
t = listfirst(&save, &n);
loop:
if(t == N) {
/* empty switch */
if(oc == N)
return 0;
return 1;
}
if(t->op == OXCASE) {
/* rewrite and link top level cases */
t->op = OCASE;
if(oc != N)
oc->right = t;
oc = t;
/* rewrite top fall that preceed case */
if(ot != N && ot->op == OXFALL)
ot->op = OFALL;
}
/* if first statement is not case */
if(oc == N)
return 0;
ot = t;
t = listnext(&save);
goto loop;
}
Node*
selcase(Node *n, Node *var)
{
@ -1477,21 +1341,58 @@ out:
return r;
}
/*
* enumerate the special cases
* of the case statement:
* case v := <-chan // select and switch
* case v := map[] // switch
* case v := interface.(TYPE) // switch
*/
Node*
selectas(Node *name, Node *expr)
{
Node *a;
Type *t;
if(expr == N || expr->op != ORECV)
if(expr == N)
goto bad;
walktype(expr->left, Erv);
t = expr->left->type;
if(t == T)
switch(expr->op) {
default:
//dump("case", expr);
goto bad;
if(t->etype != TCHAN)
goto bad;
a = old2new(name, t->type);
case ORECV:
walktype(expr->left, Erv);
t = expr->left->type;
if(t == T)
goto bad;
if(t->etype != TCHAN)
goto bad;
t = t->type;
break;
case OINDEX:
walktype(expr->left, Erv);
walktype(expr->right, Erv);
t = expr->left->type;
if(t == T)
goto bad;
if(t->etype != TMAP)
goto bad;
t = t->type;
break;
case ODOTTYPE:
walktype(expr->left, Erv);
t = expr->left->type;
if(t == T)
goto bad;
if(t->etype != TINTER)
goto bad;
t = expr->type;
break;
}
a = old2new(name, t);
return a;
bad:
@ -1523,7 +1424,7 @@ walkselect(Node *sel)
oc = N; // last case
def = N; // default case
for(count=0; n!=N; n=listnext(&iter)) {
for(; n!=N; n=listnext(&iter)) {
setlineno(n);
switch(n->op) {