Go’s Hidden #pragmas

GopherChina 2017

Go programmer from Sydney, Australia

But first, a history lesson.


use strict;

use strict "vars";

use strict "refs";

use strict "subs";

use strict;

no strict "vars";


"use strict";

Rust #[inline(always)]
 fn super_fast_fn() { #[cfg(target_os = "macos")]
 mod macos_only { .

Warning: History time .

ALGOL 68 pragmat .

 double k. C #pragma pack(2) 
 struct T {
 int i.
 short j.

Does Go have pragmas? .

Yes. Go has pragmas // .

They're actually called pragmas in the source .

syscall/syscall_linux_amd64.go //go:noescape
 func gettimeofday(tv *Timeval) (err Errno) .

go //go:noinline
 func lshNop1(x uint64) uint64 {
 // two outer shifts should be removed
 return (((x << 5) >> 2) << 2)
 } .cmd/compile/internal/gc/testdata/arith.

 atomic.Pointer. new)
 } .StorepNoWB(noescape(ptr).go //go:nosplit
 func atomicstorep(ptr unsafe. new unsafe.Pointer) {
 writebarrierptr_prewrite((*uintptr)(ptr). runtime/atomic_pointer.

A word of caution
 🚨 .

The question is. does the usefulness justify the cost? The cost here is continued proliferation of magic comments. –Rob Pike . "Useful" is always true for a feature request. which are becoming too numerous already.

//go:noescape .

Escape Analysis func NewBook() *Book {
 b := Book{Mice: 12. Men: 9}
 return &b
 } .

Escape Analysis (cont.) func BuildLibrary() {
 b := Book{Mice: 99: Men: 3}
 } .

Answer: it depends
 🤔 .

b does not escape func AddToCollection(b *Book) {
 b.Classification = "fiction"
 } .

 } . b escapes var AvailableForLoan []*Book func AddToCollection(b *Book) {
 AvailableForLoan = append(AvailableForLoan.

_ := f.File. os.Open("/tmp/foo")
 buf := make([]byte. _ := os.Read(buf) . 4096)
 n.Read f.

err error) {
 if err := f.name.
 // At end of file.
 // It returns the number of bytes read and any error encountered.
 func (f *File) Read(b []byte) (n int. err
 } . err != nil {
 return 0.checkValid("read").File.EOF.EOF {
 err = e
 } else {
 err = &PathError{"read". f.Read // Read reads up to len(b) bytes from the File. io. err
 n. Read returns 0. os.read(b)
 if e != nil {
 if e == io. e}
 return n. e := f.

org/cl/7289048 . agl. golang. minux. Fixes #4099. remyoudompheng.org>
 Date: Tue Feb 5 07:00:38 2013 -0500 cmd/gc: add way to specify 'noescape' for extern funcs A new comment directive //go:noescape instructs the compiler
 that the following external (no body) func declaration should be
 treated as if none of its arguments escape to the heap.ma.org/issue/4099 commit fd178d6a7e62796c71258ba155b957616be86ff4
 Author: Russ Cox <rsc@golang.morsing. R=golang-dev. dave. daniel. adg. iant

/runtime/asm_$GOARCH. bytes.
 func IndexByte(s []byte.s .5) package bytes //go:noescape
 // IndexByte returns the index of the first instance of c in s..IndexByte (circa Go 1. c byte) int // .
 // or -1 if c is not present in s.

Can you use //go:noescape in your code? .

//go:norace .

8c195bdf // TODO(rsc): Remove.
 // Race instrumentation does all of the above.Nname.
 return myimportpath != "" && myimportpath == "syscall" &&
 fn. because
 // they might have been locked at the time of the fork. this function must not acquire any locks.
 // In the child.Sym.
 func isforkfunc(fn *Node) bool {
 // Special case for syscall. Put //go:norace on forkAndExecInChild instead.forkAndExecInChild.Name == "forkAndExecInChild"
 } .Func. and no new stack segments. This means
 // no rescheduling. no malloc calls.

and no new stack segments. pipe int)
 (pid int.
 // For the same reason compiler does not race instrument it. sys *SysProcAttr. dir
 *byte. envv) in child. err Errno) { .len(fd). chroot.
 func forkAndExecInChild(argv0 *byte. and exec(argv0. no malloc calls.
 // (Pipe is close-on-exec so if exec succeeds. envv []*byte. this function must not acquire any locks.go // Fork.
 // If a dup or exec fails. syscall/exec_bsd.
 // The calls to RawSyscall are okay because they are assembly
 // functions that do not grow the stack.)
 // In the child. attr *ProcAttr. argvv. This means
 // no rescheduling. it will be closed. argv. write the errno error to pipe. dup fd onto 0.. because
 // they might have been locked at the time of the fork.

Should you use //go:norace in your own code? .

//go:nosplit .

 0x000d 00013 (main. $128-0
 0x0000 00000 (main.go:5) CMPQ SP.fn(SB).go:5) MOVQ (TLS).fn t=1 size=120 args=0x0 locals=0x80
 0x0000 00000 (main.go:5) JLS 113 . Function preamble "". CX
 0x0009 00009 (main.go:5) TEXT "".

technical. digression .Warning: nerdy.

 } .
 #pragma textflag NOSPLIT
 runtime·readgstatus(G *gp)
 return runtime·atomicload(&gp->atomicstatus). casfromgscanstatus. casgstatus
 // castogscanstatus. #pragma textflag // All reads and writes of g's status go through readgstatus.

 } . #pragma textflag // All reads and writes of g's status go through
 // readgstatus. casgstatus.
 // casfrom_Gscanstatus.
 func readgstatus(gp *g) uint32 {
 return atomic.

What happens when you run out of stack with //go:nosplit? .

Can you use //go:nosplit in your own code? .

//go:noinline .

–Keith Randall . We particularly need this feature on the SSA branch because if a function is inlined. Without some sort of noinline mark the SSA- specific tests might not be testing the SSA backend at all. the code contained in that function might switch from being SSA-compiled to old-compiler-compiled.

func ishairy(n *Node. reason *string) bool . budget *int32.

 OFOR.ishairy() case OCLOSURE.
 OSWITCH.cmd/compile/internal/gc. // can't print yet
 return true .
 ORANGE. // can't print yet

func f3a_ssa(x int) *int {
 switch {
 return &x
 } .

Can you use //go:noinline in your own code? .

//go:linkname .

386 windows package runtime import _ “unsafe" //go:linkname time_now time.go // Declarations for operating systems implementing time.
 // Those systems are also expected to have nanotime subtract startNano. runtime/timeasm.now directly in assembly.now
 func time_now() (sec int64.now and nanotime return the same monotonic clock readings.amd64 darwin.
 // so that time. mono int64) . nsec int32. // +build darwin.

nsec int32. sec + minWall. mono. Local}
 return Time{hasMonotonic | uint64(sec)<<nsecShift |
 uint64(nsec).go // Provided by package runtime.minWall
 if uint64(sec)>>33 != 0 {
 return Time{uint64(nsec). mono int64) // Now returns the current local time. nsec.
 func Now() Time {
 func now() (sec int64. Local}
 } . mono := now()
 sec += unixToInternal . time/time.

Can you use //go:linkname in your own code? .

Finding a coroutine’s id .

(hold my beer) .Never. ever. do this.

But what about … .

// +build .

//go:generate .

package pdf // import "rsc.io/pdf" .

go:123 .//line /foo/bar.

Conclusion .

Thank you! Artwork @ashleymacnamara
 Gopher design @reneefrench .