mirror of
https://github.com/golang/go
synced 2024-11-11 18:01:47 -07:00
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:
parent
c6f7c176a3
commit
613383c765
@ -254,7 +254,6 @@ cgen(Node *n, Node *res)
|
||||
case OOR:
|
||||
case OXOR:
|
||||
case OADD:
|
||||
case OADDPTR:
|
||||
case OMUL:
|
||||
a = optoas(n->op, nl->type);
|
||||
goto sbop;
|
||||
|
@ -1599,7 +1599,6 @@ optoas(int op, Type *t)
|
||||
case CASE(OADD, TINT32):
|
||||
case CASE(OADD, TUINT32):
|
||||
case CASE(OADD, TPTR32):
|
||||
case CASE(OADDPTR, TPTR32):
|
||||
a = AADD;
|
||||
break;
|
||||
|
||||
|
@ -247,7 +247,6 @@ cgen(Node *n, Node *res)
|
||||
case OOR:
|
||||
case OXOR:
|
||||
case OADD:
|
||||
case OADDPTR:
|
||||
case OMUL:
|
||||
a = optoas(n->op, nl->type);
|
||||
if(a == AIMULB) {
|
||||
|
@ -1537,14 +1537,12 @@ optoas(int op, Type *t)
|
||||
case CASE(OADD, TINT32):
|
||||
case CASE(OADD, TUINT32):
|
||||
case CASE(OADD, TPTR32):
|
||||
case CASE(OADDPTR, TPTR32):
|
||||
a = AADDL;
|
||||
break;
|
||||
|
||||
case CASE(OADD, TINT64):
|
||||
case CASE(OADD, TUINT64):
|
||||
case CASE(OADD, TPTR64):
|
||||
case CASE(OADDPTR, TPTR64):
|
||||
a = AADDQ;
|
||||
break;
|
||||
|
||||
|
@ -242,7 +242,6 @@ cgen(Node *n, Node *res)
|
||||
case OOR:
|
||||
case OXOR:
|
||||
case OADD:
|
||||
case OADDPTR:
|
||||
case OMUL:
|
||||
a = optoas(n->op, nl->type);
|
||||
if(a == AIMULB) {
|
||||
|
@ -430,7 +430,6 @@ optoas(int op, Type *t)
|
||||
case CASE(OADD, TINT32):
|
||||
case CASE(OADD, TUINT32):
|
||||
case CASE(OADD, TPTR32):
|
||||
case CASE(OADDPTR, TPTR32):
|
||||
a = AADDL;
|
||||
break;
|
||||
|
||||
|
@ -1153,7 +1153,7 @@ exprfmt(Fmt *f, Node *n, int prec)
|
||||
case Csend:
|
||||
return fmtprint(f, "chan<- %N", n->left);
|
||||
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);
|
||||
else
|
||||
return fmtprint(f, "chan %N", n->left);
|
||||
|
@ -806,7 +806,8 @@ cgen_eface(Node *n, Node *res)
|
||||
void
|
||||
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;
|
||||
len = n->list->next->n;
|
||||
@ -823,6 +824,11 @@ cgen_slice(Node *n, Node *res)
|
||||
// garbage collector can see.
|
||||
|
||||
base = temp(types[TUINTPTR]);
|
||||
tmplen = temp(types[TINT]);
|
||||
if(n->op != OSLICESTR)
|
||||
tmpcap = temp(types[TINT]);
|
||||
else
|
||||
tmpcap = tmplen;
|
||||
|
||||
if(isnil(n->left)) {
|
||||
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);
|
||||
cgen(&src, 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 {
|
||||
src.type = types[tptr];
|
||||
add = nod(OADDPTR, &src, offs);
|
||||
typecheck(&add, Erv);
|
||||
cgen(add, base);
|
||||
cgen(&src, base);
|
||||
}
|
||||
|
||||
// committed to the update
|
||||
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 = *res;
|
||||
dst.xoffset += Array_array;
|
||||
dst.type = types[tptr];
|
||||
|
||||
cgen(base, &dst);
|
||||
|
||||
// dst.len = hi [ - lo ]
|
||||
dst = *res;
|
||||
dst.xoffset += Array_nel;
|
||||
dst.type = types[simtype[TUINT]];
|
||||
cgen(len, &dst);
|
||||
cgen(tmplen, &dst);
|
||||
|
||||
if(n->op != OSLICESTR) {
|
||||
// dst.cap = cap [ - lo ]
|
||||
dst = *res;
|
||||
dst.xoffset += Array_cap;
|
||||
dst.type = types[simtype[TUINT]];
|
||||
cgen(cap, &dst);
|
||||
cgen(tmpcap, &dst);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,7 +447,6 @@ enum
|
||||
OSUB, // x - y
|
||||
OOR, // x | y
|
||||
OXOR, // x ^ y
|
||||
OADDPTR, // ptr + uintptr, inserted by compiler only, used to avoid unsafe type changes during codegen
|
||||
OADDSTR, // s + "foo"
|
||||
OADDR, // &x
|
||||
OANDAND, // b0 && b1
|
||||
|
@ -1113,8 +1113,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
|
||||
// struct { byte *str; intgo len; }
|
||||
if((*xoffset & (widthptr-1)) != 0)
|
||||
fatal("twobitwalktype1: invalid alignment, %T", t);
|
||||
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0);
|
||||
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 3:0 = multiword:string
|
||||
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
|
||||
*xoffset += t->width;
|
||||
break;
|
||||
|
||||
@ -1145,9 +1144,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
|
||||
// struct { byte *array; uintgo len; uintgo cap; }
|
||||
if((*xoffset & (widthptr-1)) != 0)
|
||||
fatal("twobitwalktype1: invalid TARRAY alignment, %T", t);
|
||||
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0);
|
||||
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1);
|
||||
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 2); // 3:1 = multiword/slice
|
||||
bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
|
||||
*xoffset += t->width;
|
||||
} else
|
||||
for(i = 0; i < t->bound; i++)
|
||||
|
@ -1516,8 +1516,8 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
|
||||
*xoffset += t->width;
|
||||
break;
|
||||
case TSTRING:
|
||||
proggendata(g, BitsMultiWord);
|
||||
proggendata(g, BitsString);
|
||||
proggendata(g, BitsPointer);
|
||||
proggendata(g, BitsScalar);
|
||||
*xoffset += t->width;
|
||||
break;
|
||||
case TINTER:
|
||||
@ -1530,8 +1530,8 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
|
||||
break;
|
||||
case TARRAY:
|
||||
if(isslice(t)) {
|
||||
proggendata(g, BitsMultiWord);
|
||||
proggendata(g, BitsSlice);
|
||||
proggendata(g, BitsPointer);
|
||||
proggendata(g, BitsScalar);
|
||||
proggendata(g, BitsScalar);
|
||||
} else {
|
||||
t1 = t->type;
|
||||
|
@ -656,11 +656,15 @@ maptype(Type *key, Type *val)
|
||||
{
|
||||
Type *t;
|
||||
Type *bad;
|
||||
int atype;
|
||||
int atype, mtype;
|
||||
|
||||
if(key != nil) {
|
||||
atype = algtype1(key, &bad);
|
||||
switch(bad == T ? key->etype : bad->etype) {
|
||||
if(bad == T)
|
||||
mtype = key->etype;
|
||||
else
|
||||
mtype = bad->etype;
|
||||
switch(mtype) {
|
||||
default:
|
||||
if(atype == ANOEQ)
|
||||
yyerror("invalid map key type %T", key);
|
||||
|
@ -525,19 +525,6 @@ reswitch:
|
||||
op = n->etype;
|
||||
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 OAND:
|
||||
case OANDAND:
|
||||
|
@ -2875,14 +2875,14 @@ sliceany(Node* n, NodeList **init)
|
||||
lb = N;
|
||||
}
|
||||
|
||||
// dynamic checks convert all bounds to unsigned to save us the bound < 0 comparison
|
||||
// generate
|
||||
// if hb > bound || lb > hb { panicslice() }
|
||||
// Checking src[lb:hb:cb] or src[lb:hb].
|
||||
// if chk0 || chk1 || chk2 { panicslice() }
|
||||
chk = N;
|
||||
chk0 = N;
|
||||
chk1 = N;
|
||||
chk2 = N;
|
||||
chk0 = N; // cap(src) < cb
|
||||
chk1 = N; // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
|
||||
chk2 = N; // hb < lb
|
||||
|
||||
// All comparisons are unsigned to avoid testing < 0.
|
||||
bt = types[simtype[TUINT]];
|
||||
if(cb != N && cb->type->width > 4)
|
||||
bt = types[TUINT64];
|
||||
|
@ -2494,6 +2494,15 @@ func TestSlice(t *testing.T) {
|
||||
if 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) {
|
||||
@ -2532,6 +2541,15 @@ func TestSlice3(t *testing.T) {
|
||||
s := "hello world"
|
||||
rv = ValueOf(&s).Elem()
|
||||
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) {
|
||||
|
@ -1746,9 +1746,14 @@ func (v Value) Slice(i, j int) Value {
|
||||
|
||||
// Reinterpret as *sliceHeader to edit.
|
||||
s := (*sliceHeader)(unsafe.Pointer(&x))
|
||||
s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
|
||||
s.Len = j - 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
|
||||
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.
|
||||
s := (*sliceHeader)(unsafe.Pointer(&x))
|
||||
s.Data = unsafe.Pointer(uintptr(base) + uintptr(i)*typ.elem.Size())
|
||||
s.Len = j - 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
|
||||
return Value{typ.common(), unsafe.Pointer(&x), 0, fl}
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
func TestGCInfo(t *testing.T) {
|
||||
verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, nonStackInfo(infoScalarPtr))
|
||||
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 slice", &bssSlice, nonStackInfo(infoSlice))
|
||||
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 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 slice", &dataSlice, nonStackInfo(infoSlice))
|
||||
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 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 slice", new([]string), infoSlice)
|
||||
verifyGCInfo(t, "stack eface", new(interface{}), infoEface)
|
||||
@ -39,7 +39,7 @@ func TestGCInfo(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), nonStackInfo(infoScalarPtr))
|
||||
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 eface", escape(new(interface{})), nonStackInfo(infoEface))
|
||||
verifyGCInfo(t, "heap iface", escape(new(Iface)), nonStackInfo(infoIface))
|
||||
@ -88,8 +88,8 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
BitsString = iota
|
||||
BitsSlice
|
||||
BitsString = iota // unused
|
||||
BitsSlice // unused
|
||||
BitsIface
|
||||
BitsEface
|
||||
)
|
||||
@ -116,7 +116,7 @@ type PtrScalar struct {
|
||||
|
||||
var infoPtrScalar = []byte{BitsPointer, BitsScalar, BitsPointer, BitsScalar, BitsPointer, BitsScalar}
|
||||
|
||||
type Complex struct {
|
||||
type BigStruct struct {
|
||||
q *int
|
||||
w byte
|
||||
e [17]byte
|
||||
@ -127,27 +127,31 @@ type Complex struct {
|
||||
i string
|
||||
}
|
||||
|
||||
func infoComplex() []byte {
|
||||
func infoBigStruct() []byte {
|
||||
switch runtime.GOARCH {
|
||||
case "386", "arm":
|
||||
return []byte{
|
||||
BitsPointer, BitsScalar, BitsScalar, BitsScalar,
|
||||
BitsScalar, BitsScalar, BitsMultiWord, BitsSlice,
|
||||
BitsDead, BitsScalar, BitsScalar, BitsScalar,
|
||||
BitsScalar, BitsMultiWord, BitsString,
|
||||
BitsPointer, // q *int
|
||||
BitsScalar, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
|
||||
BitsPointer, BitsDead, BitsDead, // r []byte
|
||||
BitsScalar, BitsScalar, BitsScalar, BitsScalar, // t int; y uint16; u uint64
|
||||
BitsPointer, BitsDead, // i string
|
||||
}
|
||||
case "amd64":
|
||||
return []byte{
|
||||
BitsPointer, BitsScalar, BitsScalar, BitsScalar,
|
||||
BitsMultiWord, BitsSlice, BitsDead, BitsScalar,
|
||||
BitsScalar, BitsScalar, BitsMultiWord, BitsString,
|
||||
BitsPointer, // q *int
|
||||
BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
|
||||
BitsPointer, BitsDead, BitsDead, // r []byte
|
||||
BitsScalar, BitsScalar, BitsScalar, // t int; y uint16; u uint64
|
||||
BitsPointer, BitsDead, // i string
|
||||
}
|
||||
case "amd64p32":
|
||||
return []byte{
|
||||
BitsPointer, BitsScalar, BitsScalar, BitsScalar,
|
||||
BitsScalar, BitsScalar, BitsMultiWord, BitsSlice,
|
||||
BitsDead, BitsScalar, BitsScalar, BitsDead,
|
||||
BitsScalar, BitsScalar, BitsMultiWord, BitsString,
|
||||
BitsPointer, // q *int
|
||||
BitsScalar, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte
|
||||
BitsPointer, BitsDead, BitsDead, // r []byte
|
||||
BitsScalar, BitsScalar, BitsDead, BitsScalar, BitsScalar, // t int; y uint16; u uint64
|
||||
BitsPointer, BitsDead, // i string
|
||||
}
|
||||
default:
|
||||
panic("unknown arch")
|
||||
@ -167,7 +171,7 @@ var (
|
||||
// BSS
|
||||
bssScalarPtr ScalarPtr
|
||||
bssPtrScalar PtrScalar
|
||||
bssComplex Complex
|
||||
bssBigStruct BigStruct
|
||||
bssString string
|
||||
bssSlice []string
|
||||
bssEface interface{}
|
||||
@ -176,14 +180,14 @@ var (
|
||||
// DATA
|
||||
dataScalarPtr = ScalarPtr{q: 1}
|
||||
dataPtrScalar = PtrScalar{w: 1}
|
||||
dataComplex = Complex{w: 1}
|
||||
dataBigStruct = BigStruct{w: 1}
|
||||
dataString = "foo"
|
||||
dataSlice = []string{"foo"}
|
||||
dataEface interface{} = 42
|
||||
dataIface Iface = IfaceImpl(42)
|
||||
|
||||
infoString = []byte{BitsMultiWord, BitsString}
|
||||
infoSlice = []byte{BitsMultiWord, BitsSlice, BitsDead}
|
||||
infoString = []byte{BitsPointer, BitsDead}
|
||||
infoSlice = []byte{BitsPointer, BitsDead, BitsDead}
|
||||
infoEface = []byte{BitsMultiWord, BitsEface}
|
||||
infoIface = []byte{BitsMultiWord, BitsIface}
|
||||
)
|
||||
|
@ -260,16 +260,8 @@ dumpbv(BitVector *bv, uintptr offset)
|
||||
break;
|
||||
case BitsMultiWord:
|
||||
switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) {
|
||||
case BitsString:
|
||||
dumpint(FieldKindString);
|
||||
dumpint(offset + i / BitsPerPointer * PtrSize);
|
||||
i += BitsPerPointer;
|
||||
break;
|
||||
case BitsSlice:
|
||||
dumpint(FieldKindSlice);
|
||||
dumpint(offset + i / BitsPerPointer * PtrSize);
|
||||
i += 2 * BitsPerPointer;
|
||||
break;
|
||||
default:
|
||||
runtime·throw("unexpected garbage collection bits");
|
||||
case BitsIface:
|
||||
dumpint(FieldKindIface);
|
||||
dumpint(offset + i / BitsPerPointer * PtrSize);
|
||||
@ -495,13 +487,13 @@ dumproots(void)
|
||||
dumpint(TagData);
|
||||
dumpint((uintptr)data);
|
||||
dumpmemrange(data, edata - data);
|
||||
dumpfields((BitVector){(edata - data)*8, (uint32*)runtime·gcdatamask});
|
||||
dumpfields(runtime·gcdatamask);
|
||||
|
||||
// bss segment
|
||||
dumpint(TagBss);
|
||||
dumpint((uintptr)bss);
|
||||
dumpmemrange(bss, ebss - bss);
|
||||
dumpfields((BitVector){(ebss - bss)*8, (uint32*)runtime·gcbssmask});
|
||||
dumpfields(runtime·gcdatamask);
|
||||
|
||||
// MSpan.types
|
||||
allspans = runtime·mheap.allspans;
|
||||
@ -802,13 +794,11 @@ dumpbvtypes(BitVector *bv, byte *base)
|
||||
if((bv->data[i/32] >> i%32 & 3) != BitsMultiWord)
|
||||
continue;
|
||||
switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) {
|
||||
case BitsString:
|
||||
default:
|
||||
runtime·throw("unexpected garbage collection bits");
|
||||
case BitsIface:
|
||||
i += BitsPerPointer;
|
||||
break;
|
||||
case BitsSlice:
|
||||
i += 2 * BitsPerPointer;
|
||||
break;
|
||||
case BitsEface:
|
||||
dumptype(*(Type**)(base + i / BitsPerPointer * PtrSize));
|
||||
i += BitsPerPointer;
|
||||
|
@ -303,7 +303,6 @@ extern int8 runtime·size_to_class8[1024/8 + 1];
|
||||
extern int8 runtime·size_to_class128[(MaxSmallSize-1024)/128 + 1];
|
||||
extern void runtime·InitSizes(void);
|
||||
|
||||
|
||||
typedef struct MCacheList MCacheList;
|
||||
struct MCacheList
|
||||
{
|
||||
@ -581,6 +580,9 @@ struct StackMap
|
||||
// (the index is encoded in PCDATA_StackMapIndex).
|
||||
BitVector runtime·stackmapdata(StackMap *stackmap, int32 n);
|
||||
|
||||
extern BitVector runtime·gcdatamask;
|
||||
extern BitVector runtime·gcbssmask;
|
||||
|
||||
// defined in mgc0.go
|
||||
void runtime·gc_m_ptr(Eface*);
|
||||
void runtime·gc_g_ptr(Eface*);
|
||||
|
@ -172,8 +172,8 @@ static FinBlock *finc; // cache of free blocks
|
||||
static FinBlock *allfin; // list of all blocks
|
||||
bool runtime·fingwait;
|
||||
bool runtime·fingwake;
|
||||
byte* runtime·gcdatamask;
|
||||
byte* runtime·gcbssmask;
|
||||
BitVector runtime·gcdatamask;
|
||||
BitVector runtime·gcbssmask;
|
||||
|
||||
static Lock gclock;
|
||||
|
||||
@ -187,7 +187,7 @@ static void gchelperstart(void);
|
||||
static void flushallmcaches(void);
|
||||
static bool scanframe(Stkframe *frame, void *unused);
|
||||
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 bgsweepv = {bgsweep};
|
||||
@ -221,8 +221,6 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
|
||||
uintptr i, nobj, size, idx, x, off, scanbufpos;
|
||||
intptr ncached;
|
||||
Workbuf *wbuf;
|
||||
String *str;
|
||||
Slice *slice;
|
||||
Iface *iface;
|
||||
Eface *eface;
|
||||
Type *typ;
|
||||
@ -346,6 +344,10 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
|
||||
obj = *(byte**)(b+i);
|
||||
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.
|
||||
if(ptrmask == nil) {
|
||||
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;
|
||||
|
||||
switch(bits) {
|
||||
case BitsString:
|
||||
str = (String*)(b+i);
|
||||
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;
|
||||
default:
|
||||
runtime·throw("unexpected garbage collection bits");
|
||||
case BitsIface:
|
||||
iface = (Iface*)(b+i);
|
||||
if(iface->tab != nil) {
|
||||
@ -392,21 +380,9 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
|
||||
break;
|
||||
}
|
||||
|
||||
if(bits == BitsSlice) {
|
||||
i += 2*PtrSize;
|
||||
if(ncached == 2)
|
||||
ncached = 0;
|
||||
else if(ptrmask == nil) {
|
||||
// Refill cache and consume one quadruple.
|
||||
cached = *--ptrbitp;
|
||||
cached >>= gcBits;
|
||||
ncached = 1;
|
||||
}
|
||||
} else {
|
||||
i += PtrSize;
|
||||
cached >>= gcBits;
|
||||
ncached--;
|
||||
}
|
||||
i += PtrSize;
|
||||
cached >>= gcBits;
|
||||
ncached--;
|
||||
|
||||
markobj:
|
||||
// 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.
|
||||
switch(i) {
|
||||
case RootData:
|
||||
scanblock(data, edata - data, runtime·gcdatamask);
|
||||
scanblock(data, edata - data, (byte*)runtime·gcdatamask.data);
|
||||
break;
|
||||
|
||||
case RootBss:
|
||||
scanblock(bss, ebss - bss, runtime·gcbssmask);
|
||||
scanblock(bss, ebss - bss, (byte*)runtime·gcbssmask.data);
|
||||
break;
|
||||
|
||||
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.
|
||||
static byte*
|
||||
static BitVector
|
||||
unrollglobgcprog(byte *prog, uintptr size)
|
||||
{
|
||||
byte *mask;
|
||||
@ -1872,7 +1848,7 @@ unrollglobgcprog(byte *prog, uintptr size)
|
||||
runtime·throw("unrollglobgcprog: program does not end with insEnd");
|
||||
if(mask[masksize] != 0xa1)
|
||||
runtime·throw("unrollglobgcprog: overflow");
|
||||
return mask;
|
||||
return (BitVector){masksize*8, (uint32*)mask};
|
||||
}
|
||||
|
||||
void
|
||||
@ -2062,7 +2038,7 @@ runtime·getgcmask(byte *p, Type *t, byte **mask, uintptr *len)
|
||||
*mask = runtime·mallocgc(*len, nil, 0);
|
||||
for(i = 0; i < n; i += 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;
|
||||
}
|
||||
return;
|
||||
@ -2074,7 +2050,7 @@ runtime·getgcmask(byte *p, Type *t, byte **mask, uintptr *len)
|
||||
*mask = runtime·mallocgc(*len, nil, 0);
|
||||
for(i = 0; i < n; i += 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;
|
||||
}
|
||||
return;
|
||||
|
@ -50,8 +50,8 @@ enum {
|
||||
BitsMultiWord = 3,
|
||||
// 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.
|
||||
BitsString = 0,
|
||||
BitsSlice = 1,
|
||||
// NOT USED ANYMORE: BitsString = 0,
|
||||
// NOT USED ANYMORE: BitsSlice = 1,
|
||||
BitsIface = 2,
|
||||
BitsEface = 3,
|
||||
|
||||
|
@ -762,8 +762,6 @@ extern uint32 runtime·cpuid_ecx;
|
||||
extern uint32 runtime·cpuid_edx;
|
||||
extern DebugVars runtime·debug;
|
||||
extern uintptr runtime·maxstacksize;
|
||||
extern byte* runtime·gcdatamask;
|
||||
extern byte* runtime·gcbssmask;
|
||||
extern Note runtime·signote;
|
||||
|
||||
/*
|
||||
|
@ -592,19 +592,8 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
|
||||
break;
|
||||
case BitsMultiWord:
|
||||
switch(bv->data[(i+1) / (32 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 31) & 3) {
|
||||
case BitsString:
|
||||
// string referents are never on the stack, never need to be adjusted
|
||||
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;
|
||||
default:
|
||||
runtime·throw("unexpected garbage collection bits");
|
||||
case BitsEface:
|
||||
t = (Type*)scanp[i];
|
||||
if(t != nil && ((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0)) {
|
||||
|
@ -19,10 +19,10 @@ var bout *bufio.Writer
|
||||
|
||||
func main() {
|
||||
bout = bufio.NewWriter(os.Stdout)
|
||||
|
||||
|
||||
fmt.Fprintf(bout, "%s", programTop)
|
||||
fmt.Fprintf(bout, "func main() {\n")
|
||||
|
||||
|
||||
index := []string{
|
||||
"0",
|
||||
"1",
|
||||
@ -38,7 +38,7 @@ func main() {
|
||||
"v10",
|
||||
"v20",
|
||||
}
|
||||
|
||||
|
||||
parse := func(s string) (n int, isconst bool) {
|
||||
if s == "vminus1" {
|
||||
return -1, false
|
||||
@ -69,7 +69,7 @@ func main() {
|
||||
iconst && kconst && iv > kv,
|
||||
iconst && base == "array" && iv > Cap,
|
||||
jconst && base == "array" && jv > Cap,
|
||||
kconst && base == "array" && kv > Cap:
|
||||
kconst && base == "array" && kv > Cap:
|
||||
continue
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ func main() {
|
||||
xlen = jv - iv
|
||||
xcap = kv - iv
|
||||
}
|
||||
fmt.Fprintf(bout, "\tcheckSlice(%q, func() []byte { return %s }, %d, %d, %d)\n", expr, expr, xbase, xlen, xcap)
|
||||
fmt.Fprintf(bout, "\tcheckSlice(%q, func() []byte { return %s }, %d, %d, %d)\n", expr, expr, xbase, xlen, xcap)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -147,9 +147,13 @@ func checkSlice(desc string, f func() []byte, xbase, xlen, xcap int) {
|
||||
println(desc, "=", base, len, cap, "want panic")
|
||||
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()
|
||||
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
90
test/slicecap.go
Normal 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:])
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user