1
0
mirror of https://github.com/golang/go synced 2024-11-25 07:07:57 -07:00

gc: stricter multiple assignment + test

Fixes #693.

R=ken2
CC=golang-dev
https://golang.org/cl/5265045
This commit is contained in:
Russ Cox 2011-10-13 15:46:39 -04:00
parent fa538114ed
commit eb3aba24b5
6 changed files with 360 additions and 35 deletions

View File

@ -415,6 +415,7 @@ oldname(Sym *s)
c->funcdepth = funcdepth; c->funcdepth = funcdepth;
c->outer = n->closure; c->outer = n->closure;
n->closure = c; n->closure = c;
n->addrtaken = 1;
c->closure = n; c->closure = n;
c->xoffset = 0; c->xoffset = 0;
curfn->cvars = list(curfn->cvars, c); curfn->cvars = list(curfn->cvars, c);

View File

@ -266,6 +266,7 @@ struct Node
uchar isddd; uchar isddd;
uchar readonly; uchar readonly;
uchar implicit; // don't show in printout uchar implicit; // don't show in printout
uchar addrtaken; // address taken, even if not moved to heap
// most nodes // most nodes
Type* type; Type* type;

View File

@ -1155,6 +1155,9 @@ Jconv(Fmt *fp)
if(n->funcdepth != 0) if(n->funcdepth != 0)
fmtprint(fp, " f(%d)", n->funcdepth); fmtprint(fp, " f(%d)", n->funcdepth);
if(n->addrtaken != 0)
fmtprint(fp, " addrtaken(1)");
switch(n->esc) { switch(n->esc) {
case EscUnknown: case EscUnknown:
break; break;

View File

@ -532,6 +532,9 @@ reswitch:
default: default:
checklvalue(n->left, "take the address of"); checklvalue(n->left, "take the address of");
} }
for(l=n->left; l->op == ODOT; l=l->left)
l->addrtaken = 1;
l->addrtaken = 1;
defaultlit(&n->left, T); defaultlit(&n->left, T);
l = n->left; l = n->left;
if((t = l->type) == T) if((t = l->type) == T)

View File

@ -124,8 +124,9 @@ paramoutheap(Node *fn)
for(l=fn->dcl; l; l=l->next) { for(l=fn->dcl; l; l=l->next) {
switch(l->n->class) { switch(l->n->class) {
case PPARAMOUT:
case PPARAMOUT|PHEAP: case PPARAMOUT|PHEAP:
return 1; return l->n->addrtaken;
case PAUTO: case PAUTO:
case PAUTO|PHEAP: case PAUTO|PHEAP:
// stop early - parameters are over // stop early - parameters are over
@ -285,6 +286,9 @@ walkstmt(Node **np)
n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit)); n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit));
break; break;
} }
// move function calls out, to make reorder3's job easier.
walkexprlistsafe(n->list, &n->ninit);
ll = ascompatee(n->op, rl, n->list, &n->ninit); ll = ascompatee(n->op, rl, n->list, &n->ninit);
n->list = reorder3(ll); n->list = reorder3(ll);
break; break;
@ -1308,10 +1312,11 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
} }
if(ll != nil || r != T) if(ll != nil || r != T)
yyerror("assignment count mismatch: %d = %d", yyerror("ascompatet: assignment count mismatch: %d = %d",
count(nl), structcount(*nr)); count(nl), structcount(*nr));
if(ucount) if(ucount)
fatal("reorder2: too many function calls evaluating parameters"); fatal("ascompatet: too many function calls evaluating parameters");
return concat(nn, mm); return concat(nn, mm);
} }
@ -1790,28 +1795,242 @@ reorder1(NodeList *all)
return concat(g, r); return concat(g, r);
} }
static void reorder3save(Node**, NodeList*, NodeList*, NodeList**);
static int aliased(Node*, NodeList*, NodeList*);
/* /*
* from ascompat[ee] * from ascompat[ee]
* a,b = c,d * a,b = c,d
* simultaneous assignment. there cannot * simultaneous assignment. there cannot
* be later use of an earlier lvalue. * be later use of an earlier lvalue.
*
* function calls have been removed.
*/ */
static NodeList*
reorder3(NodeList *all)
{
NodeList *list, *early;
Node *l;
// If a needed expression may be affected by an
// earlier assignment, make an early copy of that
// expression and use the copy instead.
early = nil;
for(list=all; list; list=list->next) {
l = list->n->left;
// Save subexpressions needed on left side.
// Drill through non-dereferences.
for(;;) {
if(l->op == ODOT || l->op == OPAREN) {
l = l->left;
continue;
}
if(l->op == OINDEX && isfixedarray(l->left->type)) {
reorder3save(&l->right, all, list, &early);
l = l->left;
continue;
}
break;
}
switch(l->op) {
default:
fatal("reorder3 unexpected lvalue %#O", l->op);
case ONAME:
break;
case OINDEX:
reorder3save(&l->left, all, list, &early);
reorder3save(&l->right, all, list, &early);
break;
case OIND:
case ODOTPTR:
reorder3save(&l->left, all, list, &early);
}
// Save expression on right side.
reorder3save(&list->n->right, all, list, &early);
}
return concat(early, all);
}
static int vmatch2(Node*, Node*);
static int varexpr(Node*);
/*
* if the evaluation of *np would be affected by the
* assignments in all up to but not including stop,
* copy into a temporary during *early and
* replace *np with that temp.
*/
static void
reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early)
{
Node *n, *q;
n = *np;
if(!aliased(n, all, stop))
return;
q = temp(n->type);
q = nod(OAS, q, n);
q->typecheck = 1;
*early = list(*early, q);
*np = q->left;
}
/*
* what's the outer value that a write to n affects?
* outer value means containing struct or array.
*/
static Node*
outervalue(Node *n)
{
for(;;) {
if(n->op == ODOT || n->op == OPAREN) {
n = n->left;
continue;
}
if(n->op == OINDEX && isfixedarray(n->left->type)) {
n = n->left;
continue;
}
break;
}
return n;
}
/*
* Is it possible that the computation of n might be
* affected by writes in as up to but not including stop?
*/
static int
aliased(Node *n, NodeList *all, NodeList *stop)
{
int memwrite, varwrite;
Node *a;
NodeList *l;
if(n == N)
return 0;
// Look for obvious aliasing: a variable being assigned
// during the all list and appearing in n.
// Also record whether there are any writes to main memory.
// Also record whether there are any writes to variables
// whose addresses have been taken.
memwrite = 0;
varwrite = 0;
for(l=all; l!=stop; l=l->next) {
a = outervalue(l->n->left);
if(a->op != ONAME) {
memwrite = 1;
continue;
}
switch(n->class) {
default:
varwrite = 1;
continue;
case PAUTO:
case PPARAM:
case PPARAMOUT:
if(n->addrtaken) {
varwrite = 1;
continue;
}
if(vmatch2(a, n)) {
// Direct hit.
return 1;
}
}
}
// The variables being written do not appear in n.
// However, n might refer to computed addresses
// that are being written.
// If no computed addresses are affected by the writes, no aliasing.
if(!memwrite && !varwrite)
return 0;
// If n does not refer to computed addresses
// (that is, if n only refers to variables whose addresses
// have not been taken), no aliasing.
if(varexpr(n))
return 0;
// Otherwise, both the writes and n refer to computed memory addresses.
// Assume that they might conflict.
return 1;
}
/*
* does the evaluation of n only refer to variables
* whose addresses have not been taken?
* (and no other memory)
*/
static int
varexpr(Node *n)
{
if(n == N)
return 1;
switch(n->op) {
case OLITERAL:
return 1;
case ONAME:
switch(n->class) {
case PAUTO:
case PPARAM:
case PPARAMOUT:
if(!n->addrtaken)
return 1;
}
return 0;
case OADD:
case OSUB:
case OOR:
case OXOR:
case OMUL:
case ODIV:
case OMOD:
case OLSH:
case ORSH:
case OAND:
case OANDNOT:
case OPLUS:
case OMINUS:
case OCOM:
case OPAREN:
case OANDAND:
case OOROR:
case ODOT: // but not ODOTPTR
case OCONV:
case OCONVNOP:
case OCONVIFACE:
case ODOTTYPE:
return varexpr(n->left) && varexpr(n->right);
}
// Be conservative.
return 0;
}
/*
* is the name l mentioned in r?
*/
static int static int
vmatch2(Node *l, Node *r) vmatch2(Node *l, Node *r)
{ {
NodeList *ll; NodeList *ll;
/*
* isolate all right sides
*/
if(r == N) if(r == N)
return 0; return 0;
switch(r->op) { switch(r->op) {
case ONAME: case ONAME:
// match each right given left // match each right given left
if(l == r) return l == r;
return 1;
case OLITERAL: case OLITERAL:
return 0; return 0;
} }
@ -1825,6 +2044,10 @@ vmatch2(Node *l, Node *r)
return 0; return 0;
} }
/*
* is any name mentioned in l also mentioned in r?
* called by sinit.c
*/
int int
vmatch1(Node *l, Node *r) vmatch1(Node *l, Node *r)
{ {
@ -1863,33 +2086,6 @@ vmatch1(Node *l, Node *r)
return 0; return 0;
} }
static NodeList*
reorder3(NodeList *all)
{
Node *n1, *n2, *q;
int c1, c2;
NodeList *l1, *l2, *r;
r = nil;
for(l1=all, c1=0; l1; l1=l1->next, c1++) {
n1 = l1->n;
for(l2=all, c2=0; l2; l2=l2->next, c2++) {
n2 = l2->n;
if(c2 > c1) {
if(vmatch1(n1->left, n2->right)) {
// delay assignment to n1->left
q = temp(n1->right->type);
q = nod(OAS, n1->left, q);
n1->left = q->right;
r = list(r, q);
break;
}
}
}
}
return concat(all, r);
}
/* /*
* walk through argin parameters. * walk through argin parameters.
* generate and return code to allocate * generate and return code to allocate

121
test/reorder.go Normal file
View File

@ -0,0 +1,121 @@
// $G $D/$F.go && $L $F.$A && ./$A.out
// Copyright 2011 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.
// Check reordering of assignments.
package main
import "fmt"
func main() {
p1()
p2()
p3()
p4()
p5()
p6()
p7()
p8()
}
var gx []int
func f(i int) int {
return gx[i]
}
func check(x []int, x0, x1, x2 int) {
if x[0] != x0 || x[1] != x1 || x[2] != x2 {
fmt.Printf("%v, want %d,%d,%d\n", x, x0, x1, x2)
panic("failed")
}
}
func check3(x, y, z, xx, yy, zz int) {
if x != xx || y != yy || z != zz {
fmt.Printf("%d,%d,%d, want %d,%d,%d\n", x, y, z, xx, yy, zz)
panic("failed")
}
}
func p1() {
x := []int{1,2,3}
i := 0
i, x[i] = 1, 100
_ = i
check(x, 100, 2, 3)
}
func p2() {
x := []int{1,2,3}
i := 0
x[i], i = 100, 1
_ = i
check(x, 100, 2, 3)
}
func p3() {
x := []int{1,2,3}
y := x
gx = x
x[1], y[0] = f(0), f(1)
check(x, 2, 1, 3)
}
func p4() {
x := []int{1,2,3}
y := x
gx = x
x[1], y[0] = gx[0], gx[1]
check(x, 2, 1, 3)
}
func p5() {
x := []int{1,2,3}
y := x
p := &x[0]
q := &x[1]
*p, *q = x[1], y[0]
check(x, 2, 1, 3)
}
func p6() {
x := 1
y := 2
z := 3
px := &x
py := &y
*px, *py = y, x
check3(x, y, z, 2, 1, 3)
}
func f1(x, y, z int) (xx, yy, zz int) {
return x, y, z
}
func f2() (x, y, z int) {
return f1(2, 1, 3)
}
func p7() {
x, y, z := f2()
check3(x, y, z, 2, 1, 3)
}
func p8() {
x := []int{1,2,3}
defer func() {
err := recover()
if err == nil {
panic("not panicking")
}
check(x, 100, 2, 3)
}()
i := 0
i, x[i], x[5] = 1, 100, 500
}