1
0
mirror of https://github.com/golang/go synced 2024-11-19 14:24:47 -07:00

runtime: move dynamic makemap checks into cmd/compile

Check map invariants, type size and alignments during compile time.

Keep runtime checks for reflect by adding them to reflect_makemap.

Change-Id: Ia28610626591bf7fafb7d5a1ca318da272e54879
Reviewed-on: https://go-review.googlesource.com/59914
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Martin Möhrmann 2017-08-29 13:31:45 +02:00
parent d77d4f509c
commit 54125d191d
2 changed files with 94 additions and 40 deletions

View File

@ -121,11 +121,13 @@ func mapbucket(t *types.Type) *types.Type {
arr = types.NewArray(keytype, BUCKETSIZE)
arr.SetNoalg(true)
field = append(field, makefield("keys", arr))
keys := makefield("keys", arr)
field = append(field, keys)
arr = types.NewArray(valtype, BUCKETSIZE)
arr.SetNoalg(true)
field = append(field, makefield("values", arr))
values := makefield("values", arr)
field = append(field, values)
// Make sure the overflow pointer is the last memory in the struct,
// because the runtime assumes it can use size-ptrSize as the
@ -158,8 +160,8 @@ func mapbucket(t *types.Type) *types.Type {
if !types.Haspointers(t.Val()) && !types.Haspointers(t.Key()) && t.Val().Width <= MAXVALSIZE && t.Key().Width <= MAXKEYSIZE {
otyp = types.Types[TUINTPTR]
}
ovf := makefield("overflow", otyp)
field = append(field, ovf)
overflow := makefield("overflow", otyp)
field = append(field, overflow)
// link up fields
bucket.SetNoalg(true)
@ -167,10 +169,51 @@ func mapbucket(t *types.Type) *types.Type {
bucket.SetFields(field[:])
dowidth(bucket)
// Check invariants that map code depends on.
if BUCKETSIZE < 8 {
Fatalf("bucket size too small for proper alignment")
}
if keytype.Align > BUCKETSIZE {
Fatalf("key align too big for %v", t)
}
if valtype.Align > BUCKETSIZE {
Fatalf("value align too big for %v", t)
}
if keytype.Width > MAXKEYSIZE {
Fatalf("key size to large for %v", t)
}
if valtype.Width > MAXVALSIZE {
Fatalf("value size to large for %v", t)
}
if t.Key().Width > MAXKEYSIZE && !keytype.IsPtr() {
Fatalf("key indirect incorrect for %v", t)
}
if t.Val().Width > MAXVALSIZE && !valtype.IsPtr() {
Fatalf("value indirect incorrect for %v", t)
}
if keytype.Width%int64(keytype.Align) != 0 {
Fatalf("key size not a multiple of key align for %v", t)
}
if valtype.Width%int64(valtype.Align) != 0 {
Fatalf("value size not a multiple of value align for %v", t)
}
if bucket.Align%keytype.Align != 0 {
Fatalf("bucket align not multiple of key align %v", t)
}
if bucket.Align%valtype.Align != 0 {
Fatalf("bucket align not multiple of value align %v", t)
}
if keys.Offset%int64(keytype.Align) != 0 {
Fatalf("bad alignment of keys in mapbucket for %v", t)
}
if values.Offset%int64(valtype.Align) != 0 {
Fatalf("bad alignment of values in mapbucket for %v", t)
}
// Double-check that overflow field is final memory in struct,
// with no padding at end. See comment above.
if ovf.Offset != bucket.Width-int64(Widthptr) {
Fatalf("bad math in mapbucket for %v", t)
if overflow.Offset != bucket.Width-int64(Widthptr) {
Fatalf("bad offset of overflow in mapbucket for %v", t)
}
t.MapType().Bucket = bucket
@ -218,6 +261,13 @@ func hmap(t *types.Type) *types.Type {
hmap.SetLocal(t.Local())
hmap.SetFields(fields)
dowidth(hmap)
// The size of hmap should be 48 bytes on 64 bit
// and 28 bytes on 32 bit platforms.
if size := int64(8 + 5*Widthptr); hmap.Width != size {
Fatalf("hmap size not correct: got %d, want %d", hmap.Width, size)
}
t.MapType().Hmap = hmap
hmap.StructType().Map = t
return hmap

View File

@ -285,7 +285,9 @@ func makemap64(t *maptype, hint int64, h *hmap) *hmap {
// If h != nil, the map can be created directly in h.
// If h.buckets != nil, bucket pointed to can be used as the first bucket.
func makemap(t *maptype, hint int, h *hmap) *hmap {
if sz := unsafe.Sizeof(hmap{}); sz > 48 || sz != t.hmap.size {
// The size of hmap should be 48 bytes on 64 bit
// and 28 bytes on 32 bit platforms.
if sz := unsafe.Sizeof(hmap{}); sz != 8+5*sys.PtrSize {
println("runtime: sizeof(hmap) =", sz, ", t.hmap.size =", t.hmap.size)
throw("bad hmap size")
}
@ -298,39 +300,6 @@ func makemap(t *maptype, hint int, h *hmap) *hmap {
throw("runtime.makemap: unsupported map key type")
}
// check compiler's and reflect's math
if t.key.size > maxKeySize && (!t.indirectkey || t.keysize != uint8(sys.PtrSize)) ||
t.key.size <= maxKeySize && (t.indirectkey || t.keysize != uint8(t.key.size)) {
throw("key size wrong")
}
if t.elem.size > maxValueSize && (!t.indirectvalue || t.valuesize != uint8(sys.PtrSize)) ||
t.elem.size <= maxValueSize && (t.indirectvalue || t.valuesize != uint8(t.elem.size)) {
throw("value size wrong")
}
// invariants we depend on. We should probably check these at compile time
// somewhere, but for now we'll do it here.
if t.key.align > bucketCnt {
throw("key align too big")
}
if t.elem.align > bucketCnt {
throw("value align too big")
}
if t.key.size%uintptr(t.key.align) != 0 {
throw("key size not a multiple of key align")
}
if t.elem.size%uintptr(t.elem.align) != 0 {
throw("value size not a multiple of value align")
}
if bucketCnt < 8 {
throw("bucketsize too small for proper alignment")
}
if dataOffset%uintptr(t.key.align) != 0 {
throw("need padding in bucket (key)")
}
if dataOffset%uintptr(t.elem.align) != 0 {
throw("need padding in bucket (value)")
}
if evacuatedX+1 != evacuatedY {
// evacuate relies on this relationship
throw("bad evacuatedN")
@ -1183,6 +1152,41 @@ func ismapkey(t *_type) bool {
//go:linkname reflect_makemap reflect.makemap
func reflect_makemap(t *maptype, cap int) *hmap {
// Check invariants and reflects math.
if sz := unsafe.Sizeof(hmap{}); sz != t.hmap.size {
println("runtime: sizeof(hmap) =", sz, ", t.hmap.size =", t.hmap.size)
throw("bad hmap size")
}
if t.key.size > maxKeySize && (!t.indirectkey || t.keysize != uint8(sys.PtrSize)) ||
t.key.size <= maxKeySize && (t.indirectkey || t.keysize != uint8(t.key.size)) {
throw("key size wrong")
}
if t.elem.size > maxValueSize && (!t.indirectvalue || t.valuesize != uint8(sys.PtrSize)) ||
t.elem.size <= maxValueSize && (t.indirectvalue || t.valuesize != uint8(t.elem.size)) {
throw("value size wrong")
}
if t.key.align > bucketCnt {
throw("key align too big")
}
if t.elem.align > bucketCnt {
throw("value align too big")
}
if t.key.size%uintptr(t.key.align) != 0 {
throw("key size not a multiple of key align")
}
if t.elem.size%uintptr(t.elem.align) != 0 {
throw("value size not a multiple of value align")
}
if bucketCnt < 8 {
throw("bucketsize too small for proper alignment")
}
if dataOffset%uintptr(t.key.align) != 0 {
throw("need padding in bucket (key)")
}
if dataOffset%uintptr(t.elem.align) != 0 {
throw("need padding in bucket (value)")
}
return makemap(t, cap, nil)
}