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/cmd/gc/go.h b/src/cmd/gc/go.h
index c3da5f6..6affd08 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -1363,6 +1363,7 @@
 int	isbadimport(Strlit *s);
 int	isblank(Node *n);
 int	isblanksym(Sym *s);
+int	isdirectiface(Type*);
 int	isfixedarray(Type *t);
 int	isideal(Type *t);
 int	isinter(Type *t);
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
index 6b3cd66..66efac0 100644
--- a/src/cmd/gc/reflect.c
+++ b/src/cmd/gc/reflect.c
@@ -378,7 +378,7 @@
 
 	// type stored in interface word
 	it = t;
-	if(it->width > widthptr)
+	if(!isdirectiface(it))
 		it = ptrto(t);
 
 	// make list of methods for t,
@@ -785,6 +785,8 @@
 		i = KindSlice;
 	if(!haspointers(t))
 		i |= KindNoPointers;
+	if(isdirectiface(t))
+		i |= KindDirectIface;
 	if(gcprog)
 		i |= KindGCProg;
 	ot = duint8(s, ot, i);  // kind
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
index cd6c609..325614e 100644
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -3794,3 +3794,42 @@
 	n->typecheck = 1;
 	*init = list(*init, n);
 }
+
+/*
+ * Can this type be stored directly in an interface word?
+ */
+int
+isdirectiface(Type *t)
+{
+	// Setting IfacePointerOnly = 1 changes the
+	// interface representation so that the data word
+	// in an interface value must always be a pointer.
+	// Setting it to 0 uses the original representation,
+	// where the data word can hold a pointer or any
+	// non-pointer value no bigger than a pointer.
+	enum {
+		IfacePointerOnly = 0,
+	};
+
+	if(IfacePointerOnly) {
+		switch(t->etype) {
+		case TPTR32:
+		case TPTR64:
+		case TCHAN:
+		case TMAP:
+		case TFUNC:
+		case TUNSAFEPTR:
+			return 1;
+		case TARRAY:
+			// Array of 1 direct iface type can be direct.
+			return t->bound == 1 && isdirectiface(t->type);
+		case TSTRUCT:
+			// Struct with 1 field of direct iface type can be direct.
+			return t->type != T && t->type->down == T && isdirectiface(t->type->type);
+		}
+		return 0;
+	}
+	
+	dowidth(t);
+	return t->width <= widthptr;
+}
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index 7ae75e5..f3886cf 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -834,9 +834,7 @@
 		walkexpr(&n->left, init);
 
 		// Optimize convT2E as a two-word copy when T is uintptr-shaped.
