]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: move dynamic makemap checks into cmd/compile
authorMartin Möhrmann <moehrmann@google.com>
Tue, 29 Aug 2017 11:31:45 +0000 (13:31 +0200)
committerMartin Möhrmann <moehrmann@google.com>
Wed, 30 Aug 2017 07:07:55 +0000 (07:07 +0000)
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>
src/cmd/compile/internal/gc/reflect.go
src/runtime/hashmap.go

index 845f7a6e317feb7252a8f0ff23b4d4f6e809614c..a56edad3b46006bbb7c9bea5ffc7bae27d986542 100644 (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
index 422ccfd41a78cbb5021eb3a1a185e37108149e40..37bf6e0aeb88bd20bd3029ae188d5219e8216480 100644 (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)
 }