cmd/gc, runtime: treat slices and strings like pointers in garbage collection

Before, a slice with cap=0 or a string with len=0 might have its
base pointer pointing beyond the actual slice/string data into
the next block. The collector had to ignore slices and strings with
cap=0 in order to avoid misinterpreting the base pointer.

Now, a slice with cap=0 or a string with len=0 still has a base
pointer pointing into the actual slice/string data, no matter what.
The collector can now always scan the pointer, which means
strings and slices are no longer special.

Fixes #8404.

LGTM=khr, josharian
R=josharian, khr, dvyukov
CC=golang-codereviews
https://golang.org/cl/112570044
This commit is contained in:
Russ Cox 2014-08-25 14:38:19 -04:00
parent c6f7c176a3
commit 613383c765
25 changed files with 248 additions and 162 deletions

View file

@ -254,7 +254,6 @@ cgen(Node *n, Node *res)
case OOR: case OOR:
case OXOR: case OXOR:
case OADD: case OADD:
case OADDPTR:
case OMUL: case OMUL:
a = optoas(n->op, nl->type); a = optoas(n->op, nl->type);
goto sbop; goto sbop;

View file

@ -1599,7 +1599,6 @@ optoas(int op, Type *t)
case CASE(OADD, TINT32): case CASE(OADD, TINT32):
case CASE(OADD, TUINT32): case CASE(OADD, TUINT32):
case CASE(OADD, TPTR32): case CASE(OADD, TPTR32):
case CASE(OADDPTR, TPTR32):
a = AADD; a = AADD;
break; break;

View file

