1
0
mirror of https://github.com/golang/go synced 2024-07-05 09:50:19 +00: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:
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 OXOR:
case OADD:
case OADDPTR:
case OMUL:
a = optoas(n->op, nl->type);
goto sbop;

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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

View File

@ -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++)

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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];

View File

@ -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) {

View File

@ -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}

View File

@ -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}
)

View File

@ -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;

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 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*);

View File

@ -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;

View File

@ -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,

View File

@ -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;
/*

View File

@ -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)) {

View File

@ -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
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:])
}
}