compiler, runtime, reflect: make type descriptors more like Go 1.8

Change the type descriptor structure to be more like the one in the Go
1.8 runtime.  Specifically we add the ptrdata field, rename the gc
field to gcdata and change the type to *byte, and rearrange a few of
the fields.  The structure is still not identical to the Go 1.8
structure--we don't use any of the tricks to reduce overall executable
size--but it is more similar.

For now we don't use the new ptrdata field, and the gcdata field is
still the old format rather than the new Go 1.8 ptrmask/gcprog format.

Change-Id: I809bbd182db6cdd94983d01a4d94ce946f98d633
Reviewed-on: https://go-review.googlesource.com/41081
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/go/expressions.cc b/go/expressions.cc
index 2953b3d..b930ec2 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -14572,7 +14572,7 @@
  protected:
   Type*
   do_type()
-  { return Type::lookup_integer_type("uintptr"); }
+  { return Type::make_pointer_type(Type::lookup_integer_type("uint8")); }
 
   bool
   do_is_static_initializer() const
diff --git a/go/types.cc b/go/types.cc
index a48ea30..aa43df3 100644
--- a/go/types.cc
+++ b/go/types.cc
@@ -1482,6 +1482,7 @@
       Location bloc = Linemap::predeclared_location();
 
       Type* uint8_type = Type::lookup_integer_type("uint8");
+      Type* pointer_uint8_type = Type::make_pointer_type(uint8_type);
       Type* uint32_type = Type::lookup_integer_type("uint32");
       Type* uintptr_type = Type::lookup_integer_type("uintptr");
       Type* string_type = Type::lookup_string_type();
@@ -1548,15 +1549,16 @@
       // The type descriptor type.
 
       Struct_type* type_descriptor_type =
-	Type::make_builtin_struct_type(11,
+	Type::make_builtin_struct_type(12,
+				       "size", uintptr_type,
+				       "ptrdata", uintptr_type,
+				       "hash", uint32_type,
 				       "kind", uint8_type,
 				       "align", uint8_type,
 				       "fieldAlign", uint8_type,
-				       "size", uintptr_type,
-				       "hash", uint32_type,
 				       "hashfn", hash_fntype,
 				       "equalfn", equal_fntype,
-				       "gc", uintptr_type,
+				       "gcdata", pointer_uint8_type,
 				       "string", pointer_string_type,
 				       "", pointer_uncommon_type,
 				       "ptrToThis",
@@ -2312,30 +2314,25 @@
   const Struct_field_list* fields = td_type->struct_type()->fields();
 
   Expression_list* vals = new Expression_list();
-  vals->reserve(9);
+  vals->reserve(12);
 
   if (!this->has_pointer())
     runtime_type_kind |= RUNTIME_TYPE_KIND_NO_POINTERS;
   if (this->points_to() != NULL)
     runtime_type_kind |= RUNTIME_TYPE_KIND_DIRECT_IFACE;
+  int64_t ptrsize;
+  int64_t ptrdata;
+  if (this->needs_gcprog(gogo, &ptrsize, &ptrdata))
+    runtime_type_kind |= RUNTIME_TYPE_KIND_GC_PROG;
+
   Struct_field_list::const_iterator p = fields->begin();
-  go_assert(p->is_field_name("kind"));
-  vals->push_back(Expression::make_integer_ul(runtime_type_kind, p->type(),
-					      bloc));
-
-  ++p;
-  go_assert(p->is_field_name("align"));
-  Expression::Type_info type_info = Expression::TYPE_INFO_ALIGNMENT;
-  vals->push_back(Expression::make_type_info(this, type_info));
-
-  ++p;
-  go_assert(p->is_field_name("fieldAlign"));
-  type_info = Expression::TYPE_INFO_FIELD_ALIGNMENT;
-  vals->push_back(Expression::make_type_info(this, type_info));
-
-  ++p;
   go_assert(p->is_field_name("size"));
-  type_info = Expression::TYPE_INFO_SIZE;
+  Expression::Type_info type_info = Expression::TYPE_INFO_SIZE;
+  vals->push_back(Expression::make_type_info(this, type_info));
+
+  ++p;
+  go_assert(p->is_field_name("ptrdata"));
+  type_info = Expression::TYPE_INFO_DESCRIPTOR_PTRDATA;
   vals->push_back(Expression::make_type_info(this, type_info));
 
   ++p;
@@ -2348,6 +2345,21 @@
   vals->push_back(Expression::make_integer_ul(h, p->type(), bloc));
 
   ++p;
+  go_assert(p->is_field_name("kind"));
+  vals->push_back(Expression::make_integer_ul(runtime_type_kind, p->type(),
+					      bloc));
+
+  ++p;
+  go_assert(p->is_field_name("align"));
+  type_info = Expression::TYPE_INFO_ALIGNMENT;
+  vals->push_back(Expression::make_type_info(this, type_info));
+
+  ++p;
+  go_assert(p->is_field_name("fieldAlign"));
+  type_info = Expression::TYPE_INFO_FIELD_ALIGNMENT;
+  vals->push_back(Expression::make_type_info(this, type_info));
+
+  ++p;
   go_assert(p->is_field_name("hashfn"));
   Function_type* hash_fntype = p->type()->function_type();
 
@@ -2373,7 +2385,7 @@
     vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc));
 
   ++p;
