Golang内置函数汇编实现的求助指南

Golang内置函数汇编实现的求助指南 最近几天我一直在开发一个Go语言的编译器。我已经基本完成了词法分析器和语法分析器,很快将开始类型检查阶段。然而,当我考虑到中间表示阶段时(我承认这可能还需要几周时间),我意识到我可能对相关的一些事情不太了解。以下是我遇到的问题:

  1. 在Go语言中,像 defergo 等特性是如何实现的(从底层角度来看)?例如,我该如何编写IR/汇编代码来实现线程等功能?
  2. 内置函数(参见此处)是如何在IR/汇编层面实现的?
  3. 另外,如果能找到现有Go编译器汇编例程的链接作为参考,那将会有很大帮助,不过也许我自己能实现一些更简单的部分。 如果能提供任何参考资料或指引,我将不胜感激。提前感谢。

更多关于Golang内置函数汇编实现的求助指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

非常感谢。我会去查阅您提到的资料。

更多关于Golang内置函数汇编实现的求助指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,@Test_Zero,Go 语言的参考实现源代码可以在 golang.org/src/ 找到。

不过,像 appendmakelen 这类函数是作为编译器内置函数实现的。你需要查看编译器的实现这里,和/或检查参考实现编译后生成的汇编代码。

// 示例:使用内置函数
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函数的位置:

  1. 内置函数实现:

    • runtime/malloc.go: makeslice, makechan, makemap
    • runtime/slice.go: growslice (append的实现)
    • runtime/chan.go: makechan, chansend, chanrecv
    • runtime/map.go: makemap, mapaccess, mapassign
  2. 汇编实现参考:

    • runtime/asm_amd64.s: 平台特定的汇编实现
    • runtime/panic.go: deferproc, deferreturn
    • runtime/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. 实现建议

对于编译器开发,建议:

  1. 先实现IR转换,将AST转换为SSA形式的中间表示
  2. 内置函数处理:在IR层面将内置函数调用转换为特定的IR指令或runtime调用
  3. defer实现:在函数入口创建defer链,在返回点插入defer执行逻辑
  4. goroutine实现:转换为runtime.newproc调用,处理闭包捕获

Go官方编译器的相关代码可以在cmd/compile/internal目录下找到具体实现,特别是ssa.gowalk.go包含了内置函数的转换逻辑。

回到顶部