go version:1.17
macos
类型转换
指针类型
package main
import "fmt"
func main() {
var g Game = &LOL{GameName: "LOL"}
g.start("handsome boy")
// 为了让 &LOL{GameName: "LOL"} 逃逸到内存
fmt.Println(g)
}
type Game interface {
start(string) string
stop(string) string
}
type LOL struct {
GameName string
}
func (l *LOL) start(name string) string {
return fmt.Sprintf("%s start play %s", name, l.GameName)
}
func (l *LOL) stop(name string) string {
return fmt.Sprintf("%s stop play %s", name, l.GameName)
}
查看汇编代码:
go tool compile -S -N -l main.go
"".main STEXT size=394 args=0x0 locals=0x90 funcid=0x0
(...)
0x0026 00038 (main.go:5) FUNCDATA $2, "".main.stkobj(SB)
0x0026 00038 (main.go:6) LEAQ type."".LOL(SB), AX
0x002d 00045 (main.go:6) PCDATA $1, $0
0x002d 00045 (main.go:6) CALL runtime.newobject(SB)
0x0032 00050 (main.go:6) MOVQ AX, ""..autotmp_3+48(SP)
0x0037 00055 (main.go:6) MOVQ $3, 8(AX)
0x003f 00063 (main.go:6) PCDATA $0, $-2
0x003f 00063 (main.go:6) CMPL runtime.writeBarrier(SB), $0
0x0046 00070 (main.go:6) JEQ 74
0x0048 00072 (main.go:6) JMP 86
0x004a 00074 (main.go:6) LEAQ go.string."LOL"(SB), DX
0x0051 00081 (main.go:6) MOVQ DX, (AX)
0x0054 00084 (main.go:6) JMP 103
0x0056 00086 (main.go:6) MOVQ AX, DI
0x0059 00089 (main.go:6) LEAQ go.string."LOL"(SB), DX
0x0060 00096 (main.go:6) CALL runtime.gcWriteBarrierDX(SB)
0x0065 00101 (main.go:6) JMP 103
0x0067 00103 (main.go:6) PCDATA $0, $-1
0x0067 00103 (main.go:6) MOVQ ""..autotmp_3+48(SP), DX
0x006c 00108 (main.go:6) MOVQ DX, ""..autotmp_1+56(SP)
0x0071 00113 (main.go:6) LEAQ go.itab.*"".LOL,"".Game(SB), SI
0x0078 00120 (main.go:6) MOVQ SI, "".g+64(SP)
0x007d 00125 (main.go:6) MOVQ DX, "".g+72(SP)
0x0082 00130 (main.go:7) MOVQ "".g+64(SP), AX
0x0087 00135 (main.go:7) MOVQ "".g+72(SP), DX
0x008c 00140 (main.go:7) LEAQ go.itab.*"".LOL,"".Game(SB), SI
0x0093 00147 (main.go:7) CMPQ AX, SI
0x0096 00150 (main.go:7) JEQ 157
0x0098 00152 (main.go:7) JMP 359
0x009d 00157 (main.go:7) MOVQ DX, ""..autotmp_4+40(SP)
0x00a2 00162 (main.go:7) MOVQ DX, AX
0x00a5 00165 (main.go:7) LEAQ go.string."handsome boy"(SB), BX
0x00ac 00172 (main.go:7) MOVL $12, CX
0x00b1 00177 (main.go:7) PCDATA $1, $1
0x00b1 00177 (main.go:7) CALL "".(*LOL).start(SB)
(...)
接下俩分段解读以上代码.
0x0026 00038 (main.go:6) LEAQ type."".LOL(SB), AX // 将type."".LOL地址放入AX中
0x002d 00045 (main.go:6) PCDATA $1, $0
0x002d 00045 (main.go:6) CALL runtime.newobject(SB) //AX=&LOL AX作为参数,调用newobject,创建的LOL对象的地址放入AX中
0x0032 00050 (main.go:6) MOVQ AX, ""..autotmp_3+48(SP) //48(SP)=AX 将LOL放入栈中
0x0037 00055 (main.go:6) MOVQ $3, 8(AX) //(AX.Name).Len = 3
0x003f 00063 (main.go:6) PCDATA $0, $-2
0x003f 00063 (main.go:6) CMPL runtime.writeBarrier(SB), $0 // 比较两者
0x0046 00070 (main.go:6) JEQ 74 //相等则跳转到(0x004a) 其实会走到这里
0x0048 00072 (main.go:6) JMP 86 //不相等则跳转到(0x0056)
0x004a 00074 (main.go:6) LEAQ go.string."LOL"(SB), DX // DX= &"LOL"
0x0051 00081 (main.go:6) MOVQ DX, (AX) //(AX.Name).Data = DX = &"LOL"
0x0054 00084 (main.go:6) JMP 103 // 跳转到(0x0067)
func newobject(typ *_type) unsafe.Pointer {
return mallocgc(typ.size, typ, true)
}
将*_type放入AX中,AX作为参数传递给newobject,创建LOL对象,对象的地址又放入AX中。
0x0067 00103 (main.go:6) PCDATA $0, $-1
0x0067 00103 (main.go:6) MOVQ ""..autotmp_3+48(SP), DX // DX=48(SP)
0x006c 00108 (main.go:6) MOVQ DX, ""..autotmp_1+56(SP) //56(SP)=DX
0x0071 00113 (main.go:6) LEAQ go.itab.*"".LOL,"".Game(SB), SI // SI = *itab(LOL)
0x0078 00120 (main.go:6) MOVQ SI, "".g+64(SP) // 64(SP)=SI
0x007d 00125 (main.go:6) MOVQ DX, "".g+72(SP) // 72(SP)=DX
0x0082 00130 (main.go:7) MOVQ "".g+64(SP), AX // AX=64(SP)
0x0087 00135 (main.go:7) MOVQ "".g+72(SP), DX // DX=72(SP)
0x008c 00140 (main.go:7) LEAQ go.itab.*"".LOL,"".Game(SB), SI //SI = *itab(LOL)
0x0093 00147 (main.go:7) CMPQ AX, SI //比较下
0x0096 00150 (main.go:7) JEQ 157 //跳转到(0x009d)
0x0098 00152 (main.go:7) JMP 359
0x009d 00157 (main.go:7) MOVQ DX, ""..autotmp_4+40(SP) //40(SP)=DX
0x00a2 00162 (main.go:7) MOVQ DX, AX // AX=DX=*itab 作为第一个参数
0x00a5 00165 (main.go:7) LEAQ go.string."handsome boy"(SB), BX //BX=&"handsome boy" 作为第二个参数
0x00ac 00172 (main.go:7) MOVL $12, CX // CX=$12,作为第三个参数
0x00b1 00177 (main.go:7) PCDATA $1, $1
0x00b1 00177 (main.go:7) CALL "".(*LOL).start(SB) //调用函数
64(SP)~80(SP)共同组成了runtime.iface结构体
结构体类型
"".main STEXT size=435 args=0x0 locals=0xb8 funcid=0x0
(...)
0x002f 00047 (main.go:6) LEAQ go.string."LOL"(SB), CX // CX=&"LOL"
0x0036 00054 (main.go:6) MOVQ CX, ""..autotmp_1+136(SP) // 136(SP)=&"LOL
0x003e 00062 (main.go:6) MOVQ $3, ""..autotmp_1+144(SP) //144(SP)=3
0x004a 00074 (main.go:6) MOVQ CX, ""..autotmp_3+104(SP) //104(SP)=&"LOL"
0x004f 00079 (main.go:6) MOVQ $3, ""..autotmp_3+112(SP) //112(SP)=3
0x0058 00088 (main.go:6) LEAQ ""..autotmp_3+104(SP), CX // CX=&104(SP)
0x005d 00093 (main.go:6) TESTB AL, (CX)
0x005f 00095 (main.go:6) MOVQ ""..autotmp_3+104(SP), AX // AX=104(SP)=&"LOL"
0x0064 00100 (main.go:6) MOVQ AX, ""..autotmp_4+88(SP) //88(SP)=AX
0x0069 00105 (main.go:6) MOVQ $3, ""..autotmp_4+96(SP) //96(SP)=3
0x0072 00114 (main.go:6) MOVL $3, BX //BX=3
0x0077 00119 (main.go:6) PCDATA $1, $0
0x0077 00119 (main.go:6) CALL runtime.convTstring(SB) // AX,BX为参数
0x007c 00124 (main.go:6) MOVQ AX, ""..autotmp_5+48(SP) //48(SP)=&StringHeader{}
0x0081 00129 (main.go:6) LEAQ go.itab."".LOL,"".Game(SB), CX //CX=*itab
0x0088 00136 (main.go:6) MOVQ CX, "".g+56(SP) //56(SP)=CX=*itab
0x008d 00141 (main.go:6) MOVQ AX, "".g+64(SP) //64(SP)=AX=&StringHeader{}
0x0092 00146 (main.go:7) MOVUPS X15, ""..autotmp_1+136(SP)
0x009b 00155 (main.go:7) MOVQ "".g+56(SP), AX //AX=56(SP)=*itab
0x00a0 00160 (main.go:7) MOVQ "".g+64(SP), CX //CX=64(SP)=&StringHeader{}
0x00a5 00165 (main.go:7) LEAQ go.itab."".LOL,"".Game(SB), DX //DX=*itab
0x00ac 00172 (main.go:7) CMPQ AX, DX
0x00af 00175 (main.go:7) JEQ 182
0x00b1 00177 (main.go:7) JMP 405
0x00b6 00182 (main.go:7) MOVQ (CX), AX //AX=*CX="LOL"
0x00b9 00185 (main.go:7) MOVQ 8(CX), BX //BX=*CX.Len=3
0x00bd 00189 (main.go:7) MOVQ AX, ""..autotmp_1+136(SP)
0x00c5 00197 (main.go:7) MOVQ BX, ""..autotmp_1+144(SP)
0x00cd 00205 (main.go:7) LEAQ go.string."handsome boy"(SB), CX // CX=&"handsome boy"
0x00d4 00212 (main.go:7) MOVL $12, DI
0x00d9 00217 (main.go:7) PCDATA $1, $1
0x00d9 00217 (main.go:7) CALL "".LOL.start(SB)
(...)
func convTstring(val string) (x unsafe.Pointer) {
if val == "" {
x = unsafe.Pointer(&zeroVal[0])
} else {
x = mallocgc(unsafe.Sizeof(val), stringType, true)
*(*string)(x) = val
}
return
}
convTstring从AX和BX取参数,生成一个字符串变量,返回他的指针放在AX上,并拷贝到48(SP)上,后面的指令将*itab放到56(SP)上,*string放到64(SP)上,所以56(SP)和64(SP)组成了iface
类型断言
非空接口
非具体类型
采用如下代码来生成汇编代码
func main() {
var s Game = &LOL{GameName: "halfrost"}
v, ok := s.(Game)
fmt.Println(s)
if !ok {
fmt.Printf("%v\n", v)
}
}
"".main STEXT size=727 args=0x0 locals=0x118 funcid=0x0
(...)
0x0029 00041 (main.go:6) LEAQ type."".LOL(SB), AX //AX=*_type(LOL)
0x0030 00048 (main.go:6) PCDATA $1, $0
0x0030 00048 (main.go:6) CALL runtime.newobject(SB) //AX作为参数,调用runtime.newobject
0x0035 00053 (main.go:6) MOVQ AX, ""..autotmp_8+48(SP) //SP(48)=*LOL
0x003a 00058 (main.go:6) MOVQ $8, 8(AX) //(AX.Name).Len=8
0x0042 00066 (main.go:6) PCDATA $0, $-2
0x0042 00066 (main.go:6) CMPL runtime.writeBarrier(SB), $0
0x0049 00073 (main.go:6) JEQ 77 // goto 0x004d
0x004b 00075 (main.go:6) JMP 89
0x004d 00077 (main.go:6) LEAQ go.string."halfrost"(SB), DX // DX=&"halfrost"
0x0054 00084 (main.go:6) MOVQ DX, (AX) //(AX.Name).Data=DX=&"halfrost"
0x0057 00087 (main.go:6) JMP 106
0x0059 00089 (main.go:6) MOVQ AX, DI
0x005c 00092 (main.go:6) LEAQ go.string."halfrost"(SB), DX
0x0063 00099 (main.go:6) CALL runtime.gcWriteBarrierDX(SB)
0x0068 00104 (main.go:6) JMP 106
// jump 106
0x006a 00106 (main.go:6) PCDATA $0, $-1
0x006a 00106 (main.go:6) MOVQ ""..autotmp_8+48(SP), DX //DX=48(SP)
0x006f 00111 (main.go:6) MOVQ DX, ""..autotmp_3+56(SP) //56(SP)=DX
0x0074 00116 (main.go:6) LEAQ go.itab.*"".LOL,"".Game(SB), SI //SI=*itab
0x007b 00123 (main.go:6) MOVQ SI, "".s+112(SP) //112(SP)=SI
0x0080 00128 (main.go:6) MOVQ DX, "".s+120(SP) //120(SP)=DX
0x0085 00133 (main.go:7) MOVUPS X15, ""..autotmp_4+144(SP)
0x008e 00142 (main.go:7) MOVQ "".s+112(SP), BX //BX=112(SP)=*itab
0x0093 00147 (main.go:7) MOVQ "".s+120(SP), CX //CX=120(SP)=*LOL
0x0098 00152 (main.go:7) LEAQ type."".Game(SB), AX // AX=*_type(Game)
0x009f 00159 (main.go:7) NOP
0x00a0 00160 (main.go:7) CALL runtime.assertI2I2(SB)
0x00a5 00165 (main.go:7) MOVQ AX, ""..autotmp_4+144(SP) //144(SP)=*itab
0x00ad 00173 (main.go:7) MOVQ BX, ""..autotmp_4+152(SP) //152(SP)=&"halfrost"
0x00b5 00181 (main.go:7) TESTQ AX, AX //判断AX是否为空,这里不为空,ZF=0
0x00b8 00184 (main.go:7) SETNE ""..autotmp_5+47(SP) //ZF=0,所以47(SP)=1
0x00bd 00189 (main.go:7) MOVQ ""..autotmp_4+144(SP), DX //DX=144(SP)=*itab
0x00c5 00197 (main.go:7) MOVQ ""..autotmp_4+152(SP), SI //SI=152(SP)=*LOL
0x00cd 00205 (main.go:7) MOVQ DX, "".v+96(SP) //96(SP)=*itab
0x00d2 00210 (main.go:7) MOVQ SI, "".v+104(SP) //104(SP)=*LOL
0x00d7 00215 (main.go:7) MOVBLZX ""..autotmp_5+47(SP), DX //MOVBLZX移动一字节 DX=1
0x00dc 00220 (main.go:7) MOVB DL, "".ok+46(SP) // 只用DL的低8位
0x00e0 00224 (main.go:8) MOVBLZX "".ok+46(SP), DX
0x00e5 00229 (main.go:8) MOVB DL, ""..autotmp_5+47(SP)
(...)
分析下的assertI2I2调用
func assertI2I2(inter *interfacetype, i iface) (r iface) {
tab := i.tab
if tab == nil {
return
}
if tab.inter != inter {
tab = getitab(inter, tab._type, true)
if tab == nil {
return
}
}
r.tab = tab
r.data = i.data
return
}
0x008e 00142 (main.go:7) MOVQ "".s+112(SP), BX //BX=112(SP)=*itab
0x0093 00147 (main.go:7) MOVQ "".s+120(SP), CX //CX=120(SP)=*LOL
0x0098 00152 (main.go:7) LEAQ type."".Game(SB), AX // AX=*_type(Game)
0x009f 00159 (main.go:7) NOP
0x00a0 00160 (main.go:7) CALL runtime.assertI2I2(SB)
inter参数为*_type(Game), iface由BX和CX组成,
所以assertI2I2可以简单简化为比较AX和BX.tab.inner,若相同,则构造一个新的iface返回.
在go1.17中,assertI2I2去掉了bool返回值,那么ok这个值是这么得来的呢?
我们能看到一个在0x00b5有一个人TESTQ指令,意思是如果AX不为空,则ZF=0,而SETNE意思是如果ZF=0,则47(SP)=1(true)
而在go1.16中,assertI2I2的函数是下面这样的
func assertI2I2(inter *interfacetype, i iface) (r iface, b bool)
所以不需要向go1.17那样利用TESTQ和SETNE得到返回值
具体类型
"".main STEXT size=682 args=0x0 locals=0x108 funcid=0x0
(...)
0x0029 00041 (main.go:6) LEAQ type."".LOL(SB), AX //AX=&"LOL
0x0030 00048 (main.go:6) PCDATA $1, $0
0x0030 00048 (main.go:6) CALL runtime.newobject(SB)
0x0035 00053 (main.go:6) MOVQ AX, ""..autotmp_7+56(SP) //56(SP)=*LOL
0x003a 00058 (main.go:6) MOVQ $8, 8(AX) //(AX.Name).Len=8
0x0042 00066 (main.go:6) PCDATA $0, $-2
0x0042 00066 (main.go:6) CMPL runtime.writeBarrier(SB), $0
0x0049 00073 (main.go:6) JEQ 77 // goto 0x004d
0x004b 00075 (main.go:6) JMP 89 // goto 0x0059
// JEQ 77
0x004d 00077 (main.go:6) LEAQ go.string."halfrost"(SB), DX // (AX.Name).Data=&"halfrost"
0x0054 00084 (main.go:6) MOVQ DX, (AX)
0x0057 00087 (main.go:6) JMP 106 // goto 0x006a
// JMP 89
0x0059 00089 (main.go:6) MOVQ AX, DI
0x005c 00092 (main.go:6) LEAQ go.string."halfrost"(SB), DX
0x0063 00099 (main.go:6) CALL runtime.gcWriteBarrierDX(SB)
0x0068 00104 (main.go:6) JMP 106
// JMP 106
0x006a 00106 (main.go:6) PCDATA $0, $-1
0x006a 00106 (main.go:6) MOVQ ""..autotmp_7+56(SP), DX //DX=56(SP)
0x006f 00111 (main.go:6) MOVQ DX, ""..autotmp_3+64(SP) 64(SP)=DX
0x0074 00116 (main.go:6) LEAQ go.itab.*"".LOL,"".Game(SB), SI //SI=*itab
0x007b 00123 (main.go:6) MOVQ SI, "".s+120(SP) // 120(SP)=SI=*itab
0x0080 00128 (main.go:6) MOVQ DX, "".s+128(SP) // 128(SP)=DX=56(SP)=*LOL
0x0088 00136 (main.go:7) MOVUPS X15, ""..autotmp_4+152(SP)
0x0091 00145 (main.go:7) MOVQ "".s+128(SP), DX //DX=128(SP)
0x0099 00153 (main.go:7) LEAQ go.itab."".LOL,"".Game(SB), SI // SI=*itab
0x00a0 00160 (main.go:7) CMPQ "".s+120(SP), SI //比较120(SP)处的*itab和刚刚构造出的*itab是否相等
0x00a5 00165 (main.go:7) JEQ 169 // goto 0x00a9
0x00a7 00167 (main.go:7) JMP 183 // goto 0x00b7
// === JEQ 169
0x00a9 00169 (main.go:7) MOVQ (DX), SI //SI=LOL{}
0x00ac 00172 (main.go:7) MOVQ 8(DX), DX //DX=8
0x00b0 00176 (main.go:7) MOVL $1, AX //AX=1
0x00b5 00181 (main.go:7) JMP 191 // goto 0x00bf
// === JMP 183
0x00b7 00183 (main.go:7) XORL DX, DX
0x00b9 00185 (main.go:7) XORL AX, AX
0x00bb 00187 (main.go:7) XORL SI, SI
0x00bd 00189 (main.go:7) JMP 191
// === JMP 191
0x00bf 00191 (main.go:7) MOVQ SI, ""..autotmp_4+152(SP) // 152(SP)=LOL{}
0x00c7 00199 (main.go:7) MOVQ DX, ""..autotmp_4+160(SP) //160S(SP)=8
0x00cf 00207 (main.go:7) MOVB AL, ""..autotmp_5+47(SP) //47=1(true)
0x00d3 00211 (main.go:7) MOVQ ""..autotmp_4+152(SP), DX
0x00db 00219 (main.go:7) MOVQ ""..autotmp_4+160(SP), SI
0x00e3 00227 (main.go:7) MOVQ DX, "".v+104(SP)
(...)
具体类型不会调用runtime.assertI2I2,直接构造处iface,放在120(SP),再构造一个*itab和120(SP)的*itab比较
空接口
func main() {
var s interface{} = &LOL{GameName: "GameName"}
v, ok := s.(int)
fmt.Println(v, ok)
fmt.Println(s)
}
"".main STEXT size=534 args=0x0 locals=0xd0 funcid=0x0
(...)
0x0026 00038 (main.go:5) LEAQ type."".LOL(SB), AX //AX=*_type(LOL)
0x002d 00045 (main.go:5) PCDATA $1, $0
0x002d 00045 (main.go:5) CALL runtime.newobject(SB)
0x0032 00050 (main.go:5) MOVQ AX, ""..autotmp_8+48(SP) //48(SP)=*LOL{}
0x0037 00055 (main.go:5) MOVQ $8, 8(AX) //(AX.Name).Len=8
0x003f 00063 (main.go:5) PCDATA $0, $-2
0x003f 00063 (main.go:5) CMPL runtime.writeBarrier(SB), $0
0x0046 00070 (main.go:5) JEQ 74
0x0048 00072 (main.go:5) JMP 86
0x004a 00074 (main.go:5) LEAQ go.string."GameName"(SB), CX //CX=&"GameName"
0x0051 00081 (main.go:5) MOVQ CX, (AX) //(AX.Name).Data=&"GameName"
0x0054 00084 (main.go:5) JMP 103 //goto 0x0067
0x0056 00086 (main.go:5) MOVQ AX, DI
0x0059 00089 (main.go:5) LEAQ go.string."GameName"(SB), CX
0x0060 00096 (main.go:5) CALL runtime.gcWriteBarrierCX(SB)
0x0065 00101 (main.go:5) JMP 103
// === JMP 103
0x0067 00103 (main.go:5) PCDATA $0, $-1
0x0067 00103 (main.go:5) MOVQ ""..autotmp_8+48(SP), CX //CX=48(SP)
0x006c 00108 (main.go:5) MOVQ CX, ""..autotmp_3+56(SP) //56(SP)=CX
0x0071 00113 (main.go:5) LEAQ type.*"".LOL(SB), DX //DX=*_type(LOL)
0x0078 00120 (main.go:5) MOVQ DX, "".s+88(SP) //88(SP)=DX
0x007d 00125 (main.go:5) MOVQ CX, "".s+96(SP) //96(SP)=CX
0x0082 00130 (main.go:6) MOVQ "".s+96(SP), CX //CX=96(SP)
0x0087 00135 (main.go:6) LEAQ type.int(SB), DX //DX=*int
0x008e 00142 (main.go:6) CMPQ "".s+88(SP), DX //比较 *_type(LOL)和*int
0x0093 00147 (main.go:6) JEQ 151 //相等 goto 0x0097
0x0095 00149 (main.go:6) JMP 162 // 不想等 goto 0x00a2
// === JEQ 151
0x0097 00151 (main.go:6) MOVQ (CX), CX //CX="GameName"
0x009a 00154 (main.go:6) MOVL $1, AX //AX=1(true)
0x009f 00159 (main.go:6) NOP
0x00a0 00160 (main.go:6) JMP 168
// === JMP 162
0x00a2 00162 (main.go:6) XORL CX, CX //CX=0
0x00a4 00164 (main.go:6) XORL AX, AX //AX=0
0x00a6 00166 (main.go:6) JMP 168 // goto 0x00a8
// === JMP 168
0x00a8 00168 (main.go:6) MOVQ CX, ""..autotmp_4+40(SP) //40(SP)="GameName"
0x00ad 00173 (main.go:6) MOVB AL, ""..autotmp_5+31(SP) //31(SP)=1
0x00b1 00177 (main.go:6) MOVQ ""..autotmp_4+40(SP), CX //CX="GameName"
0x00b6 00182 (main.go:6) MOVQ CX, "".v+32(SP) //32(SP)="GameName"
0x00bb 00187 (main.go:6) MOVBLZX ""..autotmp_5+31(SP), CX //CX=1
0x00c0 00192 (main.go:6) MOVB CL, "".ok+30(SP) //30(SP)=1
0x00c4 00196 (main.go:7) MOVBLZX "".ok+30(SP), CX //CX=1
0x00c9 00201 (main.go:7) MOVB CL, ""..autotmp_5+31(SP) //31(SP)=1
(...)
类型查询
非空接口
用一下代码验证
func main() {
var s Game = &LOL{GameName: "GameName"}
switch s.(type) {
case Game:
g := s.(Game)
g.start("game call start")
case *LOL:
l := s.(*LOL)
l.start("*lol call start")
case LOL:
l := s.(LOL)
l.start("lol call start")
}
}
得到汇编代码
"".main STEXT size=709 args=0x0 locals=0xc8 funcid=0x0
0x0026 00038 (main.go:6) LEAQ type."".LOL(SB), AX
0x002d 00045 (main.go:6) PCDATA $1, $0
0x002d 00045 (main.go:6) CALL runtime.newobject(SB)
0x0032 00050 (main.go:6) MOVQ AX, ""..autotmp_7+56(SP) // 56(SP)=&LOL{}
0x0037 00055 (main.go:6) MOVQ $8, 8(AX)
0x003f 00063 (main.go:6) PCDATA $0, $-2
0x003f 00063 (main.go:6) CMPL runtime.writeBarrier(SB), $0
0x0046 00070 (main.go:6) JEQ 74
0x0048 00072 (main.go:6) JMP 86
0x004a 00074 (main.go:6) LEAQ go.string."GameName"(SB), DX
0x0051 00081 (main.go:6) MOVQ DX, (AX)
0x0054 00084 (main.go:6) JMP 103 // goto 0x0067
0x0056 00086 (main.go:6) MOVQ AX, DI
0x0059 00089 (main.go:6) LEAQ go.string."GameName"(SB), DX
0x0060 00096 (main.go:6) CALL runtime.gcWriteBarrierDX(SB)
0x0065 00101 (main.go:6) JMP 103
// JMP 103
0x0067 00103 (main.go:6) PCDATA $0, $-1
0x0067 00103 (main.go:6) MOVQ ""..autotmp_7+56(SP), DX // DX=&LOL{}
0x006c 00108 (main.go:6) MOVQ DX, ""..autotmp_4+64(SP) // 64(SP)=&LOL
0x0071 00113 (main.go:6) LEAQ go.itab.*"".LOL,"".Game(SB), SI // SI=*itab
0x0078 00120 (main.go:6) MOVQ SI, "".s+80(SP) // 80(SP)=*itab
0x007d 00125 (main.go:6) MOVQ DX, "".s+88(SP) // 88(SP)=&LOL
0x0082 00130 (main.go:7) MOVQ "".s+80(SP), DX // DX=*itab
0x0087 00135 (main.go:7) MOVQ "".s+88(SP), SI // SI=&LOL{}
0x008c 00140 (main.go:7) MOVQ DX, ""..autotmp_8+128(SP) // 128(SP)=DX=*itab
0x0094 00148 (main.go:7) MOVQ SI, ""..autotmp_8+136(SP) // 136(SP)=SI=&LOL{}
0x009c 00156 (main.go:7) NOP
0x00a0 00160 (main.go:7) TESTQ DX, DX
0x00a3 00163 (main.go:7) JNE 170 // goto 0x00aa
0x00a5 00165 (main.go:7) JMP 635
到目前为止,函数栈分布如下:
接下来继续分析汇编代码:
0x00a0 00160 (main.go:7) TESTQ DX, DX
0x00a3 00163 (main.go:7) JNE 170 // goto 0x00aa
0x00a5 00165 (main.go:7) JMP 635 // goto 0x0145
// JNE 170 由上面代码块跳转过来
0x00aa 00170 (main.go:7) MOVL 16(DX), DX //DX=hash
0x00ad 00173 (main.go:7) MOVL DX, ""..autotmp_10+44(SP) // 44(SP)=hash
0x00b1 00177 (main.go:8) MOVQ ""..autotmp_8+128(SP), BX //BX=*itab
0x00b9 00185 (main.go:8) MOVQ ""..autotmp_8+136(SP), CX //CX=&LOL{}
0x00c1 00193 (main.go:8) LEAQ type."".Game(SB), AX //AX=*_type(Game)
0x00c8 00200 (main.go:8) PCDATA $1, $1
0x00c8 00200 (main.go:8) CALL runtime.assertI2I2(SB) //call assertI2I2 AX为第一个参数,BX、CX共同组成第二个参数
0x00cd 00205 (main.go:8) TESTQ AX, AX
0x00d0 00208 (main.go:8) SETNE ""..autotmp_9+43(SP) //43(SP)=1 这里按照示例的代码,会匹配成功
0x00d5 00213 (main.go:8) JNE 217 // goto 0x00d9
0x00d7 00215 (main.go:8) JMP 325
我们先分析assertI2I2返回true的情况
// JNE 217
0x00d9 00217 (main.go:8) PCDATA $1, $-1
0x00d9 00217 (main.go:8) JMP 219 // goto 0x00db
// JMP 219
0x00db 00219 (main.go:9) MOVUPS X15, ""..autotmp_5+160(SP)
0x00e4 00228 (main.go:9) MOVQ "".s+88(SP), CX //CX=&LOL{}
0x00e9 00233 (main.go:9) MOVQ CX, ""..autotmp_12+72(SP) //72=&LOL{}
0x00ee 00238 (main.go:9) MOVQ "".s+80(SP), BX // BX=*itab
0x00f3 00243 (main.go:9) LEAQ type."".Game(SB), AX //AX=*_type(Game)
0x00fa 00250 (main.go:9) PCDATA $1, $2
0x00fa 00250 (main.go:9) CALL runtime.assertI2I(SB) // call assertI2I
0x00ff 00255 (main.go:9) MOVQ AX, ""..autotmp_5+160(SP) //160(SP)=*itab
0x0107 00263 (main.go:9) MOVQ ""..autotmp_12+72(SP), CX //CX=&LOL{}
// =======接下来就是正常的函数调用了
0x010c 00268 (main.go:9) MOVQ CX, ""..autotmp_5+168(SP)
0x0114 00276 (main.go:9) MOVQ AX, "".g+112(SP)
0x0119 00281 (main.go:9) MOVQ CX, "".g+120(SP)
0x011e 00286 (main.go:10) MOVQ "".g+112(SP), CX
再分析assertI2I2返回false的情况, 从之前的代码。能看到jump 325(0x0145)
看下itab的值
go.itab.*"".LOL,"".Game SRODATA dupok size=40
0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0010 40 91 75 d1 00 00 00 00 00 00 00 00 00 00 00 00 @.u.............
0x0020 00 00 00 00 00 00 00 00 ........
rel 0+8 t=1 type."".Game+0
rel 8+8 t=1 type.*"".LOL+0
rel 24+8 t=-32767 "".(*LOL).start+0
rel 32+8 t=-32767 "".(*LOL).stop+0
// jump 325
0x0145 00325 (main.go:7) CMPL ""..autotmp_10+44(SP), $-1584800941
0x014d 00333 (main.go:8) JEQ 340
0x014f 00335 (main.go:8) JMP 496
// JEQ 340 如果匹配上了,走到这里
0x0154 00340 (main.go:16) MOVQ ""..autotmp_8+128(SP), DX //DX=*itab
0x015c 00348 (main.go:16) LEAQ go.itab."".LOL,"".Game(SB), SI //SI=*itab
0x0163 00355 (main.go:16) CMPQ DX, SI //在比较下*itab是否相同
0x0166 00358 (main.go:16) JEQ 362
0x0168 00360 (main.go:16) JMP 369
0x016a 00362 (main.go:16) MOVL $1, AX
0x016f 00367 (main.go:16) JMP 373
0x0171 00369 (main.go:16) XORL AX, AX
0x0173 00371 (main.go:16) JMP 373
0x0175 00373 (main.go:16) MOVB AL, ""..autotmp_9+43(SP)
0x0179 00377 (main.go:16) TESTB AL, AL
0x017b 00379 (main.go:16) JNE 383
0x017d 00381 (main.go:16) JMP 491
0x017f 00383 (main.go:16) PCDATA $1, $-1
0x017f 00383 (main.go:16) NOP
0x0180 00384 (main.go:16) JMP 386
0x0182 00386 (main.go:17) MOVUPS X15, ""..autotmp_6+144(SP)
0x018b 00395 (main.go:17) MOVQ "".s+80(SP), AX
0x0190 00400 (main.go:17) MOVQ "".s+88(SP), DX
0x0195 00405 (main.go:17) LEAQ go.itab."".LOL,"".Game(SB), SI
0x019c 00412 (main.go:17) NOP
0x01a0 00416 (main.go:17) CMPQ AX, SI
0x01a3 00419 (main.go:17) JEQ 426
0x01a5 00421 (main.go:17) JMP 677
0x01aa 00426 (main.go:17) MOVQ (DX), SI
0x01ad 00429 (main.go:17) MOVQ 8(DX), DX
0x01b1 00433 (main.go:17) MOVQ SI, ""..autotmp_6+144(SP)
0x01b9 00441 (main.go:17) MOVQ DX, ""..autotmp_6+152(SP)
0x01c1 00449 (main.go:17) MOVQ SI, "".l+96(SP)
0x01c6 00454 (main.go:17) MOVQ DX, "".l+104(SP)
0x01cb 00459 (main.go:18) MOVQ "".l+96(SP), AX
0x01d0 00464 (main.go:18) MOVQ "".l+104(SP), BX
0x01d5 00469 (main.go:18) LEAQ go.string."lol call start"(SB), CX
0x01dc 00476 (main.go:18) MOVL $14, DI
0x01e1 00481 (main.go:18) PCDATA $1, $0
0x01e1 00481 (main.go:18) CALL "".LOL.start(SB)
CMPL ""..autotmp_10+44(SP), $-1584800941就是在匹配hash值,