Golang编译工具中未使用的PCDATA操作码问题探讨
Golang编译工具中未使用的PCDATA操作码问题探讨
zylthinking@linux:~/code/goc$ go tool compile -S goid.go
"".Goid STEXT size=70 args=0x8 locals=0x10
0x0000 00000 (goid.go:21) TEXT "".Goid(SB), ABIInternal, $16-8
0x0000 00000 (goid.go:21) MOVQ (TLS), CX
0x0009 00009 (goid.go:21) CMPQ SP, 16(CX)
0x000d 00013 (goid.go:21) PCDATA $0, $-2
0x000d 00013 (goid.go:21) JLS 63
0x000f 00015 (goid.go:21) PCDATA $0, $-1
0x000f 00015 (goid.go:21) SUBQ $16, SP
0x0013 00019 (goid.go:21) MOVQ BP, 8(SP)
0x0018 00024 (goid.go:21) LEAQ 8(SP), BP
0x001d 00029 (goid.go:21) PCDATA $0, $-2
0x001d 00029 (goid.go:21) PCDATA $1, $-2
0x001d 00029 (goid.go:21) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (goid.go:21) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001d 00029 (goid.go:21) FUNCDATA $2, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
0x001d 00029 (goid.go:22) PCDATA $0, $0
0x001d 00029 (goid.go:22) PCDATA $1, $0
0x001d 00029 (goid.go:22) CALL "".getg(SB)
如上所示,在同一个程序计数器(PC)地址 00029 处出现了两个重复的 PCDATA 指令: 0x001d 00029 (goid.go:21) PCDATA $0, $-2 0x001d 00029 (goid.go:21) PCDATA $1, $-2 0x001d 00029 (goid.go:21) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (goid.go:21) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x001d 00029 (goid.go:21) FUNCDATA $2, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB) 0x001d 00029 (goid.go:22) PCDATA $0, $0 0x001d 00029 (goid.go:22) PCDATA $1, $0
显然,后面的 PCDATA $0, $0 会覆盖掉前面的 PCDATA $0, $-2。 这究竟是一个无用的行,还是有什么我不了解的特殊含义?
另外,PCDATA $0, $-2 似乎告诉运行时此处对于异步抢占是不安全的,而 PCDATA $0, $-2 是安全的;那么 PCDATA $0, $0 又是什么意思呢?
我同样不理解的是: 0x001d 00029 (goid.go:21) FUNCDATA $2, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB) 0x001d 00029 (goid.go:22) PCDATA $0, $0 为什么同一个程序计数器(PC)地址会映射到源代码的不同行?
更多关于Golang编译工具中未使用的PCDATA操作码问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang编译工具中未使用的PCDATA操作码问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go汇编输出中,PCDATA指令用于存储程序计数器相关的元数据,这些元数据主要用于垃圾回收和栈展开。你观察到的现象是正常的编译器行为。
关于重复的PCDATA指令,后面的PCDATA $0, $0确实会覆盖前面的PCDATA $0, $-2。这不是无用行,而是编译器生成的冗余指令,通常发生在基本块边界或函数调用前后。编译器有时会保守地插入元数据指令,确保所有执行路径都有正确的元数据。
PCDATA的第一个参数表示元数据类型:
$0:栈展开信息(用于异步抢占和垃圾回收)$1:参数指针信息
第二个参数是具体值:
$-2:表示"unsafe-point"(不可抢占点)$-1:表示"safe-point"(可抢占点)$0或正整数:表示栈展开表的具体索引
PCDATA $0, $0表示使用栈展开表的第0个条目,这通常是函数的默认展开信息。
关于同一PC地址映射到不同源代码行,这是因为编译器在生成调试信息时,会将机器指令映射回源代码行。当多条指令对应同一PC地址时,调试信息会选择其中一条指令的行号。在你的例子中,FUNCDATA指令被映射到第21行,而随后的PCDATA指令被映射到第22行,这是因为编译器在生成行号信息时的选择。
示例代码说明:
// goid.go
package main
import (
"fmt"
"runtime"
)
func getg() uint64
func Goid() int64 {
g := getg()
return *(*int64)(unsafe.Pointer(g + 152))
}
func main() {
fmt.Println(Goid())
fmt.Println(runtime.NumGoroutine())
}
对应的汇编输出中可以看到类似的模式:
// 编译命令:go tool compile -S goid.go
"".Goid STEXT size=70 args=0x8 locals=0x10
// ... 前面的指令
0x001d 00029 (goid.go:21) PCDATA $0, $-2
0x001d 00029 (goid.go:21) PCDATA $1, $-2
0x001d 00029 (goid.go:21) FUNCDATA $0, gclocals·...
0x001d 00029 (goid.go:21) FUNCDATA $1, gclocals·...
0x001d 00029 (goid.go:21) FUNCDATA $2, gclocals·...
0x001d 00029 (goid.go:22) PCDATA $0, $0
0x001d 00029 (goid.go:22) PCDATA $1, $0
0x001d 00029 (goid.go:22) CALL "".getg(SB)
这种重复的PCDATA指令不会影响程序正确性,只是编译器生成的冗余元数据。运行时系统会使用最后的有效PCDATA值。

