diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index c3da5f636a..6affd08dc1 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -1363,6 +1363,7 @@ int is64(Type *t); 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 6b3cd66bb6..66efac07d0 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -378,7 +378,7 @@ methods(Type *t) // 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 @@ dcommontype(Sym *s, int ot, Type *t) 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 cd6c609567..325614e6bc 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -3794,3 +3794,42 @@ checknil(Node *x, NodeList **init) 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 7ae75e5617..f3886cf73a 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -834,9 +834,7 @@ walkexpr(Node **np, NodeList **init) 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 @@ walkexpr(Node **np, NodeList **init) 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 6e24830128..98af9fb64c 100644 --- a/src/pkg/database/sql/convert_test.go +++ b/src/pkg/database/sql/convert_test.go @@ -283,6 +283,26 @@ func TestValueConverters(t *testing.T) { // 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 @@ func TestRawBytesAllocs(t *testing.T) { 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 f122711731..d9781699e0 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -3213,6 +3213,9 @@ func checkSameType(t *testing.T, x, y interface{}) { } 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 d7d4974597..47aecd0023 100644 --- a/src/pkg/reflect/type.go +++ b/src/pkg/reflect/type.go @@ -383,12 +383,11 @@ type Method struct { 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 @@ func (gc *gcProg) appendProg(t *rtype) { 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 @@ func SliceOf(t Type) Type { // // 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 @@ func arrayOf(count int, elem Type) Type { 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 @@ func arrayOf(count int, elem Type) Type { 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 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin // 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 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin 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 576cbc3984..dda852a3ec 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -82,7 +82,7 @@ type Value struct { // 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 @@ func packEface(v Value) interface{} { 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 @@ func unpackEface(i interface{}) Value { 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 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) { 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 @@ func storeRcvr(v Value, p unsafe.Pointer) { 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 @@ func (v Value) Index(i int) Value { 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 @@ func (v Value) Index(i int) Value { return Value{typ, val, 0, fl} case String: - fl := v.flag&flagRO | flag(Uint8<= 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 @@ func (v Value) MapIndex(key Value) Value { 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 @@ func (v Value) MapKeys() []Value { // 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 @@ func (v Value) recv(nb bool) (val Value, ok bool) { 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 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) { 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 @@ func Zero(typ Type) Value { } 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 @@ func convertOp(dst, src *rtype) func(Value, Type) Value { // 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())< 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())< ptrSize { + if !isDirectIface(typ) { ptr := unsafe_New(typ) switch typ.size { case 8: @@ -2506,9 +2526,13 @@ func makeComplex(f flag, v complex128, t Type) Value { return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<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 @@ itab_callback(Itab *tab) 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 9bd6fc7617..60dfb49dbe 100644 --- a/src/pkg/runtime/iface.go +++ b/src/pkg/runtime/iface.go @@ -135,7 +135,7 @@ func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab { 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 @@ func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer) } 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 @@ func assertI2T(t *_type, i fInterface) (r struct{}) { 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 @@ func assertI2T2(t *_type, i fInterface) (r byte) { 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 @@ func assertE2T(t *_type, e interface{}) (r struct{}) { 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 @@ func assertE2T2(t *_type, e interface{}) (r byte) { 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 8b9447dad6..f4143669e7 100644 --- a/src/pkg/runtime/malloc.c +++ b/src/pkg/runtime/malloc.c @@ -459,7 +459,7 @@ setFinalizer(Eface obj, Eface finalizer) } 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 @@ setFinalizer(Eface obj, Eface finalizer) 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 f116efaba4..ce7e062109 100644 --- a/src/pkg/runtime/malloc.go +++ b/src/pkg/runtime/malloc.go @@ -14,15 +14,6 @@ const ( 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 ef44d7f786..3583d77d19 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -367,7 +367,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask) 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 @@ scanblock(byte *b, uintptr n, byte *ptrmask) 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 @@ runfinq(void) } 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 772080af55..b4e992e658 100644 --- a/src/pkg/runtime/stack.c +++ b/src/pkg/runtime/stack.c @@ -585,7 +585,7 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f) 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 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f) 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 0000000000..5985536289 --- /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 bf6ade08d6..7c611e8ba6 100644 --- a/src/pkg/runtime/typekind.h +++ b/src/pkg/runtime/typekind.h @@ -33,8 +33,9 @@ enum { 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, }; diff --git a/test/live.go b/test/live.go index 6ac1d6a464..1c01f8dc49 100644 --- a/test/live.go +++ b/test/live.go @@ -118,7 +118,10 @@ var i9 interface{} func f9() bool { g8() x := i9 - return x != 99 + // using complex number in comparison so that + // there is always a convT2E, no matter what the + // interface rules are. + return x != 99.0i // ERROR "live at call to convT2E: x" } // liveness formerly confused by UNDEF followed by RET, @@ -184,7 +187,7 @@ func f11c() *int { func f12() *int { if b { - select{} + select {} } else { return nil } @@ -215,7 +218,7 @@ func f15() { var x string _ = &x x = g15() // ERROR "live at call to g15: x" - print(x) // ERROR "live at call to printstring: x" + print(x) // ERROR "live at call to printstring: x" } func g15() string @@ -287,7 +290,7 @@ var ch chan *byte func f19() { // dest temporary for channel receive. var z *byte - + if b { z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$" } @@ -348,21 +351,21 @@ func f25(b bool) { var x string _ = &x x = g15() // ERROR "live at call to g15: x" - print(x) // ERROR "live at call to printstring: x" + print(x) // ERROR "live at call to printstring: x" } // ERROR "live at call to deferreturn: x" func g25() - + // non-escaping ... slices passed to function call should die on return, // so that the temporaries do not stack and do not cause ambiguously // live variables. func f26(b bool) { if b { - print26(1,2,3) // ERROR "live at call to print26: autotmp_[0-9]+$" + print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$" } - print26(4,5,6) // ERROR "live at call to print26: autotmp_[0-9]+$" - print26(7,8,9) // ERROR "live at call to print26: autotmp_[0-9]+$" + print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$" + print26((*int)(nil), (*int)(nil), (*int)(nil)) // ERROR "live at call to print26: autotmp_[0-9]+$" println() } @@ -374,10 +377,10 @@ func print26(...interface{}) func f27(b bool) { x := 0 if b { - call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$" + call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$" } - call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$" - call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$" + call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$" + call27(func() { x++ }) // ERROR "live at call to call27: autotmp_[0-9]+$" println() } @@ -386,10 +389,10 @@ func f27(b bool) { func f27defer(b bool) { x := 0 if b { - defer call27(func() {x++}) // ERROR "live at call to deferproc: autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+$" + defer call27(func() { x++ }) // ERROR "live at call to deferproc: autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+$" } - defer call27(func() {x++}) // ERROR "live at call to deferproc: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$" "ambiguously live" - println() // ERROR "live at call to printnl: autotmp_[0-9]+ autotmp_[0-9]+$" + defer call27(func() { x++ }) // ERROR "live at call to deferproc: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$" "ambiguously live" + println() // ERROR "live at call to printnl: autotmp_[0-9]+ autotmp_[0-9]+$" } // ERROR "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$" // and newproc (go) escapes to the heap @@ -397,9 +400,9 @@ func f27defer(b bool) { func f27go(b bool) { x := 0 if b { - go call27(func() {x++}) // ERROR "live at call to newobject: &x" "live at call to newproc: &x$" + go call27(func() { x++ }) // ERROR "live at call to newobject: &x" "live at call to newproc: &x$" } - go call27(func() {x++}) // ERROR "live at call to newobject: &x" + go call27(func() { x++ }) // ERROR "live at call to newobject: &x" println() } @@ -412,11 +415,11 @@ var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 string func f28(b bool) { if b { - print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" + print(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" } - print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" - print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" -} + print(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" + print(s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" +} // map iterator should die on end of range loop @@ -584,13 +587,13 @@ func f39a() (x []int) { func f39b() (x [10]*int) { x = [10]*int{new(int)} // ERROR "live at call to newobject: x" - println() // ERROR "live at call to printnl: x" + println() // ERROR "live at call to printnl: x" return x } func f39c() (x [10]*int) { x = [10]*int{new(int)} // ERROR "live at call to newobject: x" - println() // ERROR "live at call to printnl: x" + println() // ERROR "live at call to printnl: x" return } @@ -603,7 +606,7 @@ type T40 struct { func newT40() *T40 { ret := T40{ // ERROR "live at call to makemap: &ret" - make(map[int]int), + make(map[int]int), } return &ret }