1
0
mirror of https://github.com/golang/go synced 2024-10-05 06:11:21 -06:00
go/src/cmd/gc/swt.c
Russ Cox 0dadc4fe4f cleanup in preparation for new scoping.
walkstate -> walkstmt
walktype -> walkexpr; stmts moved to walkstmt

walktype and friends have a final Node **init
argument that can have side effects appended,
making it more explicit when they do and do not happen.
this replaces the old global addtop and addtotop.

delete switch map and interface conversion cases
(dropped from the language months ago).

R=ken
OCL=31465
CL=31468
2009-07-10 16:29:26 -07:00

874 lines
15 KiB
C

// 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"
enum
{
Snorm = 0,
Strue,
Sfalse,
Stype,
Tdefault, // default case
Texprconst, // normal constant case
Texprvar, // normal variable case
Ttypenil, // case nil
Ttypeconst, // type hashes
Ttypevar, // interface type
Ncase = 4, // count needed to split
};
typedef struct Case Case;
struct Case
{
Node* node; // points at case statement
uint32 hash; // hash of a type switch
uint8 type; // type of case
uint8 diag; // suppress multiple diagnostics
uint16 ordinal; // position in switch
Case* link; // linked list to link
};
#define C ((Case*)nil)
Type*
notideal(Type *t)
{
if(t != T && t->etype == TIDEAL)
return T;
return t;
}
void
dumpcase(Case *c0)
{
Case *c;
for(c=c0; c!=C; c=c->link) {
switch(c->type) {
case Tdefault:
print("case-default\n");
print(" ord=%d\n", c->ordinal);
break;
case Texprconst:
print("case-exprconst\n");
print(" ord=%d\n", c->ordinal);
break;
case Texprvar:
print("case-exprvar\n");
print(" ord=%d\n", c->ordinal);
print(" op=%O\n", c->node->left->op);
break;
case Ttypenil:
print("case-typenil\n");
print(" ord=%d\n", c->ordinal);
break;
case Ttypeconst:
print("case-typeconst\n");
print(" ord=%d\n", c->ordinal);
print(" hash=%ux\n", c->hash);
break;
case Ttypevar:
print("case-typevar\n");
print(" ord=%d\n", c->ordinal);
break;
default:
print("case-???\n");
print(" ord=%d\n", c->ordinal);
print(" op=%O\n", c->node->left->op);
print(" hash=%ux\n", c->hash);
break;
}
}
print("\n");
}
static int
ordlcmp(Case *c1, Case *c2)
{
// sort default first
if(c1->type == Tdefault)
return -1;
if(c2->type == Tdefault)
return +1;
// sort nil second
if(c1->type == Ttypenil)
return -1;
if(c2->type == Ttypenil)
return +1;
// sort by ordinal
if(c1->ordinal > c2->ordinal)
return +1;
if(c1->ordinal < c2->ordinal)
return -1;
return 0;
}
static int
exprcmp(Case *c1, Case *c2)
{
int ct, n;
Node *n1, *n2;
// sort non-constants last
if(c1->type != Texprconst)
return +1;
if(c2->type != Texprconst)
return -1;
n1 = c1->node->left;
n2 = c2->node->left;
ct = n1->val.ctype;
if(ct != n2->val.ctype) {
// invalid program, but return a sort
// order so that we can give a better
// error later.
return ct - n2->val.ctype;
}
// sort by constant value
n = 0;
switch(ct) {
case CTFLT:
n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
break;
case CTINT:
n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval);
break;
case CTSTR:
n = cmpslit(n1, n2);
break;
}
return n;
}
static int
typecmp(Case *c1, Case *c2)
{
// sort non-constants last
if(c1->type != Ttypeconst)
return +1;
if(c2->type != Ttypeconst)
return -1;
// sort by hash code
if(c1->hash > c2->hash)
return +1;
if(c1->hash < c2->hash)
return -1;
return 0;
}
static Case*
csort(Case *l, int(*f)(Case*, Case*))
{
Case *l1, *l2, *le;
if(l == C || l->link == C)
return l;
l1 = l;
l2 = l;
for(;;) {
l2 = l2->link;
if(l2 == C)
break;
l2 = l2->link;
if(l2 == C)
break;
l1 = l1->link;
}
l2 = l1->link;
l1->link = C;
l1 = csort(l, f);
l2 = csort(l2, f);
/* set up lead element */
if((*f)(l1, l2) < 0) {
l = l1;
l1 = l1->link;
} else {
l = l2;
l2 = l2->link;
}
le = l;
for(;;) {
if(l1 == C) {
while(l2) {
le->link = l2;
le = l2;
l2 = l2->link;
}
le->link = C;
break;
}
if(l2 == C) {
while(l1) {
le->link = l1;
le = l1;
l1 = l1->link;
}
break;
}
if((*f)(l1, l2) < 0) {
le->link = l1;
le = l1;
l1 = l1->link;
} else {
le->link = l2;
le = l2;
l2 = l2->link;
}
}
le->link = C;
return l;
}
/*
* walktype
*/
Type*
sw0(Node *c, Type *place, int arg)
{
if(c == N)
return T;
switch(c->op) {
default:
if(arg == Stype) {
yyerror("inappropriate case for a type switch");
return T;
}
walkexpr(c, Erv, nil);
break;
case OTYPESW:
if(arg != Stype)
yyerror("inappropriate type case");
break;
case OAS:
yyerror("inappropriate assignment in a case statement");
break;
}
return T;
}
/*
* return the first type
*/
Type*
sw1(Node *c, Type *place, int arg)
{
if(place != T)
return notideal(c->type);
return place;
}
/*
* return a suitable type
*/
Type*
sw2(Node *c, Type *place, int arg)
{
return types[TINT]; // botch
}
/*
* check that switch type
* is compat with all the cases
*/
Type*
sw3(Node *c, Type *place, int arg)
{
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 parameter function.
* four passes of these are used to allocate
* types to cases and switch
*/
Type*
walkcases(Node *sw, Type*(*call)(Node*, Type*, int arg), int arg)
{
Iter save;
Node *n;
Type *place;
int32 lno;
lno = setlineno(sw);
place = call(sw->ntest, T, arg);
n = listfirst(&save, &sw->nbody->left);
if(n == N || 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 && !n->diag) {
setlineno(n);
place = call(n->left, place, arg);
}
n = listnext(&save);
goto loop;
}
Node*
newlabel(void)
{
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, save1;
Node *os, *oc, *n, *n1, *c;
Node *cas, *stat, *def;
Node *go, *br;
int32 lno;
lno = setlineno(sw);
n = listfirst(&save, &sw->nbody);
if(n == N || n->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);
for(; n != N; n = listnext(&save)) {
lno = setlineno(n);
if(n->op != OXCASE)
fatal("casebody %O", n->op);
n->op = OCASE;
go = nod(OGOTO, newlabel(), N);
c = n->left;
if(c == N) {
if(def != N)
yyerror("more than one default case");
// reuse original default case
n->right = go;
def = n;
}
// expand multi-valued cases
for(; c!=N; c=c->right) {
if(c->op != OLIST) {
// reuse original case
n->left = c;
n->right = go;
cas = list(cas, n);
break;
}
cas = list(cas, nod(OCASE, c->left, go));
}
stat = list(stat, nod(OLABEL, go->left, N));
os = N;
for(n1 = listfirst(&save1, &n->nbody); n1 != N; n1 = listnext(&save1)) {
os = n1;
stat = list(stat, n1);
}
// botch - shouldnt fall thru declaration
if(os != N && os->op == OXFALL)
os->op = OFALL;
else
stat = list(stat, br);
}
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;
}
Case*
mkcaselist(Node *sw, int arg)
{
Iter save;
Node *n;
Case *c, *c1;
int ord;
c = C;
ord = 0;
n = listfirst(&save, &sw->nbody->left);
loop:
if(n == N)
goto done;
c1 = mal(sizeof(*c1));
c1->link = c;
c = c1;
ord++;
c->ordinal = ord;
c->node = n;
if(n->left == N) {
c->type = Tdefault;
goto next;
}
switch(arg) {
case Stype:
c->hash = 0;
if(n->left->left == N) {
c->type = Ttypenil;
goto next;
}
if(istype(n->left->left->type, TINTER)) {
c->type = Ttypevar;
goto next;
}
c->hash = typehash(n->left->left->type, 1, 0);
c->type = Ttypeconst;
goto next;
case Snorm:
case Strue:
case Sfalse:
c->type = Texprvar;
switch(consttype(n->left)) {
case CTFLT:
case CTINT:
case CTSTR:
c->type = Texprconst;
}
goto next;
}
next:
n = listnext(&save);
goto loop;
done:
if(c == C)
return C;
// sort by value and diagnose duplicate cases
switch(arg) {
case Stype:
c = csort(c, typecmp);
for(c1=c; c1->link!=C; c1=c1->link) {
if(typecmp(c1, c1->link) != 0)
continue;
setlineno(c1->link->node);
yyerror("duplicate case in switch");
print(" previous case at %L\n",
c1->node->lineno);
}
break;
case Snorm:
case Strue:
case Sfalse:
c = csort(c, exprcmp);
for(c1=c; c1->link!=C; c1=c1->link) {
if(exprcmp(c1, c1->link) != 0)
continue;
setlineno(c1->link->node);
yyerror("duplicate case in switch");
print(" previous case at %L\n",
c1->node->lineno);
}
break;
}
// put list back in processing order
c = csort(c, ordlcmp);
return c;
}
static Node* exprname;
Node*
exprbsw(Case *c0, int ncase, int arg)
{
Node *cas;
Node *a, *n;
Case *c;
int i, half, lno;
cas = N;
if(ncase < Ncase) {
for(i=0; i<ncase; i++) {
n = c0->node;
lno = setlineno(n);
switch(arg) {
case Strue:
a = nod(OIF, N, N);
a->ntest = n->left; // if val
a->nbody = n->right; // then goto l
break;
case Sfalse:
a = nod(OIF, N, N);
a->ntest = nod(ONOT, n->left, N); // if !val
a->nbody = n->right; // then goto l
break;
default:
a = nod(OIF, N, N);
a->ntest = nod(OEQ, exprname, n->left); // if name == val
a->nbody = n->right; // then goto l
break;
}
cas = list(cas, a);
c0 = c0->link;
lineno = lno;
}
return cas;
}
// find the middle and recur
c = c0;
half = ncase>>1;
for(i=1; i<half; i++)
c = c->link;
a = nod(OIF, N, N);
a->ntest = nod(OLE, exprname, c->node->left);
a->nbody = exprbsw(c0, half, arg);
a->nelse = exprbsw(c->link, ncase-half, arg);
return a;
}
/*
* normal (expression) switch.
* rebulid case statements into if .. goto
*/
void
exprswitch(Node *sw)
{
Node *def, *cas;
Node *a;
Case *c0, *c, *c1;
Type *t;
int arg, ncase;
arg = Snorm;
if(isconst(sw->ntest, CTBOOL)) {
arg = Strue;
if(sw->ntest->val.u.bval == 0)
arg = Sfalse;
}
walkexpr(sw->ntest, Erv, &sw->ninit);
/*
* pass 0,1,2,3
* walk the cases as appropriate for switch type
*/
walkcases(sw, sw0, arg);
t = notideal(sw->ntest->type);
if(t == T)
t = walkcases(sw, sw1, arg);
if(t == T)
t = walkcases(sw, sw2, arg);
if(t == T)
return;
walkcases(sw, sw3, arg);
convlit(sw->ntest, t);
/*
* convert the switch into OIF statements
*/
exprname = N;
cas = N;
if(arg != Strue && arg != Sfalse) {
exprname = nod(OXXX, N, N);
tempname(exprname, sw->ntest->type);
cas = nod(OAS, exprname, sw->ntest);
}
c0 = mkcaselist(sw, arg);
if(c0 != C && c0->type == Tdefault) {
def = c0->node->right;
c0 = c0->link;
} else {
def = nod(OBREAK, N, N);
}
loop:
if(c0 == C) {
cas = list(cas, def);
sw->nbody->left = rev(cas);
walkstmt(sw->nbody);
return;
}
// deal with the variables one-at-a-time
if(c0->type != Texprconst) {
a = exprbsw(c0, 1, arg);
cas = list(cas, a);
c0 = c0->link;
goto loop;
}
// do binary search on run of constants
ncase = 1;
for(c=c0; c->link!=C; c=c->link) {
if(c->link->type != Texprconst)
break;
ncase++;
}
// break the chain at the count
c1 = c->link;
c->link = C;
// sort and compile constants
c0 = csort(c0, exprcmp);
a = exprbsw(c0, ncase, arg);
cas = list(cas, a);
c0 = c1;
goto loop;
}
static Node* hashname;
static Node* facename;
static Node* boolname;
Node*
typeone(Node *t)
{
Node *a, *b, *dcl;
a = t->left->left; // var
dcl = nod(ODCL, a, N);
a = nod(OLIST, a, boolname); // var,bool
b = nod(ODOTTYPE, facename, N);
b->type = t->left->left->type; // interface.(type)
a = nod(OAS, a, b); // var,bool = interface.(type)
b = nod(OIF, N, N);
b->ntest = boolname;
b->nbody = t->right; // if bool { goto l }
return list(list(dcl, a), b);
}
Node*
typebsw(Case *c0, int ncase)
{
Node *cas;
Node *a, *n;
Case *c;
int i, half;
Val v;
cas = N;
if(ncase < Ncase) {
for(i=0; i<ncase; i++) {
n = c0->node;
switch(c0->type) {
case Ttypenil:
v.ctype = CTNIL;
a = nod(OIF, N, N);
a->ntest = nod(OEQ, facename, nodlit(v));
a->nbody = n->right; // if i==nil { goto l }
cas = list(cas, a);
break;
case Ttypevar:
a = typeone(n);
cas = list(cas, a);
break;
case Ttypeconst:
a = nod(OIF, N, N);
a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
a->nbody = rev(typeone(n));
cas = list(cas, a);
break;
}
c0 = c0->link;
}
return cas;
}
// find the middle and recur
c = c0;
half = ncase>>1;
for(i=1; i<half; i++)
c = c->link;
a = nod(OIF, N, N);
a->ntest = nod(OLE, hashname, nodintconst(c->hash));
a->nbody = typebsw(c0, half);
a->nelse = typebsw(c->link, ncase-half);
return a;
}
/*
* convert switch of the form
* switch v := i.(type) { case t1: ..; case t2: ..; }
* into if statements
*/
void
typeswitch(Node *sw)
{
Node *cas, *def;
Node *a;
Case *c, *c0, *c1;
int ncase;
Type *t;
if(sw->ntest == nil)
return;
if(sw->ntest->right == nil) {
setlineno(sw);
yyerror("type switch must have an assignment");
return;
}
walkexpr(sw->ntest->right, Erv, &sw->ninit);
if(!istype(sw->ntest->right->type, TINTER)) {
yyerror("type switch must be on an interface");
return;
}
walkcases(sw, sw0, Stype);
cas = N;
/*
* predeclare temporary variables
* and the boolean var
*/
facename = nod(OXXX, N, N);
tempname(facename, sw->ntest->right->type);
a = nod(OAS, facename, sw->ntest->right);
cas = list(cas, a);
boolname = nod(OXXX, N, N);
tempname(boolname, types[TBOOL]);
hashname = nod(OXXX, N, N);
tempname(hashname, types[TUINT32]);
t = sw->ntest->right->type;
if(isnilinter(t))
a = syslook("efacethash", 1);
else
a = syslook("ifacethash", 1);
argtype(a, t);
a = nod(OCALL, a, facename);
a = nod(OAS, hashname, a);
cas = list(cas, a);
c0 = mkcaselist(sw, Stype);
if(c0 != C && c0->type == Tdefault) {
def = c0->node->right;
c0 = c0->link;
} else {
def = nod(OBREAK, N, N);
}
loop:
if(c0 == C) {
cas = list(cas, def);
sw->nbody->left = rev(cas);
walkstmt(sw->nbody);
return;
}
// deal with the variables one-at-a-time
if(c0->type != Ttypeconst) {
a = typebsw(c0, 1);
cas = list(cas, a);
c0 = c0->link;
goto loop;
}
// do binary search on run of constants
ncase = 1;
for(c=c0; c->link!=C; c=c->link) {
if(c->link->type != Ttypeconst)
break;
ncase++;
}
// break the chain at the count
c1 = c->link;
c->link = C;
// sort and compile constants
c0 = csort(c0, typecmp);
a = typebsw(c0, ncase);
cas = list(cas, a);
c0 = c1;
goto loop;
}
void
walkswitch(Node *sw)
{
/*
* reorder the body into (OLIST, cases, statements)
* cases have OGOTO into statements.
* both have inserted OBREAK statements
*/
walkstmt(sw->ninit);
if(sw->ntest == N)
sw->ntest = nodbool(1);
casebody(sw);
if(sw->ntest->op == OTYPESW) {
typeswitch(sw);
return;
}
exprswitch(sw);
}