-  go_assert(p->is_field_name("gc"));
+  go_assert(p->is_field_name("gcdata"));
   vals->push_back(Expression::make_gc_symbol(this));
 
   ++p;
@@ -2442,7 +2454,10 @@
       gogo->backend()->var_expression(t->gc_symbol_var_, VE_rvalue, bloc);
   Bexpression* addr_expr =
       gogo->backend()->address_expression(var_expr, bloc);
-  Btype* ubtype = Type::lookup_integer_type("uintptr")->get_backend(gogo);
+
+  Type* uint8_type = Type::lookup_integer_type("uint8");
+  Type* pointer_uint8_type = Type::make_pointer_type(uint8_type);
+  Btype* ubtype = pointer_uint8_type->get_backend(gogo);
   return gogo->backend()->convert_expression(ubtype, addr_expr, bloc);
 }
 
@@ -2578,6 +2593,22 @@
   return Expression::make_array_composite_literal(gc_symbol_type, vals, bloc);
 }
 
+// A temporary function to convert a GC_symbol_expression, with type
+// *byte, to type uintptr, so that it can be stored in a GC symbol.
+// This will be discarded when we move to the Go 1.8 garbage
+// collector.
+
+static Expression*
+to_uintptr(Expression* expr)
+{
+  go_assert(expr->type()->points_to() != NULL);
+  Location loc = expr->location();
+  expr = Expression::make_cast(Type::make_pointer_type(Type::make_void_type()),
+			       expr, loc);
+  return Expression::make_cast(Type::lookup_integer_type("uintptr"), expr,
+			       loc);
+}
+
 // Advance the OFFSET of the GC symbol by this type's width.
 
 void
@@ -5555,7 +5586,7 @@
   (*vals)->push_back(*offset);
 
   if (this->to_type_->has_pointer())
-    (*vals)->push_back(Expression::make_gc_symbol(this->to_type_));
+    (*vals)->push_back(to_uintptr(Expression::make_gc_symbol(this->to_type_)));
   this->advance_gc_offset(offset);
 }
 
@@ -8002,7 +8033,7 @@
   (*vals)->push_back(*offset);
 
   if (element_size != 0 && ok)
-    (*vals)->push_back(Expression::make_gc_symbol(element_type));
+    (*vals)->push_back(to_uintptr(Expression::make_gc_symbol(element_type)));
   this->advance_gc_offset(offset);
 }
 
@@ -8075,7 +8106,7 @@
 	  Expression* width =
 	    Expression::make_type_info(this, Expression::TYPE_INFO_SIZE);
   	  (*vals)->push_back(width);
