cmd/gc, runtime: refactor interface inlining decision into compiler

We need to change the interface value representation for
concurrent garbage collection, so that there is no ambiguity
about whether the data word holds a pointer or scalar.

This CL does NOT make any representation changes.

Instead, it removes representation assumptions from
various pieces of code throughout the tree.
The isdirectiface function in cmd/gc/subr.c is now
the only place that decides that policy.
The policy propagates out from there in the reflect
metadata, as a new flag in the internal kind value.

A follow-up CL will change the representation by
changing the isdirectiface function. If that CL causes
problems, it will be easy to roll back.

Update #8405.

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews, r
https://golang.org/cl/129090043
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index 576cbc3..dda852a 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -82,7 +82,7 @@
 	// This repeats typ.Kind() except for method values.
 	// The remaining 23+ bits give a method number for method values.
 	// If flag.kind() != Func, code can assume that flagMethod is unset.
-	// If typ.size > ptrSize, code can assume that flagIndir is set.
+	// If !isDirectIface(typ), code can assume that flagIndir is set.
 	flag
 
 	// A method value represents a curried method invocation
@@ -128,7 +128,10 @@
 	e := (*emptyInterface)(unsafe.Pointer(&i))
 	// First, fill in the data portion of the interface.
 	switch {
-	case t.size > ptrSize:
+	case !isDirectIface(t):
+		if v.flag&flagIndir == 0 {
+			panic("bad indir")
+		}
 		// Value is indirect, and so is the interface we're making.
 		ptr := v.ptr
 		if v.flag&flagAddr != 0 {
@@ -172,7 +175,7 @@
 		return Value{}
 	}
 	f := flag(t.Kind()) << flagKindShift
-	if t.size > ptrSize {
+	if !isDirectIface(t) {
 		return Value{t, unsafe.Pointer(e.word), 0, f | flagIndir}
 	}
 	if t.pointers() {
@@ -607,8 +610,8 @@
 		off += -off & uintptr(typ.align-1)
 		addr := unsafe.Pointer(uintptr(ptr) + off)
 		v := Value{typ, nil, 0, flag(typ.Kind()) << flagKindShift}
-		if typ.size > ptrSize {
-			// value does not fit in word.
+		if !isDirectIface(typ) {
+			// value cannot be inlined in interface data.
 			// Must make a copy, because f might keep a reference to it,
 			// and we cannot let f keep a reference to the stack frame
 			// after this function returns, not even a read-only reference.
@@ -714,7 +717,7 @@
 		iface := (*nonEmptyInterface)(v.ptr)
 		*(*unsafe.Pointer)(p) = unsafe.Pointer(iface.word)
 	} else if v.flag&flagIndir != 0 {
-		if t.size > ptrSize {
+		if !isDirectIface(t) {
 			*(*unsafe.Pointer)(p) = v.ptr
 		} else if t.pointers() {
 			*(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
@@ -987,7 +990,13 @@
 			val = unsafe.Pointer(uintptr(v.ptr) + offset)
 		case typ.pointers():
 			if offset != 0 {
-				panic("can't Index(i) with i!=0 on ptrLike value")
+				// This is an array stored inline in an interface value.
+				// And the array element type has pointers.
+				// Since the inline storage space is only a single word,
+				// this implies we must be holding an array of length 1
+				// with an element type that is a single pointer.
+				// If the offset is not 0, something has gone wrong.
+				panic("reflect: internal error: unexpected array index")
 			}
 			val = v.ptr
 		case bigEndian:
@@ -1014,14 +1023,13 @@
 		return Value{typ, val, 0, fl}
 
 	case String:
-		fl := v.flag&flagRO | flag(Uint8<<flagKindShift)
+		fl := v.flag&flagRO | flag(Uint8<<flagKindShift) | flagIndir
 		s := (*stringHeader)(v.ptr)
 		if i < 0 || i >= s.Len {
 			panic("reflect: string index out of range")
 		}
-		b := uintptr(0)
-		*(*byte)(unsafe.Pointer(&b)) = *(*byte)(unsafe.Pointer(uintptr(s.Data) + uintptr(i)))
-		return Value{uint8Type, nil, b, fl}
+		p := unsafe.Pointer(uintptr(s.Data) + uintptr(i))
+		return Value{uint8Type, p, 0, fl}
 	}
 	panic(&ValueError{"reflect.Value.Index", k})
 }
@@ -1209,7 +1217,7 @@
 	typ := tt.elem
 	fl := (v.flag | key.flag) & flagRO
 	fl |= flag(typ.Kind()) << flagKindShift
-	if typ.size > ptrSize {
+	if !isDirectIface(typ) {
 		// Copy result so future changes to the map
 		// won't change the underlying value.
 		c := unsafe_New(typ)
@@ -1249,7 +1257,7 @@
 			// we can do about it.
 			break
 		}
-		if keyType.size > ptrSize {
+		if !isDirectIface(keyType) {
 			// Copy result so future changes to the map
 			// won't change the underlying value.
 			c := unsafe_New(keyType)
@@ -1448,7 +1456,7 @@
 	t := tt.elem
 	val = Value{t, nil, 0, flag(t.Kind()) << flagKindShift}
 	var p unsafe.Pointer
-	if t.size > ptrSize {
+	if !isDirectIface(t) {
 		p = unsafe_New(t)
 		val.ptr = p
 		val.flag |= flagIndir
@@ -2190,7 +2198,7 @@
 		t := tt.elem
 		p := runcases[chosen].val
 		fl := flag(t.Kind()) << flagKindShift
-		if t.size > ptrSize {
+		if !isDirectIface(t) {
 			recv = Value{t, p, 0, fl | flagIndir}
 		} else if t.pointers() {
 			recv = Value{t, *(*unsafe.Pointer)(p), 0, fl}
@@ -2291,7 +2299,7 @@
 	}
 	t := typ.common()
 	fl := flag(t.Kind()) << flagKindShift
-	if t.size <= ptrSize {
+	if isDirectIface(t) {
 		return Value{t, nil, 0, fl}
 	}
 	return Value{t, unsafe_New(typ.(*rtype)), 0, fl | flagIndir}
@@ -2450,10 +2458,18 @@
 // where t is a signed or unsigned int type.
 func makeInt(f flag, bits uint64, t Type) Value {
 	typ := t.common()
-	if typ.size > ptrSize {
-		// Assume ptrSize >= 4, so this must be uint64.
+	if !isDirectIface(typ) {
 		ptr := unsafe_New(typ)
-		*(*uint64)(unsafe.Pointer(ptr)) = bits
+		switch typ.size {
+		case 1:
+			*(*uint8)(unsafe.Pointer(ptr)) = uint8(bits)
+		case 2:
+			*(*uint16)(unsafe.Pointer(ptr)) = uint16(bits)
+		case 4:
+			*(*uint32)(unsafe.Pointer(ptr)) = uint32(bits)
+		case 8:
+			*(*uint64)(unsafe.Pointer(ptr)) = bits
+		}
 		return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
 	}
 	var s uintptr
@@ -2474,10 +2490,14 @@
 // where t is a float32 or float64 type.
 func makeFloat(f flag, v float64, t Type) Value {
 	typ := t.common()
-	if typ.size > ptrSize {
-		// Assume ptrSize >= 4, so this must be float64.
+	if !isDirectIface(typ) {
 		ptr := unsafe_New(typ)
-		*(*float64)(unsafe.Pointer(ptr)) = v
+		switch typ.size {
+		case 4:
+			*(*float32)(unsafe.Pointer(ptr)) = float32(v)
+		case 8:
+			*(*float64)(unsafe.Pointer(ptr)) = v
+		}
 		return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
 	}
 
@@ -2495,7 +2515,7 @@
 // where t is a complex64 or complex128 type.
 func makeComplex(f flag, v complex128, t Type) Value {
 	typ := t.common()
-	if typ.size > ptrSize {
+	if !isDirectIface(typ) {
 		ptr := unsafe_New(typ)
 		switch typ.size {
 		case 8:
@@ -2506,9 +2526,13 @@
 		return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
 	}
 
-	// Assume ptrSize <= 8 so this must be complex64.
 	var s uintptr
-	*(*complex64)(unsafe.Pointer(&s)) = complex64(v)
+	switch typ.size {
+	case 8:
+		*(*complex64)(unsafe.Pointer(&s)) = complex64(v)
+	case 16:
+		*(*complex128)(unsafe.Pointer(&s)) = v
+	}
 	return Value{typ, nil, s, f | flag(typ.Kind())<<flagKindShift}
 }