返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

8.1 call convention

发布于 2024-10-12 19:16:08 字数 3322 浏览 0 评论 0 收藏 0

Go 底层调用约定(low-level calling convention)。

当函数调用时,调用函数(caller)会将参数(arg)传递给被调用函数(callee),并接收其返回值(ret)。而所谓调用约定是指如何传递参数(栈内存或寄存器,参数入栈次序),谁来为参数和返回值提供内存,并维持栈平衡。

go 1.15 linux/amd64

参数,返回值

函数返回值通过堆栈内存传递,而非寄存器。

C 等语言习惯使用寄存器传递参数和返回值。

RDI、RSI、RDX、RCX、R8、R9 传参,RAX 返回。

超出的参数入栈,或使用其他寄存器,看具体的编译器。

有个提案 《[define register-based calling convention]》,建议 Go 使用寄存器。

不过似乎没被接受。

由调用者(caller)准备参数和返回值存储空间。

被调用者(callee)负责将返回值复制到调用者内存。

自 0(SP) 开始,向后依次存放调用参数,最后是返回值。

调用者栈帧分上下两部分,分别用于函数调用和局部变量。

编译器在调用前总是将必要的寄存器数据保存到栈。

被调用者可自由使用寄存器,无需提前保存(callee-save registers)。

栈帧(stack frame)用 SP 指向栈顶(低地址),BP 指向栈低。

栈帧包括存储调用者 BP 的空间。

调用结束,恢复 SP、BP,指向调用者栈帧。

package main

func add(x, y int) int {
	z := x + y
	return z
}

func main() {
	x, y := 17, 34
	z := add(x, y)
	_ = z
}
$ go build -gcflags "-N -l -S"

"".add STEXT nosplit size=52 args=0x18 locals=0x10
    0x0000  TEXT    "".add(SB), $16-24               SP +---------+-------------
    0x0000  SUBQ    $16, SP                             | z       |
    0x0004  MOVQ    BP, 8(SP)                        BP +---------+      add  
    0x0009  LEAQ    8(SP), BP                           | main.BP |  
                                                     16 +---------+-------------
    0x000e  MOVQ    $0, "".~r2+40(SP)                   | main.PC |
    0x0017  MOVQ    "".x+24(SP), AX                  24 +---------+-------------
    0x001c  ADDQ    "".y+32(SP), AX                     | arg x   |
    0x0021  MOVQ    AX, "".z(SP)                     32 +---------+
    0x0025  MOVQ    AX, "".~r2+40(SP)                   | arg y   |      main
                                                     40 +---------+
    0x002a  MOVQ    8(SP), BP                           | ret     |
    0x002f  ADDQ    $16, SP                          48 +---------+
    0x0033  RET                                         | ...     |
    
"".main STEXT size=97 args=0x0 locals=0x38
    0x000f  SUBQ    $56, SP                          SP +---------+-------------
    0x0013  MOVQ    BP, 48(SP)                          | arg x   |
    0x0018  LEAQ    48(SP), BP                        8 +---------+
                                                        | arg y   |     <call>
    0x001d  MOVQ    $17, "".x+40(SP)                 16 +---------+
    0x0026  MOVQ    $34, "".y+32(SP)                    | ret     |  
    0x002f  MOVQ    "".x+40(SP), AX                  24 +---------+ . . . . . . . 
    0x0034  MOVQ    AX, (SP)                            | z = ret |  
    0x0038  MOVQ    $34, 8(SP)                       32 +---------+ 
    0x0041  CALL    "".add(SB)                          | y = 34  |     <local>
    0x0046  MOVQ    16(SP), AX                       40 +---------+     
    0x004b  MOVQ    AX, "".z+24(SP)                     | x = 17  |    
                                                     BP +---------+
    0x0050  MOVQ    48(SP), BP                          | ...     |
    0x0055  ADDQ    $56, SP                          56 +---------+--------------
    0x0059  RET

注:汇编指令及示意图使用十进制。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文