-	  (*vals)->push_back(Expression::make_gc_symbol(this));
+	  (*vals)->push_back(to_uintptr(Expression::make_gc_symbol(this)));
   	}
       this->advance_gc_offset(offset);
     }
diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go
index 0325260..00349ba 100644
--- a/libgo/go/reflect/type.go
+++ b/libgo/go/reflect/type.go
@@ -259,20 +259,21 @@
 // with a unique tag like `reflect:"array"` or `reflect:"ptr"`
 // so that code cannot convert from, say, *arrayType to *ptrType.
 type rtype struct {
-	kind       uint8 // enumeration for C
-	align      int8  // alignment of variable with this type
-	fieldAlign uint8 // alignment of struct field with this type
-	_          uint8 // unused/padding
 	size       uintptr
-	hash       uint32 // hash of type; avoids computation in hash tables
+	ptrdata    uintptr // size of memory prefix holding all pointers
+	hash       uint32  // hash of type; avoids computation in hash tables
+	kind       uint8   // enumeration for C
+	align      int8    // alignment of variable with this type
+	fieldAlign uint8   // alignment of struct field with this type
+	_          uint8   // unused/padding
 
 	hashfn  func(unsafe.Pointer, uintptr) uintptr     // hash function
 	equalfn func(unsafe.Pointer, unsafe.Pointer) bool // equality function
 
-	gc            unsafe.Pointer // garbage collection data
-	string        *string        // string form; unnecessary  but undeniably useful
-	*uncommonType                // (relatively) uncommon fields
-	ptrToThis     *rtype         // type for pointer to this type, if used in binary or has methods
+	gcdata        *byte   // garbage collection data
+	string        *string // string form; unnecessary  but undeniably useful
+	*uncommonType         // (relatively) uncommon fields
+	ptrToThis     *rtype  // type for pointer to this type, if used in binary or has methods
 }
 
 // Method on non-interface type
@@ -1190,15 +1191,15 @@
 	pp.elem = t
 
 	if t.kind&kindNoPointers != 0 {
-		pp.gc = unsafe.Pointer(&ptrDataGCProg)
+		pp.gcdata = (*byte)(unsafe.Pointer(&ptrDataGCProg))
 	} else {
-		pp.gc = unsafe.Pointer(&ptrGC{
+		pp.gcdata = (*byte)(unsafe.Pointer(&ptrGC{
 			width:  pp.size,
 			op:     _GC_PTR,
 			off:    0,
-			elemgc: t.gc,
+			elemgc: unsafe.Pointer(t.gcdata),
 			end:    _GC_END,
-		})
+		}))
 	}
 
 	q := canonicalize(&pp.rtype)
@@ -1584,16 +1585,16 @@
 	ch.uncommonType = nil
 	ch.ptrToThis = nil
 
-	ch.gc = unsafe.Pointer(&chanGC{
+	ch.gcdata = (*byte)(unsafe.Pointer(&chanGC{
 		width: ch.size,
 		op:    _GC_CHAN_PTR,
 		off:   0,
 		typ:   &ch.rtype,
 		end:   _GC_END,
-	})
+	}))
 
-	// INCORRECT. Uncomment to check that TestChanOfGC fails when ch.gc is wrong.
-	// ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END})
+	// INCORRECT. Uncomment to check that TestChanOfGC fails when ch.gcdata is wrong.
+	// ch.gcdata = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END})
 
 	return cachePut(ckey, &ch.rtype)
 }
@@ -1734,7 +1735,7 @@
 	ft.ptrToThis = nil
 
 	// TODO(cmang): Generate GC data for funcs.
-	ft.gc = unsafe.Pointer(&ptrDataGCProg)
+	ft.gcdata = (*byte)(unsafe.Pointer(&ptrDataGCProg))
 
 	funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype)
 
@@ -1913,7 +1914,7 @@
 		fieldAlign: uint8(maxAlign),
 		size:       size,
 		kind:       kind,
