Golang内置函数汇编实现的求助指南
Golang内置函数汇编实现的求助指南 最近几天我一直在开发一个Go语言的编译器。我已经基本完成了词法分析器和语法分析器,很快将开始类型检查阶段。然而,当我考虑到中间表示阶段时(我承认这可能还需要几周时间),我意识到我可能对相关的一些事情不太了解。以下是我遇到的问题:
- 在Go语言中,像
defer、go等特性是如何实现的(从底层角度来看)?例如,我该如何编写IR/汇编代码来实现线程等功能? - 内置函数(参见此处)是如何在IR/汇编层面实现的?
- 另外,如果能找到现有Go编译器汇编例程的链接作为参考,那将会有很大帮助,不过也许我自己能实现一些更简单的部分。 如果能提供任何参考资料或指引,我将不胜感激。提前感谢。
更多关于Golang内置函数汇编实现的求助指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
你好,@Test_Zero,Go 语言的参考实现源代码可以在 golang.org/src/ 找到。
不过,像 append、make 和 len 这类函数是作为编译器内置函数实现的。你需要查看编译器的实现这里,和/或检查参考实现编译后生成的汇编代码。
// 示例:使用内置函数
package main
import "fmt"
func main() {
slice := make([]int, 0, 5)
slice = append(slice, 1, 2, 3)
fmt.Println("Slice length:", len(slice))
}
关于Go内置函数和关键特性的汇编实现
1. defer和go的底层实现
defer的实现机制:
// defer在编译时会被转换为runtime.deferproc和runtime.deferreturn调用
// 示例函数的defer实现:
func example() {
defer fmt.Println("deferred")
// 实际编译后的伪汇编:
// 1. 创建_defer结构体并压入goroutine的defer链
// 2. 函数返回前调用deferreturn执行延迟函数
}
// runtime/panic.go中的_defer结构体:
type _defer struct {
siz int32
started bool
sp uintptr // 栈指针
pc uintptr // 程序计数器
fn *funcval
_panic *_panic
link *_defer // 链表链接
}
go关键字的实现:
// go语句编译为runtime.newproc调用
go func() {
println("goroutine")
}
// 对应的runtime实现:
// src/runtime/proc.go
func newproc(siz int32, fn *funcval) {
argp := add(unsafe.Pointer(&fn), sys.PtrSize)
pc := getcallerpc()
systemstack(func() {
newproc1(fn, argp, siz, pc)
})
}
2. 内置函数的汇编实现
len和cap的实现示例:
// 对于切片,len编译为读取切片结构体的len字段
// 伪汇编表示:
// slice := make([]int, 10, 20)
// len(slice) 编译为:
// MOVQ 8(SP), AX // 读取len字段(切片结构体偏移8字节)
// 具体内置函数的runtime实现位置:
// - len/cap: runtime/malloc.go, runtime/slice.go
// - make: runtime/malloc.go 中的makeslice
// - append: runtime/slice.go 中的growslice
// - panic/recover: runtime/panic.go
make的实现:
// make([]T, len, cap) 编译为调用runtime.makeslice
func makeslice(et *_type, len, cap int) unsafe.Pointer {
mem, overflow := math.MulUintptr(et.size, uintptr(cap))
if overflow || mem > maxAlloc || len < 0 || len > cap {
panicmakeslicelen()
}
return mallocgc(mem, et, true)
}
3. 参考实现和代码位置
关键runtime函数的位置:
-
内置函数实现:
runtime/malloc.go:makeslice,makechan,makemapruntime/slice.go:growslice(append的实现)runtime/chan.go:makechan,chansend,chanrecvruntime/map.go:makemap,mapaccess,mapassign
-
汇编实现参考:
runtime/asm_amd64.s: 平台特定的汇编实现runtime/panic.go:deferproc,deferreturnruntime/proc.go:newproc,gostart
具体汇编示例(deferproc):
// runtime/asm_amd64.s 中的deferproc实现
TEXT runtime·deferproc(SB), NOSPLIT, $0-16
MOVQ siz+0(FP), CX // 参数大小
MOVQ fn+8(FP), DX // 函数地址
// 获取goroutine的g结构
get_tls(BX)
MOVQ g(BX), BX
// 分配_defer结构
MOVQ g_deferpool(BX), AX
TESTQ AX, AX
JZ alloc
// 已有defer池,从池中获取
MOVQ (AX), CX
MOVQ CX, g_deferpool(BX)
JMP initdefer
alloc:
// 分配新的_defer结构
MOVQ $runtime·deferSize(SB), CX
CALL runtime·mallocgc(SB)
initdefer:
// 初始化_defer结构
MOVQ siz+0(FP), CX
MOVQ CX, 0(AX) // siz
MOVQ fn+8(FP), CX
MOVQ CX, 16(AX) // fn
// ... 更多初始化代码
append的runtime实现:
// runtime/slice.go
func growslice(et *_type, old slice, cap int) slice {
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
for newcap < cap {
newcap += newcap / 4
}
}
}
// 内存分配和复制逻辑...
}
4. 实现建议
对于编译器开发,建议:
- 先实现IR转换,将AST转换为SSA形式的中间表示
- 内置函数处理:在IR层面将内置函数调用转换为特定的IR指令或runtime调用
- defer实现:在函数入口创建defer链,在返回点插入defer执行逻辑
- goroutine实现:转换为runtime.newproc调用,处理闭包捕获
Go官方编译器的相关代码可以在cmd/compile/internal目录下找到具体实现,特别是ssa.go和walk.go包含了内置函数的转换逻辑。

