Please answer these questions before submitting your issue. Thanks!
What did you do?
Please consider the following program:
package xxx
type Iface interface {
DoSomething()
}
type myStruct struct {
}
func (ms *myStruct) DoSomething() {
println("mystruct·DoSomething")
}
func test() {
var i Iface = &myStruct{}
i.DoSomething()
}
(https://play.golang.org/p/1SSSkjvvcy)
Note in test(): type behind i is statically known to be *myStruct
What did you expect to see?
Code generated for test() directly calls myStruct.DoSomething
What did you see instead?
"".test t=1 size=80 args=0x0 locals=0x18
0x0000 00000 (devirt.go:14) TEXT "".test(SB), $24-0
0x0000 00000 (devirt.go:14) MOVQ (TLS), CX
0x0009 00009 (devirt.go:14) CMPQ SP, 16(CX)
0x000d 00013 (devirt.go:14) JLS 73
0x000f 00015 (devirt.go:14) SUBQ $24, SP
0x0013 00019 (devirt.go:14) MOVQ BP, 16(SP)
0x0018 00024 (devirt.go:14) LEAQ 16(SP), BP
0x001d 00029 (devirt.go:14) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (devirt.go:14) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (devirt.go:15) LEAQ type."".myStruct(SB), AX
0x0024 00036 (devirt.go:15) MOVQ AX, (SP)
0x0028 00040 (devirt.go:15) PCDATA $0, $0
0x0028 00040 (devirt.go:15) CALL runtime.newobject(SB)
0x002d 00045 (devirt.go:15) MOVQ 8(SP), AX
0x0032 00050 (devirt.go:16) MOVQ go.itab.*"".myStruct,"".Iface+32(SB), CX
0x0039 00057 (devirt.go:16) MOVQ AX, (SP)
0x003d 00061 (devirt.go:16) PCDATA $0, $0
0x003d 00061 (devirt.go:16) CALL CX
0x003f 00063 (devirt.go:17) MOVQ 16(SP), BP
0x0044 00068 (devirt.go:17) ADDQ $24, SP
0x0048 00072 (devirt.go:17) RET
0x0049 00073 (devirt.go:17) NOP
0x0049 00073 (devirt.go:14) PCDATA $0, $-1
0x0049 00073 (devirt.go:14) CALL runtime.morestack_noctxt(SB)
0x004e 00078 (devirt.go:14) JMP 0
Note the indirect call in offsets 00050 - 00061.
Does this issue reproduce with the latest release (go1.8)?
Yes
Context
This issue originally came in the context of using reflect.Typeof(v).Comparable() to detect whether a value can be used as key in a map:
kisielk/og-rek#30 (comment)
Current code for reflect.Typeof() is just a pointer cast to reflect/runtime.rtype + nil check and then the result is returned as reflect.Type interface:
https://github.com/golang/go/blob/f072283b/src/reflect/type.go#L1405
https://github.com/golang/go/blob/f072283b/src/reflect/type.go#L3025
So since result of reflect.Typeof(v) is statically known to be either:
code generated for reflect.Typeof(v).Comparable() should ideally be:
- cast v to rtype
- check for nil - if yes panic
- directly call rtype·Comparable()
but currently it does the call indirectly - similar to my above original example for myStruct:
...
0x001d 00029 (x.go:6) MOVQ "".v+48(FP), AX
0x0022 00034 (x.go:6) MOVQ AX, reflect.i·2+16(SP)
0x0027 00039 (x.go:6) MOVQ "".v+56(FP), AX
0x002c 00044 (x.go:6) MOVQ AX, reflect.i·2+24(SP)
0x0031 00049 (x.go:6) MOVQ reflect.i·2+16(SP), AX
0x0036 00054 (x.go:6) TESTQ AX, AX
0x0039 00057 (x.go:6) JEQ 95
0x003b 00059 (x.go:6) LEAQ go.itab.*reflect.rtype,reflect.Type(SB), CX
0x0042 00066 (x.go:6) MOVQ 64(CX), CX
0x0046 00070 (x.go:6) MOVQ AX, (SP)
0x004a 00074 (x.go:6) PCDATA $0, $1
0x004a 00074 (x.go:6) CALL CX
...
So implementing this kind of local devirtualization should imho help in many cases where reflect is used.
Possibly related issues:
#19165
#16869
System details
go version devel +f072283bce Thu Mar 2 06:08:42 2017 +0000 linux/amd64
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/kirr/go"
GORACE=""
GOROOT="/home/kirr/src/tools/go/go"
GOTOOLDIR="/home/kirr/src/tools/go/go/pkg/tool/linux_amd64"
GCCGO="/usr/bin/gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build669846645=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOROOT/bin/go version: go version devel +f072283bce Thu Mar 2 06:08:42 2017 +0000 linux/amd64
GOROOT/bin/go tool compile -V: compile version devel +f072283bce Thu Mar 2 06:08:42 2017 +0000 X:framepointer
uname -sr: Linux 4.9.0-1-amd64
Distributor ID: Debian
Description: Debian GNU/Linux 9.0 (stretch)
Release: 9.0
Codename: stretch
/lib/x86_64-linux-gnu/libc.so.6: GNU C Library (Debian GLIBC 2.24-9) stable release version 2.24, by Roland McGrath et al.
gdb --version: GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Thanks beforehand,
Kirill
/cc @randall77, @dr2chase
Please answer these questions before submitting your issue. Thanks!
What did you do?
Please consider the following program:
(https://play.golang.org/p/1SSSkjvvcy)
Note in test(): type behind
iis statically known to be*myStructWhat did you expect to see?
Code generated for
test()directly callsmyStruct.DoSomethingWhat did you see instead?
Note the indirect call in offsets 00050 - 00061.
Does this issue reproduce with the latest release (go1.8)?
Yes
Context
This issue originally came in the context of using
reflect.Typeof(v).Comparable()to detect whether a value can be used as key in a map:kisielk/og-rek#30 (comment)
Current code for
reflect.Typeof()is just a pointer cast toreflect/runtime.rtype+ nil check and then the result is returned asreflect.Typeinterface:https://github.com/golang/go/blob/f072283b/src/reflect/type.go#L1405
https://github.com/golang/go/blob/f072283b/src/reflect/type.go#L3025
So since result of
reflect.Typeof(v)is statically known to be either:code generated for
reflect.Typeof(v).Comparable()should ideally be:but currently it does the call indirectly - similar to my above original example for myStruct:
So implementing this kind of local devirtualization should imho help in many cases where reflect is used.
Possibly related issues:
#19165
#16869
System details
Thanks beforehand,
Kirill
/cc @randall77, @dr2chase