-		gc:         gcPtr,
+		gcdata:     (*byte)(gcPtr),
 	}
 	s := "bucket(" + *ktyp.string + "," + *etyp.string + ")"
 	b.string = &s
@@ -1922,7 +1923,7 @@
 
 // Take the GC program for "t" and append it to the GC program "gc".
 func appendGCProgram(gc []uintptr, t *rtype, offset uintptr) []uintptr {
-	p := t.gc
+	p := unsafe.Pointer(t.gcdata)
 	p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) // skip size
 loop:
 	for {
@@ -1967,9 +1968,9 @@
 	offset = (offset + 1) / 2 * 2
 	offset += unsafe.Sizeof(uint16(0)) // bucketsize
 	offset = (offset + ptrsize - 1) / ptrsize * ptrsize
-	// gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // buckets
+	// gc = append(gc, _GC_PTR, offset, uintptr(bucket.gcdata)) // buckets
 	offset += ptrsize
-	// gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // oldbuckets
+	// gc = append(gc, _GC_PTR, offset, uintptr(bucket.gcdata)) // oldbuckets
 	offset += ptrsize
 	offset += ptrsize // nevacuate
 	gc = append(gc, _GC_END)
@@ -1977,7 +1978,7 @@
 
 	h := new(rtype)
 	h.size = offset
-	// h.gc = unsafe.Pointer(&gc[0])
+	// h.gcdata = unsafe.Pointer(&gc[0])
 	s := "hmap(" + *bucket.string + ")"
 	h.string = &s
 	return h
@@ -2038,19 +2039,19 @@
 	slice.ptrToThis = nil
 
 	if typ.size == 0 {
-		slice.gc = unsafe.Pointer(&sliceEmptyGCProg)
+		slice.gcdata = (*byte)(unsafe.Pointer(&sliceEmptyGCProg))
 	} else {
-		slice.gc = unsafe.Pointer(&sliceGC{
+		slice.gcdata = (*byte)(unsafe.Pointer(&sliceGC{
 			width:  slice.size,
 			op:     _GC_SLICE,
 			off:    0,
-			elemgc: typ.gc,
+			elemgc: unsafe.Pointer(typ.gcdata),
 			end:    _GC_END,
-		})
+		}))
 	}
 
-	// INCORRECT. Uncomment to check that TestSliceOfOfGC fails when slice.gc is wrong.
-	// slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END})
+	// INCORRECT. Uncomment to check that TestSliceOfOfGC fails when slice.gcdata is wrong.
+	// slice.gcdata = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END})
 
 	return cachePut(ckey, &slice.rtype)
 }
@@ -2229,7 +2230,7 @@
 	if !hasPtr {
 		typ.kind |= kindNoPointers
 		gc := [...]uintptr{size, _GC_END}
-		typ.gc = unsafe.Pointer(&gc[0])
+		typ.gcdata = (*byte)(unsafe.Pointer(&gc[0]))
 	} else {
 		typ.kind &^= kindNoPointers
 		gc := []uintptr{size}
@@ -2237,7 +2238,7 @@
 			gc = appendGCProgram(gc, ft.typ, ft.offset)
 		}
 		gc = append(gc, _GC_END)
-		typ.gc = unsafe.Pointer(&gc[0])
+		typ.gcdata = (*byte)(unsafe.Pointer(&gc[0]))
 	}
 
 	typ.hashfn = func(p unsafe.Pointer, seed uintptr) uintptr {
@@ -2365,18 +2366,18 @@
 		// No pointers.
 		array.kind |= kindNoPointers
 		gc := [...]uintptr{array.size, _GC_END}
-		array.gc = unsafe.Pointer(&gc[0])
+		array.gcdata = (*byte)(unsafe.Pointer(&gc[0]))
 
 	case count == 1:
 		// In memory, 1-element array looks just like the element.
 		array.kind |= typ.kind & kindGCProg
-		array.gc = typ.gc
+		array.gcdata = typ.gcdata
 
 	default:
 		gc := []uintptr{array.size, _GC_ARRAY_START, 0, uintptr(count), typ.size}
 		gc = appendGCProgram(gc, typ, 0)
 		gc = append(gc, _GC_ARRAY_NEXT, _GC_END)
-		array.gc = unsafe.Pointer(&gc[0])
+		array.gcdata = (*byte)(unsafe.Pointer(&gc[0]))
 	}
 
 	array.kind &^= kindDirectIface