@ -247,7 +247,6 @@ cgen(Node *n, Node *res)
case OOR: case OOR:
case OXOR: case OXOR:
case OADD: case OADD:
case OADDPTR:
case OMUL: case OMUL:
a = optoas(n->op, nl->type); a = optoas(n->op, nl->type);
if(a == AIMULB) { if(a == AIMULB) {

View file

@ -1537,14 +1537,12 @@ optoas(int op, Type *t)
case CASE(OADD, TINT32): case CASE(OADD, TINT32):
case CASE(OADD, TUINT32): case CASE(OADD, TUINT32):
case CASE(OADD, TPTR32): case CASE(OADD, TPTR32):
case CASE(OADDPTR, TPTR32):
a = AADDL; a = AADDL;
break; break;
case CASE(OADD, TINT64): case CASE(OADD, TINT64):
case CASE(OADD, TUINT64): case CASE(OADD, TUINT64):
case CASE(OADD, TPTR64): case CASE(OADD, TPTR64):
case CASE(OADDPTR, TPTR64):
a = AADDQ; a = AADDQ;
break; break;

View file

@ -242,7 +242,6 @@ cgen(Node *n, Node *res)
case OOR: case OOR:
case OXOR: case OXOR:
case OADD: case OADD:
case OADDPTR:
case OMUL: case OMUL:
a = optoas(n->op, nl->type); a = optoas(n->op, nl->type);
if(a == AIMULB) { if(a == AIMULB) {

View file

@ -430,7 +430,6 @@ optoas(int op, Type *t)
case CASE(OADD, TINT32): case CASE(OADD, TINT32):
case CASE(OADD, TUINT32): case CASE(OADD, TUINT32):
case CASE(OADD, TPTR32): case CASE(OADD, TPTR32):
case CASE(OADDPTR, TPTR32):
a = AADDL; a = AADDL;
break; break;

View file

@ -1153,7 +1153,7 @@ exprfmt(Fmt *f, Node *n, int prec)
case Csend: case Csend:
return fmtprint(f, "chan<- %N", n->left); return fmtprint(f, "chan<- %N", n->left);
default: default:
if(n->left != N && n->left->op == TCHAN && n->left->sym == S && n->left->etype == Crecv) if(n->left != N && n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv)
return fmtprint(f, "chan (%N)", n->left); return fmtprint(f, "chan (%N)", n->left);
else else
return fmtprint(f, "chan %N", n->left); return fmtprint(f, "chan %N", n->left);

View file

@ -806,7 +806,8 @@ cgen_eface(Node *n, Node *res)
void void
cgen_slice(Node *n, Node *res) cgen_slice(Node *n, Node *res)
{ {
Node src, dst, *cap, *len, *offs, *add, *base; Node src, dst, *cap, *len, *offs, *add, *base, *tmpcap, *tmplen, *cmp, con;
Prog *p1, *p2;
cap = n->list->n; cap = n->list->n;
len = n->list->next->n; len = n->list->next->n;
@ -823,6 +824,11 @@ cgen_slice(Node *n, Node *res)
// garbage collector can see. // garbage collector can see.
base = temp(types[TUINTPTR]); base = temp(types[TUINTPTR]);
tmplen = temp(types[TINT]);
if(n->op != OSLICESTR)
tmpcap = temp(types[TINT]);
else
tmpcap = tmplen;
if(isnil(n->left)) { if(isnil(n->left)) {
tempname(&src, n->left->type); tempname(&src, n->left->type);
@ -837,43 +843,62 @@ cgen_slice(Node *n, Node *res)
fatal("slicearr is supposed to work on pointer: %+N\n", n); fatal("slicearr is supposed to work on pointer: %+N\n", n);
cgen(&src, base); cgen(&src, base);
cgen_checknil(base); cgen_checknil(base);
if(offs != N) {
add = nod(OADD, base, offs);
typecheck(&add, Erv);
cgen(add, base);
}
} else if(offs == N) {
src.type = types[tptr];
cgen(&src, base);
} else { } else {
src.type = types[tptr]; src.type = types[tptr];
add = nod(OADDPTR, &src, offs); cgen(&src, base);
typecheck(&add, Erv);
cgen(add, base);
} }
// committed to the update // committed to the update
gvardef(res); gvardef(res);
// compute len and cap.
// len = n-i, cap = m-i, and offs = i*width.
// computing offs last lets the multiply overwrite i.
cgen(len, tmplen);
if(n->op != OSLICESTR)
cgen(cap, tmpcap);
// if new cap != 0 { base += add }
// This avoids advancing base past the end of the underlying array/string,
// so that it cannot point at the next object in memory.
// If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero.
// In essence we are replacing x[i:j:k] where i == j == k
// or x[i:j] where i == j == cap(x) with x[0:0:0].
if(offs != N) {
p1 = gjmp(P);
p2 = gjmp(P);
patch(p1, pc);
nodconst(&con, tmpcap->type, 0);
cmp = nod(OEQ, tmpcap, &con);
typecheck(&cmp, Erv);
bgen(cmp, 1, -1, p2);
add = nod(OADD, base, offs);
typecheck(&add, Erv);
cgen(add, base);
patch(p2, pc);
}
// dst.array = src.array [ + lo *width ] // dst.array = src.array [ + lo *width ]
dst = *res; dst = *res;
dst.xoffset += Array_array; dst.xoffset += Array_array;
dst.type = types[tptr]; dst.type = types[tptr];
cgen(base, &dst); cgen(base, &dst);
// dst.len = hi [ - lo ] // dst.len = hi [ - lo ]
dst = *res; dst = *res;
dst.xoffset += Array_nel; dst.xoffset += Array_nel;
dst.type = types[simtype[TUINT]]; dst.type = types[simtype[TUINT]];
cgen(len, &dst); cgen(tmplen, &dst);
if(n->op != OSLICESTR) { if(n->op != OSLICESTR) {
// dst.cap = cap [ - lo ] // dst.cap = cap [ - lo ]
dst = *res; dst = *res;
dst.xoffset += Array_cap; dst.xoffset += Array_cap;
dst.type = types[simtype[TUINT]]; dst.type = types[simtype[TUINT]];
cgen(cap, &dst); cgen(tmpcap, &dst);
} }
} }

View file

@ -447,7 +447,6 @@ enum
OSUB, // x - y OSUB, // x - y
OOR, // x | y OOR, // x | y
OXOR, // x ^ y OXOR, // x ^ y
OADDPTR, // ptr + uintptr, inserted by compiler only, used to avoid unsafe type changes during codegen
OADDSTR, // s + "foo" OADDSTR, // s + "foo"
OADDR, // &x OADDR, // &x
OANDAND, // b0 && b1 OANDAND, // b0 && b1

View file

@ -1113,8 +1113,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
// struct { byte *str; intgo len; } // struct { byte *str; intgo len; }
if((*xoffset & (widthptr-1)) != 0) if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid alignment, %T", t); fatal("twobitwalktype1: invalid alignment, %T", t);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0); bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 3:0 = multiword:string
*xoffset += t->width; *xoffset += t->width;
break; break;
@ -1145,9 +1144,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
// struct { byte *array; uintgo len; uintgo cap; } // struct { byte *array; uintgo len; uintgo cap; }
if((*xoffset & (widthptr-1)) != 0) if((*xoffset & (widthptr-1)) != 0)
fatal("twobitwalktype1: invalid TARRAY alignment, %T", t); fatal("twobitwalktype1: invalid TARRAY alignment, %T", t);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0); bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1);
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 2); // 3:1 = multiword/slice
*xoffset += t->width; *xoffset += t->width;
} else } else
for(i = 0; i < t->bound; i++) for(i = 0; i < t->bound; i++)

View file

@ -1516,8 +1516,8 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
*xoffset += t->width; *xoffset += t->width;
break; break;
case TSTRING: case TSTRING:
proggendata(g, BitsMultiWord); proggendata(g, BitsPointer);
proggendata(g, BitsString); proggendata(g, BitsScalar);
*xoffset += t->width; *xoffset += t->width;
break; break;
case TINTER: case TINTER:
@ -1530,8 +1530,8 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
break; break;
case TARRAY: case TARRAY:
if(isslice(t)) { if(isslice(t)) {
proggendata(g, BitsMultiWord); proggendata(g, BitsPointer);
proggendata(g, BitsSlice); proggendata(g, BitsScalar);
proggendata(g, BitsScalar); proggendata(g, BitsScalar);
} else { } else {
t1 = t->type; t1 = t->type;

View file

@ -656,11 +656,15 @@ maptype(Type *key, Type *val)
{ {
Type *t; Type *t;
Type *bad; Type *bad;
int atype; int atype, mtype;
if(key != nil) { if(key != nil) {
atype = algtype1(key, &bad); atype = algtype1(key, &bad);
switch(bad == T ? key->etype : bad->etype) { if(bad == T)
mtype = key->etype;
else
mtype = bad->etype;
switch(mtype) {
default: default:
if(atype == ANOEQ) if(atype == ANOEQ)
yyerror("invalid map key type %T", key); yyerror("invalid map key type %T", key);

View file

@ -525,19 +525,6 @@ reswitch:
op = n->etype; op = n->etype;
goto arith; goto arith;
case OADDPTR:
ok |= Erv;
l = typecheck(&n->left, Erv);
r = typecheck(&n->right, Erv);
if(l->type == T || r->type == T)
goto error;
if(l->type->etype != tptr)
fatal("bad OADDPTR left type %E for %N", l->type->etype, n->left);
if(r->type->etype != TUINTPTR)
fatal("bad OADDPTR right type %E for %N", r->type->etype, n->right);
n->type = types[tptr];
goto ret;
case OADD: case OADD:
case OAND: case OAND:
case OANDAND: case OANDAND:

View file

@ -2875,14 +2875,14 @@ sliceany(Node* n, NodeList **init)
lb = N; lb = N;
} }
// dynamic checks convert all bounds to unsigned to save us the bound < 0 comparison // Checking src[lb:hb:cb] or src[lb:hb].
// generate // if chk0 || chk1 || chk2 { panicslice() }
// if hb > bound || lb > hb { panicslice() }
chk = N; chk = N;
chk0 = N; chk0 = N; // cap(src) < cb
chk1 = N; chk1 = N; // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
chk2 = N; chk2 = N; // hb < lb
// All comparisons are unsigned to avoid testing < 0.
bt = types[simtype[TUINT]]; bt = types[simtype[TUINT]];
if(cb != N && cb->type->width > 4) if(cb != N && cb->type->width > 4)
bt = types[TUINT64]; bt = types[TUINT64];

View file

@ -2494,6 +2494,15 @@ func TestSlice(t *testing.T) {
if vs != s[3:5] { if vs != s[3:5] {
t.Errorf("s.Slice(3, 5) = %q; expected %q", vs, s[3:5]) t.Errorf("s.Slice(3, 5) = %q; expected %q", vs, s[3:5])
} }
rv := ValueOf(&xs).Elem()
rv = rv.Slice(3, 4)
ptr2 := rv.Pointer()
rv = rv.Slice(5, 5)
ptr3 := rv.Pointer()
if ptr3 != ptr2 {
t.Errorf("xs.Slice(3,4).Slice3(5,5).Pointer() = %#x, want %#x", ptr3, ptr2)
}
} }
func TestSlice3(t *testing.T) { func TestSlice3(t *testing.T) {
@ -2532,6 +2541,15 @@ func TestSlice3(t *testing.T) {
s := "hello world" s := "hello world"
rv = ValueOf(&s).Elem() rv = ValueOf(&s).Elem()
shouldPanic(func() { rv.Slice3(1, 2, 3) }) shouldPanic(func() { rv.Slice3(1, 2, 3) })
rv = ValueOf(&xs).Elem()
rv = rv.Slice3(3, 5, 7)
ptr2 := rv.Pointer()
rv = rv.Slice3(4, 4, 4)
ptr3 := rv.Pointer()
if ptr3 != ptr2 {
t.Errorf("xs.Slice3(3,5,7).Slice3(4,4,4).Pointer() = %#x, want %#x", ptr3, ptr2)
}
} }
func TestSetLenCap(t *testing.T) { func TestSetLenCap(t *testing.T) {

View file

@ -1746,9 +1746,14 @@ func (v Value) Slice(i, j int) Value {
// Reinterpret as *sliceHeader to edit. // Reinterpret as *sliceHeader to edit.
s := (*sliceHeader)(unsafe.Pointer(&x)) s := (*sliceHeader)(unsafe.Pointer(&x))
s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
s.Len = j - i s.Len = j - i
s.Cap = cap - i s.Cap = cap - i
if cap-i > 0 {
s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
} else {
// do not advance pointer, to avoid pointing beyond end of slice
s.Data = base
}
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
return Value{typ.common(), unsafe.Pointer(&x), 0, fl} return Value{typ.common(), unsafe.Pointer(&x), 0, fl}
@ -1793,9 +1798,14 @@ func (v Value) Slice3(i, j, k int) Value {
// Reinterpret as *sliceHeader to edit. // Reinterpret as *sliceHeader to edit.
s := (*sliceHeader)(unsafe.Pointer(&x)) s := (*sliceHeader)(unsafe.Pointer(&x))
s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
s.Len = j - i s.Len = j - i
s.Cap = k - i s.Cap = k - i
if k-i > 0 {
s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
} else {
// do not advance pointer, to avoid pointing beyond end of slice
s.Data = base
}
fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift
return Value{typ.common(), unsafe.Pointer(&x), 0, fl} return Value{typ.common(), unsafe.Pointer(&x), 0, fl}

View file

@ -14,7 +14,7 @@ import (
func TestGCInfo(t *testing.T) { func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, nonStackInfo(infoScalarPtr)) verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, nonStackInfo(infoScalarPtr))
verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, nonStackInfo(infoPtrScalar)) verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, nonStackInfo(infoPtrScalar))
verifyGCInfo(t, "bss Complex", &bssComplex, nonStackInfo(infoComplex())) verifyGCInfo(t, "bss BigStruct", &bssBigStruct, nonStackInfo(infoBigStruct()))
verifyGCInfo(t, "bss string", &bssString, nonStackInfo(infoString)) verifyGCInfo(t, "bss string", &bssString, nonStackInfo(infoString))
verifyGCInfo(t, "bss slice", &bssSlice, nonStackInfo(infoSlice)) verifyGCInfo(t, "bss slice", &bssSlice, nonStackInfo(infoSlice))
verifyGCInfo(t, "bss eface", &bssEface, nonStackInfo(infoEface)) verifyGCInfo(t, "bss eface", &bssEface, nonStackInfo(infoEface))
@ -22,7 +22,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, nonStackInfo(infoScalarPtr)) verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, nonStackInfo(infoScalarPtr))
verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, nonStackInfo(infoPtrScalar)) verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, nonStackInfo(infoPtrScalar))
verifyGCInfo(t, "data Complex", &dataComplex, nonStackInfo(infoComplex())) verifyGCInfo(t, "data BigStruct", &dataBigStruct, nonStackInfo(infoBigStruct()))
verifyGCInfo(t, "data string", &dataString, nonStackInfo(infoString)) verifyGCInfo(t, "data string", &dataString, nonStackInfo(infoString))
verifyGCInfo(t, "data slice", &dataSlice, nonStackInfo(infoSlice)) verifyGCInfo(t, "data slice", &dataSlice, nonStackInfo(infoSlice))
verifyGCInfo(t, "data eface", &dataEface, nonStackInfo(infoEface)) verifyGCInfo(t, "data eface", &dataEface, nonStackInfo(infoEface))
@ -30,7 +30,7 @@ func TestGCInfo(t *testing.T) {
verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr) verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr)
verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar) verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar)
verifyGCInfo(t, "stack Complex", new(Complex), infoComplex()) verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct())
verifyGCInfo(t, "stack string", new(string), infoString) verifyGCInfo(t, "stack string", new(string), infoString)
verifyGCInfo(t, "stack slice", new([]string), infoSlice) verifyGCInfo(t, "stack slice", new([]string), infoSlice)
verifyGCInfo(t, "stack eface", new(interface{}), infoEface) verifyGCInfo(t, "stack eface", new(interface{}), infoEface)
@ -39,7 +39,7 @@ func TestGCInfo(t *testing.T) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), nonStackInfo(infoScalarPtr)) verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), nonStackInfo(infoScalarPtr))
verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), nonStackInfo(infoPtrScalar)) verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), nonStackInfo(infoPtrScalar))
verifyGCInfo(t, "heap Complex", escape(new(Complex)), nonStackInfo(infoComplex())) verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), nonStackInfo(infoBigStruct()))
verifyGCInfo(t, "heap string", escape(new(string)), nonStackInfo(infoString)) verifyGCInfo(t, "heap string", escape(new(string)), nonStackInfo(infoString))
verifyGCInfo(t, "heap eface", escape(new(interface{})), nonStackInfo(infoEface)) verifyGCInfo(t, "heap eface", escape(new(interface{})), nonStackInfo(infoEface))
verifyGCInfo(t, "heap iface", escape(new(Iface)), nonStackInfo(infoIface)) verifyGCInfo(t, "heap iface", escape(new(Iface)), nonStackInfo(infoIface))
@ -88,8 +88,8 @@ const (
) )
const ( const (
BitsString = iota BitsString = iota // unused
BitsSlice BitsSlice // unused
BitsIface BitsIface
BitsEface BitsEface
) )
@ -116,7 +116,7 @@ type PtrScalar struct {
var infoPtrScalar = []byte{BitsPointer, BitsScalar, BitsPointer, BitsScalar, BitsPointer, BitsScalar} var infoPtrScalar = []byte{BitsPointer, BitsScalar, BitsPointer, BitsScalar, BitsPointer, BitsScalar}
type Complex struct { type BigStruct struct {
q *int q *int
w byte w byte
e [17]byte e [17]byte
@ -127,27 +127,31 @@ type Complex struct {
i string i string
} }
func infoComplex() []byte { func infoBigStruct() []byte {
switch runtime.GOARCH { switch runtime.GOARCH {
case "386", "arm": case "386", "arm":
return []byte{ return []byte{
BitsPointer, BitsScalar, BitsScalar, BitsScalar, BitsPointer, // q *int
BitsScalar, BitsScalar, BitsMultiWord, BitsSlice, BitsScalar, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
BitsDead, BitsScalar, BitsScalar, BitsScalar, BitsPointer, BitsDead, BitsDead, // r []byte
BitsScalar, BitsMultiWord, BitsString, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // t int; y uint16; u uint64
BitsPointer, BitsDead, // i string
} }
case "amd64": case "amd64":
return []byte{ return []byte{
BitsPointer, BitsScalar, BitsScalar, BitsScalar, BitsPointer, // q *int
BitsMultiWord, BitsSlice, BitsDead, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
BitsScalar, BitsScalar, BitsMultiWord, BitsString, BitsPointer, BitsDead, BitsDead, // r []byte
BitsScalar, BitsScalar, BitsScalar, // t int; y uint16; u uint64
BitsPointer, BitsDead, // i string
} }
case "amd64p32": case "amd64p32":
return []byte{ return []byte{
BitsPointer, BitsScalar, BitsScalar, BitsScalar, BitsPointer, // q *int
BitsScalar, BitsScalar, BitsMultiWord, BitsSlice, BitsScalar, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
BitsDead, BitsScalar, BitsScalar, BitsDead, BitsPointer, BitsDead, BitsDead, // r []byte
BitsScalar, BitsScalar, BitsMultiWord, BitsString, BitsScalar, BitsScalar, BitsDead, BitsScalar, BitsScalar, // t int; y uint16; u uint64
BitsPointer, BitsDead, // i string
} }
default: default:
panic("unknown arch") panic("unknown arch")
@ -167,7 +171,7 @@ var (
// BSS // BSS
bssScalarPtr ScalarPtr bssScalarPtr ScalarPtr
bssPtrScalar PtrScalar bssPtrScalar PtrScalar
bssComplex Complex bssBigStruct BigStruct
bssString string bssString string
bssSlice []string bssSlice []string
bssEface interface{} bssEface interface{}
@ -176,14 +180,14 @@ var (
// DATA // DATA
dataScalarPtr = ScalarPtr{q: 1} dataScalarPtr = ScalarPtr{q: 1}
dataPtrScalar = PtrScalar{w: 1} dataPtrScalar = PtrScalar{w: 1}
dataComplex = Complex{w: 1} dataBigStruct = BigStruct{w: 1}
dataString = "foo" dataString = "foo"
dataSlice = []string{"foo"} dataSlice = []string{"foo"}
dataEface interface{} = 42 dataEface interface{} = 42
dataIface Iface = IfaceImpl(42) dataIface Iface = IfaceImpl(42)
infoString = []byte{BitsMultiWord, BitsString} infoString = []byte{BitsPointer, BitsDead}
infoSlice = []byte{BitsMultiWord, BitsSlice, BitsDead} infoSlice = []byte{BitsPointer, BitsDead, BitsDead}
infoEface = []byte{BitsMultiWord, BitsEface} infoEface = []byte{BitsMultiWord, BitsEface}
infoIface = []byte{BitsMultiWord, BitsIface} infoIface = []byte{BitsMultiWord, BitsIface}
) )

View file

@ -260,16 +260,8 @@ dumpbv(BitVector *bv, uintptr offset)
break; break;
case BitsMultiWord: case BitsMultiWord:
switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) { switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) {
case BitsString: default:
dumpint(FieldKindString); runtime·throw("unexpected garbage collection bits");
dumpint(offset + i / BitsPerPointer * PtrSize);
i += BitsPerPointer;
break;
case BitsSlice:
dumpint(FieldKindSlice);
dumpint(offset + i / BitsPerPointer * PtrSize);
i += 2 * BitsPerPointer;
break;
case BitsIface: case BitsIface:
dumpint(FieldKindIface); dumpint(FieldKindIface);
dumpint(offset + i / BitsPerPointer * PtrSize); dumpint(offset + i / BitsPerPointer * PtrSize);
@ -495,13 +487,13 @@ dumproots(void)
dumpint(TagData); dumpint(TagData);
dumpint((uintptr)data); dumpint((uintptr)data);
dumpmemrange(data, edata - data); dumpmemrange(data, edata - data);
dumpfields((BitVector){(edata - data)*8, (uint32*)runtime·gcdatamask}); dumpfields(runtime·gcdatamask);
// bss segment // bss segment
dumpint(TagBss); dumpint(TagBss);
dumpint((uintptr)bss); dumpint((uintptr)bss);
dumpmemrange(bss, ebss - bss); dumpmemrange(bss, ebss - bss);
dumpfields((BitVector){(ebss - bss)*8, (uint32*)runtime·gcbssmask}); dumpfields(runtime·gcdatamask);
// MSpan.types // MSpan.types
allspans = runtime·mheap.allspans; allspans = runtime·mheap.allspans;
@ -802,13 +794,11 @@ dumpbvtypes(BitVector *bv, byte *base)
if((bv->data[i/32] >> i%32 & 3) != BitsMultiWord) if((bv->data[i/32] >> i%32 & 3) != BitsMultiWord)
continue; continue;
switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) { switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) {
case BitsString: default:
runtime·throw("unexpected garbage collection bits");
case BitsIface: case BitsIface:
i += BitsPerPointer; i += BitsPerPointer;
break; break;
case BitsSlice:
i += 2 * BitsPerPointer;
break;
case BitsEface: case BitsEface:
dumptype(*(Type**)(base + i / BitsPerPointer * PtrSize)); dumptype(*(Type**)(base + i / BitsPerPointer * PtrSize));
i += BitsPerPointer; i += BitsPerPointer;

View file

@ -303,7 +303,6 @@ extern int8 runtime·size_to_class8[1024/8 + 1];
extern int8 runtime·size_to_class128[(MaxSmallSize-1024)/128 + 1]; extern int8 runtime·size_to_class128[(MaxSmallSize-1024)/128 + 1];
extern void runtime·InitSizes(void); extern void runtime·InitSizes(void);
typedef struct MCacheList MCacheList; typedef struct MCacheList MCacheList;
struct MCacheList struct MCacheList
{ {
@ -581,6 +580,9 @@ struct StackMap
// (the index is encoded in PCDATA_StackMapIndex). // (the index is encoded in PCDATA_StackMapIndex).
BitVector runtime·stackmapdata(StackMap *stackmap, int32 n); BitVector runtime·stackmapdata(StackMap *stackmap, int32 n);
extern BitVector runtime·gcdatamask;
extern BitVector runtime·gcbssmask;
// defined in mgc0.go // defined in mgc0.go
void runtime·gc_m_ptr(Eface*); void runtime·gc_m_ptr(Eface*);
void runtime·gc_g_ptr(Eface*); void runtime·gc_g_ptr(Eface*);

View file

@ -172,8 +172,8 @@ static FinBlock *finc; // cache of free blocks
static FinBlock *allfin; // list of all blocks static FinBlock *allfin; // list of all blocks
bool runtime·fingwait; bool runtime·fingwait;
bool runtime·fingwake; bool runtime·fingwake;
byte* runtime·gcdatamask; BitVector runtime·gcdatamask;
byte* runtime·gcbssmask; BitVector runtime·gcbssmask;
static Lock gclock; static Lock gclock;
@ -187,7 +187,7 @@ static void gchelperstart(void);
static void flushallmcaches(void); static void flushallmcaches(void);
static bool scanframe(Stkframe *frame, void *unused); static bool scanframe(Stkframe *frame, void *unused);
static void scanstack(G *gp); static void scanstack(G *gp);
static byte* unrollglobgcprog(byte *prog, uintptr size); static BitVector unrollglobgcprog(byte *prog, uintptr size);
static FuncVal runfinqv = {runfinq}; static FuncVal runfinqv = {runfinq};
static FuncVal bgsweepv = {bgsweep}; static FuncVal bgsweepv = {bgsweep};
@ -221,8 +221,6 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
uintptr i, nobj, size, idx, x, off, scanbufpos; uintptr i, nobj, size, idx, x, off, scanbufpos;
intptr ncached; intptr ncached;
Workbuf *wbuf; Workbuf *wbuf;
String *str;
Slice *slice;
Iface *iface; Iface *iface;
Eface *eface; Eface *eface;
Type *typ; Type *typ;
@ -346,6 +344,10 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
obj = *(byte**)(b+i); obj = *(byte**)(b+i);
goto markobj; goto markobj;
} }
// With those three out of the way, must be multi-word.
if(bits != BitsMultiWord)
runtime·throw("unexpected garbage collection bits");
// Find the next pair of bits. // Find the next pair of bits.
if(ptrmask == nil) { if(ptrmask == nil) {
if(ncached <= 0) { if(ncached <= 0) {
@ -358,22 +360,8 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
bits = (ptrmask[((i+PtrSize)/PtrSize)/4]>>((((i+PtrSize)/PtrSize)%4)*BitsPerPointer))&BitsMask; bits = (ptrmask[((i+PtrSize)/PtrSize)/4]>>((((i+PtrSize)/PtrSize)%4)*BitsPerPointer))&BitsMask;
switch(bits) { switch(bits) {
case BitsString: default:
str = (String*)(b+i); runtime·throw("unexpected garbage collection bits");
if(str->len > 0)
obj = str->str;
break;
case BitsSlice:
slice = (Slice*)(b+i);
if(Debug && slice->cap < slice->len) {
g->m->traceback = 2;
runtime·printf("bad slice in object %p: %p/%p/%p\n",
b, slice->array, slice->len, slice->cap);
runtime·throw("bad slice in heap object");
}
if(slice->cap > 0)
obj = slice->array;
break;
case BitsIface: case BitsIface:
iface = (Iface*)(b+i); iface = (Iface*)(b+i);
if(iface->tab != nil) { if(iface->tab != nil) {
@ -392,21 +380,9 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
break; break;
} }
if(bits == BitsSlice) { i += PtrSize;
i += 2*PtrSize; cached >>= gcBits;
if(ncached == 2) ncached--;
ncached = 0;
else if(ptrmask == nil) {
// Refill cache and consume one quadruple.
cached = *--ptrbitp;
cached >>= gcBits;
ncached = 1;
}
} else {
i += PtrSize;
cached >>= gcBits;
ncached--;
}
markobj: markobj:
// At this point we have extracted the next potential pointer. // At this point we have extracted the next potential pointer.
@ -513,11 +489,11 @@ markroot(ParFor *desc, uint32 i)
// Note: if you add a case here, please also update heapdump.c:dumproots. // Note: if you add a case here, please also update heapdump.c:dumproots.
switch(i) { switch(i) {
case RootData: case RootData:
scanblock(data, edata - data, runtime·gcdatamask); scanblock(data, edata - data, (byte*)runtime·gcdatamask.data);
break; break;
case RootBss: case RootBss:
scanblock(bss, ebss - bss, runtime·gcbssmask); scanblock(bss, ebss - bss, (byte*)runtime·gcbssmask.data);
break; break;
case RootFinalizers: case RootFinalizers:
@ -1852,7 +1828,7 @@ unrollgcprog1(byte *mask, byte *prog, uintptr *ppos, bool inplace, bool sparse)
} }
// Unrolls GC program prog for data/bss, returns dense GC mask. // Unrolls GC program prog for data/bss, returns dense GC mask.
static byte* static BitVector
unrollglobgcprog(byte *prog, uintptr size) unrollglobgcprog(byte *prog, uintptr size)
{ {
byte *mask; byte *mask;
@ -1872,7 +1848,7 @@ unrollglobgcprog(byte *prog, uintptr size)
runtime·throw("unrollglobgcprog: program does not end with insEnd"); runtime·throw("unrollglobgcprog: program does not end with insEnd");
if(mask[masksize] != 0xa1) if(mask[masksize] != 0xa1)
runtime·throw("unrollglobgcprog: overflow"); runtime·throw("unrollglobgcprog: overflow");
return mask; return (BitVector){masksize*8, (uint32*)mask};
} }
void void
@ -2062,7 +2038,7 @@ runtime·getgcmask(byte *p, Type *t, byte **mask, uintptr *len)
*mask = runtime·mallocgc(*len, nil, 0); *mask = runtime·mallocgc(*len, nil, 0);
for(i = 0; i < n; i += PtrSize) { for(i = 0; i < n; i += PtrSize) {
off = (p+i-data)/PtrSize; off = (p+i-data)/PtrSize;
bits = (runtime·gcdatamask[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; bits = (((byte*)runtime·gcdatamask.data)[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask;
(*mask)[i/PtrSize] = bits; (*mask)[i/PtrSize] = bits;
} }
return; return;
@ -2074,7 +2050,7 @@ runtime·getgcmask(byte *p, Type *t, byte **mask, uintptr *len)
*mask = runtime·mallocgc(*len, nil, 0); *mask = runtime·mallocgc(*len, nil, 0);
for(i = 0; i < n; i += PtrSize) { for(i = 0; i < n; i += PtrSize) {
off = (p+i-bss)/PtrSize; off = (p+i-bss)/PtrSize;
bits = (runtime·gcbssmask[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; bits = (((byte*)runtime·gcbssmask.data)[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask;
(*mask)[i/PtrSize] = bits; (*mask)[i/PtrSize] = bits;
} }
return; return;

View file

@ -50,8 +50,8 @@ enum {
BitsMultiWord = 3, BitsMultiWord = 3,
// BitsMultiWord will be set for the first word of a multi-word item. // BitsMultiWord will be set for the first word of a multi-word item.
// When it is set, one of the following will be set for the second word. // When it is set, one of the following will be set for the second word.
BitsString = 0, // NOT USED ANYMORE: BitsString = 0,
BitsSlice = 1, // NOT USED ANYMORE: BitsSlice = 1,
BitsIface = 2, BitsIface = 2,
BitsEface = 3, BitsEface = 3,

View file

@ -762,8 +762,6 @@ extern uint32 runtime·cpuid_ecx;
extern uint32 runtime·cpuid_edx; extern uint32 runtime·cpuid_edx;
extern DebugVars runtime·debug; extern DebugVars runtime·debug;
extern uintptr runtime·maxstacksize; extern uintptr runtime·maxstacksize;
extern byte* runtime·gcdatamask;
extern byte* runtime·gcbssmask;
extern Note runtime·signote; extern Note runtime·signote;
/* /*

View file

@ -592,19 +592,8 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
break; break;
case BitsMultiWord: case BitsMultiWord:
switch(bv->data[(i+1) / (32 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 31) & 3) { switch(bv->data[(i+1) / (32 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 31) & 3) {
case BitsString: default:
// string referents are never on the stack, never need to be adjusted runtime·throw("unexpected garbage collection bits");
i++; // skip len
break;
case BitsSlice:
p = scanp[i];
if(minp <= p && p < maxp) {
if(StackDebug >= 3)
runtime·printf("adjust slice %p\n", p);
scanp[i] = p + delta;
}
i += 2; // skip len, cap
break;
case BitsEface: case BitsEface:
t = (Type*)scanp[i]; t = (Type*)scanp[i];
if(t != nil && ((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0)) { if(t != nil && ((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0)) {

View file

@ -147,9 +147,13 @@ func checkSlice(desc string, f func() []byte, xbase, xlen, xcap int) {
println(desc, "=", base, len, cap, "want panic") println(desc, "=", base, len, cap, "want panic")
return return
} }
if base != uintptr(xbase) || len != uintptr(xlen) || cap != uintptr(xcap) { if cap != 0 && base != uintptr(xbase) || base >= 10 || len != uintptr(xlen) || cap != uintptr(xcap) {
notOK() notOK()
println(desc, "=", base, len, cap, "want", xbase, xlen, xcap) if cap == 0 {
println(desc, "=", base, len, cap, "want", "0-9", xlen, xcap)
} else {
println(desc, "=", base, len, cap, "want", xbase, xlen, xcap)
}
} }
} }

90
test/slicecap.go Normal file
View file

@ -0,0 +1,90 @@
// run
// Copyright 2014 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.
package main
import "unsafe"
var (
hello = "hello"
bytes = []byte{1, 2, 3, 4, 5}
ints = []int32{1, 2, 3, 4, 5}
five = 5
ok = true
)
func notOK() {
if ok {
println("BUG:")
ok = false
}
}
func checkString(desc, s string) {
p1 := *(*uintptr)(unsafe.Pointer(&s))
p2 := *(*uintptr)(unsafe.Pointer(&hello))
if p1-p2 >= 5 {
notOK()
println("string", desc, "has invalid base")
}
}
func checkBytes(desc string, s []byte) {
p1 := *(*uintptr)(unsafe.Pointer(&s))
p2 := *(*uintptr)(unsafe.Pointer(&bytes))
if p1-p2 >= 5 {
println("byte slice", desc, "has invalid base")
}
}
func checkInts(desc string, s []int32) {
p1 := *(*uintptr)(unsafe.Pointer(&s))
p2 := *(*uintptr)(unsafe.Pointer(&ints))
if p1-p2 >= 5*4 {
println("int slice", desc, "has invalid base")
}
}
func main() {
{
x := hello
checkString("x", x)
checkString("x[5:]", x[5:])
checkString("x[five:]", x[five:])
checkString("x[5:five]", x[5:five])
checkString("x[five:5]", x[five:5])
checkString("x[five:five]", x[five:five])
checkString("x[1:][2:][2:]", x[1:][2:][2:])
y := x[4:]
checkString("y[1:]", y[1:])
}
{
x := bytes
checkBytes("x", x)
checkBytes("x[5:]", x[5:])
checkBytes("x[five:]", x[five:])
checkBytes("x[5:five]", x[5:five])
checkBytes("x[five:5]", x[five:5])
checkBytes("x[five:five]", x[five:five])
checkBytes("x[1:][2:][2:]", x[1:][2:][2:])
y := x[4:]
checkBytes("y[1:]", y[1:])
}
{
x := ints
checkInts("x", x)
checkInts("x[5:]", x[5:])
checkInts("x[five:]", x[five:])
checkInts("x[5:five]", x[5:five])
checkInts("x[five:5]", x[five:5])
checkInts("x[five:five]", x[five:five])
checkInts("x[1:][2:][2:]", x[1:][2:][2:])
y := x[4:]
checkInts("y[1:]", y[1:])
}
}