golang-design/under-the-hood

ch6sched:stack.md 6.7.3 G 的创生,示例编译后的代码新版本有变化

cnbailian opened this issue · 2 comments

问题描述

欧神你好,在《6.7.3 G 的创生》一章中,示例代码在新版本编译后有一些变化。以下是我的理解:

	0x001d 00029 (hello.go:8)	MOVL	$16, (SP) // 将 16 放到 SP 的位置,16 是第一个参数 siz,因为是 int32,所以是 MOVL。数字是 16 是因为有 string 和 string.len 两个参数加一起占 16 个字节
	0x0024 00036 (hello.go:8)	LEAQ	"".hello·f(SB), AX // 将 hello 的调用地址传给 AX
	0x002b 00043 (hello.go:8)	MOVQ	AX, 8(SP) // 将 hello 的调用地址放入 8(SP) 的位置
	0x0030 00048 (hello.go:8)	LEAQ	go.string."hello world"(SB), AX // 将“hello world”放入 AX
	0x0037 00055 (hello.go:8)	MOVQ	AX, 16(SP) // 将“hello world”放在 16(SP) 的位置
	0x003c 00060 (hello.go:8)	MOVQ	$11, 24(SP) // 将 $11 放在 24(SP) 的位置,11 是 string 的长度,string 是结构体,结构体在传参中会扁平化为多个参数

在新版本变化后,栈布局的图依然能帮助理解,但有些细节不一致了,在我看来现在的布局是这样的:

             栈布局
40(SP)+-----------------+      高地址
      |    caller BP    |       
32(SP)+-----------------+ <-- main.BP
      |  11 string.len  |
24(SP)+-----------------+ 
      |  "hello world"  |
16(SP)+-----------------+ <-- fn + sys.PtrSize
      |      hello      |
8(SP) +-----------------+ <-- fn
      |       siz       |
(SP)  +-----------------+ <-- SP
      |    newproc PC   |  
      +-----------------+ callerpc: 要运行的 Goroutine 的 PC
      |                 |
      |                 |       低地址

对比两个图,我有些疑惑:
hello 函数地址现在占了 8 个字节,而不是原有图中的两个字节,是因为内存对齐的需求吗?
“hello world” 现在是字符串完整的值传递,而不是地址传递,是有什么区别吗?
“newproc PC” 是如何计算出的在 main.SP 下方的地址呢?我在生成的汇编代码中没有发现这里的处理。

环境

$ go version
go version go1.15.5 darwin/amd64
  1. 原图中使用的是 16 进制,0x10 表示十进制下的 16
  2. 你生成的代码中 helloworld 仍然是通过地址的方式放入 AX 的,参见 LEA 指令
  3. 例子最后因为需要调用 newproc,执行 CALL 指令,他的本质是 push+jmp,newproc PC 是指 newproc 函数的 program counter,因此会执行压栈,从而 newproc PC 被放在了 SP 的位置。

感谢解惑!我这边生成的代码是十进制就没仔细看,提出了低级的问题,抱歉抱歉