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:
parent
d77d4f509c
commit
54125d191d
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user