Skip to content

Commit 8680fa6

Browse files
committed
runtime: record type information for hashtable internal structures.
Remove all hashtable-specific GC code. Fixes bug 6119. R=cshapiro, dvyukov, khr CC=golang-dev https://codereview.appspot.com/13078044
1 parent 6e7f2a5 commit 8680fa6

File tree

9 files changed

+304
-403
lines changed

9 files changed

+304
-403
lines changed

src/cmd/gc/go.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ struct Type
186186
// TARRAY
187187
vlong bound; // negative is dynamic array
188188

189+
// TMAP
190+
Type* bucket; // internal type representing a hash bucket
191+
Type* hmap; // internal type representing a Hmap (map header object)
192+
189193
int32 maplineno; // first use of TFORW as map key
190194
int32 embedlineno; // first use of TFORW as embedded type
191195

src/cmd/gc/reflect.c

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,135 @@ lsort(Sig *l, int(*f)(Sig*, Sig*))
101101
return l;
102102
}
103103

104+
// Builds a type respresenting a Bucket structure for
105+
// the given map type. This type is not visible to users -
106+
// we include only enough information to generate a correct GC
107+
// program for it.
108+
// Make sure this stays in sync with ../../pkg/runtime/hashmap.c!
109+
enum {
110+
BUCKETSIZE = 8,
111+
MAXKEYSIZE = 128,
112+
MAXVALSIZE = 128,
113+
};
114+
115+
static Type*
116+
mapbucket(Type *t)
117+
{
118+
Type *keytype, *valtype;
119+
Type *bucket;
120+
Type *overflowfield, *keysfield, *valuesfield;
121+
int32 offset;
122+
123+
if(t->bucket != T)
124+
return t->bucket;
125+
126+
keytype = t->down;
127+
valtype = t->type;
128+
if(keytype->width > MAXKEYSIZE)
129+
keytype = ptrto(keytype);
130+
if(valtype->width > MAXVALSIZE)
131+
valtype = ptrto(valtype);
132+
133+
bucket = typ(TSTRUCT);
134+
135+
// The first field is: uint8 topbits[BUCKETSIZE].
136+
// We don't need to encode it as GC doesn't care about it.
137+
offset = BUCKETSIZE * 1;
138+
139+
overflowfield = typ(TFIELD);
140+
overflowfield->type = ptrto(bucket);
141+
overflowfield->width = offset; // "width" is offset in structure
142+
overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name
143+
overflowfield->sym->name = "overflow";
144+
offset += widthptr;
145+
146+
keysfield = typ(TFIELD);
147+
keysfield->type = typ(TARRAY);
148+
keysfield->type->type = keytype;
149+
keysfield->type->bound = BUCKETSIZE;
150+
keysfield->type->width = BUCKETSIZE * keytype->width;
151+
keysfield->width = offset;
152+
keysfield->sym = mal(sizeof(Sym));
153+
keysfield->sym->name = "keys";
154+
offset += BUCKETSIZE * keytype->width;
155+
156+
valuesfield = typ(TFIELD);
157+
valuesfield->type = typ(TARRAY);
158+
valuesfield->type->type = valtype;
159+
valuesfield->type->bound = BUCKETSIZE;
160+
valuesfield->type->width = BUCKETSIZE * valtype->width;
161+
valuesfield->width = offset;
162+
valuesfield->sym = mal(sizeof(Sym));
163+
valuesfield->sym->name = "values";
164+
offset += BUCKETSIZE * valtype->width;
165+
166+
// link up fields
167+
bucket->type = overflowfield;
168+
overflowfield->down = keysfield;
169+
keysfield->down = valuesfield;
170+
valuesfield->down = T;
171+
172+
bucket->width = offset;
173+
bucket->local = t->local;
174+
t->bucket = bucket;
175+
return bucket;
176+
}
177+
178+
// Builds a type respresenting a Hmap structure for
179+
// the given map type. This type is not visible to users -
180+
// we include only enough information to generate a correct GC
181+
// program for it.
182+
// Make sure this stays in sync with ../../pkg/runtime/hashmap.c!
183+
static Type*
184+
hmap(Type *t)
185+
{
186+
Type *h, *bucket;
187+
Type *bucketsfield, *oldbucketsfield;
188+
int32 offset;
189+
190+
if(t->hmap != T)
191+
return t->hmap;
192+
193+
bucket = mapbucket(t);
194+
h = typ(TSTRUCT);
195+
196+
offset = widthint; // count
197+
offset += 4; // flags
198+
offset += 4; // hash0
199+
offset += 1; // B
200+
offset += 1; // keysize
201+
offset += 1; // valuesize
202+
offset = (offset + 1) / 2 * 2;
203+
offset += 2; // bucketsize
204+
offset = (offset + widthptr - 1) / widthptr * widthptr;
205+
206+
bucketsfield = typ(TFIELD);
207+
bucketsfield->type = ptrto(bucket);
208+
bucketsfield->width = offset;
209+
bucketsfield->sym = mal(sizeof(Sym));
210+
bucketsfield->sym->name = "buckets";
211+
offset += widthptr;
212+
213+
oldbucketsfield = typ(TFIELD);
214+
oldbucketsfield->type = ptrto(bucket);
215+
oldbucketsfield->width = offset;
216+
oldbucketsfield->sym = mal(sizeof(Sym));
217+
oldbucketsfield->sym->name = "oldbuckets";
218+
offset += widthptr;
219+
220+
offset += widthptr; // nevacuate (last field in Hmap)
221+
222+
// link up fields
223+
h->type = bucketsfield;
224+
bucketsfield->down = oldbucketsfield;
225+
oldbucketsfield->down = T;
226+
227+
h->width = offset;
228+
h->local = t->local;
229+
t->hmap = h;
230+
return h;
231+
}
232+
104233
/*
105234
* f is method type, with receiver.
106235
* return function type, receiver as first argument (or not).
@@ -715,7 +844,7 @@ static Sym*
715844
dtypesym(Type *t)
716845
{
717846
int ot, xt, n, isddd, dupok;
718-
Sym *s, *s1, *s2, *slink;
847+
Sym *s, *s1, *s2, *s3, *s4, *slink;
719848
Sig *a, *m;
720849
Type *t1, *tbase, *t2;
721850

@@ -855,10 +984,14 @@ dtypesym(Type *t)
855984
// ../../pkg/runtime/type.go:/MapType
856985
s1 = dtypesym(t->down);
857986
s2 = dtypesym(t->type);
987+
s3 = dtypesym(mapbucket(t));
988+
s4 = dtypesym(hmap(t));
858989
ot = dcommontype(s, ot, t);
859990
xt = ot - 2*widthptr;
860991
ot = dsymptr(s, ot, s1, 0);
861992
ot = dsymptr(s, ot, s2, 0);
993+
ot = dsymptr(s, ot, s3, 0);
994+
ot = dsymptr(s, ot, s4, 0);
862995
break;
863996

864997
case TPTR32:
@@ -1118,9 +1251,9 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
11181251
// NOTE: Any changes here need to be made to reflect.MapOf as well.
11191252
if(*off % widthptr != 0)
11201253
fatal("dgcsym1: invalid alignment, %T", t);
1121-
ot = duintptr(s, ot, GC_MAP_PTR);
1254+
ot = duintptr(s, ot, GC_PTR);
11221255
ot = duintptr(s, ot, *off);
1123-
ot = dsymptr(s, ot, dtypesym(t), 0);
1256+
ot = dsymptr(s, ot, dgcsym(hmap(t)), 0);
11241257
*off += t->width;
11251258
break;
11261259

src/pkg/reflect/type.go

Lines changed: 122 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,11 @@ type interfaceType struct {
313313

314314
// mapType represents a map type.
315315
type mapType struct {
316-
rtype `reflect:"map"`
317-
key *rtype // map key type
318-
elem *rtype // map element (value) type
316+
rtype `reflect:"map"`
317+
key *rtype // map key type
318+
elem *rtype // map element (value) type
319+
bucket *rtype // internal bucket structure
320+
hmap *rtype // internal map header
319321
}
320322

321323
// ptrType represents a pointer type.
@@ -354,7 +356,6 @@ const (
354356
_GC_ARRAY_START
355357
_GC_ARRAY_NEXT
356358
_GC_CALL
357-
_GC_MAP_PTR
358359
_GC_CHAN_PTR
359360
_GC_STRING
360361
_GC_EFACE
@@ -1400,11 +1401,11 @@ func cachePut(k cacheKey, t *rtype) Type {
14001401
return t
14011402
}
14021403

1403-
// garbage collection bytecode program for chan or map.
1404+
// garbage collection bytecode program for chan.
14041405
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
1405-
type chanMapGC struct {
1406+
type chanGC struct {
14061407
width uintptr // sizeof(map)
1407-
op uintptr // _GC_MAP_PTR or _GC_CHAN_PTR
1408+
op uintptr // _GC_CHAN_PTR
14081409
off uintptr // 0
14091410
typ *rtype // map type
14101411
end uintptr // _GC_END
@@ -1467,7 +1468,7 @@ func ChanOf(dir ChanDir, t Type) Type {
14671468
ch.uncommonType = nil
14681469
ch.ptrToThis = nil
14691470

1470-
ch.gc = unsafe.Pointer(&chanMapGC{
1471+
ch.gc = unsafe.Pointer(&chanGC{
14711472
width: ch.size,
14721473
op: _GC_CHAN_PTR,
14731474
off: 0,
@@ -1521,24 +1522,129 @@ func MapOf(key, elem Type) Type {
15211522
mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
15221523
mt.key = ktyp
15231524
mt.elem = etyp
1525+
mt.bucket = bucketOf(ktyp, etyp)
1526+
mt.hmap = hMapOf(mt.bucket)
15241527
mt.uncommonType = nil
15251528
mt.ptrToThis = nil
15261529

1527-
mt.gc = unsafe.Pointer(&chanMapGC{
1528-
width: mt.size,
1529-
op: _GC_MAP_PTR,
1530-
off: 0,
1531-
typ: &mt.rtype,
1532-
end: _GC_END,
1533-
})
1534-
15351530
// INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues
15361531
// fail when mt.gc is wrong.
15371532
//mt.gc = unsafe.Pointer(&badGC{width: mt.size, end: _GC_END})
15381533

15391534
return cachePut(ckey, &mt.rtype)
15401535
}
15411536

1537+
// Make sure these routines stay in sync with ../../pkg/runtime/hashmap.c!
1538+
// These types exist only for GC, so we only fill out GC relevant info.
1539+
// Currently, that's just size and the GC program. We also fill in string
1540+
// for possible debugging use.
1541+
const (
1542+
BUCKETSIZE = 8
1543+
MAXKEYSIZE = 128
1544+
MAXVALSIZE = 128
1545+
)
1546+
1547+
func bucketOf(ktyp, etyp *rtype) *rtype {
1548+
if ktyp.size > MAXKEYSIZE {
1549+
ktyp = PtrTo(ktyp).(*rtype)
1550+
}
1551+
if etyp.size > MAXVALSIZE {
1552+
etyp = PtrTo(etyp).(*rtype)
1553+
}
1554+
ptrsize := unsafe.Sizeof(uintptr(0))
1555+
1556+
gc := make([]uintptr, 1) // first entry is size, filled in at the end
1557+
offset := BUCKETSIZE * unsafe.Sizeof(uint8(0)) // topbits
1558+
gc = append(gc, _GC_PTR, offset, 0 /*self pointer set below*/) // overflow
1559+
offset += ptrsize
1560+
1561+
// keys
1562+
if ktyp.kind&kindNoPointers == 0 {
1563+
gc = append(gc, _GC_ARRAY_START, offset, BUCKETSIZE, ktyp.size)
1564+
gc = appendGCProgram(gc, ktyp)
1565+
gc = append(gc, _GC_ARRAY_NEXT)
1566+
}
1567+
offset += BUCKETSIZE * ktyp.size
1568+
1569+
// values
1570+
if etyp.kind&kindNoPointers == 0 {
1571+
gc = append(gc, _GC_ARRAY_START, offset, BUCKETSIZE, etyp.size)
1572+
gc = appendGCProgram(gc, etyp)
1573+
gc = append(gc, _GC_ARRAY_NEXT)
1574+
}
1575+
offset += BUCKETSIZE * etyp.size
1576+
1577+
gc = append(gc, _GC_END)
1578+
gc[0] = offset
1579+
gc[3] = uintptr(unsafe.Pointer(&gc[0])) // set self pointer
1580+
1581+
b := new(rtype)
1582+
b.size = offset
1583+
b.gc = unsafe.Pointer(&gc[0])
1584+
s := "bucket(" + *ktyp.string + "," + *etyp.string + ")"
1585+
b.string = &s
1586+
return b
1587+
}
1588+
1589+
// Take the GC program for "t" and append it to the GC program "gc".
1590+
func appendGCProgram(gc []uintptr, t *rtype) []uintptr {
1591+
p := t.gc
1592+
p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) // skip size
1593+
loop:
1594+
for {
1595+
var argcnt int
1596+
switch *(*uintptr)(p) {
1597+
case _GC_END:
1598+
// Note: _GC_END not included in append
1599+
break loop
1600+
case _GC_ARRAY_NEXT:
1601+
argcnt = 0
1602+
case _GC_APTR, _GC_STRING, _GC_EFACE, _GC_IFACE:
1603+
argcnt = 1
1604+
case _GC_PTR, _GC_CALL, _GC_CHAN_PTR, _GC_SLICE:
1605+
argcnt = 2
1606+
case _GC_ARRAY_START, _GC_REGION:
1607+
argcnt = 3
1608+
default:
1609+
panic("unknown GC program op for " + *t.string + ": " + strconv.FormatUint(*(*uint64)(p), 10))
1610+
}
1611+
for i := 0; i < argcnt+1; i++ {
1612+
gc = append(gc, *(*uintptr)(p))
1613+
p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0)))
1614+
}
1615+
}
1616+
return gc
1617+
}
1618+
func hMapOf(bucket *rtype) *rtype {
1619+
ptrsize := unsafe.Sizeof(uintptr(0))
1620+
1621+
// make gc program & compute hmap size
1622+
gc := make([]uintptr, 1) // first entry is size, filled in at the end
1623+
offset := unsafe.Sizeof(uint(0)) // count
1624+
offset += unsafe.Sizeof(uint32(0)) // flags
1625+
offset += unsafe.Sizeof(uint32(0)) // hash0
1626+
offset += unsafe.Sizeof(uint8(0)) // B
1627+
offset += unsafe.Sizeof(uint8(0)) // keysize
1628+
offset += unsafe.Sizeof(uint8(0)) // valuesize
1629+
offset = (offset + 1) / 2 * 2
1630+
offset += unsafe.Sizeof(uint16(0)) // bucketsize
1631+
offset = (offset + ptrsize - 1) / ptrsize * ptrsize
1632+
gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // buckets
1633+
offset += ptrsize
1634+
gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // oldbuckets
1635+
offset += ptrsize
1636+
offset += ptrsize // nevacuate
1637+
gc = append(gc, _GC_END)
1638+
gc[0] = offset
1639+
1640+
h := new(rtype)
1641+
h.size = offset
1642+
h.gc = unsafe.Pointer(&gc[0])
1643+
s := "hmap(" + *bucket.string + ")"
1644+
h.string = &s
1645+
return h
1646+
}
1647+
15421648
// garbage collection bytecode program for slice of non-zero-length values.
15431649
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
15441650
type sliceGC struct {

0 commit comments

Comments
 (0)