diff --git a/libgo/go/runtime/type.go b/libgo/go/runtime/type.go
index cfee35a..6788f24 100644
--- a/libgo/go/runtime/type.go
+++ b/libgo/go/runtime/type.go
@@ -9,17 +9,18 @@
 import "unsafe"
 
 type _type struct {
+	size       uintptr
+	ptrdata    uintptr
+	hash       uint32
 	kind       uint8
 	align      int8
 	fieldAlign uint8
 	_          uint8
-	size       uintptr
-	hash       uint32
 
 	hashfn  func(unsafe.Pointer, uintptr) uintptr
 	equalfn func(unsafe.Pointer, unsafe.Pointer) bool
 
-	gc     unsafe.Pointer
+	gcdata *byte
 	string *string
 	*uncommontype
 	ptrToThis *_type
diff --git a/libgo/runtime/go-type.h b/libgo/runtime/go-type.h
index e155254..03806f6 100644
--- a/libgo/runtime/go-type.h
+++ b/libgo/runtime/go-type.h
@@ -66,6 +66,17 @@
 
 struct __go_type_descriptor
 {
+  /* The size in bytes of a value of this type.  Note that all types
+     in Go have a fixed size.  */
+  uintptr_t __size;
+
+  /* The size of the memory prefix of a value of this type that holds
+     all pointers.  */
+  uintptr_t __ptrdata;
+
+  /* The type's hash code.  */
+  uint32_t __hash;
+
   /* The type code for this type, one of the type kind values above.
      This is used by unsafe.Reflect and unsafe.Typeof to determine the
      type descriptor to return for this type itself.  It is also used
@@ -78,13 +89,6 @@
   /* The alignment in bytes of a struct field with this type.  */
   unsigned char __field_align;
 
-  /* The size in bytes of a value of this type.  Note that all types
-     in Go have a fixed size.  */
-  uintptr_t __size;
-
-  /* The type's hash code.  */
-  uint32_t __hash;
-
   /* This function takes a pointer to a value of this type, and the
      size of this type, and returns a hash code.  We pass the size
      explicitly becaues it means that we can share a single instance
@@ -96,7 +100,7 @@
   const FuncVal *__equalfn;
 
   /* The garbage collection data. */
-  const uintptr *__gc;
+  const byte *__gcdata;
 
   /* A string describing this type.  This is only used for
      debugging.  */
diff --git a/libgo/runtime/go-unsafe-pointer.c b/libgo/runtime/go-unsafe-pointer.c
index 3a97ee1..483f6ab 100644
--- a/libgo/runtime/go-unsafe-pointer.c
+++ b/libgo/runtime/go-unsafe-pointer.c
@@ -46,22 +46,24 @@
 
 const struct __go_type_descriptor unsafe_Pointer =
 {
+  /* __size */
+  sizeof (void *),
+  /* __ptrdata */
+  sizeof (void *),
+  /* __hash */
+  78501163U,
   /* __code */
   GO_UNSAFE_POINTER | GO_DIRECT_IFACE,
   /* __align */
   __alignof (void *),
   /* __field_align */
   offsetof (struct field_align, p) - 1,
-  /* __size */
-  sizeof (void *),
-  /* __hash */
-  78501163U,
   /* __hashfn */
   &runtime_pointerhash_descriptor,
   /* __equalfn */
   &runtime_pointerequal_descriptor,
-  /* __gc */
-  unsafe_Pointer_gc,
+  /* __gcdata */
+  (const byte*)(unsafe_Pointer_gc),
   /* __reflection */
   &reflection_string,
   /* __uncommon */
@@ -95,22 +97,24 @@
 {
   /* __common */
   {
+    /* __size */
+    sizeof (void *),
+    /* __ptrdata */
+    sizeof (void *),
+    /* __hash */
+    1256018616U,
     /* __code */
     GO_PTR | GO_DIRECT_IFACE,
     /* __align */
     __alignof (void *),
     /* __field_align */
     offsetof (struct field_align, p) - 1,
-    /* __size */
-    sizeof (void *),
-    /* __hash */
-    1256018616U,
     /* __hashfn */
     &runtime_pointerhash_descriptor,
     /* __equalfn */
     &runtime_pointerequal_descriptor,
-    /* __gc */
-    pointer_unsafe_Pointer_gc,
+    /* __gcdata */
+    (const byte*)(pointer_unsafe_Pointer_gc),
     /* __reflection */
     &preflection_string,
     /* __uncommon */
diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c
index 357641d..8e7d7ef 100644
--- a/libgo/runtime/mgc0.c
+++ b/libgo/runtime/mgc0.c
@@ -674,7 +674,7 @@
 		// Runtime and gc think differently about closures.
 		 runtime_strstr((const char *)t->string->str, (const char*)"struct { F uintptr") != (const char *)t->string->str)) {
 		pc1 = (uintptr*)objti;
-		pc2 = (const uintptr*)t->__gc;
+		pc2 = (const uintptr*)t->__gcdata;
 		// A simple best-effort check until first GC_END.
 		for(j = 1; pc1[j] != GC_END && pc2[j] != GC_END; j++) {
 			if(pc1[j] != pc2[j]) {
@@ -801,13 +801,13 @@
 				t = (Type*)(type & ~(uintptr)(PtrSize-1));
 				switch(type & (PtrSize-1)) {
 				case TypeInfo_SingleObject:
-					pc = (const uintptr*)t->__gc;
+					pc = (const uintptr*)t->__gcdata;
 					precise_type = true;  // type information about 'b' is precise
 					stack_top.count = 1;
 					stack_top.elemsize = pc[0];
 					break;
 				case TypeInfo_Array:
-					pc = (const uintptr*)t->__gc;
+					pc = (const uintptr*)t->__gcdata;
 					if(pc[0] == 0)
 						goto next_block;
 					precise_type = true;  // type information about 'b' is precise
@@ -926,11 +926,11 @@
 						// dgcsym1 in case TPTR32/case TPTR64. See rationale there.
 						et = ((const PtrType*)t)->elem;
 						if(!(et->__code & kindNoPointers))
-							objti = (uintptr)((const PtrType*)t)->elem->__gc;
+							objti = (uintptr)((const PtrType*)t)->elem->__gcdata;
 					}
 				} else {
 					obj = eface->data;
-					objti = (uintptr)t->__gc;
+					objti = (uintptr)t->__gcdata;
 				}
 			}
 			break;
@@ -964,11 +964,11 @@
 						// dgcsym1 in case TPTR32/case TPTR64. See rationale there.
 						et = ((const PtrType*)t)->elem;
 						if(!(et->__code & kindNoPointers))
-							objti = (uintptr)((const PtrType*)t)->elem->__gc;
+							objti = (uintptr)((const PtrType*)t)->elem->__gcdata;
 					}
 				} else {
 					obj = iface->data;
-					objti = (uintptr)t->__gc;
+					objti = (uintptr)t->__gcdata;
 				}
 			}
 			break;
@@ -1095,7 +1095,7 @@
 					// (Channel routines zero the unused part, so the current
 					// code does not lead to leaks, it's just a little inefficient.)
 					*sbuf.obj.pos++ = (Obj){chan->buf, chancap*chantype->elem->__size,
-						(uintptr)chantype->elem->__gc | PRECISE | LOOP};
+						(uintptr)chantype->elem->__gcdata | PRECISE | LOOP};
 					if(sbuf.obj.pos == sbuf.obj.end)
 						flushobjbuf(&sbuf);
 				}