-		if(!isinter(n->left->type) && isnilinter(n->type) &&
-		   (n->left->type->width == widthptr) &&
-		   isint[simsimtype(n->left->type)]) {
+		if(isnilinter(n->type) && isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
 			l = nod(OEFACE, typename(n->left->type), n->left);
 			l->type = n->type;
 			l->typecheck = n->typecheck;
@@ -884,8 +882,7 @@
 			l->addable = 1;
 			ll = list(ll, l);
 
-			if(n->left->type->width == widthptr &&
-		   	   isint[simsimtype(n->left->type)]) {
+			if(isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
 				/* For pointer types, we can make a special form of optimization
 				 *
 				 * These statements are put onto the expression init list:
diff --git a/src/pkg/database/sql/convert_test.go b/src/pkg/database/sql/convert_test.go
index 6e24830..98af9fb 100644
--- a/src/pkg/database/sql/convert_test.go
+++ b/src/pkg/database/sql/convert_test.go
@@ -283,6 +283,26 @@
 
 // Tests that assigning to RawBytes doesn't allocate (and also works).
 func TestRawBytesAllocs(t *testing.T) {
+	var tests = []struct {
+		name string
+		in   interface{}
+		want string
+	}{
+		{"uint64", uint64(12345678), "12345678"},
+		{"uint32", uint32(1234), "1234"},
+		{"uint16", uint16(12), "12"},
+		{"uint8", uint8(1), "1"},
+		{"uint", uint(123), "123"},
+		{"int", int(123), "123"},
+		{"int8", int8(1), "1"},
+		{"int16", int16(12), "12"},
+		{"int32", int32(1234), "1234"},
+		{"int64", int64(12345678), "12345678"},
+		{"float32", float32(1.5), "1.5"},
+		{"float64", float64(64), "64"},
+		{"bool", false, "false"},
+	}
+
 	buf := make(RawBytes, 10)
 	test := func(name string, in interface{}, want string) {
 		if err := convertAssign(&buf, in); err != nil {
@@ -301,20 +321,11 @@
 			t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
 		}
 	}
+
 	n := testing.AllocsPerRun(100, func() {
-		test("uint64", uint64(12345678), "12345678")
-		test("uint32", uint32(1234), "1234")
-		test("uint16", uint16(12), "12")
-		test("uint8", uint8(1), "1")
-		test("uint", uint(123), "123")
-		test("int", int(123), "123")
-		test("int8", int8(1), "1")
-		test("int16", int16(12), "12")
-		test("int32", int32(1234), "1234")
-		test("int64", int64(12345678), "12345678")
-		test("float32", float32(1.5), "1.5")
-		test("float64", float64(64), "64")
-		test("bool", false, "false")
+		for _, tt := range tests {
+			test(tt.name, tt.in, tt.want)
+		}
 	})
 
 	// The numbers below are only valid for 64-bit interface word sizes,
diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go
index f122711..d978169 100644
--- a/src/pkg/reflect/all_test.go
+++ b/src/pkg/reflect/all_test.go
@@ -3213,6 +3213,9 @@
 }
 
 func TestArrayOf(t *testing.T) {
+	// TODO(rsc): Finish ArrayOf and enable-test.
+	t.Skip("ArrayOf is not finished (and not exported)")
+
 	// check construction and use of type not in binary
 	type T int
 	at := ArrayOf(10, TypeOf(T(1)))
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index d7d4974..47aecd0 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -383,12 +383,11 @@
 	Index int   // index for Type.Method
 }
 
-// High bit says whether type has
-// embedded pointers,to help garbage collector.
 const (
-	kindMask       = 0x3f
-	kindGCProg     = 0x40
-	kindNoPointers = 0x80
+	kindDirectIface = 1 << 5
+	kindGCProg      = 1 << 6 // Type.gc points to GC program
+	kindNoPointers  = 1 << 7
+	kindMask        = (1 << 5) - 1
 )
 
 func (k Kind) String() string {
@@ -1503,6 +1502,7 @@
 	var prog []byte
 	if t.kind&kindGCProg != 0 {
 		// Ensure that the runtime has unrolled GC program.
+		// TODO(rsc): Do not allocate.
 		unsafe_New(t)
 		// The program is stored in t.gc[0], skip unroll flag.
 		prog = (*[1 << 30]byte)(unsafe.Pointer(t.gc[0]))[1:]
@@ -1652,6 +1652,8 @@
 //
 // TODO(rsc): Unexported for now. Export once the alg field is set correctly
 // for the type. This may require significant work.
+//
+// TODO(rsc): TestArrayOf is also disabled. Re-enable.
 func arrayOf(count int, elem Type) Type {
 	typ := elem.(*rtype)
 	slice := SliceOf(elem)
@@ -1676,6 +1678,7 @@
 	prototype := *(**arrayType)(unsafe.Pointer(&iarray))
 	array := new(arrayType)
 	*array = *prototype
+	// TODO: Set extra kind bits correctly.
 	array.string = &s
 	array.hash = fnv1(typ.hash, '[')
 	for n := uint32(count); n > 0; n >>= 8 {
@@ -1692,6 +1695,7 @@
 	array.fieldAlign = typ.fieldAlign
 	// TODO: array.alg
 	// TODO: array.gc
+	// TODO:
 	array.uncommonType = nil
 	array.ptrToThis = nil
 	array.zero = unsafe.Pointer(&make([]byte, array.size)[0])
@@ -1763,7 +1767,7 @@
 		// Reflect uses the "interface" calling convention for
 		// methods, where receivers take one word of argument
 		// space no matter how big they actually are.
-		if rcvr.size > ptrSize {
+		if !isDirectIface(rcvr) {
 			// we pass a pointer to the receiver.
 			gc.append(bitsPointer)
 		} else if rcvr.pointers() {
@@ -1813,3 +1817,8 @@
 	layoutCache.Unlock()
 	return x, argSize, retOffset
 }
+
+// isDirectIface reports whether t is stored directly in an interface value.
+func isDirectIface(t *rtype) bool {
+	return t.kind&kindDirectIface != 0
+}
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}
 }
 
diff --git a/src/pkg/runtime/alg.go b/src/pkg/runtime/alg.go
index 409f0fa..650f684 100644
--- a/src/pkg/runtime/alg.go
+++ b/src/pkg/runtime/alg.go
@@ -117,7 +117,7 @@
 		// but we can print a better error.
 		panic(errorString("hash of unhashable type " + *t._string))
 	}
-	if uintptr(t.size) <= ptrSize {
+	if isDirectIface(t) {
 		return c1 * fn(unsafe.Pointer(&a.data), uintptr(t.size), h^c0)
 	} else {
 		return c1 * fn(a.data, uintptr(t.size), h^c0)
@@ -135,7 +135,7 @@
 		// but we can print a better error.
 		panic(errorString("hash of unhashable type " + *t._string))
 	}
-	if uintptr(t.size) <= ptrSize {
+	if isDirectIface(t) {
 		return c1 * fn(unsafe.Pointer(&a.data), uintptr(t.size), h^c0)
 	} else {
 		return c1 * fn(a.data, uintptr(t.size), h^c0)
@@ -208,7 +208,7 @@
 		// but we can print a better error.
 		panic(errorString("comparing uncomparable type " + *t._string))
 	}
-	if uintptr(t.size) <= ptrSize {
+	if isDirectIface(t) {
 		return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)), uintptr(t.size))
 	}
 	return eq(x.data, y.data, uintptr(t.size))
@@ -232,7 +232,7 @@
 		// but we can print a better error.
 		panic(errorString("comparing uncomparable type " + *t._string))
 	}
-	if uintptr(t.size) <= ptrSize {
+	if isDirectIface(t) {
 		return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)), uintptr(t.size))
 	}
 	return eq(x.data, y.data, uintptr(t.size))
diff --git a/src/pkg/runtime/heapdump.c b/src/pkg/runtime/heapdump.c
index aa817fc..babb32f 100644
--- a/src/pkg/runtime/heapdump.c
+++ b/src/pkg/runtime/heapdump.c
@@ -196,7 +196,7 @@
 		write((byte*)".", 1);
 		write(t->x->name->str, t->x->name->len);
 	}
-	dumpbool(t->size > PtrSize || (t->kind & KindNoPointers) == 0);
+	dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0);
 	dumpfields((BitVector){0, nil});
 }
 
@@ -584,7 +584,7 @@
 	dumpint(TagItab);
 	dumpint((uintptr)tab);
 	t = tab->type;
-	dumpbool(t->size > PtrSize || (t->kind & KindNoPointers) == 0);
+	dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0);
 }
 
 static void
diff --git a/src/pkg/runtime/iface.go b/src/pkg/runtime/iface.go
index 9bd6fc7..60dfb49 100644
--- a/src/pkg/runtime/iface.go
+++ b/src/pkg/runtime/iface.go
@@ -135,7 +135,7 @@
 func convT2E(t *_type, elem unsafe.Pointer) (e interface{}) {
 	size := uintptr(t.size)
 	ep := (*eface)(unsafe.Pointer(&e))
-	if size <= ptrSize {
+	if isDirectIface(t) {
 		ep._type = t
 		memmove(unsafe.Pointer(&ep.data), elem, size)
 	} else {
@@ -157,7 +157,7 @@
 	}
 	size := uintptr(t.size)
 	pi := (*iface)(unsafe.Pointer(&i))
-	if size <= ptrSize {
+	if isDirectIface(t) {
 		pi.tab = tab
 		memmove(unsafe.Pointer(&pi.data), elem, size)
 	} else {
@@ -182,7 +182,7 @@
 		panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""})
 	}
 	size := uintptr(t.size)
-	if size <= ptrSize {
+	if isDirectIface(t) {
 		memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size)
 	} else {
 		memmove(unsafe.Pointer(&r), ip.data, size)
@@ -202,7 +202,7 @@
 		return
 	}
 	*ok = true
-	if size <= ptrSize {
+	if isDirectIface(t) {
 		memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size)
 	} else {
 		memmove(unsafe.Pointer(&r), ip.data, size)
@@ -226,7 +226,7 @@
 		panic(&TypeAssertionError{"", *ep._type._string, *t._string, ""})
 	}
 	size := uintptr(t.size)
-	if size <= ptrSize {
+	if isDirectIface(t) {
 		memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size)
 	} else {
 		memmove(unsafe.Pointer(&r), ep.data, size)
@@ -245,7 +245,7 @@
 		return
 	}
 	*ok = true
-	if size <= ptrSize {
+	if isDirectIface(t) {
 		memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size)
 	} else {
 		memmove(unsafe.Pointer(&r), ep.data, size)
diff --git a/src/pkg/runtime/malloc.c b/src/pkg/runtime/malloc.c
index 8b9447d..f414366 100644
--- a/src/pkg/runtime/malloc.c
+++ b/src/pkg/runtime/malloc.c
@@ -459,7 +459,7 @@
 	}
 	if(finalizer.type != nil) {
 		runtime·createfing();
-		if(finalizer.type->kind != KindFunc)
+		if((finalizer.type->kind&KindMask) != KindFunc)
 			goto badfunc;
 		ft = (FuncType*)finalizer.type;
 		if(ft->dotdotdot || ft->in.len != 1)
@@ -467,12 +467,12 @@
 		fint = *(Type**)ft->in.array;
 		if(fint == obj.type) {
 			// ok - same type
-		} else if(fint->kind == KindPtr && (fint->x == nil || fint->x->name == nil || obj.type->x == nil || obj.type->x->name == nil) && ((PtrType*)fint)->elem == ((PtrType*)obj.type)->elem) {
+		} else if((fint->kind&KindMask) == KindPtr && (fint->x == nil || fint->x->name == nil || obj.type->x == nil || obj.type->x->name == nil) && ((PtrType*)fint)->elem == ((PtrType*)obj.type)->elem) {
 			// ok - not same type, but both pointers,
 			// one or the other is unnamed, and same element type, so assignable.
-		} else if(fint->kind == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0) {
+		} else if((fint->kind&KindMask) == KindInterface && ((InterfaceType*)fint)->mhdr.len == 0) {
 			// ok - satisfies empty interface
-		} else if(fint->kind == KindInterface && runtime·ifaceE2I2((InterfaceType*)fint, obj, &iface)) {
+		} else if((fint->kind&KindMask) == KindInterface && runtime·ifaceE2I2((InterfaceType*)fint, obj, &iface)) {
 			// ok - satisfies non-empty interface
 		} else
 			goto badfunc;
diff --git a/src/pkg/runtime/malloc.go b/src/pkg/runtime/malloc.go
index f116efa..ce7e062 100644
--- a/src/pkg/runtime/malloc.go
+++ b/src/pkg/runtime/malloc.go
@@ -14,15 +14,6 @@
 	flagNoScan = 1 << 0 // GC doesn't have to scan object
 	flagNoZero = 1 << 1 // don't zero memory
 
-	kindArray      = 17
-	kindFunc       = 19
-	kindInterface  = 20
-	kindPtr        = 22
-	kindStruct     = 25
-	kindMask       = 1<<6 - 1
-	kindGCProg     = 1 << 6
-	kindNoPointers = 1 << 7
-
 	maxTinySize   = 16
 	tinySizeClass = 2
 	maxSmallSize  = 32 << 10
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index ef44d7f..3583d77 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -367,7 +367,7 @@
 				iface = (Iface*)(b+i);
 				if(iface->tab != nil) {
 					typ = iface->tab->type;
-					if(typ->size > PtrSize || !(typ->kind&KindNoPointers))
+					if(!(typ->kind&KindDirectIface) || !(typ->kind&KindNoPointers))
 						obj = iface->data;
 				}
 				break;
@@ -375,7 +375,7 @@
 				eface = (Eface*)(b+i);
 				typ = eface->type;
 				if(typ != nil) {
-					if(typ->size > PtrSize || !(typ->kind&KindNoPointers))
+					if(!(typ->kind&KindDirectIface) || !(typ->kind&KindNoPointers))
 						obj = eface->data;
 				}
 				break;
@@ -1675,7 +1675,7 @@
 				}
 				if(f->fint == nil)
 					runtime·throw("missing type in runfinq");
-				if(f->fint->kind == KindPtr) {
+				if((f->fint->kind&KindMask) == KindPtr) {
 					// direct use of pointer
 					*(void**)frame = f->arg;
 				} else if(((InterfaceType*)f->fint)->mhdr.len == 0) {
diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c
index 772080a..b4e992e 100644
--- a/src/pkg/runtime/stack.c
+++ b/src/pkg/runtime/stack.c
@@ -585,7 +585,7 @@
 				break;
 			case BitsEface:
 				t = (Type*)scanp[i];
-				if(t != nil && (t->size > PtrSize || (t->kind & KindNoPointers) == 0)) {
+				if(t != nil && ((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0)) {
 					p = scanp[i+1];
 					if(minp <= p && p < maxp) {
 						if(StackDebug >= 3)
@@ -602,7 +602,7 @@
 				if(tab != nil) {
 					t = tab->type;
 					//runtime·printf("          type=%p\n", t);
-					if(t->size > PtrSize || (t->kind & KindNoPointers) == 0) {
+					if((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0) {
 						p = scanp[i+1];
 						if(minp <= p && p < maxp) {
 							if(StackDebug >= 3)
diff --git a/src/pkg/runtime/typekind.go b/src/pkg/runtime/typekind.go
new file mode 100644
index 0000000..5985536
--- /dev/null
+++ b/src/pkg/runtime/typekind.go
@@ -0,0 +1,44 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+const (
+	kindBool = 1 + iota
+	kindInt
+	kindInt8
+	kindInt16
+	kindInt32
+	kindInt64
+	kindUint
+	kindUint8
+	kindUint16
+	kindUint32
+	kindUint64
+	kindUintptr
+	kindFloat32
+	kindFloat64
+	kindComplex64
+	kindComplex128
+	kindArray
+	kindChan
+	kindFunc
+	kindInterface
+	kindMap
+	kindPtr
+	kindSlice
+	kindString
+	kindStruct
+	kindUnsafePointer
+
+	kindDirectIface = 1 << 5
+	kindGCProg      = 1 << 6 // Type.gc points to GC program
+	kindNoPointers  = 1 << 7
+	kindMask        = (1 << 5) - 1
+)
+
+// isDirectIface reports whether t is stored directly in an interface value.
+func isDirectIface(t *_type) bool {
+	return t.kind&kindDirectIface != 0
+}
diff --git a/src/pkg/runtime/typekind.h b/src/pkg/runtime/typekind.h
index bf6ade0..7c611e8 100644
--- a/src/pkg/runtime/typekind.h
+++ b/src/pkg/runtime/typekind.h
@@ -33,8 +33,9 @@
 	KindStruct,
 	KindUnsafePointer,
 
+	KindDirectIface = 1<<5,
 	KindGCProg = 1<<6,	// Type.gc points to GC program
 	KindNoPointers = 1<<7,
-	KindMask = (1<<6)-1,
+	KindMask = (1<<5)-1,
 };