golang 编译指示


编译指示

函数声明前一行写上//go:后面跟上编译指示,在编译的时候,go编译器会进行指定的操作。

源码中包含了所有的编译指示

const (
    // Func pragmas.
    Nointerface    syntax.Pragma = 1 << iota
    Noescape                     // func parameters don't escape
    Norace                       // func must not have race detector annotations
    Nosplit                      // func should not execute on separate stack
    Noinline                     // func should not be inlined
    CgoUnsafeArgs                // treat a pointer to one arg as a pointer to them all
    UintptrEscapes               // pointers converted to uintptr escape

    // Runtime-only func pragmas.
    // See ../../../../runtime/README.md for detailed descriptions.
    Systemstack        // func must run on system stack
    Nowritebarrier     // emit compiler error instead of write barrier
    Nowritebarrierrec  // error on write barrier in this or recursive callees
    Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees

    // Runtime-only type pragmas
    NotInHeap // values of this type must not be heap allocated
)

func pragmaValue(verb string) syntax.Pragma {
    switch verb {
    case "go:nointerface":
        if objabi.Fieldtrack_enabled != 0 {
            return Nointerface
        }
    case "go:noescape":
        return Noescape
    case "go:norace":
        return Norace
    case "go:nosplit":
        return Nosplit
    case "go:noinline":
        return Noinline
    case "go:systemstack":
        return Systemstack
    case "go:nowritebarrier":
        return Nowritebarrier
    case "go:nowritebarrierrec":
        return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
    case "go:yeswritebarrierrec":
        return Yeswritebarrierrec
    case "go:cgo_unsafe_args":
        return CgoUnsafeArgs
    case "go:uintptrescapes":
        // For the next function declared in the file
        // any uintptr arguments may be pointer values
        // converted to uintptr. This directive
        // ensures that the referenced allocated
        // object, if any, is retained and not moved
        // until the call completes, even though from
        // the types alone it would appear that the
        // object is no longer needed during the
        // call. The conversion to uintptr must appear
        // in the argument list.
        // Used in syscall/dll_windows.go.
        return UintptrEscapes
    case "go:notinheap":
        return NotInHeap
    }
    return 0
}

nointerface

noescape

禁止逃逸,而且它必须指示一个只有声明没有主体的函数。
最显而易见的好处是,GC 压力变小了。
因为它已经告诉编译器,下面的函数无论如何都不会逃逸,那么当函数返回时,其中的资源也会一并都被销毁。
不过,这么做代表会绕过编译器的逃逸检查,一旦进入运行时,就有可能导致严重的错误及后果。

norace

跳过竞态检测

nosplit

跳过栈溢出检测,正是因为一个 Goroutine 的起始栈大小是有限制的,且比较小的,才可以做到支持并发很多 Goroutine,并高效调度。
stack.go 源码中可以看到,_StackMin 是 2048 字节,也就是 2k,它不是一成不变的,当不够用时,它会动态地增长。
那么,必然有一个检测的机制,来保证可以及时地知道栈不够用了,然后再去增长。
回到话题,nosplit 就是将这个跳过这个机制。

noinline

标记编译的时候不用内联

systemstack

nowritebarrier

nowritebarrierrec

yeswritebarrierrec

cgo_unsafe_args

uintptrescapes

notinheap

linkname

示例://go:linkname localname importpath.name

编译器指令的使用linkname instructs”importpath.name”为对象的文件的符号名的变量或函数被作为“localname”代码。因为本指令的类型系统和CAN subvert包装modularity只读文件,它是在一个“功能”不安全”。

参考文献

  1. Go 语言编译器的 “//go:” 详解
  2. Command compile

文章作者: djaigo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 djaigo !
评论
  目录