上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
8.1 调用约定
调用约定(Calling Conventions)是 应用二进制接口 (ABI)的组成部分,内容包括:
- 参数和返回值如何传递?(寄存器还是调用栈)
- 按什么顺序?
- 谁负责内存分配和清理?
当我们反汇编,或者阅读 runtime 包内汇编代码时,可看到 ABIInternal 这个声明。
其全称是 “Internal Application Binary Interface”,简称 ABI,定义内存数据布局和函数调用约定。ABI 定义并不稳定,比如 1.17 之前是 stack-based,而现在则是 register-based。
这个改变可带来 5% ~ 10% 左右的性能提升。
如编写 Go Assembly 代码,建议参考《 A Quick Guide to Go's Assembler 》,该文档为 ABI0 稳定版本。本文仅对 AMD64 下的调用规范作简单说明和演示,更多细节,请阅读《 Go internal ABI specification 》。
专用寄存器:
- RSP: stack pointer (fixed)
- RBP: frame pointer (fixed)
- RDX: closure context pointer
- R12: None
- R13: None
- R14: current groutine
- R15: GOT
- X15: zero value (fixed)
调用寄存器:
- integer: RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11
- float: X0 ~ X14
参数超出寄存器数量,则入栈。
//go:noinline func test(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 int) int { x := a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 return x } func main() { x := test(11, 12, 13, 14, 15, 16, 17, 18, 19, 20) println(x) }
$ go build -gcflags "-S" "".main STEXT size=133 args=0x0 locals=0x60 funcid=0x0 align=0x0 0x0000 TEXT "".main(SB), ABIInternal, $96-0 0x0006 SUBQ $96, SP 0x000a MOVQ BP, 88(SP) 0x000f LEAQ 88(SP), BP 0x0014 MOVQ $20, (SP) ; a10 (stack) 0x001c MOVL $11, AX ; a1 0x0021 MOVL $12, BX ; a2 0x0026 MOVL $13, CX ; a3 0x002b MOVL $14, DI ; a4 0x0030 MOVL $15, SI ; a5 0x0035 MOVL $16, R8 ; a6 0x003b MOVL $17, R9 ; a7 0x0041 MOVL $18, R10 ; a8 0x0047 MOVL $19, R11 ; a9 0x004d CALL "".test(SB) 0x0052 MOVQ AX, "".x+80(SP) ; ret 0x0057 CALL runtime.printlock(SB) 0x005c MOVQ "".x+80(SP), AX 0x0061 CALL runtime.printint(SB) 0x0066 CALL runtime.printnl(SB) 0x006b CALL runtime.printunlock(SB) 0x0070 MOVQ 88(SP), BP 0x0075 ADDQ $96, SP 0x0079 RET "".test STEXT nosplit size=35 args=0x50 locals=0x0 funcid=0x0 align=0x0 0x0000 TEXT "".test(SB), NOSPLIT|ABIInternal, $0-80 0x0000 LEAQ (BX)(AX*1), DX ; DX = AX + BX 0x0004 ADDQ DX, CX ; CX += DX 0x0007 ADDQ DI, CX ; CX += DI 0x000a ADDQ SI, CX ; CX += SI 0x000d ADDQ R8, CX ; CX += R8 0x0010 ADDQ R9, CX ; CX += R9 0x0013 ADDQ R10, CX ; CX += R10 0x0016 ADDQ R11, CX ; CX += R11 0x0019 MOVQ "".a10+8(SP), DX ; DX = a10, skip call_ret_pc 0x001e LEAQ (DX)(CX*1), AX ; AX = DX + CX, return 0x0022 RET
对于复合类型,可使用多个相邻的寄存器。
type data struct { x int y int } //go:noinline func test(a1 int, a2 data, a3 int) data { d := data { x: a1 + a2.x, y: a3 + a2.y, } return d } func main() { d := data{100, 200} r := test(11, d, 13) println(r.x, r.y) }
"".main STEXT size=112 args=0x0 locals=0x38 funcid=0x0 align=0x0 0x0000 TEXT "".main(SB), ABIInternal, $56-0 0x0014 MOVL $11, AX ; a1 0x0019 MOVL $100, BX ; a2.x 0x001e MOVL $200, CX ; a2.y 0x0023 MOVL $13, DI ; a3 0x0028 CALL "".test(SB) 0x002d MOVQ AX, ""..autotmp_8+40(SP) ; ret.x 0x0032 MOVQ BX, ""..autotmp_9+32(SP) ; ret.y 0x0037 CALL runtime.printlock(SB) 0x003c MOVQ ""..autotmp_8+40(SP), AX 0x0041 CALL runtime.printint(SB) 0x0046 CALL runtime.printsp(SB) 0x004b MOVQ ""..autotmp_9+32(SP), AX 0x0050 CALL runtime.printint(SB) 0x0055 CALL runtime.printnl(SB) 0x005a CALL runtime.printunlock(SB)
浮点参数或返回值使用 X 寄存器,可与 R 寄存器混编。
//go:noinline func test(a1 int, a2 float32, a3 int, a4 float32) (int, float32) { return a1 + a3, a2 + a4 } func main() { x, y := test(11, 1.0, 13, 2.0) println(x, y) }
"".main STEXT size=133 args=0x0 locals=0x38 funcid=0x0 align=0x0 0x0000 TEXT "".main(SB), ABIInternal, $56-0 0x0006 SUBQ $56, SP 0x000a MOVQ BP, 48(SP) 0x000f LEAQ 48(SP), BP 0x0014 MOVL $11, AX ; a1 0x0019 MOVSS $f32.3f800000(SB), X0 ; a2 0x0021 MOVL $13, BX ; a3 0x0026 MOVSS $f32.40000000(SB), X1 ; a4 0x002e CALL "".test(SB) 0x0033 MOVQ AX, "".x+40(SP) ; ret.0 0x0038 MOVSS X0, "".y+36(SP) ; ret.1 "".test STEXT nosplit size=8 args=0x20 locals=0x0 funcid=0x0 align=0x0 0x0000 TEXT "".test(SB), NOSPLIT|ABIInternal, $0-32 0x0000 ADDQ BX, AX ; ret.0 AX 0x0003 ADDSS X1, X0 ; ret.1 X0 0x0007 RET
有关 stack-based 调用约定,请阅读《源码剖析》早期版本。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论