mirror of
https://github.com/golang/go
synced 2024-11-02 13:42:29 +00:00
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
This commit is contained in:
parent
2e2beb567c
commit
1806a5732b
18 changed files with 230 additions and 105 deletions
|
@ -1363,6 +1363,7 @@ int is64(Type *t);
|
||||||
int isbadimport(Strlit *s);
|
int isbadimport(Strlit *s);
|
||||||
int isblank(Node *n);
|
int isblank(Node *n);
|
||||||
int isblanksym(Sym *s);
|
int isblanksym(Sym *s);
|
||||||
|
int isdirectiface(Type*);
|
||||||
int isfixedarray(Type *t);
|
int isfixedarray(Type *t);
|
||||||
int isideal(Type *t);
|
int isideal(Type *t);
|
||||||
int isinter(Type *t);
|
int isinter(Type *t);
|
||||||
|
|
|
@ -378,7 +378,7 @@ methods(Type *t)
|
||||||
|
|
||||||
// type stored in interface word
|
// type stored in interface word
|
||||||
it = t;
|
it = t;
|
||||||
if(it->width > widthptr)
|
if(!isdirectiface(it))
|
||||||
it = ptrto(t);
|
it = ptrto(t);
|
||||||
|
|
||||||
// make list of methods for t,
|
// make list of methods for t,
|
||||||
|
@ -785,6 +785,8 @@ dcommontype(Sym *s, int ot, Type *t)
|
||||||
i = KindSlice;
|
i = KindSlice;
|
||||||
if(!haspointers(t))
|
if(!haspointers(t))
|
||||||
i |= KindNoPointers;
|
i |= KindNoPointers;
|
||||||
|
if(isdirectiface(t))
|
||||||
|
i |= KindDirectIface;
|
||||||
if(gcprog)
|
if(gcprog)
|
||||||
i |= KindGCProg;
|
i |= KindGCProg;
|
||||||
ot = duint8(s, ot, i); // kind
|
ot = duint8(s, ot, i); // kind
|
||||||
|
|
|
@ -3794,3 +3794,42 @@ checknil(Node *x, NodeList **init)
|
||||||
n->typecheck = 1;
|
n->typecheck = 1;
|
||||||
*init = list(*init, n);
|
*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;
|
||||||
|
}
|
||||||
|
|
|
@ -834,9 +834,7 @@ walkexpr(Node **np, NodeList **init)
|
||||||
walkexpr(&n->left, init);
|
walkexpr(&n->left, init);
|
||||||
|
|
||||||
// Optimize convT2E as a two-word copy when T is uintptr-shaped.
|
// Optimize convT2E as a two-word copy when T is uintptr-shaped.
|
||||||
if(!isinter(n->left->type) && isnilinter(n->type) &&
|
if(isnilinter(n->type) && isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
|
||||||
(n->left->type->width == widthptr) &&
|
|
||||||
isint[simsimtype(n->left->type)]) {
|
|
||||||
l = nod(OEFACE, typename(n->left->type), n->left);
|
l = nod(OEFACE, typename(n->left->type), n->left);
|
||||||
l->type = n->type;
|
l->type = n->type;
|
||||||
l->typecheck = n->typecheck;
|
l->typecheck = n->typecheck;
|
||||||
|
@ -884,8 +882,7 @@ walkexpr(Node **np, NodeList **init)
|
||||||
l->addable = 1;
|
l->addable = 1;
|
||||||
ll = list(ll, l);
|
ll = list(ll, l);
|
||||||
|
|
||||||
if(n->left->type->width == widthptr &&
|
if(isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
|
||||||
isint[simsimtype(n->left->type)]) {
|
|
||||||
/* For pointer types, we can make a special form of optimization
|
/* For pointer types, we can make a special form of optimization
|
||||||
*
|
*
|
||||||
* These statements are put onto the expression init list:
|
* These statements are put onto the expression init list:
|
||||||
|
|
|
@ -283,6 +283,26 @@ func TestValueConverters(t *testing.T) {
|
||||||
|
|
||||||
// Tests that assigning to RawBytes doesn't allocate (and also works).
|
// Tests that assigning to RawBytes doesn't allocate (and also works).
|
||||||
func TestRawBytesAllocs(t *testing.T) {
|
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)
|
buf := make(RawBytes, 10)
|
||||||
test := func(name string, in interface{}, want string) {
|
test := func(name string, in interface{}, want string) {
|
||||||
if err := convertAssign(&buf, in); err != nil {
|
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))
|
t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n := testing.AllocsPerRun(100, func() {
|
n := testing.AllocsPerRun(100, func() {
|
||||||
test("uint64", uint64(12345678), "12345678")
|
for _, tt := range tests {
|
||||||
test("uint32", uint32(1234), "1234")
|
test(tt.name, tt.in, tt.want)
|
||||||
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")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// The numbers below are only valid for 64-bit interface word sizes,
|
// The numbers below are only valid for 64-bit interface word sizes,
|
||||||
|
|
|
@ -3213,6 +3213,9 @@ func checkSameType(t *testing.T, x, y interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayOf(t *testing.T) {
|
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
|
// check construction and use of type not in binary
|
||||||
type T int
|
type T int
|
||||||
at := ArrayOf(10, TypeOf(T(1)))
|
at := ArrayOf(10, TypeOf(T(1)))
|
||||||
|
|
|
@ -383,12 +383,11 @@ type Method struct {
|
||||||
Index int // index for Type.Method
|
Index int // index for Type.Method
|
||||||
}
|
}
|
||||||
|
|
||||||
// High bit says whether type has
|
|
||||||
// embedded pointers,to help garbage collector.
|
|
||||||
const (
|
const (
|
||||||
kindMask = 0x3f
|
kindDirectIface = 1 << 5
|
||||||
kindGCProg = 0x40
|
kindGCProg = 1 << 6 // Type.gc points to GC program
|
||||||
kindNoPointers = 0x80
|
kindNoPointers = 1 << 7
|
||||||
|
kindMask = (1 << 5) - 1
|
||||||
)
|
)
|
||||||
|
|
||||||
func (k Kind) String() string {
|
func (k Kind) String() string {
|
||||||
|
@ -1503,6 +1502,7 @@ func (gc *gcProg) appendProg(t *rtype) {
|
||||||
var prog []byte
|
var prog []byte
|
||||||
if t.kind&kindGCProg != 0 {
|
if t.kind&kindGCProg != 0 {
|
||||||
// Ensure that the runtime has unrolled GC program.
|
// Ensure that the runtime has unrolled GC program.
|
||||||
|
// TODO(rsc): Do not allocate.
|
||||||
unsafe_New(t)
|
unsafe_New(t)
|
||||||
// The program is stored in t.gc[0], skip unroll flag.
|
// The program is stored in t.gc[0], skip unroll flag.
|
||||||
prog = (*[1 << 30]byte)(unsafe.Pointer(t.gc[0]))[1:]
|
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
|
// TODO(rsc): Unexported for now. Export once the alg field is set correctly
|
||||||
// for the type. This may require significant work.
|
// for the type. This may require significant work.
|
||||||
|
//
|
||||||
|
// TODO(rsc): TestArrayOf is also disabled. Re-enable.
|
||||||
func arrayOf(count int, elem Type) Type {
|
func arrayOf(count int, elem Type) Type {
|
||||||
typ := elem.(*rtype)
|
typ := elem.(*rtype)
|
||||||
slice := SliceOf(elem)
|
slice := SliceOf(elem)
|
||||||
|
@ -1676,6 +1678,7 @@ func arrayOf(count int, elem Type) Type {
|
||||||
prototype := *(**arrayType)(unsafe.Pointer(&iarray))
|
prototype := *(**arrayType)(unsafe.Pointer(&iarray))
|
||||||
array := new(arrayType)
|
array := new(arrayType)
|
||||||
*array = *prototype
|
*array = *prototype
|
||||||
|
// TODO: Set extra kind bits correctly.
|
||||||
array.string = &s
|
array.string = &s
|
||||||
array.hash = fnv1(typ.hash, '[')
|
array.hash = fnv1(typ.hash, '[')
|
||||||
for n := uint32(count); n > 0; n >>= 8 {
|
for n := uint32(count); n > 0; n >>= 8 {
|
||||||
|
@ -1692,6 +1695,7 @@ func arrayOf(count int, elem Type) Type {
|
||||||
array.fieldAlign = typ.fieldAlign
|
array.fieldAlign = typ.fieldAlign
|
||||||
// TODO: array.alg
|
// TODO: array.alg
|
||||||
// TODO: array.gc
|
// TODO: array.gc
|
||||||
|
// TODO:
|
||||||
array.uncommonType = nil
|
array.uncommonType = nil
|
||||||
array.ptrToThis = nil
|
array.ptrToThis = nil
|
||||||
array.zero = unsafe.Pointer(&make([]byte, array.size)[0])
|
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
|
// Reflect uses the "interface" calling convention for
|
||||||
// methods, where receivers take one word of argument
|
// methods, where receivers take one word of argument
|
||||||
// space no matter how big they actually are.
|
// space no matter how big they actually are.
|
||||||
if rcvr.size > ptrSize {
|
if !isDirectIface(rcvr) {
|
||||||
// we pass a pointer to the receiver.
|
// we pass a pointer to the receiver.
|
||||||
gc.append(bitsPointer)
|
gc.append(bitsPointer)
|
||||||
} else if rcvr.pointers() {
|
} else if rcvr.pointers() {
|
||||||
|
@ -1813,3 +1817,8 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
|
||||||
layoutCache.Unlock()
|
layoutCache.Unlock()
|
||||||
return x, argSize, retOffset
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ type Value struct {
|
||||||
// This repeats typ.Kind() except for method values.
|
// This repeats typ.Kind() except for method values.
|
||||||
// The remaining 23+ bits give a method number 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 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
|
flag
|
||||||
|
|
||||||
// A method value represents a curried method invocation
|
// A method value represents a curried method invocation
|
||||||
|
@ -128,7 +128,10 @@ func packEface(v Value) interface{} {
|
||||||
e := (*emptyInterface)(unsafe.Pointer(&i))
|
e := (*emptyInterface)(unsafe.Pointer(&i))
|
||||||
// First, fill in the data portion of the interface.
|
// First, fill in the data portion of the interface.
|
||||||
switch {
|
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.
|
// Value is indirect, and so is the interface we're making.
|
||||||
ptr := v.ptr
|
ptr := v.ptr
|
||||||
if v.flag&flagAddr != 0 {
|
if v.flag&flagAddr != 0 {
|
||||||
|
@ -172,7 +175,7 @@ func unpackEface(i interface{}) Value {
|
||||||
return Value{}
|
return Value{}
|
||||||
}
|
}
|
||||||
f := flag(t.Kind()) << flagKindShift
|
f := flag(t.Kind()) << flagKindShift
|
||||||
if t.size > ptrSize {
|
if !isDirectIface(t) {
|
||||||
return Value{t, unsafe.Pointer(e.word), 0, f | flagIndir}
|
return Value{t, unsafe.Pointer(e.word), 0, f | flagIndir}
|
||||||
}
|
}
|
||||||
if t.pointers() {
|
if t.pointers() {
|
||||||
|
@ -607,8 +610,8 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
|
||||||
off += -off & uintptr(typ.align-1)
|
off += -off & uintptr(typ.align-1)
|
||||||
addr := unsafe.Pointer(uintptr(ptr) + off)
|
addr := unsafe.Pointer(uintptr(ptr) + off)
|
||||||
v := Value{typ, nil, 0, flag(typ.Kind()) << flagKindShift}
|
v := Value{typ, nil, 0, flag(typ.Kind()) << flagKindShift}
|
||||||
if typ.size > ptrSize {
|
if !isDirectIface(typ) {
|
||||||
// value does not fit in word.
|
// value cannot be inlined in interface data.
|
||||||
// Must make a copy, because f might keep a reference to it,
|
// Must make a copy, because f might keep a reference to it,
|
||||||
// and we cannot let f keep a reference to the stack frame
|
// and we cannot let f keep a reference to the stack frame
|
||||||
// after this function returns, not even a read-only reference.
|
// 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)
|
iface := (*nonEmptyInterface)(v.ptr)
|
||||||
*(*unsafe.Pointer)(p) = unsafe.Pointer(iface.word)
|
*(*unsafe.Pointer)(p) = unsafe.Pointer(iface.word)
|
||||||
} else if v.flag&flagIndir != 0 {
|
} else if v.flag&flagIndir != 0 {
|
||||||
if t.size > ptrSize {
|
if !isDirectIface(t) {
|
||||||
*(*unsafe.Pointer)(p) = v.ptr
|
*(*unsafe.Pointer)(p) = v.ptr
|
||||||
} else if t.pointers() {
|
} else if t.pointers() {
|
||||||
*(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(v.ptr)
|
*(*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)
|
val = unsafe.Pointer(uintptr(v.ptr) + offset)
|
||||||
case typ.pointers():
|
case typ.pointers():
|
||||||
if offset != 0 {
|
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
|
val = v.ptr
|
||||||
case bigEndian:
|
case bigEndian:
|
||||||
|
@ -1014,14 +1023,13 @@ func (v Value) Index(i int) Value {
|
||||||
return Value{typ, val, 0, fl}
|
return Value{typ, val, 0, fl}
|
||||||
|
|
||||||
case String:
|
case String:
|
||||||
fl := v.flag&flagRO | flag(Uint8<<flagKindShift)
|
fl := v.flag&flagRO | flag(Uint8<<flagKindShift) | flagIndir
|
||||||
s := (*stringHeader)(v.ptr)
|
s := (*stringHeader)(v.ptr)
|
||||||
if i < 0 || i >= s.Len {
|
if i < 0 || i >= s.Len {
|
||||||
panic("reflect: string index out of range")
|
panic("reflect: string index out of range")
|
||||||
}
|
}
|
||||||
b := uintptr(0)
|
p := unsafe.Pointer(uintptr(s.Data) + uintptr(i))
|
||||||
*(*byte)(unsafe.Pointer(&b)) = *(*byte)(unsafe.Pointer(uintptr(s.Data) + uintptr(i)))
|
return Value{uint8Type, p, 0, fl}
|
||||||
return Value{uint8Type, nil, b, fl}
|
|
||||||
}
|
}
|
||||||
panic(&ValueError{"reflect.Value.Index", k})
|
panic(&ValueError{"reflect.Value.Index", k})
|
||||||
}
|
}
|
||||||
|
@ -1209,7 +1217,7 @@ func (v Value) MapIndex(key Value) Value {
|
||||||
typ := tt.elem
|
typ := tt.elem
|
||||||
fl := (v.flag | key.flag) & flagRO
|
fl := (v.flag | key.flag) & flagRO
|
||||||
fl |= flag(typ.Kind()) << flagKindShift
|
fl |= flag(typ.Kind()) << flagKindShift
|
||||||
if typ.size > ptrSize {
|
if !isDirectIface(typ) {
|
||||||
// Copy result so future changes to the map
|
// Copy result so future changes to the map
|
||||||
// won't change the underlying value.
|
// won't change the underlying value.
|
||||||
c := unsafe_New(typ)
|
c := unsafe_New(typ)
|
||||||
|
@ -1249,7 +1257,7 @@ func (v Value) MapKeys() []Value {
|
||||||
// we can do about it.
|
// we can do about it.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if keyType.size > ptrSize {
|
if !isDirectIface(keyType) {
|
||||||
// Copy result so future changes to the map
|
// Copy result so future changes to the map
|
||||||
// won't change the underlying value.
|
// won't change the underlying value.
|
||||||
c := unsafe_New(keyType)
|
c := unsafe_New(keyType)
|
||||||
|
@ -1448,7 +1456,7 @@ func (v Value) recv(nb bool) (val Value, ok bool) {
|
||||||
t := tt.elem
|
t := tt.elem
|
||||||
val = Value{t, nil, 0, flag(t.Kind()) << flagKindShift}
|
val = Value{t, nil, 0, flag(t.Kind()) << flagKindShift}
|
||||||
var p unsafe.Pointer
|
var p unsafe.Pointer
|
||||||
if t.size > ptrSize {
|
if !isDirectIface(t) {
|
||||||
p = unsafe_New(t)
|
p = unsafe_New(t)
|
||||||
val.ptr = p
|
val.ptr = p
|
||||||
val.flag |= flagIndir
|
val.flag |= flagIndir
|
||||||
|
@ -2190,7 +2198,7 @@ func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) {
|
||||||
t := tt.elem
|
t := tt.elem
|
||||||
p := runcases[chosen].val
|
p := runcases[chosen].val
|
||||||
fl := flag(t.Kind()) << flagKindShift
|
fl := flag(t.Kind()) << flagKindShift
|
||||||
if t.size > ptrSize {
|
if !isDirectIface(t) {
|
||||||
recv = Value{t, p, 0, fl | flagIndir}
|
recv = Value{t, p, 0, fl | flagIndir}
|
||||||
} else if t.pointers() {
|
} else if t.pointers() {
|
||||||
recv = Value{t, *(*unsafe.Pointer)(p), 0, fl}
|
recv = Value{t, *(*unsafe.Pointer)(p), 0, fl}
|
||||||
|
@ -2291,7 +2299,7 @@ func Zero(typ Type) Value {
|
||||||
}
|
}
|
||||||
t := typ.common()
|
t := typ.common()
|
||||||
fl := flag(t.Kind()) << flagKindShift
|
fl := flag(t.Kind()) << flagKindShift
|
||||||
if t.size <= ptrSize {
|
if isDirectIface(t) {
|
||||||
return Value{t, nil, 0, fl}
|
return Value{t, nil, 0, fl}
|
||||||
}
|
}
|
||||||
return Value{t, unsafe_New(typ.(*rtype)), 0, fl | flagIndir}
|
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.
|
// where t is a signed or unsigned int type.
|
||||||
func makeInt(f flag, bits uint64, t Type) Value {
|
func makeInt(f flag, bits uint64, t Type) Value {
|
||||||
typ := t.common()
|
typ := t.common()
|
||||||
if typ.size > ptrSize {
|
if !isDirectIface(typ) {
|
||||||
// Assume ptrSize >= 4, so this must be uint64.
|
|
||||||
ptr := unsafe_New(typ)
|
ptr := unsafe_New(typ)
|
||||||
|
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
|
*(*uint64)(unsafe.Pointer(ptr)) = bits
|
||||||
|
}
|
||||||
return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
|
return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
|
||||||
}
|
}
|
||||||
var s uintptr
|
var s uintptr
|
||||||
|
@ -2474,10 +2490,14 @@ func makeInt(f flag, bits uint64, t Type) Value {
|
||||||
// where t is a float32 or float64 type.
|
// where t is a float32 or float64 type.
|
||||||
func makeFloat(f flag, v float64, t Type) Value {
|
func makeFloat(f flag, v float64, t Type) Value {
|
||||||
typ := t.common()
|
typ := t.common()
|
||||||
if typ.size > ptrSize {
|
if !isDirectIface(typ) {
|
||||||
// Assume ptrSize >= 4, so this must be float64.
|
|
||||||
ptr := unsafe_New(typ)
|
ptr := unsafe_New(typ)
|
||||||
|
switch typ.size {
|
||||||
|
case 4:
|
||||||
|
*(*float32)(unsafe.Pointer(ptr)) = float32(v)
|
||||||
|
case 8:
|
||||||
*(*float64)(unsafe.Pointer(ptr)) = v
|
*(*float64)(unsafe.Pointer(ptr)) = v
|
||||||
|
}
|
||||||
return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
|
return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2495,7 +2515,7 @@ func makeFloat(f flag, v float64, t Type) Value {
|
||||||
// where t is a complex64 or complex128 type.
|
// where t is a complex64 or complex128 type.
|
||||||
func makeComplex(f flag, v complex128, t Type) Value {
|
func makeComplex(f flag, v complex128, t Type) Value {
|
||||||
typ := t.common()
|
typ := t.common()
|
||||||
if typ.size > ptrSize {
|
if !isDirectIface(typ) {
|
||||||
ptr := unsafe_New(typ)
|
ptr := unsafe_New(typ)
|
||||||
switch typ.size {
|
switch typ.size {
|
||||||
case 8:
|
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())<<flagKindShift}
|
return Value{typ, ptr, 0, f | flagIndir | flag(typ.Kind())<<flagKindShift}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume ptrSize <= 8 so this must be complex64.
|
|
||||||
var s uintptr
|
var s uintptr
|
||||||
|
switch typ.size {
|
||||||
|
case 8:
|
||||||
*(*complex64)(unsafe.Pointer(&s)) = complex64(v)
|
*(*complex64)(unsafe.Pointer(&s)) = complex64(v)
|
||||||
|
case 16:
|
||||||
|
*(*complex128)(unsafe.Pointer(&s)) = v
|
||||||
|
}
|
||||||
return Value{typ, nil, s, f | flag(typ.Kind())<<flagKindShift}
|
return Value{typ, nil, s, f | flag(typ.Kind())<<flagKindShift}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,7 @@ func interhash(a *iface, s, h uintptr) uintptr {
|
||||||
// but we can print a better error.
|
// but we can print a better error.
|
||||||
panic(errorString("hash of unhashable type " + *t._string))
|
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)
|
return c1 * fn(unsafe.Pointer(&a.data), uintptr(t.size), h^c0)
|
||||||
} else {
|
} else {
|
||||||
return c1 * fn(a.data, uintptr(t.size), h^c0)
|
return c1 * fn(a.data, uintptr(t.size), h^c0)
|
||||||
|
@ -135,7 +135,7 @@ func nilinterhash(a *eface, s, h uintptr) uintptr {
|
||||||
// but we can print a better error.
|
// but we can print a better error.
|
||||||
panic(errorString("hash of unhashable type " + *t._string))
|
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)
|
return c1 * fn(unsafe.Pointer(&a.data), uintptr(t.size), h^c0)
|
||||||
} else {
|
} else {
|
||||||
return c1 * fn(a.data, uintptr(t.size), h^c0)
|
return c1 * fn(a.data, uintptr(t.size), h^c0)
|
||||||
|
@ -208,7 +208,7 @@ func efaceeq(p, q interface{}) bool {
|
||||||
// but we can print a better error.
|
// but we can print a better error.
|
||||||
panic(errorString("comparing uncomparable type " + *t._string))
|
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(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)), uintptr(t.size))
|
||||||
}
|
}
|
||||||
return eq(x.data, y.data, uintptr(t.size))
|
return eq(x.data, y.data, uintptr(t.size))
|
||||||
|
@ -232,7 +232,7 @@ func ifaceeq(p, q interface {
|
||||||
// but we can print a better error.
|
// but we can print a better error.
|
||||||
panic(errorString("comparing uncomparable type " + *t._string))
|
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(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)), uintptr(t.size))
|
||||||
}
|
}
|
||||||
return eq(x.data, y.data, uintptr(t.size))
|
return eq(x.data, y.data, uintptr(t.size))
|
||||||
|
|
|
@ -196,7 +196,7 @@ dumptype(Type *t)
|
||||||
write((byte*)".", 1);
|
write((byte*)".", 1);
|
||||||
write(t->x->name->str, t->x->name->len);
|
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});
|
dumpfields((BitVector){0, nil});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,7 +584,7 @@ itab_callback(Itab *tab)
|
||||||
dumpint(TagItab);
|
dumpint(TagItab);
|
||||||
dumpint((uintptr)tab);
|
dumpint((uintptr)tab);
|
||||||
t = tab->type;
|
t = tab->type;
|
||||||
dumpbool(t->size > PtrSize || (t->kind & KindNoPointers) == 0);
|
dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -135,7 +135,7 @@ func typ2Itab(t *_type, inter *interfacetype, cache **itab) *itab {
|
||||||
func convT2E(t *_type, elem unsafe.Pointer) (e interface{}) {
|
func convT2E(t *_type, elem unsafe.Pointer) (e interface{}) {
|
||||||
size := uintptr(t.size)
|
size := uintptr(t.size)
|
||||||
ep := (*eface)(unsafe.Pointer(&e))
|
ep := (*eface)(unsafe.Pointer(&e))
|
||||||
if size <= ptrSize {
|
if isDirectIface(t) {
|
||||||
ep._type = t
|
ep._type = t
|
||||||
memmove(unsafe.Pointer(&ep.data), elem, size)
|
memmove(unsafe.Pointer(&ep.data), elem, size)
|
||||||
} else {
|
} else {
|
||||||
|
@ -157,7 +157,7 @@ func convT2I(t *_type, inter *interfacetype, cache **itab, elem unsafe.Pointer)
|
||||||
}
|
}
|
||||||
size := uintptr(t.size)
|
size := uintptr(t.size)
|
||||||
pi := (*iface)(unsafe.Pointer(&i))
|
pi := (*iface)(unsafe.Pointer(&i))
|
||||||
if size <= ptrSize {
|
if isDirectIface(t) {
|
||||||
pi.tab = tab
|
pi.tab = tab
|
||||||
memmove(unsafe.Pointer(&pi.data), elem, size)
|
memmove(unsafe.Pointer(&pi.data), elem, size)
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,7 +182,7 @@ func assertI2T(t *_type, i fInterface) (r struct{}) {
|
||||||
panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""})
|
panic(&TypeAssertionError{*tab.inter.typ._string, *tab._type._string, *t._string, ""})
|
||||||
}
|
}
|
||||||
size := uintptr(t.size)
|
size := uintptr(t.size)
|
||||||
if size <= ptrSize {
|
if isDirectIface(t) {
|
||||||
memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size)
|
memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size)
|
||||||
} else {
|
} else {
|
||||||
memmove(unsafe.Pointer(&r), ip.data, size)
|
memmove(unsafe.Pointer(&r), ip.data, size)
|
||||||
|
@ -202,7 +202,7 @@ func assertI2T2(t *_type, i fInterface) (r byte) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
*ok = true
|
*ok = true
|
||||||
if size <= ptrSize {
|
if isDirectIface(t) {
|
||||||
memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size)
|
memmove(unsafe.Pointer(&r), unsafe.Pointer(&ip.data), size)
|
||||||
} else {
|
} else {
|
||||||
memmove(unsafe.Pointer(&r), ip.data, size)
|
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, ""})
|
panic(&TypeAssertionError{"", *ep._type._string, *t._string, ""})
|
||||||
}
|
}
|
||||||
size := uintptr(t.size)
|
size := uintptr(t.size)
|
||||||
if size <= ptrSize {
|
if isDirectIface(t) {
|
||||||
memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size)
|
memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size)
|
||||||
} else {
|
} else {
|
||||||
memmove(unsafe.Pointer(&r), ep.data, size)
|
memmove(unsafe.Pointer(&r), ep.data, size)
|
||||||
|
@ -245,7 +245,7 @@ func assertE2T2(t *_type, e interface{}) (r byte) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
*ok = true
|
*ok = true
|
||||||
if size <= ptrSize {
|
if isDirectIface(t) {
|
||||||
memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size)
|
memmove(unsafe.Pointer(&r), unsafe.Pointer(&ep.data), size)
|
||||||
} else {
|
} else {
|
||||||
memmove(unsafe.Pointer(&r), ep.data, size)
|
memmove(unsafe.Pointer(&r), ep.data, size)
|
||||||
|
|
|
@ -459,7 +459,7 @@ setFinalizer(Eface obj, Eface finalizer)
|
||||||
}
|
}
|
||||||
if(finalizer.type != nil) {
|
if(finalizer.type != nil) {
|
||||||
runtime·createfing();
|
runtime·createfing();
|
||||||
if(finalizer.type->kind != KindFunc)
|
if((finalizer.type->kind&KindMask) != KindFunc)
|
||||||
goto badfunc;
|
goto badfunc;
|
||||||
ft = (FuncType*)finalizer.type;
|
ft = (FuncType*)finalizer.type;
|
||||||
if(ft->dotdotdot || ft->in.len != 1)
|
if(ft->dotdotdot || ft->in.len != 1)
|
||||||
|
@ -467,12 +467,12 @@ setFinalizer(Eface obj, Eface finalizer)
|
||||||
fint = *(Type**)ft->in.array;
|
fint = *(Type**)ft->in.array;
|
||||||
if(fint == obj.type) {
|
if(fint == obj.type) {
|
||||||
// ok - same 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,
|
// ok - not same type, but both pointers,
|
||||||
// one or the other is unnamed, and same element type, so assignable.
|
// 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
|
// 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
|
// ok - satisfies non-empty interface
|
||||||
} else
|
} else
|
||||||
goto badfunc;
|
goto badfunc;
|
||||||
|
|
|
@ -14,15 +14,6 @@ const (
|
||||||
flagNoScan = 1 << 0 // GC doesn't have to scan object
|
flagNoScan = 1 << 0 // GC doesn't have to scan object
|
||||||
flagNoZero = 1 << 1 // don't zero memory
|
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
|
maxTinySize = 16
|
||||||
tinySizeClass = 2
|
tinySizeClass = 2
|
||||||
maxSmallSize = 32 << 10
|
maxSmallSize = 32 << 10
|
||||||
|
|
|
@ -367,7 +367,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
|
||||||
iface = (Iface*)(b+i);
|
iface = (Iface*)(b+i);
|
||||||
if(iface->tab != nil) {
|
if(iface->tab != nil) {
|
||||||
typ = iface->tab->type;
|
typ = iface->tab->type;
|
||||||
if(typ->size > PtrSize || !(typ->kind&KindNoPointers))
|
if(!(typ->kind&KindDirectIface) || !(typ->kind&KindNoPointers))
|
||||||
obj = iface->data;
|
obj = iface->data;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -375,7 +375,7 @@ scanblock(byte *b, uintptr n, byte *ptrmask)
|
||||||
eface = (Eface*)(b+i);
|
eface = (Eface*)(b+i);
|
||||||
typ = eface->type;
|
typ = eface->type;
|
||||||
if(typ != nil) {
|
if(typ != nil) {
|
||||||
if(typ->size > PtrSize || !(typ->kind&KindNoPointers))
|
if(!(typ->kind&KindDirectIface) || !(typ->kind&KindNoPointers))
|
||||||
obj = eface->data;
|
obj = eface->data;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1675,7 +1675,7 @@ runfinq(void)
|
||||||
}
|
}
|
||||||
if(f->fint == nil)
|
if(f->fint == nil)
|
||||||
runtime·throw("missing type in runfinq");
|
runtime·throw("missing type in runfinq");
|
||||||
if(f->fint->kind == KindPtr) {
|
if((f->fint->kind&KindMask) == KindPtr) {
|
||||||
// direct use of pointer
|
// direct use of pointer
|
||||||
*(void**)frame = f->arg;
|
*(void**)frame = f->arg;
|
||||||
} else if(((InterfaceType*)f->fint)->mhdr.len == 0) {
|
} else if(((InterfaceType*)f->fint)->mhdr.len == 0) {
|
||||||
|
|
|
@ -585,7 +585,7 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
|
||||||
break;
|
break;
|
||||||
case BitsEface:
|
case BitsEface:
|
||||||
t = (Type*)scanp[i];
|
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];
|
p = scanp[i+1];
|
||||||
if(minp <= p && p < maxp) {
|
if(minp <= p && p < maxp) {
|
||||||
if(StackDebug >= 3)
|
if(StackDebug >= 3)
|
||||||
|
@ -602,7 +602,7 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
|
||||||
if(tab != nil) {
|
if(tab != nil) {
|
||||||
t = tab->type;
|
t = tab->type;
|
||||||
//runtime·printf(" type=%p\n", t);
|
//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];
|
p = scanp[i+1];
|
||||||
if(minp <= p && p < maxp) {
|
if(minp <= p && p < maxp) {
|
||||||
if(StackDebug >= 3)
|
if(StackDebug >= 3)
|
||||||
|
|
44
src/pkg/runtime/typekind.go
Normal file
44
src/pkg/runtime/typekind.go
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -33,8 +33,9 @@ enum {
|
||||||
KindStruct,
|
KindStruct,
|
||||||
KindUnsafePointer,
|
KindUnsafePointer,
|
||||||
|
|
||||||
|
KindDirectIface = 1<<5,
|
||||||
KindGCProg = 1<<6, // Type.gc points to GC program
|
KindGCProg = 1<<6, // Type.gc points to GC program
|
||||||
KindNoPointers = 1<<7,
|
KindNoPointers = 1<<7,
|
||||||
KindMask = (1<<6)-1,
|
KindMask = (1<<5)-1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
11
test/live.go
11
test/live.go
|
@ -118,7 +118,10 @@ var i9 interface{}
|
||||||
func f9() bool {
|
func f9() bool {
|
||||||
g8()
|
g8()
|
||||||
x := i9
|
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,
|
// liveness formerly confused by UNDEF followed by RET,
|
||||||
|
@ -359,10 +362,10 @@ func g25()
|
||||||
|
|
||||||
func f26(b bool) {
|
func f26(b bool) {
|
||||||
if b {
|
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((*int)(nil), (*int)(nil), (*int)(nil)) // 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]+$"
|
||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue