简介

Golang 编程语言 源码研究汇总 主要关注

  • 实现
  • 网络
  • 运行时
  • 性能
  • liunx/macos/windows
  • amd64 x86 arm

更新日志

主要变化宝库

  • 语言变更
  • 工具
  • 运行时
  • 编译器
  • 连接器
  • 启动器
  • 核心类库
  • 类库变更

各个版本发布时间点

  • go1.20 (released 2023-02-01)

  • go1.19 (released 2022-08-02)

  • go1.18 (released 2022-03-15)

  • go1.17 (released 2021-08-16)

  • go1.16 (released 2021-02-16)

  • go1.15 (released 2020-08-11)

  • go1.14 (released 2020-02-25)

  • go1.13 (released 2019-09-03)

  • go1.12 (released 2019-02-25)

  • go1.11 (released 2018-08-24)

  • go1.10 (released 2018-02-16)

  • go1.9 (released 2017-08-24)

  • go1.8 (released 2017-02-16)

  • go1.7 (released 2016-08-15)

  • go1.6 (released 2016-02-17)

  • go1.5 (released 2015-08-19)

  • go1.4 (released 2014-12-10)

  • go1.3 (released 2014-06-18)

  • go1.2 (released 2013-12-01)

  • go1.1 (released 2013-05-13)

  • go1 (released 2012-03-28)

Changes to the language 语言变化

Go 1.20 includes four changes to the language.

Go 1.17 added conversions from slice to an array pointer. Go 1.20 extends this to allow conversions from a slice to an array: given a slice x, [4]byte(x) can now be written instead of ([4]byte)(x).

The unsafe package defines three new functions SliceData, String, and StringData. Along with Go 1.17's Slice, these functions now provide the complete ability to construct and deconstruct slice and string values, without depending on their exact representation.

The specification now defines that struct values are compared one field at a time, considering fields in the order they appear in the struct type definition, and stopping at the first mismatch. The specification could previously have been read as if all fields needed to be compared beyond the first mismatch. Similarly, the specification now defines that array values are compared one element at a time, in increasing index order. In both cases, the difference affects whether certain comparisons must panic. Existing programs are unchanged: the new spec wording describes what the implementations have always done.

Comparable types (such as ordinary interfaces) may now satisfy comparable constraints, even if the type arguments are not strictly comparable (comparison may panic at runtime). This makes it possible to instantiate a type parameter constrained by comparable (e.g., a type parameter for a user-defined generic map key) with a non-strictly comparable type argument such as an interface type, or a composite type containing an interface type.

Ports

Windows

Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016.

Darwin and iOS

Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later.

FreeBSD/RISC-V

Go 1.20 adds experimental support for FreeBSD on RISC-V (GOOS=freebsd, GOARCH=riscv64).

Tools

Go command

The directory $GOROOT/pkg no longer stores pre-compiled package archives for the standard library: go install no longer writes them, the go build no longer checks for them, and the Go distribution no longer ships them. Instead, packages in the standard library are built as needed and cached in the build cache, just like packages outside GOROOT. This change reduces the size of the Go distribution and also avoids C toolchain skew for packages that use cgo.

The implementation of go test -json has been improved to make it more robust. Programs that run go test -json do not need any updates. Programs that invoke go tool test2json directly should now run the test binary with -v=test2json (for example, go test -v=test2json or ./pkg.test -test.v=test2json) instead of plain -v.

A related change to go test -json is the addition of an event with Action set to start at the beginning of each test program's execution. When running multiple tests using the go command, these start events are guaranteed to be emitted in the same order as the packages named on the command line.

The go command now defines architecture feature build tags, such as amd64.v2, to allow selecting a package implementation file based on the presence or absence of a particular architecture feature. See go help buildconstraint for details.

The go subcommands now accept -C

to change directory to before performing the command, which may be useful for scripts that need to execute commands in multiple different modules.

The go build and go test commands no longer accept the -i flag, which has been deprecated since Go 1.16.

The go generate command now accepts -skip to skip //go:generate directives matching .

The go test command now accepts -skip to skip tests, subtests, or examples matching .

When the main module is located within GOPATH/src, go install no longer installs libraries for non-main packages to GOPATH/pkg, and go list no longer reports a Target field for such packages. (In module mode, compiled packages are stored in the build cache only, but a bug had caused the GOPATH install targets to unexpectedly remain in effect.)

The go build, go install, and other build-related commands now support a -pgo flag that enables profile-guided optimization, which is described in more detail in the Compiler section below. The -pgo flag specifies the file path of the profile. Specifying -pgo=auto causes the go command to search for a file named default.pgo in the main package's directory and use it if present. This mode currently requires a single main package to be specified on the command line, but we plan to lift this restriction in a future release. Specifying -pgo=off turns off profile-guided optimization.

The go build, go install, and other build-related commands now support a -cover flag that builds the specified target with code coverage instrumentation. This is described in more detail in the Cover section below.

go version The go version -m command now supports reading more types of Go binaries, most notably, Windows DLLs built with go build -buildmode=c-shared and Linux binaries without execute permission.

Cgo

The go command now disables cgo by default on systems without a C toolchain. More specifically, when the CGO_ENABLED environment variable is unset, the CC environment variable is unset, and the default C compiler (typically clang or gcc) is not found in the path, CGO_ENABLED defaults to 0. As always, you can override the default by setting CGO_ENABLED explicitly.

The most important effect of the default change is that when Go is installed on a system without a C compiler, it will now use pure Go builds for packages in the standard library that use cgo, instead of using pre-distributed package archives (which have been removed, as noted above) or attempting to use cgo and failing. This makes Go work better in some minimal container environments as well as on macOS, where pre-distributed package archives have not been used for cgo-based packages since Go 1.16.

The packages in the standard library that use cgo are net, os/user, and plugin. On macOS, the net and os/user packages have been rewritten not to use cgo: the same code is now used for cgo and non-cgo builds as well as cross-compiled builds. On Windows, the net and os/user packages have never used cgo. On other systems, builds with cgo disabled will use a pure Go version of these packages.

A consequence is that, on macOS, if Go code that uses the net package is built with -buildmode=c-archive, linking the resulting archive into a C program requires passing -lresolv when linking the C code.

On macOS, the race detector has been rewritten not to use cgo: race-detector-enabled programs can be built and run without Xcode. On Linux and other Unix systems, and on Windows, a host C toolchain is required to use the race detector.

Cover

Go 1.20 supports collecting code coverage profiles for programs (applications and integration tests), as opposed to just unit tests.

To collect coverage data for a program, build it with go build's -cover flag, then run the resulting binary with the environment variable GOCOVERDIR set to an output directory for coverage profiles. See the 'coverage for integration tests' landing page for more on how to get started. For details on the design and implementation, see the proposal.

Vet

Improved detection of loop variable capture by nested functions The vet tool now reports references to loop variables following a call to T.Parallel() within subtest function bodies. Such references may observe the value of the variable from a different iteration (typically causing test cases to be skipped) or an invalid state due to unsynchronized concurrent access.

The tool also detects reference mistakes in more places. Previously it would only consider the last statement of the loop body, but now it recursively inspects the last statements within if, switch, and select statements.

New diagnostic for incorrect time formats The vet tool now reports use of the time format 2006-02-01 (yyyy-dd-mm) with Time.Format and time.Parse. This format does not appear in common date standards, but is frequently used by mistake when attempting to use the ISO 8601 date format (yyyy-mm-dd).

Runtime

Some of the garbage collector's internal data structures were reorganized to be both more space and CPU efficient. This change reduces memory overheads and improves overall CPU performance by up to 2%.

The garbage collector behaves less erratically with respect to goroutine assists in some circumstances.

Go 1.20 adds a new runtime/coverage package containing APIs for writing coverage profile data at runtime from long-running and/or server programs that do not terminate via os.Exit().

Compiler

Go 1.20 adds preview support for profile-guided optimization (PGO). PGO enables the toolchain to perform application- and workload-specific optimizations based on run-time profile information. Currently, the compiler supports pprof CPU profiles, which can be collected through usual means, such as the runtime/pprof or net/http/pprof packages. To enable PGO, pass the path of a pprof profile file via the -pgo flag to go build, as mentioned above. Go 1.20 uses PGO to more aggressively inline functions at hot call sites. Benchmarks for a representative set of Go programs show enabling profile-guided inlining optimization improves performance about 3–4%. See the PGO user guide for detailed documentation. We plan to add more profile-guided optimizations in future releases. Note that profile-guided optimization is a preview, so please use it with appropriate caution.

The Go 1.20 compiler upgraded its front-end to use a new way of handling the compiler's internal data, which fixes several generic-types issues and enables type declarations within generic functions and methods.

The compiler now rejects anonymous interface cycles with a compiler error by default. These arise from tricky uses of embedded interfaces and have always had subtle correctness issues, yet we have no evidence that they're actually used in practice. Assuming no reports from users adversely affected by this change, we plan to update the language specification for Go 1.22 to formally disallow them so tools authors can stop supporting them too.

Go 1.18 and 1.19 saw regressions in build speed, largely due to the addition of support for generics and follow-on work. Go 1.20 improves build speeds by up to 10%, bringing it back in line with Go 1.17. Relative to Go 1.19, generated code performance is also generally slightly improved.

Linker

On Linux, the linker now selects the dynamic interpreter for glibc or musl at link time.

On Windows, the Go linker now supports modern LLVM-based C toolchains.

Go 1.20 uses go: and type: prefixes for compiler-generated symbols rather than go. and type.. This avoids confusion for user packages whose name starts with go.. The debug/gosym package understands this new naming convention for binaries built with Go 1.20 and newer.

Bootstrap

When building a Go release from source and GOROOT_BOOTSTRAP is not set, previous versions of Go looked for a Go 1.4 or later bootstrap toolchain in the directory $HOME/go1.4 (%HOMEDRIVE%%HOMEPATH%\go1.4 on Windows). Go 1.18 and Go 1.19 looked first for $HOME/go1.17 or $HOME/sdk/go1.17 before falling back to $HOME/go1.4, in anticipation of requiring Go 1.17 for use when bootstrapping Go 1.20. Go 1.20 does require a Go 1.17 release for bootstrapping, but we realized that we should adopt the latest point release of the bootstrap toolchain, so it requires Go 1.17.13. Go 1.20 looks for $HOME/go1.17.13 or $HOME/sdk/go1.17.13 before falling back to $HOME/go1.4 (to support systems that hard-coded the path $HOME/go1.4 but have installed a newer Go toolchain there). In the future, we plan to move the bootstrap toolchain forward approximately once a year, and in particular we expect that Go 1.22 will require the final point release of Go 1.20 for bootstrap.

Core library

New crypto/ecdh package

Go 1.20 adds a new crypto/ecdh package to provide explicit support for Elliptic Curve Diffie-Hellman key exchanges over NIST curves and Curve25519.

Programs should use crypto/ecdh instead of the lower-level functionality in crypto/elliptic for ECDH, and third-party modules for more advanced use cases.

Wrapping multiple errors

Go 1.20 expands support for error wrapping to permit an error to wrap multiple other errors.

An error e can wrap more than one error by providing an Unwrap method that returns a []error.

The errors.Is and errors.As functions have been updated to inspect multiply wrapped errors.

The fmt.Errorf function now supports multiple occurrences of the %w format verb, which will cause it to return an error that wraps all of those error operands.

The new function errors.Join returns an error wrapping a list of errors.

HTTP ResponseController

The new "net/http".ResponseController type provides access to extended per-request functionality not handled by the "net/http".ResponseWriter interface.

Previously, we have added new per-request functionality by defining optional interfaces which a ResponseWriter can implement, such as Flusher. These interfaces are not discoverable and clumsy to use.

The ResponseController type provides a clearer, more discoverable way to add per-handler controls. Two such controls also added in Go 1.20 are SetReadDeadline and SetWriteDeadline, which allow setting per-request read and write deadlines. For example:

func RequestHandler(w ResponseWriter, r *Request) { rc := http.NewResponseController(w) rc.SetWriteDeadline(time.Time{}) // disable Server.WriteTimeout when sending a large response io.Copy(w, bigData) }

New ReverseProxy Rewrite hook

The httputil.ReverseProxy forwarding proxy includes a new Rewrite hook function, superseding the previous Director hook.

The Rewrite hook accepts a ProxyRequest parameter, which includes both the inbound request received by the proxy and the outbound request that it will send. Unlike Director hooks, which only operate on the outbound request, this permits Rewrite hooks to avoid certain scenarios where a malicious inbound request may cause headers added by the hook to be removed before forwarding. See issue #50580.

The ProxyRequest.SetURL method routes the outbound request to a provided destination and supersedes the NewSingleHostReverseProxy function. Unlike NewSingleHostReverseProxy, SetURL also sets the Host header of the outbound request.

The ProxyRequest.SetXForwarded method sets the X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto headers of the outbound request. When using a Rewrite, these headers are not added by default.

An example of a Rewrite hook using these features is:

proxyHandler := &httputil.ReverseProxy{ Rewrite: func(r httputil.ProxyRequest) { r.SetURL(outboundURL) // Forward request to outboundURL. r.SetXForwarded() // Set X-Forwarded- headers. r.Out.Header.Set("X-Additional-Header", "header set by the proxy") }, } ReverseProxy no longer adds a User-Agent header to forwarded requests when the incoming request does not have one.

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind. There are also various performance improvements, not enumerated here.

archive/tar

When the GODEBUG=tarinsecurepath=0 environment variable is set, Reader.Next method will now return the error ErrInsecurePath for an entry with a file name that is an absolute path, refers to a location outside the current directory, contains invalid characters, or (on Windows) is a reserved name such as NUL. A future version of Go may disable insecure paths by default.

archive/zip

When the GODEBUG=zipinsecurepath=0 environment variable is set, NewReader will now return the error ErrInsecurePath when opening an archive which contains any file name that is an absolute path, refers to a location outside the current directory, contains invalid characters, or (on Windows) is a reserved names such as NUL. A future version of Go may disable insecure paths by default.

Reading from a directory file that contains file data will now return an error. The zip specification does not permit directory files to contain file data, so this change only affects reading from invalid archives.

bytes

The new CutPrefix and CutSuffix functions are like TrimPrefix and TrimSuffix but also report whether the string was trimmed.

The new Clone function allocates a copy of a byte slice.

context

The new WithCancelCause function provides a way to cancel a context with a given error. That error can be retrieved by calling the new Cause function.

crypto/ecdsa

When using supported curves, all operations are now implemented in constant time. This led to an increase in CPU time between 5% and 30%, mostly affecting P-384 and P-521.

The new PrivateKey.ECDH method converts an ecdsa.PrivateKey to an ecdh.PrivateKey.

crypto/ed25519

The PrivateKey.Sign method and the VerifyWithOptions function now support signing pre-hashed messages with Ed25519ph, indicated by an Options.HashFunc that returns crypto.SHA512. They also now support Ed25519ctx and Ed25519ph with context, indicated by setting the new Options.Context field.

crypto/rsa

The new field OAEPOptions.MGFHash allows configuring the MGF1 hash separately for OAEP decryption.

crypto/rsa now uses a new, safer, constant-time backend. This causes a CPU runtime increase for decryption operations between approximately 15% (RSA-2048 on amd64) and 45% (RSA-4096 on arm64), and more on 32-bit architectures. Encryption operations are approximately 20x slower than before (but still 5-10x faster than decryption). Performance is expected to improve in future releases. Programs must not modify or manually generate the fields of PrecomputedValues.

crypto/subtle

The new function XORBytes XORs two byte slices together.

crypto/tls

Parsed certificates are now shared across all clients actively using that certificate. The memory savings can be significant in programs that make many concurrent connections to a server or collection of servers sharing any part of their certificate chains.

For a handshake failure due to a certificate verification failure, the TLS client and server now return an error of the new type CertificateVerificationError, which includes the presented certificates.

crypto/x509

ParsePKCS8PrivateKey and MarshalPKCS8PrivateKey now support keys of type *crypto/ecdh.PrivateKey. ParsePKIXPublicKey and MarshalPKIXPublicKey now support keys of type *crypto/ecdh.PublicKey. Parsing NIST curve keys still returns values of type *ecdsa.PublicKey and *ecdsa.PrivateKey. Use their new ECDH methods to convert to the crypto/ecdh types.

The new SetFallbackRoots function allows a program to define a set of fallback root certificates in case an operating system verifier or standard platform root bundle is unavailable at runtime. It will most commonly be used with a new package, golang.org/x/crypto/x509roots/fallback, which will provide an up to date root bundle.

debug/elf

Attempts to read from a SHT_NOBITS section using Section.Data or the reader returned by Section.Open now return an error.

Additional R_LARCH_* constants are defined for use with LoongArch systems.

Additional R_PPC64_* constants are defined for use with PPC64 ELFv2 relocations.

The constant value for R_PPC64_SECTOFF_LO_DS is corrected, from 61 to 62.

debug/gosym

Due to a change of Go's symbol naming conventions, tools that process Go binaries should use Go 1.20's debug/gosym package to transparently handle both old and new binaries.

debug/pe

Additional IMAGE_FILE_MACHINE_RISCV* constants are defined for use with RISC-V systems.

encoding/binary

The ReadVarint and ReadUvarint functions will now return io.ErrUnexpectedEOF after reading a partial value, rather than io.EOF.

encoding/xml

The new Encoder.Close method can be used to check for unclosed elements when finished encoding.

The decoder now rejects element and attribute names with more than one colon, such as <a:b:c>, as well as namespaces that resolve to an empty string, such as xmlns:a="".

The decoder now rejects elements that use different namespace prefixes in the opening and closing tag, even if those prefixes both denote the same namespace.

errors

The new Join function returns an error wrapping a list of errors.

fmt

The Errorf function supports multiple occurrences of the %w format verb, returning an error that unwraps to the list of all arguments to %w.

The new FormatString function recovers the formatting directive corresponding to a State, which can be useful in Formatter. implementations.

go/ast

The new RangeStmt.Range field records the position of the range keyword in a range statement.

The new File.FileStart and File.FileEnd fields record the position of the start and end of the entire source file.

go/token

The new FileSet.RemoveFile method removes a file from a FileSet. Long-running programs can use this to release memory associated with files they no longer need.

go/types

The new Satisfies function reports whether a type satisfies a constraint. This change aligns with the new language semantics that distinguish satisfying a constraint from implementing an interface.

tml/template

Go 1.20.3 and later disallow actions in ECMAScript 6 template literals. This behavior can be reverted by the GODEBUG=jstmpllitinterp=1 setting.

io

The new OffsetWriter wraps an underlying WriterAt and provides Seek, Write, and WriteAt methods that adjust their effective file offset position by a fixed amount.

io/fs

The new error SkipAll terminates a WalkDir immediately but successfully.

math/big

The math/big package's wide scope and input-dependent timing make it ill-suited for implementing cryptography. The cryptography packages in the standard library no longer call non-trivial Int methods on attacker-controlled inputs. In the future, the determination of whether a bug in math/big is considered a security vulnerability will depend on its wider impact on the standard library.

math/rand

The math/rand package now automatically seeds the global random number generator (used by top-level functions like Float64 and Int) with a random value, and the top-level Seed function has been deprecated. Programs that need a reproducible sequence of random numbers should prefer to allocate their own random source, using rand.New(rand.NewSource(seed)).

Programs that need the earlier consistent global seeding behavior can set GODEBUG=randautoseed=0 in their environment.

The top-level Read function has been deprecated. In almost all cases, crypto/rand.Read is more appropriate.

mime

The ParseMediaType function now allows duplicate parameter names, so long as the values of the names are the same.

mime/multipart

Methods of the Reader type now wrap errors returned by the underlying io.Reader.

In Go 1.19.8 and later, this package sets limits the size of the MIME data it processes to protect against malicious inputs. Reader.NextPart and Reader.NextRawPart limit the number of headers in a part to 10000 and Reader.ReadForm limits the total number of headers in all FileHeaders to 10000. These limits may be adjusted with the GODEBUG=multipartmaxheaders setting. Reader.ReadForm further limits the number of parts in a form to 1000. This limit may be adjusted with the GODEBUG=multipartmaxparts setting.

net

The LookupCNAME function now consistently returns the contents of a CNAME record when one exists. Previously on Unix systems and when using the pure Go resolver, LookupCNAME would return an error if a CNAME record referred to a name that with no A, AAAA, or CNAME record. This change modifies LookupCNAME to match the previous behavior on Windows, allowing LookupCNAME to succeed whenever a CNAME exists.

Interface.Flags now includes the new flag FlagRunning, indicating an operationally active interface. An interface which is administratively configured but not active (for example, because the network cable is not connected) will have FlagUp set but not FlagRunning.

The new Dialer.ControlContext field contains a callback function similar to the existing Dialer.Control hook, that additionally accepts the dial context as a parameter. Control is ignored when ControlContext is not nil.

The Go DNS resolver recognizes the trust-ad resolver option. When options trust-ad is set in resolv.conf, the Go resolver will set the AD bit in DNS queries. The resolver does not make use of the AD bit in responses.

DNS resolution will detect changes to /etc/nsswitch.conf and reload the file when it changes. Checks are made at most once every five seconds, matching the previous handling of /etc/hosts and /etc/resolv.conf.

net/http

The ResponseWriter.WriteHeader function now supports sending 1xx status codes.

The new Server.DisableGeneralOptionsHandler configuration setting allows disabling the default OPTIONS * handler.

The new Transport.OnProxyConnectResponse hook is called when a Transport receives an HTTP response from a proxy for a CONNECT request.

The HTTP server now accepts HEAD requests containing a body, rather than rejecting them as invalid.

HTTP/2 stream errors returned by net/http functions may be converted to a golang.org/x/net/http2.StreamError using errors.As.

Leading and trailing spaces are trimmed from cookie names, rather than being rejected as invalid. For example, a cookie setting of "name =value" is now accepted as setting the cookie "name".

A Cookie with an empty Expires field is now considered valid. Cookie.Valid only checks Expires when it is set.

net/netip

The new IPv6LinkLocalAllRouters and IPv6Loopback functions are the net/netip equivalents of net.IPv6loopback and net.IPv6linklocalallrouters.

os

On Windows, the name NUL is no longer treated as a special case in Mkdir and Stat.

On Windows, File.Stat now uses the file handle to retrieve attributes when the file is a directory. Previously it would use the path passed to Open, which may no longer be the file represented by the file handle if the file has been moved or replaced. This change modifies Open to open directories without the FILE_SHARE_DELETE access, which match the behavior of regular files.

On Windows, File.Seek now supports seeking to the beginning of a directory.

os/exec

The new Cmd fields Cancel and WaitDelay specify the behavior of the Cmd when its associated Context is canceled or its process exits with I/O pipes still held open by a child process.

path/filepath

The new error SkipAll terminates a Walk immediately but successfully.

The new IsLocal function reports whether a path is lexically local to a directory. For example, if IsLocal(p) is true, then Open(p) will refer to a file that is lexically within the subtree rooted at the current directory.

reflect

The new Value.Comparable and Value.Equal methods can be used to compare two Values for equality. Comparable reports whether Equal is a valid operation for a given Value receiver.

The new Value.Grow method extends a slice to guarantee space for another n elements.

The new Value.SetZero method sets a value to be the zero value for its type.

Go 1.18 introduced Value.SetIterKey and Value.SetIterValue methods. These are optimizations: v.SetIterKey(it) is meant to be equivalent to v.Set(it.Key()). The implementations incorrectly omitted a check for use of unexported fields that was present in the unoptimized forms. Go 1.20 corrects these methods to include the unexported field check.

regexp

Go 1.19.2 and Go 1.18.7 included a security fix to the regular expression parser, making it reject very large expressions that would consume too much memory. Because Go patch releases do not introduce new API, the parser returned syntax.ErrInternalError in this case. Go 1.20 adds a more specific error, syntax.ErrLarge, which the parser now returns instead.

runtime/cgo

Go 1.20 adds new Incomplete marker type. Code generated by cgo will use cgo.Incomplete to mark an incomplete C type.

runtime/metrics

Go 1.20 adds new supported metrics, including the current GOMAXPROCS setting (/sched/gomaxprocs:threads), the number of cgo calls executed (/cgo/go-to-c-calls:calls), total mutex block time (/sync/mutex/wait/total:seconds), and various measures of time spent in garbage collection.

Time-based histogram metrics are now less precise, but take up much less memory.

runtime/pprof

Mutex profile samples are now pre-scaled, fixing an issue where old mutex profile samples would be scaled incorrectly if the sampling rate changed during execution.

Profiles collected on Windows now include memory mapping information that fixes symbolization issues for position-independent binaries.

runtime/trace

The garbage collector's background sweeper now yields less frequently, resulting in many fewer extraneous events in execution traces.

strings

The new CutPrefix and CutSuffix functions are like TrimPrefix and TrimSuffix but also report whether the string was trimmed.

sync

The new Map methods Swap, CompareAndSwap, and CompareAndDelete allow existing map entries to be updated atomically.

syscall

On FreeBSD, compatibility shims needed for FreeBSD 11 and earlier have been removed.

On Linux, additional CLONE_* constants are defined for use with the SysProcAttr.Cloneflags field.

On Linux, the new SysProcAttr.CgroupFD and SysProcAttr.UseCgroupFD fields provide a way to place a child process into a specific cgroup.

testing

The new method B.Elapsed reports the current elapsed time of the benchmark, which may be useful for calculating rates to report with ReportMetric.

Calling T.Run from a function passed to T.Cleanup was never well-defined, and will now panic.

time

The new time layout constants DateTime, DateOnly, and TimeOnly provide names for three of the most common layout strings used in a survey of public Go source code.

The new Time.Compare method compares two times.

Parse now ignores sub-nanosecond precision in its input, instead of reporting those digits as an error.

The Time.MarshalJSON method is now more strict about adherence to RFC 3339.

unicode/utf16

The new AppendRune function appends the UTF-16 encoding of a given rune to a uint16 slice, analogous to utf8.AppendRune.

Profile-guided optimization

Beginning in Go 1.20, the Go compiler supports profile-guided optimization (PGO) to further optimize builds.

Overview

Profile-guided optimization (PGO), also known as feedback-directed optimization (FDO), is a compiler optimization technique that feeds information (a profile) from representative runs of the application back into to the compiler for the next build of the application, which uses that information to make more informed optimization decisions. For example, the compiler may decide to more aggressively inline functions which the profile indicates are called frequently.

In Go, the compiler uses CPU pprof profiles as the input profile, such as from runtime/pprof or net/http/pprof.

As of Go 1.20, benchmarks for a representative set of Go programs show that building with PGO improves performance by around 2-4%. We expect performance gains to generally increase over time as additional optimizations take advantage of PGO in future versions of Go.

Collecting profiles

The Go compiler expects a CPU pprof profile as the input to PGO. Profiles generated by the Go runtime (such as from runtime/pprof and net/http/pprof) can be used directly as the compiler input. It may also be possible to use/convert profiles from other profiling systems. See the appendix for additional information.

For best results, it is important that profiles are representative of actual behavior in the application’s production environment. Using an unrepresentative profile is likely to result in a binary with little to no improvement in production. Thus, collecting profiles directly from the production environment is recommended, and is the primary method that Go’s PGO is designed for.

The typical workflow is as follows:

  • Build and release an initial binary (without PGO).(构建release)
  • Collect profiles from production. (收集线上分析数据)
  • When it’s time to release an updated binary, build from the latest source and provide the production profile. GOTO 2 Go PGO is generally robust to skew between the profiled version of an application and the version building with the profile, as well as to building with profiles collected from already-optimized binaries. This is what makes this iterative lifecycle possible. See the AutoFDO section for additional details about this workflow.

If it is difficult or impossible to collect from the production environment (e.g., a command-line tool distributed to end users), it is also possible to collect from a representative benchmark. Note that constructing representative benchmarks is often quite difficult (as is keeping them representative as the application evolves). In particular, microbenchmarks are usually bad candidates for PGO profiling, as they only exercise a small part of the application, which yields small gains when applied to the whole program.

Building with PGO

The go build -pgo flag controls PGO profile selection. Setting this flag to anything other than -pgo=off enables PGO optimizations.

The standard approach is to store a pprof CPU profile with filename default.pgo in the main package directory of the profiled binary, and build with go build -pgo=auto, which will pick up default.pgo files automatically.

Commiting profiles directly in the source repository is recommended as profiles are an input to the build important for reproducible (and performant!) builds. Storing alongside the source simplifies the build experience as there are no additional steps to get the profile beyond fetching the source.

Note: In Go 1.20, the default is -pgo=off. A future version is likely to change the default to -pgo=auto to automatically build any binary with default.pgo with PGO.

Note: In Go 1.20, -pgo=auto only works with a single main package. Attempting to build multiple main packages (go build -pgo=auto ./cmd/foo ./cmd/bar) will result in a build error. This is https://go.dev/issue/58099.

For more complex scenarios (e.g., different profiles for different scenarios of one binary, unable to store profile with source, etc), you may directly pass a path to the profile to use (e.g., go build -pgo=/tmp/foo.pprof).

Note: A path passed to -pgo applies to all main packages. e.g., go build -pgo=/tmp/foo.pprof ./cmd/foo ./cmd/bar applies foo.pprof to both binaries foo and bar, which is often not what you want. Usually different binaries should have different profiles, passed via separate go build invocations.

Notes

Collecting representative profiles from production Your production environment is the best source of representative profiles for your application, as described in Collecting profiles.

The simplest way to start with this is to add net/http/pprof to your application and then fetch /debug/pprof/profile?seconds=30 from an arbitrary instance of your service. This is a great way to get started, but there are ways that this may be unrepresentative:

This instance may not be doing anything at the moment it gets profiled, even though it is usually busy.

Traffic patterns may change throughout the day, making behavior change throughout the day.

Instances may perform long-running operations (e.g., 5 minutes doing operation A, then 5 minutes doing operation B, etc). A 30s profile will likely only cover a single operation type.

Instances may not receive fair distributions of requests (some instances receive more of one type of request than others).

A more robust strategy is collecting multiple profiles at different times from different instances to limit the impact of differences between individual instance profiles. Multiple profiles may then be merged into a single profile for use with PGO.

Many organizations run “continuous profiling” services that perform this kind of fleet-wide sampling profiling automatically, which could then be used as a source of profiles for PGO.

Merging profiles The pprof tool can merge multiple profiles like this:

$ go tool pprof -proto a.pprof b.pprof > merged.pprof This merge is effectively a straightforward sum of samples in the input, regardless of wall duration of the profile. As a result, when profiling a small time slice of an application (e.g., a server that runs indefinitely), you likely want to ensure that all profiles have the same wall duration (i.e., all profiles are collected for 30s). Otherwise, profiles with longer wall duration will be overrepresented in the merged profile.

AutoFDO

Go PGO is designed to support an “AutoFDO” style workflow.

Let’s take a closer look at the workflow described in Collecting profiles:

  • Build and release an initial binary (without PGO).
  • Collect profiles from production.
  • When it’s time to release an updated binary, build from the latest source and provide the production profile. GOTO 2 This sounds deceptively simple, but there are a few important properties to note here:

Development is always ongoing, so the source code of the profiled version of the binary (step 2) is likely slightly different from the latest source code getting built (step 3). Go PGO is designed to be robust to this, which we refer to as source stability.

This is a closed loop. That is, after the first iteration the profiled version of the binary is already PGO-optimized with a profile from a previous iteration. Go PGO is also designed to be robust to this, which we refer to as iterative stability.

Source stability is achieved using heuristics to match samples from the profile to the compiling source. As a result, many changes to source code, such as adding new functions, have no impact on matching existing code. When the compiler is not able to match changed code, some optimizations are lost, but note that this is a graceful degradation. A single function failing to match may lose out on optimization opportunities, but overall PGO benefit is usually spread across many functions. See the source stability section for more details about matching and degradation.

Iterative stability is the prevention of cycles of variable performance in successive PGO builds (e.g., build #1 is fast, build #2 is slow, build #3 is fast, etc). We use CPU profiles to identify hot functions to target with optimizations. In theory, a hot function could be sped up so much by PGO that it no longer appears hot in the next profile and does not get optimized, making it slow again. The Go compiler takes a conservative approach to PGO optimizations, which we believe prevents significant variance. If you do observe this kind of instability, please file an issue at https://go.dev/issue/new.

Together, source and iterative stability eliminate the requirement for two-stage builds where a first, unoptimized build is profiled as a canary, and then rebuilt with PGO for production (unless absolutely peak performance is required).

Source stability and refactoring

As described in above, Go’s PGO makes a best-effort attempt to continue matching samples from older profiles to the current source code. Specifically, Go uses line offsets within functions (e.g., call on 5th line of function foo).

Many common changes will not break matching, including:

Changes in a file outside of a hot function (adding/changing code above or below the function).

Moving a function to another file in the same package (the compiler ignores source filenames altogether).

Some changes that may break matching:

Changes within a hot function (may affect line offsets).

Renaming the function (and/or type for methods) (changes symbol name).

Moving the function to another package (changes symbol name).

If the profile is relatively recent, then differences likely only affect a small number of hot functions, limiting the impact of missed optimizations in functions that fail to match. Still, degradation will slowly accumulate over time since code is rarely refactored back to its old form, so it is important to collect new profiles regularly to limit source skew from production.

One situation where profile matching may significantly degrade is a large-scale refactor that renames many functions or moves them between packages. In this case, you may take a short-term performance hit until a new profile shows the new structure.

For rote renames, an existing profile could theoretically be rewritten to change the old symbol names to the new names. github.com/google/pprof/profile contains the primitives required to rewrite a pprof profile in this way, but as of writing no off-the-shelf tool exists for this.

Performance of new code When adding new code or enabling new code paths with a flag flip, that code will not be present in the profile on the first build, and thus won’t receive PGO optimizations until a new profile reflecting the new code is collected. Keep in mind when evaluating the rollout of new code that the initial release will not represent its steady state performance.

Frequently Asked Questions 常见问题

Is it possible to optimize Go standard library packages with PGO?

Yes. PGO in Go applies to the entire program. All packages are rebuilt to consider potential profile-guided optimizations, including standard library packages.

Is it possible to optimize packages in dependent modules with PGO?

Yes. PGO in Go applies to the entire program. All packages are rebuilt to consider potential profile-guided optimizations, including packages in dependencies. This means that the unique way your application uses a dependency impacts the optimizations applied to that dependency.

Will PGO with an unrepresentative profile make my program slower than no PGO?

It should not. While a profile that is not representative of production behavior will result in optimizations in cold parts of the application, it should not make hot parts of the application slower. If you encounter a program where PGO results in worse performance than disabling PGO, please file an issue at https://go.dev/issue/new.

Can I use the same profile for different GOOS/GOARCH builds?

Yes. The format of the profiles is equivalent across OS and architecture configurations, so they may be used across different configurations. For example, a profile collected from a linux/arm64 binary may be used in a windows/amd64 build.

That said, the source stability caveats discussed above apply here as well. Any source code that differs across these configurations will not be optimized. For most applications, the vast majority of code is platform-independent, so degradation of this form is limited.

As a specific example, the internals of file handling in package os differ between Linux and Windows. If these functions are hot in the Linux profile, the Windows equivalents will not get PGO optimizations because they do not match the profiles.

You may merge profiles of different GOOS/GOARCH builds. See the next question for the tradeoffs of doing so.

How should I handle a single binary used for different workload types?

There is no obvious choice here. A single binary used for different types of workloads (e.g., a database used in a read-heavy way in one service, and write-heavy in another service) may have different hot components, which benefit from different optimizations.

There are three options:

  • Build different versions of the binary for each workload: use profiles from each workload to build multiple workload-specific builds of the binary. This will provide the best performance for each workload, but may add operational complexity with regard to handling multiple binaries and profile sources.

  • Build a single binary using only profiles from the “most important” workload: select the “most important” workload (largest footprint, most performance sensitive), and build using profiles only from that workload. This provides the best performance for the selected workload, and likely still modest performance improvements for other workloads from optimizations to common code shared across workloads.

  • Merge profiles across workloads: take profiles from each workload (weighted by total footprint) and merge them into a single “fleet-wide” profile used to build a single common profile used to build. This likely provides modest performance improvements for all workloads.

How does PGO affect build time?

Enabling PGO builds should cause measurable, but small, increases in package build times. Likely more noticeable than individual package build times is that PGO profiles apply to all packages in a binary, meaning that the first use of a profile requires a rebuild of every package in the dependency graph. These builds are cached like any other, so subsequent incremental builds using the same profile do not require complete rebuilds.

If you experience extreme increases in build time, please file an issue at https://go.dev/issue/new.

Note: In Go 1.20, profile parsing adds significant overhead, particularly for large profiles, which can significantly increase build times. This is tracked by https://go.dev/issue/58102 and will be addressed in a future release.

How does PGO affect binary size?

PGO can result in slightly larger binaries due to additional function inlining.

Appendix: alternative profile sources

CPU profiles generated by the Go runtime (via runtime/pprof, etc) are already in the correct format for direct use as PGO inputs. However, organizations may have alternative preferred tooling (e.g., Linux perf), or existing fleet-wide continuous profiling systems which they wish to use with Go PGO.

Profiles from alternative source may be used with Go PGO if converted to the pprof format, provided they follow these general requirements:

  • One of the sample indices should have type/unit “samples”/“count” or “cpu”/“nanoseconds”.

  • Samples should represent samples of CPU time at the sample location.

  • The profile must be symbolized (Function.name must be set).

  • Samples must contain stack frames for inlined functions. If inlined functions are omitted, Go will not be able to maintain iterative stability.

  • Function.start_line must be set. This is the line number of the start of the function. i.e., the line containing the func keyword. The Go compiler uses this field to compute line offsets of samples (Location.Line.line - Function.start_line). Note that many existing pprof converters omit this field.

已知问题

Note: In Go 1.20, DWARF metadata omits function start lines (DW_AT_decl_line), which may make it difficult for tools to determine the start line. This is tracked by https://go.dev/issue/57308, and is expected to be fixed in Go 1.21.

Changes to the language 语言变化

There is only one small change to the language, a very small correction to the scope of type parameters in method declarations. Existing programs are unaffected.

Memory Model 修改后和其他语言保持一致

The Go memory model has been revised to align Go with the memory model used by C, C++, Java, JavaScript, Rust, and Swift. Go only provides sequentially consistent atomics, not any of the more relaxed forms found in other languages. Along with the memory model update, Go 1.19 introduces new types in the sync/atomic package that make it easier to use atomic values, such as atomic.Int64 and atomic.Pointer[T].

Ports 增加对龙芯的支持

LoongArch 64-bit Go 1.19 adds support for the Loongson 64-bit architecture LoongArch on Linux (GOOS=linux, GOARCH=loong64). The implemented ABI is LP64D. Minimum kernel version supported is 5.19.

Note that most existing commercial Linux distributions for LoongArch come with older kernels, with a historical incompatible system call ABI. Compiled binaries will not work on these systems, even if statically linked. Users on such unsupported systems are limited to the distribution-provided Go package.

RISC-V The riscv64 port now supports passing function arguments and result using registers. Benchmarking shows typical performance improvements of 10% or more on riscv64.

Tools 工具

Doc Comments

Go 1.19 adds support for links, lists, and clearer headings in doc comments. As part of this change, gofmt now reformats doc comments to make their rendered meaning clearer. See “Go Doc Comments” for syntax details and descriptions of common mistakes now highlighted by gofmt. As another part of this change, the new package go/doc/comment provides parsing and reformatting of doc comments as well as support for rendering them to HTML, Markdown, and text.

New unix build constraint

The build constraint unix is now recognized in //go:build lines. The constraint is satisfied if the target operating system, also known as GOOS, is a Unix or Unix-like system. For the 1.19 release it is satisfied if GOOS is one of aix, android, darwin, dragonfly, freebsd, hurd, illumos, ios, linux, netbsd, openbsd, or solaris. In future releases the unix constraint may match additional newly supported operating systems.

Go command

The -trimpath flag, if set, is now included in the build settings stamped into Go binaries by go build, and can be examined using go version -m or debug.ReadBuildInfo.

go generate now sets the GOROOT environment variable explicitly in the generator's environment, so that generators can locate the correct GOROOT even if built with -trimpath.

go test and go generate now place GOROOT/bin at the beginning of the PATH used for the subprocess, so tests and generators that execute the go command will resolve it to same GOROOT.

go env now quotes entries that contain spaces in the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS, CGO_LDFLAGS, and GOGCCFLAGS variables it reports.

go list -json now accepts a comma-separated list of JSON fields to populate. If a list is specified, the JSON output will include only those fields, and go list may avoid work to compute fields that are not included. In some cases, this may suppress errors that would otherwise be reported.

The go command now caches information necessary to load some modules, which should result in a speed-up of some go list invocations.

Vet

The vet checker “errorsas” now reports when errors.As is called with a second argument of type *error, a common mistake.

Runtime 运行时

增加软内存限制 The runtime now includes support for a soft memory limit. This memory limit includes the Go heap and all other memory managed by the runtime, and excludes external memory sources such as mappings of the binary itself, memory managed in other languages, and memory held by the operating system on behalf of the Go program. This limit may be managed via runtime/debug.SetMemoryLimit or the equivalent GOMEMLIMIT environment variable. The limit works in conjunction with runtime/debug.SetGCPercent / GOGC, and will be respected even if GOGC=off, allowing Go programs to always make maximal use of their memory limit, improving resource efficiency in some cases. See the GC guide for a detailed guide explaining the soft memory limit in more detail, as well as a variety of common use-cases and scenarios. Please note that small memory limits, on the order of tens of megabytes or less, are less likely to be respected due to external latency factors, such as OS scheduling. See issue 52433 for more details. Larger memory limits, on the order of hundreds of megabytes or more, are stable and production-ready.

In order to limit the effects of GC thrashing when the program's live heap size approaches the soft memory limit, the Go runtime also attempts to limit total GC CPU utilization to 50%, excluding idle time, choosing to use more memory over preventing application progress. In practice, we expect this limit to only play a role in exceptional cases, and the new runtime metric /gc/limiter/last-enabled:gc-cycle reports when this last occurred.

The runtime now schedules many fewer GC worker goroutines on idle operating system threads when the application is idle enough to force a periodic GC cycle.

The runtime will now allocate initial goroutine stacks based on the historic average stack usage of goroutines. This avoids some of the early stack growth and copying needed in the average case in exchange for at most 2x wasted space on below-average goroutines.

On Unix operating systems, Go programs that import package os now automatically increase the open file limit (RLIMIT_NOFILE) to the maximum allowed value; that is, they change the soft limit to match the hard limit. This corrects artificially low limits set on some systems for compatibility with very old C programs using the select system call. Go programs are not helped by that limit, and instead even simple programs like gofmt often ran out of file descriptors on such systems when processing many files in parallel. One impact of this change is that Go programs that in turn execute very old C programs in child processes may run those programs with too high a limit. This can be corrected by setting the hard limit before invoking the Go program.

Unrecoverable fatal errors (such as concurrent map writes, or unlock of unlocked mutexes) now print a simpler traceback excluding runtime metadata (equivalent to a fatal panic) unless GOTRACEBACK=system or crash. Runtime-internal fatal error tracebacks always include full metadata regardless of the value of GOTRACEBACK

Support for debugger-injected function calls has been added on ARM64, enabling users to call functions from their binary in an interactive debugging session when using a debugger that is updated to make use of this functionality.

The address sanitizer support added in Go 1.18 now handles function arguments and global variables more precisely.

Compiler 编译器

The compiler now uses a jump table to implement large integer and string switch statements. Performance improvements for the switch statement vary but can be on the order of 20% faster. (GOARCH=amd64 and GOARCH=arm64 only)

The Go compiler now requires the -p=importpath flag to build a linkable object file. This is already supplied by the go command and by Bazel. Any other build systems that invoke the Go compiler directly will need to make sure they pass this flag as well.

The Go compiler no longer accepts the -importmap flag. Build systems that invoke the Go compiler directly must use the -importcfg flag instead.

Assembler 汇编器

Like the compiler, the assembler now requires the -p=importpath flag to build a linkable object file. This is already supplied by the go command. Any other build systems that invoke the Go assembler directly will need to make sure they pass this flag as well.

Linker 连接器

On ELF platforms, the linker now emits compressed DWARF sections in the standard gABI format (SHF_COMPRESSED), instead of the legacy .zdebug format.

Core library 核心库

New atomic types

The sync/atomic package defines new atomic types Bool, Int32, Int64, Uint32, Uint64, Uintptr, and Pointer. These types hide the underlying values so that all accesses are forced to use the atomic APIs. Pointer also avoids the need to convert to unsafe.Pointer at call sites. Int64 and Uint64 are automatically aligned to 64-bit boundaries in structs and allocated data, even on 32-bit systems.

PATH lookups

Command and LookPath no longer allow results from a PATH search to be found relative to the current directory. This removes a common source of security problems but may also break existing programs that depend on using, say, exec.Command("prog") to run a binary named prog (or, on Windows, prog.exe) in the current directory. See the os/exec package documentation for information about how best to update such programs.

On Windows, Command and LookPath now respect the NoDefaultCurrentDirectoryInExePath environment variable, making it possible to disable the default implicit search of “.” in PATH lookups on Windows systems.

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind. There are also various performance improvements, not enumerated here.

archive/zip

Reader now ignores non-ZIP data at the start of a ZIP file, matching most other implementations. This is necessary to read some Java JAR files, among other uses.

crypto/elliptic

Operating on invalid curve points (those for which the IsOnCurve method returns false, and which are never returned by Unmarshal or by a Curve method operating on a valid point) has always been undefined behavior and can lead to key recovery attacks. If an invalid point is supplied to Marshal, MarshalCompressed, Add, Double, or ScalarMult, they will now panic.

ScalarBaseMult operations on the P224, P384, and P521 curves are now up to three times faster, leading to similar speedups in some ECDSA operations. The generic (not platform optimized) P256 implementation was replaced with one derived from a formally verified model; this might lead to significant slowdowns on 32-bit platforms.

crypto/rand

Read no longer buffers random data obtained from the operating system between calls. Applications that perform many small reads at high frequency might choose to wrap Reader in a bufio.Reader for performance reasons, taking care to use io.ReadFull to ensure no partial reads occur.

On Plan 9, Read has been reimplemented, replacing the ANSI X9.31 algorithm with a fast key erasure generator.

The Prime implementation was changed to use only rejection sampling, which removes a bias when generating small primes in non-cryptographic contexts, removes one possible minor timing leak, and better aligns the behavior with BoringSSL, all while simplifying the implementation. The change does produce different outputs for a given random source stream compared to the previous implementation, which can break tests written expecting specific results from specific deterministic random sources. To help prevent such problems in the future, the implementation is now intentionally non-deterministic with respect to the input stream.

crypto/tls

The GODEBUG option tls10default=1 has been removed. It is still possible to enable TLS 1.0 client-side by setting Config.MinVersion.

The TLS server and client now reject duplicate extensions in TLS handshakes, as required by RFC 5246, Section 7.4.1.4 and RFC 8446, Section 4.2.

crypto/x509

CreateCertificate no longer supports creating certificates with SignatureAlgorithm set to MD5WithRSA.

CreateCertificate no longer accepts negative serial numbers.

CreateCertificate will not emit an empty SEQUENCE anymore when the produced certificate has no extensions.

Removal of the GODEBUG optionx509sha1=1, originally planned for Go 1.19, has been rescheduled to a future release. Applications using it should work on migrating. Practical attacks against SHA-1 have been demonstrated since 2017 and publicly trusted Certificate Authorities have not issued SHA-1 certificates since 2015.

ParseCertificate and ParseCertificateRequest now reject certificates and CSRs which contain duplicate extensions.

The new CertPool.Clone and CertPool.Equal methods allow cloning a CertPool and checking the equivalence of two CertPools respectively.

The new function ParseRevocationList provides a faster, safer to use CRL parser which returns a RevocationList. Parsing a CRL also populates the new RevocationList fields RawIssuer, Signature, AuthorityKeyId, and Extensions, which are ignored by CreateRevocationList.

The new method RevocationList.CheckSignatureFrom checks that the signature on a CRL is a valid signature from a Certificate.

The ParseCRL and ParseDERCRL functions are now deprecated in favor of ParseRevocationList. The Certificate.CheckCRLSignature method is deprecated in favor of RevocationList.CheckSignatureFrom.

The path builder of Certificate.Verify was overhauled and should now produce better chains and/or be more efficient in complicated scenarios. Name constraints are now also enforced on non-leaf certificates.

crypto/x509/pkix

The types CertificateList and TBSCertificateList have been deprecated. The new crypto/x509 CRL functionality should be used instead.

debug/elf

The new EM_LOONGARCH and R_LARCH_* constants support the loong64 port.

debug/pe

The new File.COFFSymbolReadSectionDefAux method, which returns a COFFSymbolAuxFormat5, provides access to COMDAT information in PE file sections. These are supported by new IMAGE_COMDAT_* and IMAGE_SCN_* constants.

encoding/binary

The new interface AppendByteOrder provides efficient methods for appending a uint16, uint32, or uint64 to a byte slice. BigEndian and LittleEndian now implement this interface.

Similarly, the new functions AppendUvarint and AppendVarint are efficient appending versions of PutUvarint and PutVarint.

encoding/csv

The new method Reader.InputOffset reports the reader's current input position as a byte offset, analogous to encoding/json's Decoder.InputOffset.

encoding/xml

The new method Decoder.InputPos reports the reader's current input position as a line and column, analogous to encoding/csv's Decoder.FieldPos.

flag

The new function TextVar defines a flag with a value implementing encoding.TextUnmarshaler, allowing command-line flag variables to have types such as big.Int, netip.Addr, and time.Time.

fmt

The new functions Append, Appendf, and Appendln append formatted data to byte slices.

go/parser

The parser now recognizes ~x as a unary expression with operator token.TILDE, allowing better error recovery when a type constraint such as ~int is used in an incorrect context.

go/types

The new methods Func.Origin and Var.Origin return the corresponding Object of the generic type for synthetic Func and Var objects created during type instantiation.

It is no longer possible to produce an infinite number of distinct-but-identical Named type instantiations via recursive calls to Named.Underlying or Named.Method.

hash/maphash

The new functions Bytes and String provide an efficient way hash a single byte slice or string. They are equivalent to using the more general Hash with a single write, but they avoid setup overhead for small inputs.

html/template

The type FuncMap is now an alias for text/template's FuncMap instead of its own named type. This allows writing code that operates on a FuncMap from either setting.

Go 1.19.8 and later disallow actions in ECMAScript 6 template literals. This behavior can be reverted by the GODEBUG=jstmpllitinterp=1 setting.

image/draw

Draw with the Src operator preserves non-premultiplied-alpha colors when destination and source images are both image.NRGBA or both image.NRGBA64. This reverts a behavior change accidentally introduced by a Go 1.18 library optimization; the code now matches the behavior in Go 1.17 and earlier.

io

NopCloser's result now implements WriterTo whenever its input does.

MultiReader's result now implements WriterTo unconditionally. If any underlying reader does not implement WriterTo, it is simulated appropriately.

mime

On Windows only, the mime package now ignores a registry entry recording that the extension .js should have MIME type text/plain. This is a common unintentional misconfiguration on Windows systems. The effect is that .js will have the default MIME type text/javascript; charset=utf-8. Applications that expect text/plain on Windows must now explicitly call AddExtensionType.

mime/multipart

In Go 1.19.8 and later, this package sets limits the size of the MIME data it processes to protect against malicious inputs. Reader.NextPart and Reader.NextRawPart limit the number of headers in a part to 10000 and Reader.ReadForm limits the total number of headers in all FileHeaders to 10000. These limits may be adjusted with the GODEBUG=multipartmaxheaders setting. Reader.ReadForm further limits the number of parts in a form to 1000. This limit may be adjusted with the GODEBUG=multipartmaxparts setting.

net

The pure Go resolver will now use EDNS(0) to include a suggested maximum reply packet length, permitting reply packets to contain up to 1232 bytes (the previous maximum was 512). In the unlikely event that this causes problems with a local DNS resolver, setting the environment variable GODEBUG=netdns=cgo to use the cgo-based resolver should work. Please report any such problems on the issue tracker.

When a net package function or method returns an "I/O timeout" error, the error will now satisfy errors.Is(err, context.DeadlineExceeded). When a net package function returns an "operation was canceled" error, the error will now satisfy errors.Is(err, context.Canceled). These changes are intended to make it easier for code to test for cases in which a context cancellation or timeout causes a net package function or method to return an error, while preserving backward compatibility for error messages.

Resolver.PreferGo is now implemented on Windows and Plan 9. It previously only worked on Unix platforms. Combined with Dialer.Resolver and Resolver.Dial, it's now possible to write portable programs and be in control of all DNS name lookups when dialing.

The net package now has initial support for the netgo build tag on Windows. When used, the package uses the Go DNS client (as used by Resolver.PreferGo) instead of asking Windows for DNS results. The upstream DNS server it discovers from Windows may not yet be correct with complex system network configurations, however.

net/http

ResponseWriter.WriteHeader now supports sending user-defined 1xx informational headers.

The io.ReadCloser returned by MaxBytesReader will now return the defined error type MaxBytesError when its read limit is exceeded.

The HTTP client will handle a 3xx response without a Location header by returning it to the caller, rather than treating it as an error.

net/url

The new JoinPath function and URL.JoinPath method create a new URL by joining a list of path elements.

The URL type now distinguishes between URLs with no authority and URLs with an empty authority. For example, http:///path has an empty authority (host), while http:/path has none.

The new URL field OmitHost is set to true when a URL has an empty authority.

os/exec

A Cmd with a non-empty Dir field and nil Env now implicitly sets the PWD environment variable for the subprocess to match Dir.

The new method Cmd.Environ reports the environment that would be used to run the command, including the implicitly set PWD variable.

reflect

The method Value.Bytes now accepts addressable arrays in addition to slices.

The methods Value.Len and Value.Cap now successfully operate on a pointer to an array and return the length of that array, to match what the builtin len and cap functions do.

regexp/syntax

Go 1.18 release candidate 1, Go 1.17.8, and Go 1.16.15 included a security fix to the regular expression parser, making it reject very deeply nested expressions. Because Go patch releases do not introduce new API, the parser returned syntax.ErrInternalError in this case. Go 1.19 adds a more specific error, syntax.ErrNestingDepth, which the parser now returns instead.

runtime

The GOROOT function now returns the empty string (instead of "go") when the binary was built with the -trimpath flag set and the GOROOT variable is not set in the process environment.

runtime/metrics

The new /sched/gomaxprocs:threads metric reports the current runtime.GOMAXPROCS value.

The new /cgo/go-to-c-calls:calls metric reports the total number of calls made from Go to C. This metric is identical to the runtime.NumCgoCall function.

The new /gc/limiter/last-enabled:gc-cycle metric reports the last GC cycle when the GC CPU limiter was enabled. See the runtime notes for details about the GC CPU limiter.

runtime/pprof

Stop-the-world pause times have been significantly reduced when collecting goroutine profiles, reducing the overall latency impact to the application.

MaxRSS is now reported in heap profiles for all Unix operating systems (it was previously only reported for GOOS=android, darwin, ios, and linux).

runtime/race race

The race detector has been upgraded to use thread sanitizer version v3 on all supported platforms except windows/amd64 and openbsd/amd64, which remain on v2. Compared to v2, it is now typically 1.5x to 2x faster, uses half as much memory, and it supports an unlimited number of goroutines. On Linux, the race detector now requires at least glibc version 2.17 and GNU binutils 2.26.

The race detector is now supported on GOARCH=s390x.

Race detector support for openbsd/amd64 has been removed from thread sanitizer upstream, so it is unlikely to ever be updated from v2.

runtime/trace

When tracing and the CPU profiler are enabled simultaneously, the execution trace includes CPU profile samples as instantaneous events.

sort

The sorting algorithm has been rewritten to use pattern-defeating quicksort, which is faster for several common scenarios.

The new function Find is like Search but often easier to use: it returns an additional boolean reporting whether an equal value was found.

strconv

Quote and related functions now quote the rune U+007F as \x7f, not \u007f, for consistency with other ASCII values.

syscall

On PowerPC (GOARCH=ppc64, ppc64le), Syscall, Syscall6, RawSyscall, and RawSyscall6 now always return 0 for return value r2 instead of an undefined value.

On AIX and Solaris, Getrusage is now defined.

time

The new method Duration.Abs provides a convenient and safe way to take the absolute value of a duration, converting −2⁶³ to 2⁶³−1. (This boundary case can happen as the result of subtracting a recent time from the zero time.)

The new method Time.ZoneBounds returns the start and end times of the time zone in effect at a given time. It can be used in a loop to enumerate all the known time zone transitions at a given location.

Changes to the language 语言变化

Generics 泛型

Go 1.18 includes an implementation of generic features as described by the Type Parameters Proposal. This includes major - but fully backward-compatible - changes to the language.

These new language changes required a large amount of new code that has not had significant testing in production settings. That will only happen as more people write and use generic code. We believe that this feature is well implemented and high quality. However, unlike most aspects of Go, we can't back up that belief with real world experience. Therefore, while we encourage the use of generics where it makes sense, please use appropriate caution when deploying generic code in production.

While we believe that the new language features are well designed and clearly specified, it is possible that we have made mistakes. We want to stress that the Go 1 compatibility guarantee says "If it becomes necessary to address an inconsistency or incompleteness in the specification, resolving the issue could affect the meaning or legality of existing programs. We reserve the right to address such issues, including updating the implementations." It also says "If a compiler or library has a bug that violates the specification, a program that depends on the buggy behavior may break if the bug is fixed. We reserve the right to fix such bugs." In other words, it is possible that there will be code using generics that will work with the 1.18 release but break in later releases. We do not plan or expect to make any such change. However, breaking 1.18 programs in future releases may become necessary for reasons that we cannot today foresee. We will minimize any such breakage as much as possible, but we can't guarantee that the breakage will be zero.

The following is a list of the most visible changes. For a more comprehensive overview, see the proposal. For details see the language spec.

The syntax for function and type declarations now accepts type parameters. Parameterized functions and types can be instantiated by following them with a list of type arguments in square brackets. The new token ~ has been added to the set of operators and punctuation. The syntax for Interface types now permits the embedding of arbitrary types (not just type names of interfaces) as well as union and ~T type elements. Such interfaces may only be used as type constraints. An interface now defines a set of types as well as a set of methods. The new predeclared identifier any is an alias for the empty interface. It may be used instead of interface{}. The new predeclared identifier comparable is an interface that denotes the set of all types which can be compared using == or !=. It may only be used as (or embedded in) a type constraint. There are three experimental packages using generics that may be useful. These packages are in x/exp repository; their API is not covered by the Go 1 guarantee and may change as we gain more experience with generics.

golang.org/x/exp/constraints Constraints that are useful for generic code, such as constraints.Ordered.

golang.org/x/exp/slices A collection of generic functions that operate on slices of any element type.

golang.org/x/exp/maps A collection of generic functions that operate on maps of any key or element type.

The current generics implementation has the following known limitations:

The Go compiler cannot handle type declarations inside generic functions or methods. We hope to provide support for this feature in a future release. The Go compiler does not accept arguments of type parameter type with the predeclared functions real, imag, and complex. We hope to remove this restriction in a future release. The Go compiler only supports calling a method m on a value x of type parameter type P if m is explicitly declared by P's constraint interface. Similarly, method values x.m and method expressions P.m also are only supported if m is explicitly declared by P, even though m might be in the method set of P by virtue of the fact that all types in P implement m. We hope to remove this restriction in a future release. The Go compiler does not support accessing a struct field x.f where x is of type parameter type even if all types in the type parameter's type set have a field f. We may remove this restriction in a future release. Embedding a type parameter, or a pointer to a type parameter, as an unnamed field in a struct type is not permitted. Similarly, embedding a type parameter in an interface type is not permitted. Whether these will ever be permitted is unclear at present. A union element with more than one term may not contain an interface type with a non-empty method set. Whether this will ever be permitted is unclear at present. Generics also represent a large change for the Go ecosystem. While we have updated several core tools with generics support, there is much more to do. It will take time for remaining tools, documentation, and libraries to catch up with these language changes.

bug修复

The Go 1.18 compiler now correctly reports declared but not used errors for variables that are set inside a function literal but are never used. Before Go 1.18, the compiler did not report an error in such cases. This fixes long-outstanding compiler issue #8560. As a result of this change, (possibly incorrect) programs may not compile anymore. The necessary fix is straightforward: fix the program if it was in fact incorrect, or use the offending variable, for instance by assigning it to the blank identifier _. Since go vet always pointed out this error, the number of affected programs is likely very small.

The Go 1.18 compiler now reports an overflow when passing a rune constant expression such as '1' << 32 as an argument to the predeclared functions print and println, consistent with the behavior of user-defined functions. Before Go 1.18, the compiler did not report an error in such cases but silently accepted such constant arguments if they fit into an int64. As a result of this change, (possibly incorrect) programs may not compile anymore. The necessary fix is straightforward: fix the program if it was in fact incorrect, or explicitly convert the offending argument to the correct type. Since go vet always pointed out this error, the number of affected programs is likely very small.

Ports

AMD64

Go 1.18 introduces the new GOAMD64 environment variable, which selects at compile time a minimum target version of the AMD64 architecture. Allowed values are v1, v2, v3, or v4. Each higher level requires, and takes advantage of, additional processor features. A detailed description can be found here.

The GOAMD64 environment variable defaults to v1.

RISC-V

The 64-bit RISC-V architecture on Linux (the linux/riscv64 port) now supports the c-archive and c-shared build modes.

Linux

Go 1.18 requires Linux kernel version 2.6.32 or later.

Windows

The windows/arm and windows/arm64 ports now support non-cooperative preemption, bringing that capability to all four Windows ports, which should hopefully address subtle bugs encountered when calling into Win32 functions that block for extended periods of time.

iOS

On iOS (the ios/arm64 port) and iOS simulator running on AMD64-based macOS (the ios/amd64 port), Go 1.18 now requires iOS 12 or later; support for previous versions has been discontinued.

FreeBSD

Go 1.18 is the last release that is supported on FreeBSD 11.x, which has already reached end-of-life. Go 1.19 will require FreeBSD 12.2+ or FreeBSD 13.0+. FreeBSD 13.0+ will require a kernel with the COMPAT_FREEBSD12 option set (this is the default).

Tools 工具

  • Fuzzing Go 1.18 includes an implementation of fuzzing as described by the fuzzing proposal.

See the fuzzing landing page to get started.

Please be aware that fuzzing can consume a lot of memory and may impact your machine’s performance while it runs. Also be aware that the fuzzing engine writes values that expand test coverage to a fuzz cache directory within $GOCACHE/fuzz while it runs. There is currently no limit to the number of files or total bytes that may be written to the fuzz cache, so it may occupy a large amount of storage (possibly several GBs).

  • Go command go get go get no longer builds or installs packages in module-aware mode. go get is now dedicated to adjusting dependencies in go.mod. Effectively, the -d flag is always enabled. To install the latest version of an executable outside the context of the current module, use go install example.com/cmd@latest. Any version query may be used instead of latest. This form of go install was added in Go 1.16, so projects supporting older versions may need to provide install instructions for both go install and go get. go get now reports an error when used outside a module, since there is no go.mod file to update. In GOPATH mode (with GO111MODULE=off), go get still builds and installs packages, as before.

Automatic go.mod and go.sum updates The go mod graph, go mod vendor, go mod verify, and go mod why subcommands no longer automatically update the go.mod and go.sum files. (Those files can be updated explicitly using go get, go mod tidy, or go mod download.)

go version The go command now embeds version control information in binaries. It includes the currently checked-out revision, commit time, and a flag indicating whether edited or untracked files are present. Version control information is embedded if the go command is invoked in a directory within a Git, Mercurial, Fossil, or Bazaar repository, and the main package and its containing main module are in the same repository. This information may be omitted using the flag -buildvcs=false.

Additionally, the go command embeds information about the build, including build and tool tags (set with -tags), compiler, assembler, and linker flags (like -gcflags), whether cgo was enabled, and if it was, the values of the cgo environment variables (like CGO_CFLAGS). Both VCS and build information may be read together with module information using go version -m file or runtime/debug.ReadBuildInfo (for the currently running binary) or the new debug/buildinfo package.

The underlying data format of the embedded build information can change with new go releases, so an older version of go may not handle the build information produced with a newer version of go. To read the version information from a binary built with go 1.18, use the go version command and the debug/buildinfo package from go 1.18+.

go mod download If the main module's go.mod file specifies go 1.17 or higher, go mod download without arguments now downloads source code for only the modules explicitly required in the main module's go.mod file. (In a go 1.17 or higher module, that set already includes all dependencies needed to build the packages and tests in the main module.) To also download source code for transitive dependencies, use go mod download all.

go mod vendor The go mod vendor subcommand now supports a -o flag to set the output directory. (Other go commands still read from the vendor directory at the module root when loading packages with -mod=vendor, so the main use for this flag is for third-party tools that need to collect package source code.)

go mod tidy The go mod tidy command now retains additional checksums in the go.sum file for modules whose source code is needed to verify that each imported package is provided by only one module in the build list. Because this condition is rare and failure to apply it results in a build error, this change is not conditioned on the go version in the main module's go.mod file.

go work The go command now supports a "Workspace" mode. If a go.work file is found in the working directory or a parent directory, or one is specified using the GOWORK environment variable, it will put the go command into workspace mode. In workspace mode, the go.work file will be used to determine the set of main modules used as the roots for module resolution, instead of using the normally-found go.mod file to specify the single main module. For more information see the go work documentation.

go build -asan The go build command and related commands now support an -asan flag that enables interoperation with C (or C++) code compiled with the address sanitizer (C compiler option -fsanitize=address).

go test The go command now supports additional command line options for the new fuzzing support described above:

go test supports -fuzz, -fuzztime, and -fuzzminimizetime options. For documentation on these see go help testflag. go clean supports a -fuzzcache option. For documentation see go help clean. //go:build lines Go 1.17 introduced //go:build lines as a more readable way to write build constraints, instead of // +build lines. As of Go 1.17, gofmt adds //go:build lines to match existing +build lines and keeps them in sync, while go vet diagnoses when they are out of sync.

Since the release of Go 1.18 marks the end of support for Go 1.16, all supported versions of Go now understand //go:build lines. In Go 1.18, go fix now removes the now-obsolete // +build lines in modules declaring go 1.18 or later in their go.mod files.

For more information, see https://go.dev/design/draft-gobuild.

Gofmt gofmt now reads and formats input files concurrently, with a memory limit proportional to GOMAXPROCS. On a machine with multiple CPUs, gofmt should now be significantly faster.

Vet Updates for Generics The vet tool is updated to support generic code. In most cases, it reports an error in generic code whenever it would report an error in the equivalent non-generic code after substituting for type parameters with a type from their type set. For example, vet reports a format error in

func Print[T ~int|~string](t T) { fmt.Printf("%d", t) } because it would report a format error in the non-generic equivalent of Print[string]: func PrintString(x string) { fmt.Printf("%d", x) } Precision improvements for existing checkers The cmd/vet checkers copylock, printf, sortslice, testinggoroutine, and tests have all had moderate precision improvements to handle additional code patterns. This may lead to newly reported errors in existing packages. For example, the printf checker now tracks formatting strings created by concatenating string constants. So vet will report an error in:

// fmt.Printf formatting directive %d is being passed to Println. fmt.Println("%d"+ ≡ x (mod 2)+"\n", x%2)

Runtime 运行时

The garbage collector now includes non-heap sources of garbage collector work (e.g., stack scanning) when determining how frequently to run. As a result, garbage collector overhead is more predictable when these sources are significant. For most applications these changes will be negligible; however, some Go applications may now use less memory and spend more time on garbage collection, or vice versa, than before. The intended workaround is to tweak GOGC where necessary.

The runtime now returns memory to the operating system more efficiently and has been tuned to work more aggressively as a result.

Go 1.17 generally improved the formatting of arguments in stack traces, but could print inaccurate values for arguments passed in registers. This is improved in Go 1.18 by printing a question mark (?) after each value that may be inaccurate.

The built-in function append now uses a slightly different formula when deciding how much to grow a slice when it must allocate a new underlying array. The new formula is less prone to sudden transitions in allocation behavior.

Compiler 编译器

Go 1.17 implemented a new way of passing function arguments and results using registers instead of the stack on 64-bit x86 architecture on selected operating systems. Go 1.18 expands the supported platforms to include 64-bit ARM (GOARCH=arm64), big- and little-endian 64-bit PowerPC (GOARCH=ppc64, ppc64le), as well as 64-bit x86 architecture (GOARCH=amd64) on all operating systems. On 64-bit ARM and 64-bit PowerPC systems, benchmarking shows typical performance improvements of 10% or more.

As mentioned in the Go 1.17 release notes, this change does not affect the functionality of any safe Go code and is designed to have no impact on most assembly code. See the Go 1.17 release notes for more details.

The compiler now can inline functions that contain range loops or labeled for loops.

The new -asan compiler option supports the new go command -asan option.

Because the compiler's type checker was replaced in its entirety to support generics, some error messages now may use different wording than before. In some cases, pre-Go 1.18 error messages provided more detail or were phrased in a more helpful way. We intend to address these cases in Go 1.19.

Because of changes in the compiler related to supporting generics, the Go 1.18 compile speed can be roughly 15% slower than the Go 1.17 compile speed. The execution time of the compiled code is not affected. We intend to improve the speed of the compiler in future releases.

Linker 连接器

The linker emits far fewer relocations. As a result, most codebases will link faster, require less memory to link, and generate smaller binaries. Tools that process Go binaries should use Go 1.18's debug/gosym package to transparently handle both old and new binaries.

The new -asan linker option supports the new go command -asan option.

Bootstrap 启动器

When building a Go release from source and GOROOT_BOOTSTRAP is not set, previous versions of Go looked for a Go 1.4 or later bootstrap toolchain in the directory $HOME/go1.4 (%HOMEDRIVE%%HOMEPATH%\go1.4 on Windows). Go now looks first for $HOME/go1.17 or $HOME/sdk/go1.17 before falling back to $HOME/go1.4. We intend for Go 1.19 to require Go 1.17 or later for bootstrap, and this change should make the transition smoother. For more details, see go.dev/issue/44505.

Core library 核心类库

New debug/buildinfo package

The new debug/buildinfo package provides access to module versions, version control information, and build flags embedded in executable files built by the go command. The same information is also available via runtime/debug.ReadBuildInfo for the currently running binary and via go version -m on the command line.

New net/netip package

The new net/netip package defines a new IP address type, Addr. Compared to the existing net.IP type, the netip.Addr type takes less memory, is immutable, and is comparable so it supports == and can be used as a map key.

In addition to Addr, the package defines AddrPort, representing an IP and port, and Prefix, representing a network CIDR prefix.

The package also defines several functions to create and examine these new types: AddrFrom4, AddrFrom16, AddrFromSlice, AddrPortFrom, IPv4Unspecified, IPv6LinkLocalAllNodes, IPv6Unspecified, MustParseAddr, MustParseAddrPort, MustParsePrefix, ParseAddr, ParseAddrPort, ParsePrefix, PrefixFrom.

The net package includes new methods that parallel existing methods, but return netip.AddrPort instead of the heavier-weight net.IP or *net.UDPAddr types: Resolver.LookupNetIP, UDPConn.ReadFromUDPAddrPort, UDPConn.ReadMsgUDPAddrPort, UDPConn.WriteToUDPAddrPort, UDPConn.WriteMsgUDPAddrPort. The new UDPConn methods support allocation-free I/O.

The net package also now includes functions and methods to convert between the existing TCPAddr/UDPAddr types and netip.AddrPort: TCPAddrFromAddrPort, UDPAddrFromAddrPort, TCPAddr.AddrPort, UDPAddr.AddrPort.

TLS 1.0 and 1.1 disabled by default client-side

If Config.MinVersion is not set, it now defaults to TLS 1.2 for client connections. Any safely up-to-date server is expected to support TLS 1.2, and browsers have required it since 2020. TLS 1.0 and 1.1 are still supported by setting Config.MinVersion to VersionTLS10. The server-side default is unchanged at TLS 1.0.

The default can be temporarily reverted to TLS 1.0 by setting the GODEBUG=tls10default=1 environment variable. This option will be removed in Go 1.19.

Rejecting SHA-1 certificates

crypto/x509 will now reject certificates signed with the SHA-1 hash function. This doesn't apply to self-signed root certificates. Practical attacks against SHA-1 have been demonstrated since 2017 and publicly trusted Certificate Authorities have not issued SHA-1 certificates since 2015.

This can be temporarily reverted by setting the GODEBUG=x509sha1=1 environment variable. This option will be removed in a future release.

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind.

bufio

The new Writer.AvailableBuffer method returns an empty buffer with a possibly non-empty capacity for use with append-like APIs. After appending, the buffer can be provided to a succeeding Write call and possibly avoid any copying.

The Reader.Reset and Writer.Reset methods now use the default buffer size when called on objects with a nil buffer.

bytes

The new Cut function slices a []byte around a separator. It can replace and simplify many common uses of Index, IndexByte, IndexRune, and SplitN.

Trim, TrimLeft, and TrimRight are now allocation free and, especially for small ASCII cutsets, up to 10 times faster.

The Title function is now deprecated. It doesn't handle Unicode punctuation and language-specific capitalization rules, and is superseded by the golang.org/x/text/cases package.

crypto/elliptic

The P224, P384, and P521 curve implementations are now all backed by code generated by the addchain and fiat-crypto projects, the latter of which is based on a formally-verified model of the arithmetic operations. They now use safer complete formulas and internal APIs. P-224 and P-384 are now approximately four times faster. All specific curve implementations are now constant-time.

Operating on invalid curve points (those for which the IsOnCurve method returns false, and which are never returned by Unmarshal or a Curve method operating on a valid point) has always been undefined behavior, can lead to key recovery attacks, and is now unsupported by the new backend. If an invalid point is supplied to a P224, P384, or P521 method, that method will now return a random point. The behavior might change to an explicit panic in a future release.

crypto/tls

The new Conn.NetConn method allows access to the underlying net.Conn.

crypto/x509 Certificate.Verify now uses platform APIs to verify certificate validity on macOS and iOS when it is called with a nil VerifyOpts.Roots or when using the root pool returned from SystemCertPool.

SystemCertPool is now available on Windows.

On Windows, macOS, and iOS, when a CertPool returned by SystemCertPool has additional certificates added to it, Certificate.Verify will do two verifications: one using the platform verifier APIs and the system roots, and one using the Go verifier and the additional roots. Chains returned by the platform verifier APIs will be prioritized.

CertPool.Subjects is deprecated. On Windows, macOS, and iOS the CertPool returned by SystemCertPool will return a pool which does not include system roots in the slice returned by Subjects, as a static list can't appropriately represent the platform policies and might not be available at all from the platform APIs.

Support for signing certificates using signature algorithms that depend on the MD5 hash (MD5WithRSA) may be removed in Go 1.19.

debug/dwarf

The StructField and BasicType structs both now have a DataBitOffset field, which holds the value of the DW_AT_data_bit_offset attribute if present.

debug/elf

The R_PPC64_RELATIVE constant has been added.

debug/plan9obj

The File.Symbols method now returns the new exported error value ErrNoSymbols if the file has no symbol section.

embed

A go:embed directive may now start with all: to include files whose names start with dot or underscore.

go/ast

Per the proposal Additions to go/ast and go/token to support parameterized functions and types the following additions are made to the go/ast package:

the FuncType and TypeSpec nodes have a new field TypeParams to hold type parameters, if any. The new expression node IndexListExpr represents index expressions with multiple indices, used for function and type instantiations with more than one explicit type argument. go/constant The new Kind.String method returns a human-readable name for the receiver kind.

go/token

The new constant TILDE represents the ~ token per the proposal Additions to go/ast and go/token to support parameterized functions and types .

go/types

The new Config.GoVersion field sets the accepted Go language version.

Per the proposal Additions to go/types to support type parameters the following additions are made to the go/types package:

The new type TypeParam, factory function NewTypeParam, and associated methods are added to represent a type parameter. The new type TypeParamList holds a list of type parameters. The new type TypeList holds a list of types. The new factory function NewSignatureType allocates a Signature with (receiver or function) type parameters. To access those type parameters, the Signature type has two new methods Signature.RecvTypeParams and Signature.TypeParams. Named types have four new methods: Named.Origin to get the original parameterized types of instantiated types, Named.TypeArgs and Named.TypeParams to get the type arguments or type parameters of an instantiated or parameterized type, and Named.SetTypeParams to set the type parameters (for instance, when importing a named type where allocation of the named type and setting of type parameters cannot be done simultaneously due to possible cycles). The Interface type has four new methods: Interface.IsComparable and Interface.IsMethodSet to query properties of the type set defined by the interface, and Interface.MarkImplicit and Interface.IsImplicit to set and test whether the interface is an implicit interface around a type constraint literal. The new types Union and Term, factory functions NewUnion and NewTerm, and associated methods are added to represent type sets in interfaces. The new function Instantiate instantiates a parameterized type. The new Info.Instances map records function and type instantiations through the new Instance type. The new type ArgumentError and associated methods are added to represent an error related to a type argument. The new type Context and factory function NewContext are added to facilitate sharing of identical type instances across type-checked packages, via the new Config.Context field. The predicates AssignableTo, ConvertibleTo, Implements, Identical, IdenticalIgnoreTags, and AssertableTo now also work with arguments that are or contain generalized interfaces, i.e. interfaces that may only be used as type constraints in Go code. Note that the behavior of AssignableTo, ConvertibleTo, Implements, and AssertableTo is undefined with arguments that are uninstantiated generic types, and AssertableTo is undefined if the first argument is a generalized interface.

html/template

Within a range pipeline the new {{break}} command will end the loop early and the new {{continue}} command will immediately start the next loop iteration.

The and function no longer always evaluates all arguments; it stops evaluating arguments after the first argument that evaluates to false. Similarly, the or function now stops evaluating arguments after the first argument that evaluates to true. This makes a difference if any of the arguments is a function call.

image/draw

The Draw and DrawMask fallback implementations (used when the arguments are not the most common image types) are now faster when those arguments implement the optional draw.RGBA64Image and image.RGBA64Image interfaces that were added in Go 1.17.

net

net.Error.Temporary has been deprecated.

net/http

On WebAssembly targets, the Dial, DialContext, DialTLS and DialTLSContext method fields in Transport will now be correctly used, if specified, for making HTTP requests.

The new Cookie.Valid method reports whether the cookie is valid.

The new MaxBytesHandler function creates a Handler that wraps its ResponseWriter and Request.Body with a MaxBytesReader.

When looking up a domain name containing non-ASCII characters, the Unicode-to-ASCII conversion is now done in accordance with Nontransitional Processing as defined in the Unicode IDNA Compatibility Processing standard (UTS #46). The interpretation of four distinct runes are changed: ß, ς, zero-width joiner U+200D, and zero-width non-joiner U+200C. Nontransitional Processing is consistent with most applications and web browsers.

os/user

User.GroupIds now uses a Go native implementation when cgo is not available.

reflect

The new Value.SetIterKey and Value.SetIterValue methods set a Value using a map iterator as the source. They are equivalent to Value.Set(iter.Key()) and Value.Set(iter.Value()), but do fewer allocations.

The new Value.UnsafePointer method returns the Value's value as an unsafe.Pointer. This allows callers to migrate from Value.UnsafeAddr and Value.Pointer to eliminate the need to perform uintptr to unsafe.Pointer conversions at the callsite (as unsafe.Pointer rules require).

The new MapIter.Reset method changes its receiver to iterate over a different map. The use of MapIter.Reset allows allocation-free iteration over many maps.

A number of methods ( Value.CanInt, Value.CanUint, Value.CanFloat, Value.CanComplex ) have been added to Value to test if a conversion is safe.

Value.FieldByIndexErr has been added to avoid the panic that occurs in Value.FieldByIndex when stepping through a nil pointer to an embedded struct.

reflect.Ptr and reflect.PtrTo have been renamed to reflect.Pointer and reflect.PointerTo, respectively, for consistency with the rest of the reflect package. The old names will continue to work, but will be deprecated in a future Go release.

regexp

regexp now treats each invalid byte of a UTF-8 string as U+FFFD.

runtime/debug

The BuildInfo struct has two new fields, containing additional information about how the binary was built:

GoVersion holds the version of Go used to build the binary. Settings is a slice of BuildSettings structs holding key/value pairs describing the build. runtime/pprof The CPU profiler now uses per-thread timers on Linux. This increases the maximum CPU usage that a profile can observe, and reduces some forms of bias.

strconv

strconv.Unquote now rejects Unicode surrogate halves.

strings

The new Cut function slices a string around a separator. It can replace and simplify many common uses of Index, IndexByte, IndexRune, and SplitN.

The new Clone function copies the input string without the returned cloned string referencing the input string's memory.

Trim, TrimLeft, and TrimRight are now allocation free and, especially for small ASCII cutsets, up to 10 times faster.

The Title function is now deprecated. It doesn't handle Unicode punctuation and language-specific capitalization rules, and is superseded by the golang.org/x/text/cases package.

sync

The new methods Mutex.TryLock, RWMutex.TryLock, and RWMutex.TryRLock, will acquire the lock if it is not currently held.

syscall

The new function SyscallN has been introduced for Windows, allowing for calls with arbitrary number of arguments. As a result, Syscall, Syscall6, Syscall9, Syscall12, Syscall15, and Syscall18 are deprecated in favor of SyscallN.

SysProcAttr.Pdeathsig is now supported in FreeBSD.

syscall/js

The Wrapper interface has been removed.

testing

The precedence of / in the argument for -run and -bench has been increased. A/B|C/D used to be treated as A/(B|C)/D and is now treated as (A/B)|(C/D).

If the -run option does not select any tests, the -count option is ignored. This could change the behavior of existing tests in the unlikely case that a test changes the set of subtests that are run each time the test function itself is run.

The new testing.F type is used by the new fuzzing support described above. Tests also now support the command line options -test.fuzz, -test.fuzztime, and -test.fuzzminimizetime.

text/template

Within a range pipeline the new {{break}} command will end the loop early and the new {{continue}} command will immediately start the next loop iteration.

The and function no longer always evaluates all arguments; it stops evaluating arguments after the first argument that evaluates to false. Similarly, the or function now stops evaluating arguments after the first argument that evaluates to true. This makes a difference if any of the arguments is a function call.

text/template/parse

The package supports the new text/template and html/template {{break}} command via the new constant NodeBreak and the new type BreakNode, and similarly supports the new {{continue}} command via the new constant NodeContinue and the new type ContinueNode.

unicode/utf8

The new AppendRune function appends the UTF-8 encoding of a rune to a []byte.

doc

这个文档是通过 go test cmd/go -v -run=TestDocsUpToDate -fixdocs 生成的

Usage:

go <command> [arguments]

The commands are:

bug         报告一个bug
build       编译包和依赖
clean       删除object文件和缓存
doc         显示 package or symbol 的文档
env         打印环境信息
fix         修改老的api到新的api
fmt         格式化代码
generate    generate Go files by processing source
get         安装和更新依赖
install     编译和安装
list        显示包和模块
mod         module maintenance
work        workspace maintenance
run         编译和执行go程序
test        执行测试用例
tool        执行特殊的操作
version     打印go版本
vet         报告可能存在的错误

使用 go help <command> 显示某个某个命令的帮助信息

Additional help topics:

buildconstraint 构建约束
buildmode       构建模式
c               calling between Go and C
cache           build and test caching
environment     environment variables
filetype        file types
go.mod          the go.mod file
gopath          GOPATH environment variable
gopath-get      legacy GOPATH go get
goproxy         module proxy protocol
importpath      import path syntax
modules         modules, module versions, and more
module-get      module-aware go get
module-auth     module authentication using go.sum
packages        package lists and patterns
private         configuration for downloading non-public code
testflag        testing flags
testfunc        testing functions
vcs             controlling version control with GOVCS

Use "go help " for more information about that topic.

Start a bug report 给golang 提bug

Usage:

go bug

打开github 提交bug

Compile packages and dependencies 编译 package 和依赖

Usage:

go build [-o output] [build flags] [packages]

Build compiles the packages named by the import paths, along with their dependencies, but it does not install the results.

If the arguments to build are a list of .go files from a single directory, build treats them as a list of source files specifying a single package.

When compiling packages, build ignores files that end in '_test.go'.

When compiling a single main package, build writes the resulting executable to an output file named after the first source file ('go build ed.go rx.go' writes 'ed' or 'ed.exe') or the source code directory ('go build unix/sam' writes 'sam' or 'sam.exe'). The '.exe' suffix is added when writing a Windows executable.

When compiling multiple packages or a single non-main package, build compiles the packages but discards the resulting object, serving only as a check that the packages can be built.

The -o flag forces build to write the resulting executable or object to the named output file or directory, instead of the default behavior described in the last two paragraphs. If the named output is an existing directory or ends with a slash or backslash, then any resulting executables will be written to that directory.

The build flags are shared by the build, clean, get, install, list, run, and test commands:

-C dir
	Change to dir before running the command.                   //修改命令执行目录
	Any files named on the command line are interpreted after
	changing directories.
-a
	force rebuilding of packages that are already up-to-date.   //强制重建
-n
	print the commands but do not run them.  //打印命令不执行
-p n
	the number of programs, such as build commands or
	test binaries, that can be run in parallel.
	The default is GOMAXPROCS, normally the number of CPUs available.
-race
	enable data race detection.                                   //开启竞态检测
	Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64,
	linux/ppc64le and linux/arm64 (only for 48-bit VMA).
-msan   
	enable interoperation with memory sanitizer.            //开启内存泄漏检测
	Supported only on linux/amd64, linux/arm64, freebsd/amd64
	and only with Clang/LLVM as the host C compiler.
	PIE build mode will be used on all platforms except linux/amd64.
-asan
	enable interoperation with address sanitizer.           //开启地址泄漏检测
	Supported only on linux/arm64, linux/amd64.
	Supported only on linux/amd64 or linux/arm64 and only with GCC 7 and higher
	or Clang/LLVM 9 and higher.
-cover                                                       //开启覆盖率检测
	enable code coverage instrumentation (requires
	that GOEXPERIMENT=coverageredesign be set).
-coverpkg pattern1,pattern2,pattern3                        //覆盖率检测匹配
	For a build that targets package 'main' (e.g. building a Go
	executable), apply coverage analysis to each package matching
	the patterns. The default is to apply coverage analysis to
	packages in the main Go module. See 'go help packages' for a
	description of package patterns.  Sets -cover.
-v                                                      
	print the names of packages as they are compiled.
-work                                                     //工作临时目录
	print the name of the temporary work directory and
	do not delete it when exiting.
-x
	print the commands.

-asmflags '[pattern=]arg list'                          //汇编参数
	arguments to pass on each go tool asm invocation.
-buildmode mode
	build mode to use. See 'go help buildmode' for more.
-buildvcs
	Whether to stamp binaries with version control information
	("true", "false", or "auto"). By default ("auto"), version control
	information is stamped into a binary if the main package, the main module
	containing it, and the current directory are all in the same repository.
	Use -buildvcs=false to always omit version control information, or
	-buildvcs=true to error out if version control information is available but
	cannot be included due to a missing tool or ambiguous directory structure.
-compiler name                                          //使用的编译器
	name of compiler to use, as in runtime.Compiler (gccgo or gc).
-gccgoflags '[pattern=]arg list'                        //gcc go 编译器的配置
	arguments to pass on each gccgo compiler/linker invocation.
-gcflags '[pattern=]arg list'                           //go gc 编译器的配置
	arguments to pass on each go tool compile invocation.
-installsuffix suffix                                   //后缀
	a suffix to use in the name of the package installation directory,
	in order to keep output separate from default builds.
	If using the -race flag, the install suffix is automatically set to race
	or, if set explicitly, has _race appended to it. Likewise for the -msan
	and -asan flags. Using a -buildmode option that requires non-default compile
	flags has a similar effect.
-ldflags '[pattern=]arg list'                           //连接器配置
	arguments to pass on each go tool link invocation.
-linkshared                                             //链接共享库
	build code that will be linked against shared libraries previously
	created with -buildmode=shared.
-mod mode
	module download mode to use: readonly, vendor, or mod.
	By default, if a vendor directory is present and the go version in go.mod
	is 1.14 or higher, the go command acts as if -mod=vendor were set.
	Otherwise, the go command acts as if -mod=readonly were set.
	See https://golang.org/ref/mod#build-commands for details.
-modcacherw
	leave newly-created directories in the module cache read-write
	instead of making them read-only.
-modfile file                                           //指定go.mod
	in module aware mode, read (and possibly write) an alternate go.mod
	file instead of the one in the module root directory. A file named
	"go.mod" must still be present in order to determine the module root
	directory, but it is not accessed. When -modfile is specified, an
	alternate go.sum file is also used: its path is derived from the
	-modfile flag by trimming the ".mod" extension and appending ".sum".
-overlay file
	read a JSON config file that provides an overlay for build operations.
	The file is a JSON struct with a single field, named 'Replace', that
	maps each disk file path (a string) to its backing file path, so that
	a build will run as if the disk file path exists with the contents
	given by the backing file paths, or as if the disk file path does not
	exist if its backing file path is empty. Support for the -overlay flag
	has some limitations: importantly, cgo files included from outside the
	include path must be in the same directory as the Go package they are
	included from, and overlays will not appear when binaries and tests are
	run through go run and go test respectively.
-pgo file                                               //性能优化
	specify the file path of a profile for profile-guided optimization (PGO).
	When the special name "auto" is specified, for each main package in the
	build, the go command selects a file named "default.pgo" in the package's
	directory if that file exists, and applies it to the (transitive)
	dependencies of the main package (other packages are not affected).
	Special name "off" turns off PGO. The default is "auto".
-pkgdir dir                                                 //指定package目录
	install and load all packages from dir instead of the usual locations.
	For example, when building with a non-standard configuration,
	use -pkgdir to keep generated packages in a separate location.
-tags tag,list                                                 //指定构建tag
	a comma-separated list of additional build tags to consider satisfied
	during the build. For more information about build tags, see
	'go help buildconstraint'. (Earlier versions of Go used a
	space-separated list, and that form is deprecated but still recognized.)
-trimpath
	remove all file system paths from the resulting executable.
	Instead of absolute file system paths, the recorded file names
	will begin either a module path@version (when using modules),
	or a plain import path (when using the standard library, or GOPATH).
-toolexec 'cmd args'
	a program to use to invoke toolchain programs like vet and asm.
	For example, instead of running asm, the go command will run
	'cmd args /path/to/asm <arguments for asm>'.
	The TOOLEXEC_IMPORTPATH environment variable will be set,
	matching 'go list -f {{.ImportPath}}' for the package being built.

The -asmflags, -gccgoflags, -gcflags, and -ldflags flags accept a space-separated list of arguments to pass to an underlying tool during the build. To embed spaces in an element in the list, surround it with either single or double quotes. The argument list may be preceded by a package pattern and an equal sign, which restricts the use of that argument list to the building of packages matching that pattern (see 'go help packages' for a description of package patterns). Without a pattern, the argument list applies only to the packages named on the command line. The flags may be repeated with different patterns in order to specify different arguments for different sets of packages. If a package matches patterns given in multiple flags, the latest match on the command line wins. For example, 'go build -gcflags=-S fmt' prints the disassembly only for package fmt, while 'go build -gcflags=all=-S fmt' prints the disassembly for fmt and all its dependencies.

For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, run 'go help gopath'. For more about calling between Go and C/C++, run 'go help c'.

Note: Build adheres to certain conventions such as those described by 'go help gopath'. Not all projects can follow these conventions, however. Installations that have their own conventions or that use a separate software build system may choose to use lower-level invocations such as 'go tool compile' and 'go tool link' to avoid some of the overheads and design decisions of the build tool.

See also: go install, go get, go clean.

Remove object files and cached files 删除对象文件和缓存文件

Usage:

go clean [clean flags] [build flags] [packages]

Clean removes object files from package source directories. The go command builds most objects in a temporary directory, so go clean is mainly concerned with object files left by other tools or by manual invocations of go build.

If a package argument is given or the -i or -r flag is set, clean removes the following files from each of the source directories corresponding to the import paths:

_obj/            old object directory, left from Makefiles
_test/           old test directory, left from Makefiles
_testmain.go     old gotest file, left from Makefiles
test.out         old test log, left from Makefiles
build.out        old test log, left from Makefiles
*.[568ao]        object files, left from Makefiles

DIR(.exe)        from go build
DIR.test(.exe)   from go test -c
MAINFILE(.exe)   from go build MAINFILE.go
*.so             from SWIG

In the list, DIR represents the final path element of the directory, and MAINFILE is the base name of any Go source file in the directory that is not included when building the package.

The -i flag causes clean to remove the corresponding installed archive or binary (what 'go install' would create). 删除二进制文件

The -n flag causes clean to print the remove commands it would execute, but not run them. 什么都不做

The -r flag causes clean to be applied recursively to all the dependencies of the packages named by the import paths.

The -x flag causes clean to print remove commands as it executes them.

The -cache flag causes clean to remove the entire go build cache.

The -testcache flag causes clean to expire all test results in the go build cache.

The -modcache flag causes clean to remove the entire module download cache, including unpacked source code of versioned dependencies.

The -fuzzcache flag causes clean to remove files stored in the Go build cache for fuzz testing. The fuzzing engine caches files that expand code coverage, so removing them may make fuzzing less effective until new inputs are found that provide the same coverage. These files are distinct from those stored in testdata directory; clean does not remove those files.

For more about build flags, see 'go help build'.

For more about specifying packages, see 'go help packages'.

Show documentation for package or symbol 显示包或者符号的文档

Usage:

go doc [doc flags] [package|[package.]symbol[.methodOrField]]

Doc prints the documentation comments associated with the item identified by its arguments (a package, const, func, type, var, method, or struct field) followed by a one-line summary of each of the first-level items "under" that item (package-level declarations for a package, methods for a type, etc.).

Doc accepts zero, one, or two arguments.

Given no arguments, that is, when run as

go doc

it prints the package documentation for the package in the current directory. If the package is a command (package main), the exported symbols of the package are elided from the presentation unless the -cmd flag is provided.

When run with one argument, the argument is treated as a Go-syntax-like representation of the item to be documented. What the argument selects depends on what is installed in GOROOT and GOPATH, as well as the form of the argument, which is schematically one of these:

go doc <pkg>
go doc <sym>[.<methodOrField>]
go doc [<pkg>.]<sym>[.<methodOrField>]
go doc [<pkg>.][<sym>.]<methodOrField>

The first item in this list matched by the argument is the one whose documentation is printed. (See the examples below.) However, if the argument starts with a capital letter it is assumed to identify a symbol or method in the current directory.

For packages, the order of scanning is determined lexically in breadth-first order. That is, the package presented is the one that matches the search and is nearest the root and lexically first at its level of the hierarchy. The GOROOT tree is always scanned in its entirety before GOPATH.

If there is no package specified or matched, the package in the current directory is selected, so "go doc Foo" shows the documentation for symbol Foo in the current package.

The package path must be either a qualified path or a proper suffix of a path. The go tool's usual package mechanism does not apply: package path elements like . and ... are not implemented by go doc.

When run with two arguments, the first is a package path (full path or suffix), and the second is a symbol, or symbol with method or struct field:

go doc <pkg> <sym>[.<methodOrField>]

In all forms, when matching symbols, lower-case letters in the argument match either case but upper-case letters match exactly. This means that there may be multiple matches of a lower-case argument in a package if different symbols have different cases. If this occurs, documentation for all matches is printed.

Examples:

go doc
	Show documentation for current package.
go doc Foo
	Show documentation for Foo in the current package.
	(Foo starts with a capital letter so it cannot match
	a package path.)
go doc encoding/json
	Show documentation for the encoding/json package.
go doc json
	Shorthand for encoding/json.
go doc json.Number (or go doc json.number)
	Show documentation and method summary for json.Number.
go doc json.Number.Int64 (or go doc json.number.int64)
	Show documentation for json.Number's Int64 method.
go doc cmd/doc
	Show package docs for the doc command.
go doc -cmd cmd/doc
	Show package docs and exported symbols within the doc command.
go doc template.new
	Show documentation for html/template's New function.
	(html/template is lexically before text/template)
go doc text/template.new # One argument
	Show documentation for text/template's New function.
go doc text/template new # Two arguments
	Show documentation for text/template's New function.

At least in the current tree, these invocations all print the
documentation for json.Decoder's Decode method:

go doc json.Decoder.Decode
go doc json.decoder.decode
go doc json.decode
cd go/src/encoding/json; go doc decode

Flags:

-all
	Show all the documentation for the package.
-c
	Respect case when matching symbols.
-cmd
	Treat a command (package main) like a regular package.
	Otherwise package main's exported symbols are hidden
	when showing the package's top-level documentation.
-short
	One-line representation for each symbol.
-src
	Show the full source code for the symbol. This will
	display the full Go source of its declaration and
	definition, such as a function definition (including
	the body), type declaration or enclosing const
	block. The output may therefore include unexported
	details.
-u
	Show documentation for unexported as well as exported
	symbols, methods, and fields.

Print Go environment information 打印golang的环境变量

Usage:

go env [-json] [-u] [-w] [var ...]

Env prints Go environment information.

By default env prints information as a shell script (on Windows, a batch file). If one or more variable names is given as arguments, env prints the value of each named variable on its own line.

The -json flag prints the environment in JSON format instead of as a shell script.

The -u flag requires one or more arguments and unsets the default setting for the named environment variables, if one has been set with 'go env -w'.

The -w flag requires one or more arguments of the form NAME=VALUE and changes the default settings of the named environment variables to the given values.

For more about environment variables, see 'go help environment'.

Update packages to use new APIs 更新package 使用新的api

Usage:

go fix [-fix list] [packages]

Fix runs the Go fix command on the packages named by the import paths.

The -fix flag sets a comma-separated list of fixes to run. The default is all known fixes. (Its value is passed to 'go tool fix -r'.)

For more about fix, see 'go doc cmd/fix'. For more about specifying packages, see 'go help packages'.

To run fix with other options, run 'go tool fix'.

See also: go fmt, go vet.

Gofmt (reformat) package sources

源代码格式化 Usage:

go fmt [-n] [-x] [packages]

Fmt runs the command 'gofmt -l -w' on the packages named by the import paths. It prints the names of the files that are modified.

For more about gofmt, see 'go doc cmd/gofmt'. For more about specifying packages, see 'go help packages'.

The -n flag prints commands that would be executed. The -x flag prints commands as they are executed.

The -mod flag's value sets which module download mode to use: readonly or vendor. See 'go help modules' for more.

To run gofmt with specific options, run gofmt itself.

See also: go fix, go vet.

Generate Go files by processing source 代码生成

Usage:

go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]

Generate runs commands described by directives within existing files. Those commands can run any process but the intent is to create or update Go source files.

Go generate is never run automatically by go build, go test, and so on. It must be run explicitly.

Go generate scans the file for directives, which are lines of the form,

//go:generate command argument...

(note: no leading spaces and no space in "//go") where command is the generator to be run, corresponding to an executable file that can be run locally. It must either be in the shell path (gofmt), a fully qualified path (/usr/you/bin/mytool), or a command alias, described below.

Note that go generate does not parse the file, so lines that look like directives in comments or multiline strings will be treated as directives.

The arguments to the directive are space-separated tokens or double-quoted strings passed to the generator as individual arguments when it is run.

Quoted strings use Go syntax and are evaluated before execution; a quoted string appears as a single argument to the generator.

To convey to humans and machine tools that code is generated, generated source should have a line that matches the following regular expression (in Go syntax):

^// Code generated .* DO NOT EDIT\.$

This line must appear before the first non-comment, non-blank text in the file.

Go generate sets several variables when it runs the generator:

$GOARCH
	The execution architecture (arm, amd64, etc.)
$GOOS
	The execution operating system (linux, windows, etc.)
$GOFILE
	The base name of the file.
$GOLINE
	The line number of the directive in the source file.
$GOPACKAGE
	The name of the package of the file containing the directive.
$GOROOT
	The GOROOT directory for the 'go' command that invoked the
	generator, containing the Go toolchain and standard library.
$DOLLAR
	A dollar sign.

Other than variable substitution and quoted-string evaluation, no special processing such as "globbing" is performed on the command line.

As a last step before running the command, any invocations of any environment variables with alphanumeric names, such as $GOFILE or $HOME, are expanded throughout the command line. The syntax for variable expansion is $NAME on all operating systems. Due to the order of evaluation, variables are expanded even inside quoted strings. If the variable NAME is not set, $NAME expands to the empty string.

A directive of the form,

//go:generate -command xxx args...

specifies, for the remainder of this source file only, that the string xxx represents the command identified by the arguments. This can be used to create aliases or to handle multiword generators. For example,

//go:generate -command foo go tool foo

specifies that the command "foo" represents the generator "go tool foo".

Generate processes packages in the order given on the command line, one at a time. If the command line lists .go files from a single directory, they are treated as a single package. Within a package, generate processes the source files in a package in file name order, one at a time. Within a source file, generate runs generators in the order they appear in the file, one at a time. The go generate tool also sets the build tag "generate" so that files may be examined by go generate but ignored during build.

For packages with invalid code, generate processes only source files with a valid package clause.

If any generator returns an error exit status, "go generate" skips all further processing for that package.

The generator is run in the package's source directory.

Go generate accepts two specific flags:

-run=""
	if non-empty, specifies a regular expression to select
	directives whose full original source text (excluding
	any trailing spaces and final newline) matches the
	expression.

-skip=""
	if non-empty, specifies a regular expression to suppress
	directives whose full original source text (excluding
	any trailing spaces and final newline) matches the
	expression. If a directive matches both the -run and
	the -skip arguments, it is skipped.

It also accepts the standard build flags including -v, -n, and -x. The -v flag prints the names of packages and files as they are processed. The -n flag prints commands that would be executed. The -x flag prints commands as they are executed.

For more about build flags, see 'go help build'.

For more about specifying packages, see 'go help packages'.

Add dependencies to current module and install them 安装依赖

Usage:

go get [-t] [-u] [-v] [build flags] [packages]

Get resolves its command-line arguments to packages at specific module versions, updates go.mod to require those versions, and downloads source code into the module cache.

To add a dependency for a package or upgrade it to its latest version:

go get example.com/pkg

To upgrade or downgrade a package to a specific version:

go get example.com/pkg@v1.2.3

To remove a dependency on a module and downgrade modules that require it:

go get example.com/mod@none

See https://golang.org/ref/mod#go-get for details.

In earlier versions of Go, 'go get' was used to build and install packages. Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install' may be used to build and install commands instead. When a version is specified, 'go install' runs in module-aware mode and ignores the go.mod file in the current directory. For example:

go install example.com/pkg@v1.2.3
go install example.com/pkg@latest

See 'go help install' or https://golang.org/ref/mod#go-install for details.

'go get' accepts the following flags.

The -t flag instructs get to consider modules needed to build tests of packages specified on the command line.

The -u flag instructs get to update modules providing dependencies of packages named on the command line to use newer minor or patch releases when available.

The -u=patch flag (not -u patch) also instructs get to update dependencies, but changes the default to select patch releases.

When the -t and -u flags are used together, get will update test dependencies as well.

The -x flag prints commands as they are executed. This is useful for debugging version control commands when a module is downloaded directly from a repository.

For more about modules, see https://golang.org/ref/mod.

For more about specifying packages, see 'go help packages'.

This text describes the behavior of get using modules to manage source code and dependencies. If instead the go command is running in GOPATH mode, the details of get's flags and effects change, as does 'go help get'. See 'go help gopath-get'.

See also: go build, go install, go clean, go mod.

Compile and install packages and dependencies

Usage:

go install [build flags] [packages]

Install compiles and installs the packages named by the import paths.

Executables are installed in the directory named by the GOBIN environment variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH environment variable is not set. Executables in $GOROOT are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN.

If the arguments have version suffixes (like @latest or @v1.0.0), "go install" builds packages in module-aware mode, ignoring the go.mod file in the current directory or any parent directory, if there is one. This is useful for installing executables without affecting the dependencies of the main module. To eliminate ambiguity about which module versions are used in the build, the arguments must satisfy the following constraints:

  • Arguments must be package paths or package patterns (with "..." wildcards). They must not be standard packages (like fmt), meta-patterns (std, cmd, all), or relative or absolute file paths.

  • All arguments must have the same version suffix. Different queries are not allowed, even if they refer to the same version.

  • All arguments must refer to packages in the same module at the same version.

  • Package path arguments must refer to main packages. Pattern arguments will only match main packages.

  • No module is considered the "main" module. If the module containing packages named on the command line has a go.mod file, it must not contain directives (replace and exclude) that would cause it to be interpreted differently than if it were the main module. The module must not require a higher version of itself.

  • Vendor directories are not used in any module. (Vendor directories are not included in the module zip files downloaded by 'go install'.)

If the arguments don't have version suffixes, "go install" may run in module-aware mode or GOPATH mode, depending on the GO111MODULE environment variable and the presence of a go.mod file. See 'go help modules' for details. If module-aware mode is enabled, "go install" runs in the context of the main module.

When module-aware mode is disabled, non-main packages are installed in the directory $GOPATH/pkg/$GOOS_$GOARCH. When module-aware mode is enabled, non-main packages are built and cached but not installed.

Before Go 1.20, the standard library was installed to $GOROOT/pkg/$GOOS_$GOARCH. Starting in Go 1.20, the standard library is built and cached but not installed. Setting GODEBUG=installgoroot=all restores the use of $GOROOT/pkg/$GOOS_$GOARCH.

For more about build flags, see 'go help build'.

For more about specifying packages, see 'go help packages'.

See also: go build, go get, go clean.

List packages or modules

Usage:

go list [-f format] [-json] [-m] [list flags] [build flags] [packages]

List lists the named packages, one per line. The most commonly-used flags are -f and -json, which control the form of the output printed for each package. Other list flags, documented below, control more specific details.

The default output shows the package import path:

bytes
encoding/json
github.com/gorilla/mux
golang.org/x/net/html

The -f flag specifies an alternate format for the list, using the syntax of package template. The default output is equivalent to -f '{{.ImportPath}}'. The struct being passed to the template is:

type Package struct {
    Dir            string   // directory containing package sources
    ImportPath     string   // import path of package in dir
    ImportComment  string   // path in import comment on package statement
    Name           string   // package name
    Doc            string   // package documentation string
    Target         string   // install path
    Shlib          string   // the shared library that contains this package (only set when -linkshared)
    Goroot         bool     // is this package in the Go root?
    Standard       bool     // is this package part of the standard Go library?
    Stale          bool     // would 'go install' do anything for this package?
    StaleReason    string   // explanation for Stale==true
    Root           string   // Go root or Go path dir containing this package
    ConflictDir    string   // this directory shadows Dir in $GOPATH
    BinaryOnly     bool     // binary-only package (no longer supported)
    ForTest        string   // package is only for use in named test
    Export         string   // file containing export data (when using -export)
    BuildID        string   // build ID of the compiled package (when using -export)
    Module         *Module  // info about package's containing module, if any (can be nil)
    Match          []string // command-line patterns matching this package
    DepOnly        bool     // package is only a dependency, not explicitly listed
    DefaultGODEBUG string  // default GODEBUG setting, for main packages

    // Source files
    GoFiles           []string   // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
    CgoFiles          []string   // .go source files that import "C"
    CompiledGoFiles   []string   // .go files presented to compiler (when using -compiled)
    IgnoredGoFiles    []string   // .go source files ignored due to build constraints
    IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
    CFiles            []string   // .c source files
    CXXFiles          []string   // .cc, .cxx and .cpp source files
    MFiles            []string   // .m source files
    HFiles            []string   // .h, .hh, .hpp and .hxx source files
    FFiles            []string   // .f, .F, .for and .f90 Fortran source files
    SFiles            []string   // .s source files
    SwigFiles         []string   // .swig files
    SwigCXXFiles      []string   // .swigcxx files
    SysoFiles         []string   // .syso object files to add to archive
    TestGoFiles       []string   // _test.go files in package
    XTestGoFiles      []string   // _test.go files outside package

    // Embedded files
    EmbedPatterns      []string // //go:embed patterns
    EmbedFiles         []string // files matched by EmbedPatterns
    TestEmbedPatterns  []string // //go:embed patterns in TestGoFiles
    TestEmbedFiles     []string // files matched by TestEmbedPatterns
    XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
    XTestEmbedFiles    []string // files matched by XTestEmbedPatterns

    // Cgo directives
    CgoCFLAGS    []string // cgo: flags for C compiler
    CgoCPPFLAGS  []string // cgo: flags for C preprocessor
    CgoCXXFLAGS  []string // cgo: flags for C++ compiler
    CgoFFLAGS    []string // cgo: flags for Fortran compiler
    CgoLDFLAGS   []string // cgo: flags for linker
    CgoPkgConfig []string // cgo: pkg-config names

    // Dependency information
    Imports      []string          // import paths used by this package
    ImportMap    map[string]string // map from source import to ImportPath (identity entries omitted)
    Deps         []string          // all (recursively) imported dependencies
    TestImports  []string          // imports from TestGoFiles
    XTestImports []string          // imports from XTestGoFiles

    // Error information
    Incomplete bool            // this package or a dependency has an error
    Error      *PackageError   // error loading package
    DepsErrors []*PackageError // errors loading dependencies
}

Packages stored in vendor directories report an ImportPath that includes the path to the vendor directory (for example, "d/vendor/p" instead of "p"), so that the ImportPath uniquely identifies a given copy of a package. The Imports, Deps, TestImports, and XTestImports lists also contain these expanded import paths. See golang.org/s/go15vendor for more about vendoring.

The error information, if any, is

type PackageError struct {
    ImportStack   []string // shortest path from package named on command line to this one
    Pos           string   // position of error (if present, file:line:col)
    Err           string   // the error itself
}

The module information is a Module struct, defined in the discussion of list -m below.

The template function "join" calls strings.Join.

The template function "context" returns the build context, defined as:

type Context struct {
    GOARCH        string   // target architecture
    GOOS          string   // target operating system
    GOROOT        string   // Go root
    GOPATH        string   // Go path
    CgoEnabled    bool     // whether cgo can be used
    UseAllFiles   bool     // use files regardless of //go:build lines, file names
    Compiler      string   // compiler to assume when computing target paths
    BuildTags     []string // build constraints to match in //go:build lines
    ToolTags      []string // toolchain-specific build constraints
    ReleaseTags   []string // releases the current release is compatible with
    InstallSuffix string   // suffix to use in the name of the install dir
}

For more information about the meaning of these fields see the documentation for the go/build package's Context type.

The -json flag causes the package data to be printed in JSON format instead of using the template format. The JSON flag can optionally be provided with a set of comma-separated required field names to be output. If so, those required fields will always appear in JSON output, but others may be omitted to save work in computing the JSON struct.

The -compiled flag causes list to set CompiledGoFiles to the Go source files presented to the compiler. Typically this means that it repeats the files listed in GoFiles and then also adds the Go code generated by processing CgoFiles and SwigFiles. The Imports list contains the union of all imports from both GoFiles and CompiledGoFiles.

The -deps flag causes list to iterate over not just the named packages but also all their dependencies. It visits them in a depth-first post-order traversal, so that a package is listed only after all its dependencies. Packages not explicitly listed on the command line will have the DepOnly field set to true.

The -e flag changes the handling of erroneous packages, those that cannot be found or are malformed. By default, the list command prints an error to standard error for each erroneous package and omits the packages from consideration during the usual printing. With the -e flag, the list command never prints errors to standard error and instead processes the erroneous packages with the usual printing. Erroneous packages will have a non-empty ImportPath and a non-nil Error field; other information may or may not be missing (zeroed).

The -export flag causes list to set the Export field to the name of a file containing up-to-date export information for the given package, and the BuildID field to the build ID of the compiled package.

The -find flag causes list to identify the named packages but not resolve their dependencies: the Imports and Deps lists will be empty. With the -find flag, the -deps, -test and -export commands cannot be used.

The -test flag causes list to report not only the named packages but also their test binaries (for packages with tests), to convey to source code analysis tools exactly how test binaries are constructed. The reported import path for a test binary is the import path of the package followed by a ".test" suffix, as in "math/rand.test". When building a test, it is sometimes necessary to rebuild certain dependencies specially for that test (most commonly the tested package itself). The reported import path of a package recompiled for a particular test binary is followed by a space and the name of the test binary in brackets, as in "math/rand [math/rand.test]" or "regexp [sort.test]". The ForTest field is also set to the name of the package being tested ("math/rand" or "sort" in the previous examples).

The Dir, Target, Shlib, Root, ConflictDir, and Export file paths are all absolute paths.

By default, the lists GoFiles, CgoFiles, and so on hold names of files in Dir (that is, paths relative to Dir, not absolute paths). The generated files added when using the -compiled and -test flags are absolute paths referring to cached copies of generated Go source files. Although they are Go source files, the paths may not end in ".go".

The -m flag causes list to list modules instead of packages.

When listing modules, the -f flag still specifies a format template applied to a Go struct, but now a Module struct:

type Module struct {
    Path       string        // module path
    Query      string        // version query corresponding to this version
    Version    string        // module version
    Versions   []string      // available module versions
    Replace    *Module       // replaced by this module
    Time       *time.Time    // time version was created
    Update     *Module       // available update (with -u)
    Main       bool          // is this the main module?
    Indirect   bool          // module is only indirectly needed by main module
    Dir        string        // directory holding local copy of files, if any
    GoMod      string        // path to go.mod file describing module, if any
    GoVersion  string        // go version used in module
    Retracted  []string      // retraction information, if any (with -retracted or -u)
    Deprecated string        // deprecation message, if any (with -u)
    Error      *ModuleError  // error loading module
    Origin     any           // provenance of module
    Reuse      bool          // reuse of old module info is safe
}

type ModuleError struct {
    Err string // the error itself
}

The file GoMod refers to may be outside the module directory if the module is in the module cache or if the -modfile flag is used.

The default output is to print the module path and then information about the version and replacement if any. For example, 'go list -m all' might print:

my/main/module
golang.org/x/text v0.3.0 => /tmp/text
rsc.io/pdf v0.1.1

The Module struct has a String method that formats this line of output, so that the default format is equivalent to -f '{{.String}}'.

Note that when a module has been replaced, its Replace field describes the replacement module, and its Dir field is set to the replacement's source code, if present. (That is, if Replace is non-nil, then Dir is set to Replace.Dir, with no access to the replaced source code.)

The -u flag adds information about available upgrades. When the latest version of a given module is newer than the current one, list -u sets the Module's Update field to information about the newer module. list -u will also set the module's Retracted field if the current version is retracted. The Module's String method indicates an available upgrade by formatting the newer version in brackets after the current version. If a version is retracted, the string "(retracted)" will follow it. For example, 'go list -m -u all' might print:

my/main/module
golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
rsc.io/pdf v0.1.1 (retracted) [v0.1.2]

(For tools, 'go list -m -u -json all' may be more convenient to parse.)

The -versions flag causes list to set the Module's Versions field to a list of all known versions of that module, ordered according to semantic versioning, earliest to latest. The flag also changes the default output format to display the module path followed by the space-separated version list.

The -retracted flag causes list to report information about retracted module versions. When -retracted is used with -f or -json, the Retracted field will be set to a string explaining why the version was retracted. The string is taken from comments on the retract directive in the module's go.mod file. When -retracted is used with -versions, retracted versions are listed together with unretracted versions. The -retracted flag may be used with or without -m.

The arguments to list -m are interpreted as a list of modules, not packages. The main module is the module containing the current directory. The active modules are the main module and its dependencies. With no arguments, list -m shows the main module. With arguments, list -m shows the modules specified by the arguments. Any of the active modules can be specified by its module path. The special pattern "all" specifies all the active modules, first the main module and then dependencies sorted by module path. A pattern containing "..." specifies the active modules whose module paths match the pattern. A query of the form path@version specifies the result of that query, which is not limited to active modules. See 'go help modules' for more about module queries.

The template function "module" takes a single string argument that must be a module path or query and returns the specified module as a Module struct. If an error occurs, the result will be a Module struct with a non-nil Error field.

When using -m, the -reuse=old.json flag accepts the name of file containing the JSON output of a previous 'go list -m -json' invocation with the same set of modifier flags (such as -u, -retracted, and -versions). The go command may use this file to determine that a module is unchanged since the previous invocation and avoid redownloading information about it. Modules that are not redownloaded will be marked in the new output by setting the Reuse field to true. Normally the module cache provides this kind of reuse automatically; the -reuse flag can be useful on systems that do not preserve the module cache.

For more about build flags, see 'go help build'.

For more about specifying packages, see 'go help packages'.

For more about modules, see https://golang.org/ref/mod.

Module maintenance

go mod 依赖管理 Go mod provides access to operations on modules.

Note that support for modules is built into all the go commands, not just 'go mod'. For example, day-to-day adding, removing, upgrading, and downgrading of dependencies should be done using 'go get'. See 'go help modules' for an overview of module functionality.

Usage:

go mod <command> [arguments]

The commands are:

download    download modules to local cache             //下载
edit        edit go.mod from tools or scripts 
graph       print module requirement graph
init        initialize new module in current directory  //初始化
tidy        add missing and remove unused modules       //增加确实的删除未使用的
vendor      make vendored copy of dependencies  
verify      verify dependencies have expected content   
why         explain why packages or modules are needed

Use "go help mod " for more information about a command.

Download modules to local cache

Usage:

go mod download [-x] [-json] [-reuse=old.json] [modules]

Download downloads the named modules, which can be module patterns selecting dependencies of the main module or module queries of the form path@version.

With no arguments, download applies to the modules needed to build and test the packages in the main module: the modules explicitly required by the main module if it is at 'go 1.17' or higher, or all transitively-required modules if at 'go 1.16' or lower.

The go command will automatically download modules as needed during ordinary execution. The "go mod download" command is useful mainly for pre-filling the local cache or to compute the answers for a Go module proxy.

By default, download writes nothing to standard output. It may print progress messages and errors to standard error.

The -json flag causes download to print a sequence of JSON objects to standard output, describing each downloaded module (or failure), corresponding to this Go struct:

type Module struct {
    Path     string // module path
    Query    string // version query corresponding to this version
    Version  string // module version
    Error    string // error loading module
    Info     string // absolute path to cached .info file
    GoMod    string // absolute path to cached .mod file
    Zip      string // absolute path to cached .zip file
    Dir      string // absolute path to cached source root directory
    Sum      string // checksum for path, version (as in go.sum)
    GoModSum string // checksum for go.mod (as in go.sum)
    Origin   any    // provenance of module
    Reuse    bool   // reuse of old module info is safe
}

The -reuse flag accepts the name of file containing the JSON output of a previous 'go mod download -json' invocation. The go command may use this file to determine that a module is unchanged since the previous invocation and avoid redownloading it. Modules that are not redownloaded will be marked in the new output by setting the Reuse field to true. Normally the module cache provides this kind of reuse automatically; the -reuse flag can be useful on systems that do not preserve the module cache.

The -x flag causes download to print the commands download executes.

See https://golang.org/ref/mod#go-mod-download for more about 'go mod download'.

See https://golang.org/ref/mod#version-queries for more about version queries.

Edit go.mod from tools or scripts

Usage:

go mod edit [editing flags] [-fmt|-print|-json] [go.mod]

Edit provides a command-line interface for editing go.mod, for use primarily by tools or scripts. It reads only go.mod; it does not look up information about the modules involved. By default, edit reads and writes the go.mod file of the main module, but a different target file can be specified after the editing flags.

The editing flags specify a sequence of editing operations.

The -fmt flag reformats the go.mod file without making other changes. This reformatting is also implied by any other modifications that use or rewrite the go.mod file. The only time this flag is needed is if no other flags are specified, as in 'go mod edit -fmt'.

The -module flag changes the module's path (the go.mod file's module line).

The -require=path@version and -droprequire=path flags add and drop a requirement on the given module path and version. Note that -require overrides any existing requirements on path. These flags are mainly for tools that understand the module graph. Users should prefer 'go get path@version' or 'go get path@none', which make other go.mod adjustments as needed to satisfy constraints imposed by other modules.

The -exclude=path@version and -dropexclude=path@version flags add and drop an exclusion for the given module path and version. Note that -exclude=path@version is a no-op if that exclusion already exists.

The -replace=old[@v]=new[@v] flag adds a replacement of the given module path and version pair. If the @v in old@v is omitted, a replacement without a version on the left side is added, which applies to all versions of the old module path. If the @v in new@v is omitted, the new path should be a local module root directory, not a module path. Note that -replace overrides any redundant replacements for old[@v], so omitting @v will drop existing replacements for specific versions.

The -dropreplace=old[@v] flag drops a replacement of the given module path and version pair. If the @v is omitted, a replacement without a version on the left side is dropped.

The -retract=version and -dropretract=version flags add and drop a retraction on the given version. The version may be a single version like "v1.2.3" or a closed interval like "[v1.1.0,v1.1.9]". Note that -retract=version is a no-op if that retraction already exists.

The -require, -droprequire, -exclude, -dropexclude, -replace, -dropreplace, -retract, and -dropretract editing flags may be repeated, and the changes are applied in the order given.

The -go=version flag sets the expected Go language version.

The -print flag prints the final go.mod in its text format instead of writing it back to go.mod.

The -json flag prints the final go.mod file in JSON format instead of writing it back to go.mod. The JSON output corresponds to these Go types:

type Module struct {
	Path    string
	Version string
}

type GoMod struct {
	Module  ModPath
	Go      string
	Require []Require
	Exclude []Module
	Replace []Replace
	Retract []Retract
}

type ModPath struct {
	Path       string
	Deprecated string
}

type Require struct {
	Path string
	Version string
	Indirect bool
}

type Replace struct {
	Old Module
	New Module
}

type Retract struct {
	Low       string
	High      string
	Rationale string
}

Retract entries representing a single version (not an interval) will have the "Low" and "High" fields set to the same value.

Note that this only describes the go.mod file itself, not other modules referred to indirectly. For the full set of modules available to a build, use 'go list -m -json all'.

Edit also provides the -C, -n, and -x build flags.

See https://golang.org/ref/mod#go-mod-edit for more about 'go mod edit'.

Print module requirement graph

Usage:

go mod graph [-go=version] [-x]

Graph prints the module requirement graph (with replacements applied) in text form. Each line in the output has two space-separated fields: a module and one of its requirements. Each module is identified as a string of the form path@version, except for the main module, which has no @version suffix.

The -go flag causes graph to report the module graph as loaded by the given Go version, instead of the version indicated by the 'go' directive in the go.mod file.

The -x flag causes graph to print the commands graph executes.

See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'.

Initialize new module in current directory

Usage:

go mod init [module-path]

Init initializes and writes a new go.mod file in the current directory, in effect creating a new module rooted at the current directory. The go.mod file must not already exist.

Init accepts one optional argument, the module path for the new module. If the module path argument is omitted, init will attempt to infer the module path using import comments in .go files, vendoring tool configuration files (like Gopkg.lock), and the current directory (if in GOPATH).

If a configuration file for a vendoring tool is present, init will attempt to import module requirements from it.

See https://golang.org/ref/mod#go-mod-init for more about 'go mod init'.

Add missing and remove unused modules

Usage:

go mod tidy [-e] [-v] [-x] [-go=version] [-compat=version]

Tidy makes sure go.mod matches the source code in the module. It adds any missing modules necessary to build the current module's packages and dependencies, and it removes unused modules that don't provide any relevant packages. It also adds any missing entries to go.sum and removes any unnecessary ones.

The -v flag causes tidy to print information about removed modules to standard error.

The -e flag causes tidy to attempt to proceed despite errors encountered while loading packages.

The -go flag causes tidy to update the 'go' directive in the go.mod file to the given version, which may change which module dependencies are retained as explicit requirements in the go.mod file. (Go versions 1.17 and higher retain more requirements in order to support lazy module loading.)

The -compat flag preserves any additional checksums needed for the 'go' command from the indicated major Go release to successfully load the module graph, and causes tidy to error out if that version of the 'go' command would load any imported package from a different module version. By default, tidy acts as if the -compat flag were set to the version prior to the one indicated by the 'go' directive in the go.mod file.

The -x flag causes tidy to print the commands download executes.

See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.

Make vendored copy of dependencies

Usage:

go mod vendor [-e] [-v] [-o outdir]

Vendor resets the main module's vendor directory to include all packages needed to build and test all the main module's packages. It does not include test code for vendored packages.

The -v flag causes vendor to print the names of vendored modules and packages to standard error.

The -e flag causes vendor to attempt to proceed despite errors encountered while loading packages.

The -o flag causes vendor to create the vendor directory at the given path instead of "vendor". The go command can only use a vendor directory named "vendor" within the module root directory, so this flag is primarily useful for other tools.

See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'.

Verify dependencies have expected content

Usage:

go mod verify

Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded. If all the modules are unmodified, verify prints "all modules verified." Otherwise it reports which modules have been changed and causes 'go mod' to exit with a non-zero status.

See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.

Explain why packages or modules are needed

Usage:

go mod why [-m] [-vendor] packages...

Why shows a shortest path in the import graph from the main module to each of the listed packages. If the -m flag is given, why treats the arguments as a list of modules and finds a path to any package in each of the modules.

By default, why queries the graph of packages matched by "go list all", which includes tests for reachable packages. The -vendor flag causes why to exclude tests of dependencies.

The output is a sequence of stanzas, one for each package or module name on the command line, separated by blank lines. Each stanza begins with a comment line "# package" or "# module" giving the target package or module. Subsequent lines give a path through the import graph, one package per line. If the package or module is not referenced from the main module, the stanza will display a single parenthesized note indicating that fact.

For example:

$ go mod why golang.org/x/text/language golang.org/x/text/encoding
# golang.org/x/text/language
rsc.io/quote
rsc.io/sampler
golang.org/x/text/language

# golang.org/x/text/encoding
(main module does not need package golang.org/x/text/encoding)
$

See https://golang.org/ref/mod#go-mod-why for more about 'go mod why'.

Workspace maintenance

go 工作区 work.go

Work provides access to operations on workspaces.

Note that support for workspaces is built into many other commands, not just 'go work'.

See 'go help modules' for information about Go's module system of which workspaces are a part.

See https://go.dev/ref/mod#workspaces for an in-depth reference on workspaces.

See https://go.dev/doc/tutorial/workspaces for an introductory tutorial on workspaces.

A workspace is specified by a go.work file that specifies a set of module directories with the "use" directive. These modules are used as root modules by the go command for builds and related operations. A workspace that does not specify modules to be used cannot be used to do builds from local modules.

go.work files are line-oriented. Each line holds a single directive, made up of a keyword followed by arguments. For example:

go 1.18

use ../foo/bar
use ./baz

replace example.com/foo v1.2.3 => example.com/bar v1.4.5

The leading keyword can be factored out of adjacent lines to create a block, like in Go imports.

use (
  ../foo/bar
  ./baz
)

The use directive specifies a module to be included in the workspace's set of main modules. The argument to the use directive is the directory containing the module's go.mod file.

The go directive specifies the version of Go the file was written at. It is possible there may be future changes in the semantics of workspaces that could be controlled by this version, but for now the version specified has no effect.

The replace directive has the same syntax as the replace directive in a go.mod file and takes precedence over replaces in go.mod files. It is primarily intended to override conflicting replaces in different workspace modules.

To determine whether the go command is operating in workspace mode, use the "go env GOWORK" command. This will specify the workspace file being used.

Usage:

go work <command> [arguments]

The commands are:

edit        edit go.work from tools or scripts
init        initialize workspace file                   //初始化
sync        sync workspace build list to modules
use         add modules to workspace file

Use "go help work " for more information about a command.

Edit go.work from tools or scripts

Usage:

go work edit [editing flags] [go.work]

Edit provides a command-line interface for editing go.work, for use primarily by tools or scripts. It only reads go.work; it does not look up information about the modules involved. If no file is specified, Edit looks for a go.work file in the current directory and its parent directories

The editing flags specify a sequence of editing operations.

The -fmt flag reformats the go.work file without making other changes. This reformatting is also implied by any other modifications that use or rewrite the go.mod file. The only time this flag is needed is if no other flags are specified, as in 'go work edit -fmt'.

The -use=path and -dropuse=path flags add and drop a use directive from the go.work file's set of module directories.

The -replace=old[@v]=new[@v] flag adds a replacement of the given module path and version pair. If the @v in old@v is omitted, a replacement without a version on the left side is added, which applies to all versions of the old module path. If the @v in new@v is omitted, the new path should be a local module root directory, not a module path. Note that -replace overrides any redundant replacements for old[@v], so omitting @v will drop existing replacements for specific versions.

The -dropreplace=old[@v] flag drops a replacement of the given module path and version pair. If the @v is omitted, a replacement without a version on the left side is dropped.

The -use, -dropuse, -replace, and -dropreplace, editing flags may be repeated, and the changes are applied in the order given.

The -go=version flag sets the expected Go language version.

The -print flag prints the final go.work in its text format instead of writing it back to go.mod.

The -json flag prints the final go.work file in JSON format instead of writing it back to go.mod. The JSON output corresponds to these Go types:

type GoWork struct {
	Go      string
	Use     []Use
	Replace []Replace
}

type Use struct {
	DiskPath   string
	ModulePath string
}

type Replace struct {
	Old Module
	New Module
}

type Module struct {
	Path    string
	Version string
}

See the workspaces reference at https://go.dev/ref/mod#workspaces for more information.

Initialize workspace file

Usage:

go work init [moddirs]

Init initializes and writes a new go.work file in the current directory, in effect creating a new workspace at the current directory.

go work init optionally accepts paths to the workspace modules as arguments. If the argument is omitted, an empty workspace with no modules will be created.

Each argument path is added to a use directive in the go.work file. The current go version will also be listed in the go.work file.

See the workspaces reference at https://go.dev/ref/mod#workspaces for more information.

Sync workspace build list to modules

Usage:

go work sync

Sync syncs the workspace's build list back to the workspace's modules

The workspace's build list is the set of versions of all the (transitive) dependency modules used to do builds in the workspace. go work sync generates that build list using the Minimal Version Selection algorithm, and then syncs those versions back to each of modules specified in the workspace (with use directives).

The syncing is done by sequentially upgrading each of the dependency modules specified in a workspace module to the version in the build list if the dependency module's version is not already the same as the build list's version. Note that Minimal Version Selection guarantees that the build list's version of each module is always the same or higher than that in each workspace module.

See the workspaces reference at https://go.dev/ref/mod#workspaces for more information.

Add modules to workspace file

Usage:

go work use [-r] moddirs

Use provides a command-line interface for adding directories, optionally recursively, to a go.work file.

A use directive will be added to the go.work file for each argument directory listed on the command line go.work file, if it exists on disk, or removed from the go.work file if it does not exist on disk.

The -r flag searches recursively for modules in the argument directories, and the use command operates as if each of the directories were specified as arguments: namely, use directives will be added for directories that exist, and removed for directories that do not exist.

See the workspaces reference at https://go.dev/ref/mod#workspaces for more information.

Compile and run Go program

编译和执行程序 Usage:

go run [build flags] [-exec xprog] package [arguments...]

Run compiles and runs the named main Go package. Typically the package is specified as a list of .go source files from a single directory, but it may also be an import path, file system path, or pattern matching a single known package, as in 'go run .' or 'go run my/cmd'.

If the package argument has a version suffix (like @latest or @v1.0.0), "go run" builds the program in module-aware mode, ignoring the go.mod file in the current directory or any parent directory, if there is one. This is useful for running programs without affecting the dependencies of the main module.

If the package argument doesn't have a version suffix, "go run" may run in module-aware mode or GOPATH mode, depending on the GO111MODULE environment variable and the presence of a go.mod file. See 'go help modules' for details. If module-aware mode is enabled, "go run" runs in the context of the main module.

By default, 'go run' runs the compiled binary directly: 'a.out arguments...'. If the -exec flag is given, 'go run' invokes the binary using xprog:

'xprog a.out arguments...'.

If the -exec flag is not given, GOOS or GOARCH is different from the system default, and a program named go_$GOOS_$GOARCH_exec can be found on the current search path, 'go run' invokes the binary using that program, for example 'go_js_wasm_exec a.out arguments...'. This allows execution of cross-compiled programs when a simulator or other execution method is available.

By default, 'go run' compiles the binary without generating the information used by debuggers, to reduce build time. To include debugger information in the binary, use 'go build'.

The exit status of Run is not the exit status of the compiled binary.

For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'.

See also: go build.

Test packages

测试

Usage:

go test [build/test flags] [packages] [build/test flags & test binary flags]

'Go test' automates testing the packages named by the import paths. It prints a summary of the test results in the format:

ok   archive/tar   0.011s
FAIL archive/zip   0.022s
ok   compress/gzip 0.033s
...

followed by detailed output for each failed package.

'Go test' recompiles each package along with any files with names matching the file pattern "*test.go". These additional files can contain test functions, benchmark functions, fuzz tests and example functions. See 'go help testfunc' for more. Each listed package causes the execution of a separate test binary. Files whose names begin with "" (including "_test.go") or "." are ignored.

Test files that declare a package with the suffix "_test" will be compiled as a separate package, and then linked and run with the main test binary.

The go tool will ignore a directory named "testdata", making it available to hold ancillary data needed by the tests.

As part of building a test binary, go test runs go vet on the package and its test source files to identify significant problems. If go vet finds any problems, go test reports those and does not run the test binary. Only a high-confidence subset of the default go vet checks are used. That subset is: atomic, bool, buildtags, directive, errorsas, ifaceassert, nilfunc, printf, and stringintconv. You can see the documentation for these and other vet tests via "go doc cmd/vet". To disable the running of go vet, use the -vet=off flag. To run all checks, use the -vet=all flag.

All test output and summary lines are printed to the go command's standard output, even if the test printed them to its own standard error. (The go command's standard error is reserved for printing errors building the tests.)

Go test runs in two different modes:

The first, called local directory mode, occurs when go test is invoked with no package arguments (for example, 'go test' or 'go test -v'). In this mode, go test compiles the package sources and tests found in the current directory and then runs the resulting test binary. In this mode, caching (discussed below) is disabled. After the package test finishes, go test prints a summary line showing the test status ('ok' or 'FAIL'), package name, and elapsed time.

The second, called package list mode, occurs when go test is invoked with explicit package arguments (for example 'go test math', 'go test ./...', and even 'go test .'). In this mode, go test compiles and tests each of the packages listed on the command line. If a package test passes, go test prints only the final 'ok' summary line. If a package test fails, go test prints the full test output. If invoked with the -bench or -v flag, go test prints the full output even for passing package tests, in order to display the requested benchmark results or verbose logging. After the package tests for all of the listed packages finish, and their output is printed, go test prints a final 'FAIL' status if any package test has failed.

In package list mode only, go test caches successful package test results to avoid unnecessary repeated running of tests. When the result of a test can be recovered from the cache, go test will redisplay the previous output instead of running the test binary again. When this happens, go test prints '(cached)' in place of the elapsed time in the summary line.

The rule for a match in the cache is that the run involves the same test binary and the flags on the command line come entirely from a restricted set of 'cacheable' test flags, defined as -benchtime, -cpu, -list, -parallel, -run, -short, -timeout, -failfast, and -v. If a run of go test has any test or non-test flags outside this set, the result is not cached. To disable test caching, use any test flag or argument other than the cacheable flags. The idiomatic way to disable test caching explicitly is to use -count=1. Tests that open files within the package's source root (usually $GOPATH) or that consult environment variables only match future runs in which the files and environment variables are unchanged. A cached test result is treated as executing in no time at all, so a successful package test result will be cached and reused regardless of -timeout setting.

In addition to the build flags, the flags handled by 'go test' itself are:

-args
    Pass the remainder of the command line (everything after -args)
    to the test binary, uninterpreted and unchanged.
    Because this flag consumes the remainder of the command line,
    the package list (if present) must appear before this flag.

-c
    Compile the test binary to pkg.test but do not run it
    (where pkg is the last element of the package's import path).
    The file name can be changed with the -o flag.

-exec xprog
    Run the test binary using xprog. The behavior is the same as
    in 'go run'. See 'go help run' for details.

-json
    Convert test output to JSON suitable for automated processing.
    See 'go doc test2json' for the encoding details.

-o file
    Compile the test binary to the named file.
    The test still runs (unless -c or -i is specified).

The test binary also accepts flags that control execution of the test; these flags are also accessible by 'go test'. See 'go help testflag' for details.

For more about build flags, see 'go help build'. For more about specifying packages, see 'go help packages'.

See also: go build, go vet.

Run specified go tool

特殊用途的go 工具

Usage:

go tool [-n] command [args...]

Tool runs the go tool command identified by the arguments. With no arguments it prints the list of known tools.

The -n flag causes tool to print the command that would be executed but not execute it.

For more about each tool command, see 'go doc cmd/'.

Print Go version

Usage:

go version [-m] [-v] [file ...]

Version prints the build information for Go binary files.

Go version reports the Go version used to build each of the named files.

If no files are named on the command line, go version prints its own version information.

If a directory is named, go version walks that directory, recursively, looking for recognized Go binaries and reporting their versions. By default, go version does not report unrecognized files found during a directory scan. The -v flag causes it to report unrecognized files.

The -m flag causes go version to print each file's embedded module version information, when available. In the output, the module information consists of multiple lines following the version line, each indented by a leading tab character.

See also: go doc runtime/debug.BuildInfo.

Report likely mistakes in packages

Usage:

go vet [-C dir] [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]

Vet runs the Go vet command on the packages named by the import paths.

For more about vet and its flags, see 'go doc cmd/vet'. For more about specifying packages, see 'go help packages'. For a list of checkers and their flags, see 'go tool vet help'. For details of a specific checker such as 'printf', see 'go tool vet help printf'.

The -C flag changes to dir before running the 'go vet' command. The -n flag prints commands that would be executed. The -x flag prints commands as they are executed.

The -vettool=prog flag selects a different analysis tool with alternative or additional checks. For example, the 'shadow' analyzer can be built and run using these commands:

go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest
go vet -vettool=$(which shadow)

The build flags supported by go vet are those that control package resolution and execution, such as -n, -x, -v, -tags, and -toolexec. For more about these flags, see 'go help build'.

See also: go fmt, go fix.

Build constraints

构建约束

A build constraint, also known as a build tag, is a condition under which a file should be included in the package. Build constraints are given by a line comment that begins

//go:build

Constraints may appear in any kind of source file (not just Go), but they must appear near the top of the file, preceded only by blank lines and other line comments. These rules mean that in Go files a build constraint must appear before the package clause.

To distinguish build constraints from package documentation, a build constraint should be followed by a blank line.

A build constraint comment is evaluated as an expression containing build tags combined by ||, &&, and ! operators and parentheses. Operators have the same meaning as in Go.

For example, the following build constraint constrains a file to build when the "linux" and "386" constraints are satisfied, or when "darwin" is satisfied and "cgo" is not:

//go:build (linux && 386) || (darwin && !cgo)

It is an error for a file to have more than one //go:build line.

During a particular build, the following build tags are satisfied:

  • the target operating system, as spelled by runtime.GOOS, set with the GOOS environment variable.
  • the target architecture, as spelled by runtime.GOARCH, set with the GOARCH environment variable.
  • any architecture features, in the form GOARCH.feature (for example, "amd64.v2"), as detailed below.
  • "unix", if GOOS is a Unix or Unix-like system.
  • the compiler being used, either "gc" or "gccgo"
  • "cgo", if the cgo command is supported (see CGO_ENABLED in 'go help environment').
  • a term for each Go major release, through the current version: "go1.1" from Go version 1.1 onward, "go1.12" from Go 1.12, and so on.
  • any additional tags given by the -tags flag (see 'go help build').

There are no separate build tags for beta or minor releases.

If a file's name, after stripping the extension and a possible _test suffix, matches any of the following patterns:

*_GOOS
*_GOARCH
*_GOOS_GOARCH

(example: source_windows_amd64.go) where GOOS and GOARCH represent any known operating system and architecture values respectively, then the file is considered to have an implicit build constraint requiring those terms (in addition to any explicit constraints in the file).

Using GOOS=android matches build tags and files as for GOOS=linux in addition to android tags and files.

Using GOOS=illumos matches build tags and files as for GOOS=solaris in addition to illumos tags and files.

Using GOOS=ios matches build tags and files as for GOOS=darwin in addition to ios tags and files.

The defined architecture feature build tags are:

  • For GOARCH=386, GO386=387 and GO386=sse2 set the 386.387 and 386.sse2 build tags, respectively.
  • For GOARCH=amd64, GOAMD64=v1, v2, and v3 correspond to the amd64.v1, amd64.v2, and amd64.v3 feature build tags.
  • For GOARCH=arm, GOARM=5, 6, and 7 correspond to the arm.5, arm.6, and arm.7 feature build tags.
  • For GOARCH=mips or mipsle, GOMIPS=hardfloat and softfloat correspond to the mips.hardfloat and mips.softfloat (or mipsle.hardfloat and mipsle.softfloat) feature build tags.
  • For GOARCH=mips64 or mips64le, GOMIPS64=hardfloat and softfloat correspond to the mips64.hardfloat and mips64.softfloat (or mips64le.hardfloat and mips64le.softfloat) feature build tags.
  • For GOARCH=ppc64 or ppc64le, GOPPC64=power8, power9, and power10 correspond to the ppc64.power8, ppc64.power9, and ppc64.power10 (or ppc64le.power8, ppc64le.power9, and ppc64le.power10) feature build tags.
  • For GOARCH=wasm, GOWASM=satconv and signext correspond to the wasm.satconv and wasm.signext feature build tags.

For GOARCH=amd64, arm, ppc64, and ppc64le, a particular feature level sets the feature build tags for all previous levels as well. For example, GOAMD64=v2 sets the amd64.v1 and amd64.v2 feature flags. This ensures that code making use of v2 features continues to compile when, say, GOAMD64=v4 is introduced. Code handling the absence of a particular feature level should use a negation:

//go:build !amd64.v2

To keep a file from being considered for any build:

//go:build ignore

(Any other unsatisfied word will work as well, but "ignore" is conventional.)

To build a file only when using cgo, and only on Linux and OS X:

//go:build cgo && (linux || darwin)

Such a file is usually paired with another file implementing the default functionality for other systems, which in this case would carry the constraint:

//go:build !(cgo && (linux || darwin))

Naming a file dns_windows.go will cause it to be included only when building the package for Windows; similarly, math_386.s will be included only when building the package for 32-bit x86.

Go versions 1.16 and earlier used a different syntax for build constraints, with a "// +build" prefix. The gofmt command will add an equivalent //go:build constraint when encountering the older syntax.

Build modes

The 'go build' and 'go install' commands take a -buildmode argument which indicates which kind of object file is to be built. Currently supported values are:

-buildmode=archive
	Build the listed non-main packages into .a files. Packages named
	main are ignored.

-buildmode=c-archive
	Build the listed main package, plus all packages it imports,
	into a C archive file. The only callable symbols will be those
	functions exported using a cgo //export comment. Requires
	exactly one main package to be listed.

-buildmode=c-shared
	Build the listed main package, plus all packages it imports,
	into a C shared library. The only callable symbols will
	be those functions exported using a cgo //export comment.
	Requires exactly one main package to be listed.

-buildmode=default
	Listed main packages are built into executables and listed
	non-main packages are built into .a files (the default
	behavior).

-buildmode=shared
	Combine all the listed non-main packages into a single shared
	library that will be used when building with the -linkshared
	option. Packages named main are ignored.

-buildmode=exe
	Build the listed main packages and everything they import into
	executables. Packages not named main are ignored.

-buildmode=pie
	Build the listed main packages and everything they import into
	position independent executables (PIE). Packages not named
	main are ignored.

-buildmode=plugin
	Build the listed main packages, plus all packages that they
	import, into a Go plugin. Packages not named main are ignored.

On AIX, when linking a C program that uses a Go archive built with -buildmode=c-archive, you must pass -Wl,-bnoobjreorder to the C compiler.

Calling between Go and C

go 和 c 互相调用

There are two different ways to call between Go and C/C++ code.

The first is the cgo tool, which is part of the Go distribution. For information on how to use it see the cgo documentation (go doc cmd/cgo).

The second is the SWIG program, which is a general tool for interfacing between languages. For information on SWIG see http://swig.org/. When running go build, any file with a .swig extension will be passed to SWIG. Any file with a .swigcxx extension will be passed to SWIG with the -c++ option.

When either cgo or SWIG is used, go build will pass any .c, .m, .s, .S or .sx files to the C compiler, and any .cc, .cpp, .cxx files to the C++ compiler. The CC or CXX environment variables may be set to determine the C or C++ compiler, respectively, to use.

Build and test caching

The go command caches build outputs for reuse in future builds. The default location for cache data is a subdirectory named go-build in the standard user cache directory for the current operating system. Setting the GOCACHE environment variable overrides this default, and running 'go env GOCACHE' prints the current cache directory.

The go command periodically deletes cached data that has not been used recently. Running 'go clean -cache' deletes all cached data.

The build cache correctly accounts for changes to Go source files, compilers, compiler options, and so on: cleaning the cache explicitly should not be necessary in typical use. However, the build cache does not detect changes to C libraries imported with cgo. If you have made changes to the C libraries on your system, you will need to clean the cache explicitly or else use the -a build flag (see 'go help build') to force rebuilding of packages that depend on the updated C libraries.

The go command also caches successful package test results. See 'go help test' for details. Running 'go clean -testcache' removes all cached test results (but not cached build results).

The go command also caches values used in fuzzing with 'go test -fuzz', specifically, values that expanded code coverage when passed to a fuzz function. These values are not used for regular building and testing, but they're stored in a subdirectory of the build cache. Running 'go clean -fuzzcache' removes all cached fuzzing values. This may make fuzzing less effective, temporarily.

The GODEBUG environment variable can enable printing of debugging information about the state of the cache:

GODEBUG=gocacheverify=1 causes the go command to bypass the use of any cache entries and instead rebuild everything and check that the results match existing cache entries.

GODEBUG=gocachehash=1 causes the go command to print the inputs for all of the content hashes it uses to construct cache lookup keys. The output is voluminous but can be useful for debugging the cache.

GODEBUG=gocachetest=1 causes the go command to print details of its decisions about whether to reuse a cached test result.

Environment variables

The go command and the tools it invokes consult environment variables for configuration. If an environment variable is unset, the go command uses a sensible default setting. To see the effective setting of the variable , run 'go env '. To change the default setting, run 'go env -w ='. Defaults changed using 'go env -w' are recorded in a Go environment configuration file stored in the per-user configuration directory, as reported by os.UserConfigDir. The location of the configuration file can be changed by setting the environment variable GOENV, and 'go env GOENV' prints the effective location, but 'go env -w' cannot change the default location. See 'go help env' for details.

General-purpose environment variables:

GO111MODULE     //控制使用风格
	Controls whether the go command runs in module-aware mode or GOPATH mode.
	May be "off", "on", or "auto".
	See https://golang.org/ref/mod#mod-commands.
GCCGO           //编译器
	The gccgo command to run for 'go build -compiler=gccgo'.
GOARCH           //cou架构
	The architecture, or processor, for which to compile code.
	Examples are amd64, 386, arm, ppc64.
GOBIN
	The directory where 'go install' will install a command.
GOCACHE         //缓存目录
	The directory where the go command will store cached
	information for reuse in future builds.
GOMODCACHE
	The directory where the go command will store downloaded modules.
GODEBUG           //调试
	Enable various debugging facilities. See https://go.dev/doc/godebug
	for details.
GOENV
	The location of the Go environment configuration file.
	Cannot be set using 'go env -w'.
	Setting GOENV=off in the environment disables the use of the
	default configuration file.
GOFLAGS
	A space-separated list of -flag=value settings to apply
	to go commands by default, when the given flag is known by
	the current command. Each entry must be a standalone flag.
	Because the entries are space-separated, flag values must
	not contain spaces. Flags listed on the command line
	are applied after this list and therefore override it.
GOINSECURE
	Comma-separated list of glob patterns (in the syntax of Go's path.Match)
	of module path prefixes that should always be fetched in an insecure
	manner. Only applies to dependencies that are being fetched directly.
	GOINSECURE does not disable checksum database validation. GOPRIVATE or
	GONOSUMDB may be used to achieve that.
GOOS                                            //构建的os
	The operating system for which to compile code.
	Examples are linux, darwin, windows, netbsd.
GOPATH
	For more details see: 'go help gopath'.
GOPROXY
	URL of Go module proxy. See https://golang.org/ref/mod#environment-variables
	and https://golang.org/ref/mod#module-proxy for details.
GOPRIVATE, GONOPROXY, GONOSUMDB
	Comma-separated list of glob patterns (in the syntax of Go's path.Match)
	of module path prefixes that should always be fetched directly
	or that should not be compared against the checksum database.
	See https://golang.org/ref/mod#private-modules.
GOROOT
	The root of the go tree.
GOSUMDB
	The name of checksum database to use and optionally its public key and
	URL. See https://golang.org/ref/mod#authenticating.
GOTMPDIR
	The directory where the go command will write
	temporary source files, packages, and binaries.
GOVCS
	Lists version control commands that may be used with matching servers.
	See 'go help vcs'.
GOWORK
	In module aware mode, use the given go.work file as a workspace file.
	By default or when GOWORK is "auto", the go command searches for a
	file named go.work in the current directory and then containing directories
	until one is found. If a valid go.work file is found, the modules
	specified will collectively be used as the main modules. If GOWORK
	is "off", or a go.work file is not found in "auto" mode, workspace
	mode is disabled.

Environment variables for use with cgo: //cgo 环境变量

AR
	The command to use to manipulate library archives when
	building with the gccgo compiler.
	The default is 'ar'.
CC                                          //C语言编译器
	The command to use to compile C code.
CGO_ENABLED
	Whether the cgo command is supported. Either 0 or 1.
CGO_CFLAGS
	Flags that cgo will pass to the compiler when compiling
	C code.
CGO_CFLAGS_ALLOW
	A regular expression specifying additional flags to allow
	to appear in #cgo CFLAGS source code directives.
	Does not apply to the CGO_CFLAGS environment variable.
CGO_CFLAGS_DISALLOW
	A regular expression specifying flags that must be disallowed
	from appearing in #cgo CFLAGS source code directives.
	Does not apply to the CGO_CFLAGS environment variable.
CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW
	Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
	but for the C preprocessor.
CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW
	Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
	but for the C++ compiler.
CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW
	Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
	but for the Fortran compiler.
CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW
	Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
	but for the linker.
CXX
	The command to use to compile C++ code.
FC
	The command to use to compile Fortran code.
PKG_CONFIG
	Path to pkg-config tool.

Architecture-specific environment variables: //特殊环境变量

GOARM
	For GOARCH=arm, the ARM architecture for which to compile.
	Valid values are 5, 6, 7.
GO386
	For GOARCH=386, how to implement floating point instructions.
	Valid values are sse2 (default), softfloat.
GOAMD64
	For GOARCH=amd64, the microarchitecture level for which to compile.
	Valid values are v1 (default), v2, v3, v4.
	See https://golang.org/wiki/MinimumRequirements#amd64
GOMIPS
	For GOARCH=mips{,le}, whether to use floating point instructions.
	Valid values are hardfloat (default), softfloat.
GOMIPS64
	For GOARCH=mips64{,le}, whether to use floating point instructions.
	Valid values are hardfloat (default), softfloat.
GOPPC64
	For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture).
	Valid values are power8 (default), power9, power10.
GOWASM
	For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
	Valid values are satconv, signext.

Environment variables for use with code coverage:

GOCOVERDIR
	Directory into which to write code coverage data files
	generated by running a "go build -cover" binary.
	Requires that GOEXPERIMENT=coverageredesign is enabled.

Special-purpose environment variables:

GCCGOTOOLDIR
	If set, where to find gccgo tools, such as cgo.
	The default is based on how gccgo was configured.
GOEXPERIMENT
	Comma-separated list of toolchain experiments to enable or disable.
	The list of available experiments may change arbitrarily over time.
	See src/internal/goexperiment/flags.go for currently valid values.
	Warning: This variable is provided for the development and testing
	of the Go toolchain itself. Use beyond that purpose is unsupported.
GOROOT_FINAL
	The root of the installed Go tree, when it is
	installed in a location other than where it is built.
	File names in stack traces are rewritten from GOROOT to
	GOROOT_FINAL.
GO_EXTLINK_ENABLED
	Whether the linker should use external linking mode
	when using -linkmode=auto with code that uses cgo.
	Set to 0 to disable external linking mode, 1 to enable it.
GIT_ALLOW_PROTOCOL
	Defined by Git. A colon-separated list of schemes that are allowed
	to be used with git fetch/clone. If set, any scheme not explicitly
	mentioned will be considered insecure by 'go get'.
	Because the variable is defined by Git, the default value cannot
	be set using 'go env -w'.

Additional information available from 'go env' but not read from the environment:

GOEXE
	The executable file name suffix (".exe" on Windows, "" on other systems).
GOGCCFLAGS
	A space-separated list of arguments supplied to the CC command.
GOHOSTARCH
	The architecture (GOARCH) of the Go toolchain binaries.
GOHOSTOS
	The operating system (GOOS) of the Go toolchain binaries.
GOMOD
	The absolute path to the go.mod of the main module.
	If module-aware mode is enabled, but there is no go.mod, GOMOD will be
	os.DevNull ("/dev/null" on Unix-like systems, "NUL" on Windows).
	If module-aware mode is disabled, GOMOD will be the empty string.
GOTOOLDIR
	The directory where the go tools (compile, cover, doc, etc...) are installed.
GOVERSION
	The version of the installed Go tree, as reported by runtime.Version.

File types

The go command examines the contents of a restricted set of files in each directory. It identifies which files to examine based on the extension of the file name. These extensions are:

.go
	Go source files.
.c, .h
	C source files.
	If the package uses cgo or SWIG, these will be compiled with the
	OS-native compiler (typically gcc); otherwise they will
	trigger an error.
.cc, .cpp, .cxx, .hh, .hpp, .hxx
	C++ source files. Only useful with cgo or SWIG, and always
	compiled with the OS-native compiler.
.m
	Objective-C source files. Only useful with cgo, and always
	compiled with the OS-native compiler.
.s, .S, .sx
	Assembler source files.
	If the package uses cgo or SWIG, these will be assembled with the
	OS-native assembler (typically gcc (sic)); otherwise they
	will be assembled with the Go assembler.
.swig, .swigcxx
	SWIG definition files.
.syso
	System object files.

Files of each of these types except .syso may contain build constraints, but the go command stops scanning for build constraints at the first item in the file that is not a blank line or //-style line comment. See the go/build package documentation for more details.

The go.mod file

A module version is defined by a tree of source files, with a go.mod file in its root. When the go command is run, it looks in the current directory and then successive parent directories to find the go.mod marking the root of the main (current) module.

The go.mod file format is described in detail at https://golang.org/ref/mod#go-mod-file.

To create a new go.mod file, use 'go mod init'. For details see 'go help mod init' or https://golang.org/ref/mod#go-mod-init.

To add missing module requirements or remove unneeded requirements, use 'go mod tidy'. For details, see 'go help mod tidy' or https://golang.org/ref/mod#go-mod-tidy.

To add, upgrade, downgrade, or remove a specific module requirement, use 'go get'. For details, see 'go help module-get' or https://golang.org/ref/mod#go-get.

To make other changes or to parse go.mod as JSON for use by other tools, use 'go mod edit'. See 'go help mod edit' or https://golang.org/ref/mod#go-mod-edit.

GOPATH environment variable

相对引入

The Go path is used to resolve import statements. It is implemented by and documented in the go/build package.

The GOPATH environment variable lists places to look for Go code. On Unix, the value is a colon-separated string. On Windows, the value is a semicolon-separated string. On Plan 9, the value is a list.

If the environment variable is unset, GOPATH defaults to a subdirectory named "go" in the user's home directory ($HOME/go on Unix, %USERPROFILE%\go on Windows), unless that directory holds a Go distribution. Run "go env GOPATH" to see the current GOPATH.

See https://golang.org/wiki/SettingGOPATH to set a custom GOPATH.

Each directory listed in GOPATH must have a prescribed structure:

The src directory holds source code. The path below src determines the import path or executable name.

The pkg directory holds installed package objects. As in the Go tree, each target operating system and architecture pair has its own subdirectory of pkg (pkg/GOOS_GOARCH).

If DIR is a directory listed in the GOPATH, a package with source in DIR/src/foo/bar can be imported as "foo/bar" and has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".

The bin directory holds compiled commands. Each command is named for its source directory, but only the final element, not the entire path. That is, the command with source in DIR/src/foo/quux is installed into DIR/bin/quux, not DIR/bin/foo/quux. The "foo/" prefix is stripped so that you can add DIR/bin to your PATH to get at the installed commands. If the GOBIN environment variable is set, commands are installed to the directory it names instead of DIR/bin. GOBIN must be an absolute path.

Here's an example directory layout:

GOPATH=/home/user/go

/home/user/go/
    src/
        foo/
            bar/               (go code in package bar)
                x.go
            quux/              (go code in package main)
                y.go
    bin/
        quux                   (installed command)
    pkg/
        linux_amd64/
            foo/
                bar.a          (installed package object)

Go searches each directory listed in GOPATH to find source code, but new packages are always downloaded into the first directory in the list.

See https://golang.org/doc/code.html for an example.

GOPATH and Modules

When using modules, GOPATH is no longer used for resolving imports. However, it is still used to store downloaded source code (in GOPATH/pkg/mod) and compiled commands (in GOPATH/bin).

Internal Directories

Code in or below a directory named "internal" is importable only by code in the directory tree rooted at the parent of "internal". Here's an extended version of the directory layout above:

/home/user/go/
    src/
        crash/
            bang/              (go code in package bang)
                b.go
        foo/                   (go code in package foo)
            f.go
            bar/               (go code in package bar)
                x.go
            internal/
                baz/           (go code in package baz)
                    z.go
            quux/              (go code in package main)
                y.go

The code in z.go is imported as "foo/internal/baz", but that import statement can only appear in source files in the subtree rooted at foo. The source files foo/f.go, foo/bar/x.go, and foo/quux/y.go can all import "foo/internal/baz", but the source file crash/bang/b.go cannot.

See https://golang.org/s/go14internal for details.

Vendor Directories

Go 1.6 includes support for using local copies of external dependencies to satisfy imports of those dependencies, often referred to as vendoring.

Code below a directory named "vendor" is importable only by code in the directory tree rooted at the parent of "vendor", and only using an import path that omits the prefix up to and including the vendor element.

Here's the example from the previous section, but with the "internal" directory renamed to "vendor" and a new foo/vendor/crash/bang directory added:

/home/user/go/
    src/
        crash/
            bang/              (go code in package bang)
                b.go
        foo/                   (go code in package foo)
            f.go
            bar/               (go code in package bar)
                x.go
            vendor/
                crash/
                    bang/      (go code in package bang)
                        b.go
                baz/           (go code in package baz)
                    z.go
            quux/              (go code in package main)
                y.go

The same visibility rules apply as for internal, but the code in z.go is imported as "baz", not as "foo/vendor/baz".

Code in vendor directories deeper in the source tree shadows code in higher directories. Within the subtree rooted at foo, an import of "crash/bang" resolves to "foo/vendor/crash/bang", not the top-level "crash/bang".

Code in vendor directories is not subject to import path checking (see 'go help importpath').

When 'go get' checks out or updates a git repository, it now also updates submodules.

Vendor directories do not affect the placement of new repositories being checked out for the first time by 'go get': those are always placed in the main GOPATH, never in a vendor subtree.

See https://golang.org/s/go15vendor for details.

Legacy GOPATH go get

The 'go get' command changes behavior depending on whether the go command is running in module-aware mode or legacy GOPATH mode. This help text, accessible as 'go help gopath-get' even in module-aware mode, describes 'go get' as it operates in legacy GOPATH mode.

Usage: go get [-d] [-f] [-t] [-u] [-v] [-fix] [build flags] [packages]

Get downloads the packages named by the import paths, along with their dependencies. It then installs the named packages, like 'go install'.

The -d flag instructs get to stop after downloading the packages; that is, it instructs get not to install the packages.

The -f flag, valid only when -u is set, forces get -u not to verify that each package has been checked out from the source control repository implied by its import path. This can be useful if the source is a local fork of the original.

The -fix flag instructs get to run the fix tool on the downloaded packages before resolving dependencies or building the code.

The -t flag instructs get to also download the packages required to build the tests for the specified packages.

The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages.

The -v flag enables verbose progress and debug output.

Get also accepts build flags to control the installation. See 'go help build'.

When checking out a new package, get creates the target directory GOPATH/src/. If the GOPATH contains multiple entries, get uses the first one. For more details see: 'go help gopath'.

When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get searches for a branch or tag named "go1". If no such version exists it retrieves the default branch of the package.

When go get checks out or updates a Git repository, it also updates any git submodules referenced by the repository.

Get never checks out or updates code stored in vendor directories.

For more about build flags, see 'go help build'.

For more about specifying packages, see 'go help packages'.

For more about how 'go get' finds source code to download, see 'go help importpath'.

This text describes the behavior of get when using GOPATH to manage source code and dependencies. If instead the go command is running in module-aware mode, the details of get's flags and effects change, as does 'go help get'. See 'go help modules' and 'go help module-get'.

See also: go build, go install, go clean.

Module proxy protocol

A Go module proxy is any web server that can respond to GET requests for URLs of a specified form. The requests have no query parameters, so even a site serving from a fixed file system (including a file:/// URL) can be a module proxy.

For details on the GOPROXY protocol, see https://golang.org/ref/mod#goproxy-protocol.

Import path syntax

An import path (see 'go help packages') denotes a package stored in the local file system. In general, an import path denotes either a standard package (such as "unicode/utf8") or a package found in one of the work spaces (For more details see: 'go help gopath').

Relative import paths

An import path beginning with ./ or ../ is called a relative path. The toolchain supports relative import paths as a shortcut in two ways.

First, a relative path can be used as a shorthand on the command line. If you are working in the directory containing the code imported as "unicode" and want to run the tests for "unicode/utf8", you can type "go test ./utf8" instead of needing to specify the full path. Similarly, in the reverse situation, "go test .." will test "unicode" from the "unicode/utf8" directory. Relative patterns are also allowed, like "go test ./..." to test all subdirectories. See 'go help packages' for details on the pattern syntax.

Second, if you are compiling a Go program not in a work space, you can use a relative path in an import statement in that program to refer to nearby code also not in a work space. This makes it easy to experiment with small multipackage programs outside of the usual work spaces, but such programs cannot be installed with "go install" (there is no work space in which to install them), so they are rebuilt from scratch each time they are built. To avoid ambiguity, Go programs cannot use relative import paths within a work space.

Remote import paths

Certain import paths also describe how to obtain the source code for the package using a revision control system.

A few common code hosting sites have special syntax:

Bitbucket (Git, Mercurial)

	import "bitbucket.org/user/project"
	import "bitbucket.org/user/project/sub/directory"

GitHub (Git)

	import "github.com/user/project"
	import "github.com/user/project/sub/directory"

Launchpad (Bazaar)

	import "launchpad.net/project"
	import "launchpad.net/project/series"
	import "launchpad.net/project/series/sub/directory"

	import "launchpad.net/~user/project/branch"
	import "launchpad.net/~user/project/branch/sub/directory"

IBM DevOps Services (Git)

	import "hub.jazz.net/git/user/project"
	import "hub.jazz.net/git/user/project/sub/directory"

For code hosted on other servers, import paths may either be qualified with the version control type, or the go tool can dynamically fetch the import path over https/http and discover where the code resides from a tag in the HTML.

To declare the code location, an import path of the form

repository.vcs/path

specifies the given repository, with or without the .vcs suffix, using the named version control system, and then the path inside that repository. The supported version control systems are:

Bazaar      .bzr
Fossil      .fossil
Git         .git
Mercurial   .hg
Subversion  .svn

For example,

import "example.org/user/foo.hg"

denotes the root directory of the Mercurial repository at example.org/user/foo or foo.hg, and

import "example.org/repo.git/foo/bar"

denotes the foo/bar directory of the Git repository at example.org/repo or repo.git.

When a version control system supports multiple protocols, each is tried in turn when downloading. For example, a Git download tries https://, then git+ssh://.

By default, downloads are restricted to known secure protocols (e.g. https, ssh). To override this setting for Git downloads, the GIT_ALLOW_PROTOCOL environment variable can be set (For more details see: 'go help environment').

If the import path is not a known code hosting site and also lacks a version control qualifier, the go tool attempts to fetch the import over https/http and looks for a tag in the document's HTML

.

The meta tag has the form:

<meta name="go-import" content="import-prefix vcs repo-root">

The import-prefix is the import path corresponding to the repository root. It must be a prefix or an exact match of the package being fetched with "go get". If it's not an exact match, another http request is made at the prefix to verify the tags match.

The meta tag should appear as early in the file as possible. In particular, it should appear before any raw JavaScript or CSS, to avoid confusing the go command's restricted parser.

The vcs is one of "bzr", "fossil", "git", "hg", "svn".

The repo-root is the root of the version control system containing a scheme and not containing a .vcs qualifier.

For example,

import "example.org/pkg/foo"

will result in the following requests:

https://example.org/pkg/foo?go-get=1 (preferred)        //首选
http://example.org/pkg/foo?go-get=1  (fallback, only with use of correctly set GOINSECURE)  //兜底

If that page contains the meta tag

<meta name="go-import" content="example.org git https://code.org/r/p/exproj">

the go tool will verify that https://example.org/?go-get=1 contains the same meta tag and then git clone https://code.org/r/p/exproj into GOPATH/src/example.org.

When using GOPATH, downloaded packages are written to the first directory listed in the GOPATH environment variable. (See 'go help gopath-get' and 'go help gopath'.)

When using modules, downloaded packages are stored in the module cache. See https://golang.org/ref/mod#module-cache.

When using modules, an additional variant of the go-import meta tag is recognized and is preferred over those listing version control systems. That variant uses "mod" as the vcs in the content value, as in:

<meta name="go-import" content="example.org mod https://code.org/moduleproxy">

This tag means to fetch modules with paths beginning with example.org from the module proxy available at the URL https://code.org/moduleproxy. See https://golang.org/ref/mod#goproxy-protocol for details about the proxy protocol.

Import path checking

When the custom import path feature described above redirects to a known code hosting site, each of the resulting packages has two possible import paths, using the custom domain or the known hosting site.

A package statement is said to have an "import comment" if it is immediately followed (before the next newline) by a comment of one of these two forms:

package math // import "path"
package math /* import "path" */

The go command will refuse to install a package with an import comment unless it is being referred to by that import path. In this way, import comments let package authors make sure the custom import path is used and not a direct path to the underlying code hosting site.

Import path checking is disabled for code found within vendor trees. This makes it possible to copy code into alternate locations in vendor trees without needing to update import comments.

Import path checking is also disabled when using modules. Import path comments are obsoleted by the go.mod file's module statement.

See https://golang.org/s/go14customimport for details.

Modules, module versions, and more

Modules are how Go manages dependencies.

A module is a collection of packages that are released, versioned, and distributed together. Modules may be downloaded directly from version control repositories or from module proxy servers.

For a series of tutorials on modules, see https://golang.org/doc/tutorial/create-module.

For a detailed reference on modules, see https://golang.org/ref/mod.

By default, the go command may download modules from https://proxy.golang.org. It may authenticate modules using the checksum database at https://sum.golang.org. Both services are operated by the Go team at Google. The privacy policies for these services are available at https://proxy.golang.org/privacy and https://sum.golang.org/privacy, respectively.

The go command's download behavior may be configured using GOPROXY, GOSUMDB, GOPRIVATE, and other environment variables. See 'go help environment' and https://golang.org/ref/mod#private-module-privacy for more information.

Module authentication using go.sum

When the go command downloads a module zip file or go.mod file into the module cache, it computes a cryptographic hash and compares it with a known value to verify the file hasn't changed since it was first downloaded. Known hashes are stored in a file in the module root directory named go.sum. Hashes may also be downloaded from the checksum database depending on the values of GOSUMDB, GOPRIVATE, and GONOSUMDB.

For details, see https://golang.org/ref/mod#authenticating.

Package lists and patterns

Many commands apply to a set of packages:

go action [packages]

Usually, [packages] is a list of import paths.

An import path that is a rooted path or that begins with a . or .. element is interpreted as a file system path and denotes the package in that directory.

Otherwise, the import path P denotes the package found in the directory DIR/src/P for some DIR listed in the GOPATH environment variable (For more details see: 'go help gopath').

If no import paths are given, the action applies to the package in the current directory.

There are four reserved names for paths that should not be used for packages to be built with the go tool:

  • "main" denotes the top-level package in a stand-alone executable.

  • "all" expands to all packages found in all the GOPATH trees. For example, 'go list all' lists all the packages on the local system. When using modules, "all" expands to all packages in the main module and their dependencies, including dependencies needed by tests of any of those.

  • "std" is like all but expands to just the packages in the standard Go library.

  • "cmd" expands to the Go repository's commands and their internal libraries.

Import paths beginning with "cmd/" only match source code in the Go repository.

An import path is a pattern if it includes one or more "..." wildcards, each of which can match any string, including the empty string and strings containing slashes. Such a pattern expands to all package directories found in the GOPATH trees with names matching the patterns.

To make common patterns more convenient, there are two special cases. First, /... at the end of the pattern can match an empty string, so that net/... matches both net and packages in its subdirectories, like net/http. Second, any slash-separated pattern element containing a wildcard never participates in a match of the "vendor" element in the path of a vendored package, so that ./... does not match packages in subdirectories of ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. Note, however, that a directory named vendor that itself contains code is not a vendored package: cmd/vendor would be a command named vendor, and the pattern cmd/... matches it. See golang.org/s/go15vendor for more about vendoring.

An import path can also name a package to be downloaded from a remote repository. Run 'go help importpath' for details.

Every package in a program must have a unique import path. By convention, this is arranged by starting each path with a unique prefix that belongs to you. For example, paths used internally at Google all begin with 'google', and paths denoting remote repositories begin with the path to the code, such as 'github.com/user/repo'.

Packages in a program need not have unique package names, but there are two reserved package names with special meaning. The name main indicates a command, not a library. Commands are built into binaries and cannot be imported. The name documentation indicates documentation for a non-Go program in the directory. Files in package documentation are ignored by the go command.

As a special case, if the package list is a list of .go files from a single directory, the command is applied to a single synthesized package made up of exactly those files, ignoring any build constraints in those files and ignoring any other files in the directory.

Directory and file names that begin with "." or "_" are ignored by the go tool, as are directories named "testdata".

Configuration for downloading non-public code

配置和下载私有代码

The go command defaults to downloading modules from the public Go module mirror at proxy.golang.org. It also defaults to validating downloaded modules, regardless of source, against the public Go checksum database at sum.golang.org. These defaults work well for publicly available source code.

The GOPRIVATE environment variable controls which modules the go command considers to be private (not available publicly) and should therefore not use the proxy or checksum database. The variable is a comma-separated list of glob patterns (in the syntax of Go's path.Match) of module path prefixes. For example,

GOPRIVATE=*.corp.example.com,rsc.io/private

causes the go command to treat as private any module with a path prefix matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private, and rsc.io/private/quux.

For fine-grained control over module download and validation, the GONOPROXY and GONOSUMDB environment variables accept the same kind of glob list and override GOPRIVATE for the specific decision of whether to use the proxy and checksum database, respectively.

For example, if a company ran a module proxy serving private modules, users would configure go using:

GOPRIVATE=*.corp.example.com
GOPROXY=proxy.example.com
GONOPROXY=none

The GOPRIVATE variable is also used to define the "public" and "private" patterns for the GOVCS variable; see 'go help vcs'. For that usage, GOPRIVATE applies even in GOPATH mode. In that case, it matches import paths instead of module paths.

The 'go env -w' command (see 'go help env') can be used to set these variables for future go command invocations.

For more details, see https://golang.org/ref/mod#private-modules.

Testing flags

The 'go test' command takes both flags that apply to 'go test' itself and flags that apply to the resulting test binary.

Several of the flags control profiling and write an execution profile suitable for "go tool pprof"; run "go tool pprof -h" for more information. The --alloc_space, --alloc_objects, and --show_bytes options of pprof control how the information is presented.

The following flags are recognized by the 'go test' command and control the execution of any test:

-bench regexp
    Run only those benchmarks matching a regular expression.
    By default, no benchmarks are run.
    To run all benchmarks, use '-bench .' or '-bench=.'.
    The regular expression is split by unbracketed slash (/)
    characters into a sequence of regular expressions, and each
    part of a benchmark's identifier must match the corresponding
    element in the sequence, if any. Possible parents of matches
    are run with b.N=1 to identify sub-benchmarks. For example,
    given -bench=X/Y, top-level benchmarks matching X are run
    with b.N=1 to find any sub-benchmarks matching Y, which are
    then run in full.

-benchtime t
    Run enough iterations of each benchmark to take t, specified
    as a time.Duration (for example, -benchtime 1h30s).
    The default is 1 second (1s).
    The special syntax Nx means to run the benchmark N times
    (for example, -benchtime 100x).

-count n
    Run each test, benchmark, and fuzz seed n times (default 1).
    If -cpu is set, run n times for each GOMAXPROCS value.
    Examples are always run once. -count does not apply to
    fuzz tests matched by -fuzz.

-cover
    Enable coverage analysis.
    Note that because coverage works by annotating the source
    code before compilation, compilation and test failures with
    coverage enabled may report line numbers that don't correspond
    to the original sources.

-covermode set,count,atomic
    Set the mode for coverage analysis for the package[s]
    being tested. The default is "set" unless -race is enabled,
    in which case it is "atomic".
    The values:
	set: bool: does this statement run?
	count: int: how many times does this statement run?
	atomic: int: count, but correct in multithreaded tests;
		significantly more expensive.
    Sets -cover.

-coverpkg pattern1,pattern2,pattern3
    Apply coverage analysis in each test to packages matching the patterns.
    The default is for each test to analyze only the package being tested.
    See 'go help packages' for a description of package patterns.
    Sets -cover.

-cpu 1,2,4
    Specify a list of GOMAXPROCS values for which the tests, benchmarks or
    fuzz tests should be executed. The default is the current value
    of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz.

-failfast
    Do not start new tests after the first test failure.

-fullpath
    Show full file names in the error messages.

-fuzz regexp
    Run the fuzz test matching the regular expression. When specified,
    the command line argument must match exactly one package within the
    main module, and regexp must match exactly one fuzz test within
    that package. Fuzzing will occur after tests, benchmarks, seed corpora
    of other fuzz tests, and examples have completed. See the Fuzzing
    section of the testing package documentation for details.

-fuzztime t
    Run enough iterations of the fuzz target during fuzzing to take t,
    specified as a time.Duration (for example, -fuzztime 1h30s).
	The default is to run forever.
    The special syntax Nx means to run the fuzz target N times
    (for example, -fuzztime 1000x).

-fuzzminimizetime t
    Run enough iterations of the fuzz target during each minimization
    attempt to take t, as specified as a time.Duration (for example,
    -fuzzminimizetime 30s).
	The default is 60s.
    The special syntax Nx means to run the fuzz target N times
    (for example, -fuzzminimizetime 100x).

-json
    Log verbose output and test results in JSON. This presents the
    same information as the -v flag in a machine-readable format.

-list regexp
    List tests, benchmarks, fuzz tests, or examples matching the regular
    expression. No tests, benchmarks, fuzz tests, or examples will be run.
    This will only list top-level tests. No subtest or subbenchmarks will be
    shown.

-parallel n             //并发测试
    Allow parallel execution of test functions that call t.Parallel, and
    fuzz targets that call t.Parallel when running the seed corpus.
    The value of this flag is the maximum number of tests to run
    simultaneously.
    While fuzzing, the value of this flag is the maximum number of
    subprocesses that may call the fuzz function simultaneously, regardless of
    whether T.Parallel is called.
    By default, -parallel is set to the value of GOMAXPROCS.
    Setting -parallel to values higher than GOMAXPROCS may cause degraded
    performance due to CPU contention, especially when fuzzing.
    Note that -parallel only applies within a single test binary.
    The 'go test' command may run tests for different packages
    in parallel as well, according to the setting of the -p flag
    (see 'go help build').

-run regexp
    Run only those tests, examples, and fuzz tests matching the regular
    expression. For tests, the regular expression is split by unbracketed
    slash (/) characters into a sequence of regular expressions, and each
    part of a test's identifier must match the corresponding element in
    the sequence, if any. Note that possible parents of matches are
    run too, so that -run=X/Y matches and runs and reports the result
    of all tests matching X, even those without sub-tests matching Y,
    because it must run them to look for those sub-tests.
    See also -skip.

-short
    Tell long-running tests to shorten their run time.
    It is off by default but set during all.bash so that installing
    the Go tree can run a sanity check but not spend time running
    exhaustive tests.

-shuffle off,on,N
    Randomize the execution order of tests and benchmarks.
    It is off by default. If -shuffle is set to on, then it will seed
    the randomizer using the system clock. If -shuffle is set to an
    integer N, then N will be used as the seed value. In both cases,
    the seed will be reported for reproducibility.

-skip regexp
    Run only those tests, examples, fuzz tests, and benchmarks that
    do not match the regular expression. Like for -run and -bench,
    for tests and benchmarks, the regular expression is split by unbracketed
    slash (/) characters into a sequence of regular expressions, and each
    part of a test's identifier must match the corresponding element in
    the sequence, if any.

-timeout d
    If a test binary runs longer than duration d, panic.
    If d is 0, the timeout is disabled.
    The default is 10 minutes (10m).

-v
    Verbose output: log all tests as they are run. Also print all
    text from Log and Logf calls even if the test succeeds.

-vet list
    Configure the invocation of "go vet" during "go test"
    to use the comma-separated list of vet checks.
    If list is empty, "go test" runs "go vet" with a curated list of
    checks believed to be always worth addressing.
    If list is "off", "go test" does not run "go vet" at all.

The following flags are also recognized by 'go test' and can be used to profile the tests during execution:

-benchmem
    Print memory allocation statistics for benchmarks.

-blockprofile block.out
    Write a goroutine blocking profile to the specified file
    when all tests are complete.
    Writes test binary as -c would.

-blockprofilerate n
    Control the detail provided in goroutine blocking profiles by
    calling runtime.SetBlockProfileRate with n.
    See 'go doc runtime.SetBlockProfileRate'.
    The profiler aims to sample, on average, one blocking event every
    n nanoseconds the program spends blocked. By default,
    if -test.blockprofile is set without this flag, all blocking events
    are recorded, equivalent to -test.blockprofilerate=1.

-coverprofile cover.out
    Write a coverage profile to the file after all tests have passed.
    Sets -cover.

-cpuprofile cpu.out
    Write a CPU profile to the specified file before exiting.
    Writes test binary as -c would.

-memprofile mem.out
    Write an allocation profile to the file after all tests have passed.
    Writes test binary as -c would.

-memprofilerate n
    Enable more precise (and expensive) memory allocation profiles by
    setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'.
    To profile all memory allocations, use -test.memprofilerate=1.

-mutexprofile mutex.out
    Write a mutex contention profile to the specified file
    when all tests are complete.
    Writes test binary as -c would.

-mutexprofilefraction n
    Sample 1 in n stack traces of goroutines holding a
    contended mutex.

-outputdir directory
    Place output files from profiling in the specified directory,
    by default the directory in which "go test" is running.

-trace trace.out
    Write an execution trace to the specified file before exiting.

Each of these flags is also recognized with an optional 'test.' prefix, as in -test.v. When invoking the generated test binary (the result of 'go test -c') directly, however, the prefix is mandatory.

The 'go test' command rewrites or removes recognized flags, as appropriate, both before and after the optional package list, before invoking the test binary.

For instance, the command

go test -v -myflag testdata -cpuprofile=prof.out -x

will compile the test binary and then run it as

pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out

(The -x flag is removed because it applies only to the go command's execution, not to the test itself.)

The test flags that generate profiles (other than for coverage) also leave the test binary in pkg.test for use when analyzing the profiles.

When 'go test' runs a test binary, it does so from within the corresponding package's source code directory. Depending on the test, it may be necessary to do the same when invoking a generated test binary directly. Because that directory may be located within the module cache, which may be read-only and is verified by checksums, the test must not write to it or any other directory within the module unless explicitly requested by the user (such as with the -fuzz flag, which writes failures to testdata/fuzz).

The command-line package list, if present, must appear before any flag not known to the go test command. Continuing the example above, the package list would have to appear before -myflag, but could appear on either side of -v.

When 'go test' runs in package list mode, 'go test' caches successful package test results to avoid unnecessary repeated running of tests. To disable test caching, use any test flag or argument other than the cacheable flags. The idiomatic way to disable test caching explicitly is to use -count=1.

To keep an argument for a test binary from being interpreted as a known flag or a package name, use -args (see 'go help test') which passes the remainder of the command line through to the test binary uninterpreted and unaltered.

For instance, the command

go test -v -args -x -v

will compile the test binary and then run it as

pkg.test -test.v -x -v

Similarly,

go test -args math

will compile the test binary and then run it as

pkg.test math

In the first example, the -x and the second -v are passed through to the test binary unchanged and with no effect on the go command itself. In the second example, the argument math is passed through to the test binary, instead of being interpreted as the package list.

Testing functions

The 'go test' command expects to find test, benchmark, and example functions in the "*_test.go" files corresponding to the package under test.

A test function is one named TestXxx (where Xxx does not start with a lower case letter) and should have the signature,

func TestXxx(t *testing.T) { ... }      //单元测试

A benchmark function is one named BenchmarkXxx and should have the signature,

func BenchmarkXxx(b *testing.B) { ... } //性能测试

A fuzz test is one named FuzzXxx and should have the signature,

func FuzzXxx(f *testing.F) { ... }      //模糊测试

An example function is similar to a test function but, instead of using *testing.T to report success or failure, prints output to os.Stdout. If the last comment in the function starts with "Output:" then the output is compared exactly against the comment (see examples below). If the last comment begins with "Unordered output:" then the output is compared to the comment, however the order of the lines is ignored. An example with no such comment is compiled but not executed. An example with no text after "Output:" is compiled, executed, and expected to produce no output.

Godoc displays the body of ExampleXxx to demonstrate the use of the function, constant, or variable Xxx. An example of a method M with receiver type T or *T is named ExampleT_M. There may be multiple examples for a given function, constant, or variable, distinguished by a trailing _xxx, where xxx is a suffix not beginning with an upper case letter.

Here is an example of an example:

func ExamplePrintln() {                 //示例函数
	Println("The output of\nthis example.")
	// Output: The output of
	// this example.
}

Here is another example where the ordering of the output is ignored:

func ExamplePerm() {
	for _, value := range Perm(4) {
		fmt.Println(value)
	}

	// Unordered output: 4
	// 2
	// 1
	// 3
	// 0
}

The entire test file is presented as the example when it contains a single example function, at least one other function, type, variable, or constant declaration, and no tests, benchmarks, or fuzz tests.

See the documentation of the testing package for more information.

Controlling version control with GOVCS

The 'go get' command can run version control commands like git to download imported code. This functionality is critical to the decentralized Go package ecosystem, in which code can be imported from any server, but it is also a potential security problem, if a malicious server finds a way to cause the invoked version control command to run unintended code.

To balance the functionality and security concerns, the 'go get' command by default will only use git and hg to download code from public servers. But it will use any known version control system (bzr, fossil, git, hg, svn) to download code from private servers, defined as those hosting packages matching the GOPRIVATE variable (see 'go help private'). The rationale behind allowing only Git and Mercurial is that these two systems have had the most attention to issues of being run as clients of untrusted servers. In contrast, Bazaar, Fossil, and Subversion have primarily been used in trusted, authenticated environments and are not as well scrutinized as attack surfaces.

The version control command restrictions only apply when using direct version control access to download code. When downloading modules from a proxy, 'go get' uses the proxy protocol instead, which is always permitted. By default, the 'go get' command uses the Go module mirror (proxy.golang.org) for public packages and only falls back to version control for private packages or when the mirror refuses to serve a public package (typically for legal reasons). Therefore, clients can still access public code served from Bazaar, Fossil, or Subversion repositories by default, because those downloads use the Go module mirror, which takes on the security risk of running the version control commands using a custom sandbox.

The GOVCS variable can be used to change the allowed version control systems for specific packages (identified by a module or import path). The GOVCS variable applies when building package in both module-aware mode and GOPATH mode. When using modules, the patterns match against the module path. When using GOPATH, the patterns match against the import path corresponding to the root of the version control repository.

The general form of the GOVCS setting is a comma-separated list of pattern:vcslist rules. The pattern is a glob pattern that must match one or more leading elements of the module or import path. The vcslist is a pipe-separated list of allowed version control commands, or "all" to allow use of any known command, or "off" to disallow all commands. Note that if a module matches a pattern with vcslist "off", it may still be downloaded if the origin server uses the "mod" scheme, which instructs the go command to download the module using the GOPROXY protocol. The earliest matching pattern in the list applies, even if later patterns might also match.

For example, consider:

GOVCS=github.com:git,evil.com:off,*:git|hg

With this setting, code with a module or import path beginning with github.com/ can only use git; paths on evil.com cannot use any version control command, and all other paths (* matches everything) can use only git or hg.

The special patterns "public" and "private" match public and private module or import paths. A path is private if it matches the GOPRIVATE variable; otherwise it is public.

If no rules in the GOVCS variable match a particular module or import path, the 'go get' command applies its default rule, which can now be summarized in GOVCS notation as 'public:git|hg,private:all'.

To allow unfettered use of any version control system for any package, use:

GOVCS=*:all

To disable all use of version control, use:

GOVCS=*:off

The 'go env -w' command (see 'go help env') can be used to set the GOVCS variable for future go command invocations.

介绍

go 和大部分其他语言一样使用相同的内存模型 不同的协程对数据的访问都使用 sync 和 atomic 处理 不要自作聪明

内存模型

The following formal definition of Go's memory model closely follows the approach presented by Hans-J. Boehm and Sarita V. Adve in “Foundations of the C++ Concurrency Memory Model”, published in PLDI 2008. The definition of data-race-free programs and the guarantee of sequential consistency for race-free programs are equivalent to the ones in that work.

The memory model describes the requirements on program executions, which are made up of goroutine executions, which in turn are made up of memory operations.

内存模型描述了对程序执行的要求,这些要求由 goroutine 执行组成,而 goroutine 执行又由内存操作组成。

A memory operation is modeled by four details:

  • its kind, indicating whether it is an ordinary data read, an ordinary data write, or a synchronizing operation such as an atomic data access, a mutex operation, or a channel operation,
  • its location in the program,
  • the memory location or variable being accessed, and
  • the values read or written by the operation.
  • 它的种类,指示它是普通数据读取、普通数据写入还是同步操作,例如原子数据访问、互斥操作或通道操作,
  • 它在程序中的位置
  • 正在访问的内存位置或变量
  • 操作读取或写入的值

Some memory operations are read-like, including read, atomic read, mutex lock, and channel receive. Other memory operations are write-like, including write, atomic write, mutex unlock, channel send, and channel close. Some, such as atomic compare-and-swap, are both read-like and write-like.

A goroutine execution is modeled as a set of memory operations executed by a single goroutine.

Requirement 1: The memory operations in each goroutine must correspond to a correct sequential execution of that goroutine, given the values read from and written to memory. That execution must be consistent with the sequenced before relation, defined as the partial order requirements set out by the Go language specification for Go's control flow constructs as well as the order of evaluation for expressions.

A Go program execution is modeled as a set of goroutine executions, together with a mapping W that specifies the write-like operation that each read-like operation reads from. (Multiple executions of the same program can have different program executions.)

Requirement 2: For a given program execution, the mapping W, when limited to synchronizing operations, must be explainable by some implicit total order of the synchronizing operations that is consistent with sequencing and the values read and written by those operations.

The synchronized before relation is a partial order on synchronizing memory operations, derived from W. If a synchronizing read-like memory operation r observes a synchronizing write-like memory operation w (that is, if W(r) = w), then w is synchronized before r. Informally, the synchronized before relation is a subset of the implied total order mentioned in the previous paragraph, limited to the information that W directly observes.

The happens before relation is defined as the transitive closure of the union of the sequenced before and synchronized before relations.

Requirement 3: For an ordinary (non-synchronizing) data read r on a memory location x, W(r) must be a write w that is visible to r, where visible means that both of the following hold:

  1. w happens before r.
  2. w does not happen before any other write w' (to x) that happens before r.

A read-write data race on memory location x consists of a read-like memory operation r on x and a write-like memory operation w on x, at least one of which is non-synchronizing, which are unordered by happens before (that is, neither r happens before w nor w happens before r).

A write-write data race on memory location x consists of two write-like memory operations w and w' on x, at least one of which is non-synchronizing, which are unordered by happens before.

Note that if there are no read-write or write-write data races on memory location x, then any read r on x has only one possible W(r): the single w that immediately precedes it in the happens before order.

More generally, it can be shown that any Go program that is data-race-free, meaning it has no program executions with read-write or write-write data races, can only have outcomes explained by some sequentially consistent interleaving of the goroutine executions. (The proof is the same as Section 7 of Boehm and Adve's paper cited above.) This property is called DRF-SC.

The intent of the formal definition is to match the DRF-SC guarantee provided to race-free programs by other languages, including C, C++, Java, JavaScript, Rust, and Swift.

Certain Go language operations such as goroutine creation and memory allocation act as synchronization operations. The effect of these operations on the synchronized-before partial order is documented in the “Synchronization” section below. Individual packages are responsible for providing similar documentation for their own operations.

Implementation Restrictions for Programs Containing Data Races 包含数据竞争的程序的实施限制

The preceding section gave a formal definition of data-race-free program execution. This section informally describes the semantics that implementations must provide for programs that do contain races.

First, any implementation can, upon detecting a data race, report the race and halt execution of the program. Implementations using ThreadSanitizer (accessed with “go build -race”) do exactly this.

Otherwise, a read r of a memory location x that is not larger than a machine word must observe some write w such that r does not happen before w and there is no write w' such that w happens before w' and w' happens before r. That is, each read must observe a value written by a preceding or concurrent write.

Additionally, observation of acausal and “out of thin air” writes is disallowed.

Reads of memory locations larger than a single machine word are encouraged but not required to meet the same semantics as word-sized memory locations, observing a single allowed write w. For performance reasons, implementations may instead treat larger operations as a set of individual machine-word-sized operations in an unspecified order. This means that races on multiword data structures can lead to inconsistent values not corresponding to a single write. When the values depend on the consistency of internal (pointer, length) or (pointer, type) pairs, as can be the case for interface values, maps, slices, and strings in most Go implementations, such races can in turn lead to arbitrary memory corruption.

Examples of incorrect synchronization are given in the “Incorrect synchronization” section below.

Examples of the limitations on implementations are given in the “Incorrect compilation” section below.

Synchronization 同步

Initialization 初始化

Program initialization runs in a single goroutine, but that goroutine may create other goroutines, which run concurrently.

If a package p imports package q, the completion of q's init functions happens before the start of any of p's.

The completion of all init functions is synchronized before the start of the function main.main.

Goroutine creation 协程创建

The go statement that starts a new goroutine is synchronized before the start of the goroutine's execution.

For example, in this program:

var a string

func f() {
	print(a)
}

func hello() {
	a = "hello, world"
	go f()
}

calling hello will print "hello, world" at some point in the future (perhaps after hello has returned).

Goroutine destruction 协程销毁

The exit of a goroutine is not guaranteed to be synchronized before any event in the program. For example, in this program:

var a string

func hello() {
	go func() { a = "hello" }()
	print(a)
}

the assignment to a is not followed by any synchronization event, so it is not guaranteed to be observed by any other goroutine. In fact, an aggressive compiler might delete the entire go statement.

If the effects of a goroutine must be observed by another goroutine, use a synchronization mechanism such as a lock or channel communication to establish a relative ordering.

Channel communication channel 通讯

Channel communication is the main method of synchronization between goroutines. Each send on a particular channel is matched to a corresponding receive from that channel, usually in a different goroutine.

A send on a channel is synchronized before the completion of the corresponding receive from that channel.

This program:

var c = make(chan int, 10)
var a string

func f() {
	a = "hello, world"
	c <- 0
}

func main() {
	go f()
	<-c
	print(a)
}

is guaranteed to print "hello, world". The write to a is sequenced before the send on c, which is synchronized before the corresponding receive on c completes, which is sequenced before the print.

The closing of a channel is synchronized before a receive that returns a zero value because the channel is closed.

In the previous example, replacing c <- 0 with close(c) yields a program with the same guaranteed behavior.

A receive from an unbuffered channel is synchronized before the completion of the corresponding send on that channel.

This program (as above, but with the send and receive statements swapped and using an unbuffered channel):

var c = make(chan int)
var a string

func f() {
	a = "hello, world"
	<-c
}

func main() {
	go f()
	c <- 0
	print(a)
}

is also guaranteed to print "hello, world". The write to a is sequenced before the receive on c, which is synchronized before the corresponding send on c completes, which is sequenced before the print.

If the channel were buffered (e.g., c = make(chan int, 1)) then the program would not be guaranteed to print "hello, world". (It might print the empty string, crash, or do something else.)

The _k_th receive on a channel with capacity C is synchronized before the completion of the k+_C_th send from that channel completes.

This rule generalizes the previous rule to buffered channels. It allows a counting semaphore to be modeled by a buffered channel: the number of items in the channel corresponds to the number of active uses, the capacity of the channel corresponds to the maximum number of simultaneous uses, sending an item acquires the semaphore, and receiving an item releases the semaphore. This is a common idiom for limiting concurrency.

This program starts a goroutine for every entry in the work list, but the goroutines coordinate using the limit channel to ensure that at most three are running work functions at a time.

var limit = make(chan int, 3)

func main() {
	for _, w := range work {
		go func(w func()) {
			limit <- 1
			w()
			<-limit
		}(w)
	}
	select{}
}

Locks 锁

The sync package implements two lock data types, sync.Mutex and sync.RWMutex.

For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() is synchronized before call m of l.Lock() returns.

This program:

var l sync.Mutex
var a string

func f() {
	a = "hello, world"
	l.Unlock()
}

func main() {
	l.Lock()
	go f()
	l.Lock()
	print(a)
}

is guaranteed to print "hello, world". The first call to l.Unlock() (in f) is synchronized before the second call to l.Lock() (in main) returns, which is sequenced before the print.

For any call to l.RLock on a sync.RWMutex variable l, there is an n such that the _n_th call to l.Unlock is synchronized before the return from l.RLock, and the matching call to l.RUnlock is synchronized before the return from call n+1 to l.Lock.

A successful call to l.TryLock (or l.TryRLock) is equivalent to a call to l.Lock (or l.RLock). An unsuccessful call has no synchronizing effect at all. As far as the memory model is concerned, l.TryLock (or l.TryRLock) may be considered to be able to return false even when the mutex l is unlocked.

Once 只执行一次

The sync package provides a safe mechanism for initialization in the presence of multiple goroutines through the use of the Once type. Multiple threads can execute once.Do(f) for a particular f, but only one will run f(), and the other calls block until f() has returned.

The completion of a single call of f() from once.Do(f) is synchronized before the return of any call of once.Do(f).

In this program:


var a string
var once sync.Once

func setup() {
	a = "hello, world"
}

func doprint() {
	once.Do(setup)
	print(a)
}

func twoprint() {
	go doprint()
	go doprint()
}

calling twoprint will call setup exactly once. The setup function will complete before either call of print. The result will be that "hello, world" will be printed twice.

Atomic Values 原子值

The APIs in the sync/atomic package are collectively “atomic operations” that can be used to synchronize the execution of different goroutines. If the effect of an atomic operation A is observed by atomic operation B, then A is synchronized before B. All the atomic operations executed in a program behave as though executed in some sequentially consistent order.

The preceding definition has the same semantics as C++’s sequentially consistent atomics and Java’s volatile variables.

Finalizers 终结器

The runtime package provides a SetFinalizer function that adds a finalizer to be called when a particular object is no longer reachable by the program. A call to SetFinalizer(x, f) is synchronized before the finalization call f(x).

Additional Mechanisms 附加机制

The sync package provides additional synchronization abstractions, including condition variables, lock-free maps, allocation pools, and wait groups. The documentation for each of these specifies the guarantees it makes concerning synchronization.

Other packages that provide synchronization abstractions should document the guarantees they make too.

Incorrect synchronization 错误示范

Programs with races are incorrect and can exhibit non-sequentially consistent executions. In particular, note that a read r may observe the value written by any write w that executes concurrently with r. Even if this occurs, it does not imply that reads happening after r will observe writes that happened before w.

In this program:

var a, b int

func f() {
	a = 1
	b = 2
}

func g() {
	print(b)
	print(a)
}

func main() {
	go f()
	g()
}

it can happen that g prints 2 and then 0.

This fact invalidates a few common idioms.

Double-checked locking is an attempt to avoid the overhead of synchronization. For example, the twoprint program might be incorrectly written as:

var a string
var done bool

func setup() {
	a = "hello, world"
	done = true
}

func doprint() {
	if !done {
		once.Do(setup)
	}
	print(a)
}

func twoprint() {
	go doprint()
	go doprint()
}

but there is no guarantee that, in doprint, observing the write to done implies observing the write to a. This version can (incorrectly) print an empty string instead of "hello, world".

Another incorrect idiom is busy waiting for a value, as in:

var a string
var done bool

func setup() {
	a = "hello, world"
	done = true
}

func main() {
	go setup()
	for !done {
	}
	print(a)
}

As before, there is no guarantee that, in main, observing the write to done implies observing the write to a, so this program could print an empty string too. Worse, there is no guarantee that the write to done will ever be observed by main, since there are no synchronization events between the two threads. The loop in main is not guaranteed to finish.

There are subtler variants on this theme, such as this program.

type T struct {
	msg string
}

var g \*T

func setup() {
	t := new(T)
	t.msg = "hello, world"
	g = t
}

func main() {
	go setup()
	for g == nil {
	}
	print(g.msg)
}

Even if main observes g != nil and exits its loop, there is no guarantee that it will observe the initialized value for g.msg.

In all these examples, the solution is the same: use explicit synchronization.

Incorrect compilation 编译错误

The Go memory model restricts compiler optimizations as much as it does Go programs. Some compiler optimizations that would be valid in single-threaded programs are not valid in all Go programs. In particular, a compiler must not introduce writes that do not exist in the original program, it must not allow a single read to observe multiple values, and it must not allow a single write to write multiple values.

All the following examples assume that `*p` and `*q` refer to memory locations accessible to multiple goroutines.

Not introducing data races into race-free programs means not moving writes out of conditional statements in which they appear. For example, a compiler must not invert the conditional in this program:

*p = 1
if cond {
	*p = 2
}

That is, the compiler must not rewrite the program into this one:

*p = 2
if !cond {
	*p = 1
}

If cond is false and another goroutine is reading *p, then in the original program, the other goroutine can only observe any prior value of *p and 1. In the rewritten program, the other goroutine can observe 2, which was previously impossible.

Not introducing data races also means not assuming that loops terminate. For example, a compiler must in general not move the accesses to *p or *q ahead of the loop in this program:

n := 0
for e := list; e != nil; e = e.next {
	n++
}
i := *p
*q = 1

If list pointed to a cyclic list, then the original program would never access *p or *q, but the rewritten program would. (Moving `*p` ahead would be safe if the compiler can prove `*p` will not panic; moving `*q` ahead would also require the compiler proving that no other goroutine can access `*q`.)

Not introducing data races also means not assuming that called functions always return or are free of synchronization operations. For example, a compiler must not move the accesses to *p or *q ahead of the function call in this program (at least not without direct knowledge of the precise behavior of f):

f()
i := *p
*q = 1

If the call never returned, then once again the original program would never access *p or *q, but the rewritten program would. And if the call contained synchronizing operations, then the original program could establish happens before edges preceding the accesses to *p and *q, but the rewritten program would not.

Not allowing a single read to observe multiple values means not reloading local variables from shared memory. For example, a compiler must not discard i and reload it a second time from *p in this program:

i := *p
if i < 0 || i >= len(funcs) {
	panic("invalid function index")
}
... complex code ...
// compiler must NOT reload i = *p here
funcs[i]()

If the complex code needs many registers, a compiler for single-threaded programs could discard i without saving a copy and then reload i = *p just before funcs[i](). A Go compiler must not, because the value of *p may have changed. (Instead, the compiler could spill i to the stack.)

Not allowing a single write to write multiple values also means not using the memory where a local variable will be written as temporary storage before the write. For example, a compiler must not use *p as temporary storage in this program:

*p = i + *p/2

That is, it must not rewrite the program into this one:

*p /= 2
*p += i

If i and *p start equal to 2, the original code does *p = 3, so a racing thread can read only 2 or 3 from *p. The rewritten code does *p = 1 and then *p = 3, allowing a racing thread to read 1 as well.

Note that all these optimizations are permitted in C/C++ compilers: a Go compiler sharing a back end with a C/C++ compiler must take care to disable optimizations that are invalid for Go.

Note that the prohibition on introducing data races does not apply if the compiler can prove that the races do not affect correct execution on the target platform. For example, on essentially all CPUs, it is valid to rewrite

n := 0
for i := 0; i < m; i++ {
	n += \*shared
}

into:

n := 0
local := \*shared
for i := 0; i < m; i++ {
	n += local
}

provided it can be proved that *shared will not fault on access, because the potential added read will not affect any existing concurrent reads or writes. On the other hand, the rewrite would not be valid in a source-to-source translator.

结尾

Go programmers writing data-race-free programs can rely on sequentially consistent execution of those programs, just as in essentially all other modern programming languages.

When it comes to programs with races, both programmers and compilers should remember the advice: don't be clever.

Data Race Detector(竞态检测)

什么是竞态

两个协程至少一个写入变量的时候发生竞争

Here is an example of a data race that can lead to crashes and memory corruption:

func main() {
	c := make(chan bool)
	m := make(map[string]string)
	go func() {
		m["1"] = "a" // First conflicting access.
		c <- true
	}()
	m["2"] = "b" // Second conflicting access.
	<-c
	for k, v := range m {
		fmt.Println(k, v)
	}
}

如何使用 使用 -race 编译参数

go run -race  main.go 

问题

首先,使用竞争检测器 (go test -race) 运行测试。 竞争检测器只能发现在运行时发生的竞争,因此它无法在未执行的代码路径中发现竞争。 如果您的测试覆盖不完整,您可能会通过在实际工作负载下运行使用 -race 构建的二进制文件来发现更多竞争。

常见案例

循环计数器

func main() {
	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func() {
			fmt.Println(i) // Not the 'i' you are looking for.
			wg.Done()
		}()
	}
	wg.Wait()
}

通常输出 55555

修复

func main() {
	var wg sync.WaitGroup
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go func(j int) {
			fmt.Println(j) // Good. Read local copy of the loop counter.
			wg.Done()
		}(i)
	}
	wg.Wait()
}

共享变量

// ParallelWrite writes data to file1 and file2, returns the errors.
func ParallelWrite(data []byte) chan error {
	res := make(chan error, 2)
	f1, err := os.Create("file1")
	if err != nil {
		res <- err
	} else {
		go func() {
			// This err is shared with the main goroutine,
			// so the write races with the write below.
			_, err = f1.Write(data)
			res <- err
			f1.Close()
		}()
	}
	f2, err := os.Create("file2") // The second conflicting write to err.
	if err != nil {
		res <- err
	} else {
		go func() {
			_, err = f2.Write(data)
			res <- err
			f2.Close()
		}()
	}
	return res
}

修复

            ...
			_, err := f1.Write(data)
			...
			_, err := f2.Write(data)
			...

没有同步访问的全局变量

var service map[string]net.Addr

func RegisterService(name string, addr net.Addr) {
	service[name] = addr
}

func LookupService(name string) net.Addr {
	return service[name]
}

修复

var (
	service   map[string]net.Addr
	serviceMu sync.Mutex
)

func RegisterService(name string, addr net.Addr) {
	serviceMu.Lock()
	defer serviceMu.Unlock()
	service[name] = addr
}

func LookupService(name string) net.Addr {
	serviceMu.Lock()
	defer serviceMu.Unlock()
	return service[name]
}

未保护的原始变量

type Watchdog struct{ last int64 }

func (w *Watchdog) KeepAlive() {
	w.last = time.Now().UnixNano() // First conflicting access.
}

func (w *Watchdog) Start() {
	go func() {
		for {
			time.Sleep(time.Second)
			// Second conflicting access.
			if w.last < time.Now().Add(-10*time.Second).UnixNano() {
				fmt.Println("No keepalives for 10 seconds. Dying.")
				os.Exit(1)
			}
		}
	}()
}

无锁修复

type Watchdog struct{ last int64 }

func (w *Watchdog) KeepAlive() {
	atomic.StoreInt64(&w.last, time.Now().UnixNano())
}

func (w *Watchdog) Start() {
	go func() {
		for {
			time.Sleep(time.Second)
			if atomic.LoadInt64(&w.last) < time.Now().Add(-10*time.Second).UnixNano() {
				fmt.Println("No keepalives for 10 seconds. Dying.")
				os.Exit(1)
			}
		}
	}()
}

不同步的发送和关闭操作

c := make(chan struct{}) // or buffered channel

// The race detector cannot derive the happens before relation
// for the following send and close operations. These two operations
// are unsynchronized and happen concurrently.
go func() { c <- struct{}{} }()
close(c)

修复

c := make(chan struct{}) // or buffered channel

go func() { c <- struct{}{} }()
<-c
close(c)

开启竞态分析的开销

内存 5-10倍 执行事件 2-20 倍

竞争检测器目前为每个延迟和恢复语句额外分配 8 个字节。 在 goroutine 退出之前,这些额外的分配不会被回收。 这意味着,如果您有一个长时间运行的 goroutine,它会定期发出 defer 和 recover 调用,程序内存使用量可能会无限增长。 这些内存分配不会显示在 runtime.ReadMemStats 或 runtime/pprof 的输出中。

汇编快速入门指南

This document is a quick outline of the unusual form of assembly language used by the gc Go compiler. The document is not comprehensive.

The assembler is based on the input style of the Plan 9 assemblers, which is documented in detail elsewhere. If you plan to write assembly language, you should read that document although much of it is Plan 9-specific. The current document provides a summary of the syntax and the differences with what is explained in that document, and describes the peculiarities that apply when writing assembly code to interact with Go.

The most important thing to know about Go's assembler is that it is not a direct representation of the underlying machine. Some of the details map precisely to the machine, but some do not. This is because the compiler suite (see this description) needs no assembler pass in the usual pipeline. Instead, the compiler operates on a kind of semi-abstract instruction set, and instruction selection occurs partly after code generation. The assembler works on the semi-abstract form, so when you see an instruction like MOV what the toolchain actually generates for that operation might not be a move instruction at all, perhaps a clear or load. Or it might correspond exactly to the machine instruction with that name. In general, machine-specific operations tend to appear as themselves, while more general concepts like memory move and subroutine call and return are more abstract. The details vary with architecture, and we apologize for the imprecision; the situation is not well-defined.

The assembler program is a way to parse a description of that semi-abstract instruction set and turn it into instructions to be input to the linker. If you want to see what the instructions look like in assembly for a given architecture, say amd64, there are many examples in the sources of the standard library, in packages such as runtime and math/big. You can also examine what the compiler emits as assembly code (the actual output may differ from what you see here):

$ cat x.go
package main

func main() {
	println(3)
}
$ GOOS=linux GOARCH=amd64 go tool compile -S x.go        # or: go build -gcflags -S x.go
"".main STEXT size=74 args=0x0 locals=0x10
	0x0000 00000 (x.go:3)	TEXT	"".main(SB), $16-0
	0x0000 00000 (x.go:3)	MOVQ	(TLS), CX
	0x0009 00009 (x.go:3)	CMPQ	SP, 16(CX)
	0x000d 00013 (x.go:3)	JLS	67
	0x000f 00015 (x.go:3)	SUBQ	$16, SP
	0x0013 00019 (x.go:3)	MOVQ	BP, 8(SP)
	0x0018 00024 (x.go:3)	LEAQ	8(SP), BP
	0x001d 00029 (x.go:3)	FUNCDATA	$0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (x.go:3)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (x.go:3)	FUNCDATA	$2, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x001d 00029 (x.go:4)	PCDATA	$0, $0
	0x001d 00029 (x.go:4)	PCDATA	$1, $0
	0x001d 00029 (x.go:4)	CALL	runtime.printlock(SB)
	0x0022 00034 (x.go:4)	MOVQ	$3, (SP)
	0x002a 00042 (x.go:4)	CALL	runtime.printint(SB)
	0x002f 00047 (x.go:4)	CALL	runtime.printnl(SB)
	0x0034 00052 (x.go:4)	CALL	runtime.printunlock(SB)
	0x0039 00057 (x.go:5)	MOVQ	8(SP), BP
	0x003e 00062 (x.go:5)	ADDQ	$16, SP
	0x0042 00066 (x.go:5)	RET
	0x0043 00067 (x.go:5)	NOP
	0x0043 00067 (x.go:3)	PCDATA	$1, $-1
	0x0043 00067 (x.go:3)	PCDATA	$0, $-1
	0x0043 00067 (x.go:3)	CALL	runtime.morestack_noctxt(SB)
	0x0048 00072 (x.go:3)	JMP	0
...

The FUNCDATA and PCDATA directives contain information for use by the garbage collector; they are introduced by the compiler.

To see what gets put in the binary after linking, use go tool objdump:

$ go build -o x.exe x.go
$ go tool objdump -s main.main x.exe
TEXT main.main(SB) /tmp/x.go
  x.go:3		0x10501c0		65488b0c2530000000	MOVQ GS:0x30, CX
  x.go:3		0x10501c9		483b6110		CMPQ 0x10(CX), SP
  x.go:3		0x10501cd		7634			JBE 0x1050203
  x.go:3		0x10501cf		4883ec10		SUBQ $0x10, SP
  x.go:3		0x10501d3		48896c2408		MOVQ BP, 0x8(SP)
  x.go:3		0x10501d8		488d6c2408		LEAQ 0x8(SP), BP
  x.go:4		0x10501dd		e86e45fdff		CALL runtime.printlock(SB)
  x.go:4		0x10501e2		48c7042403000000	MOVQ $0x3, 0(SP)
  x.go:4		0x10501ea		e8e14cfdff		CALL runtime.printint(SB)
  x.go:4		0x10501ef		e8ec47fdff		CALL runtime.printnl(SB)
  x.go:4		0x10501f4		e8d745fdff		CALL runtime.printunlock(SB)
  x.go:5		0x10501f9		488b6c2408		MOVQ 0x8(SP), BP
  x.go:5		0x10501fe		4883c410		ADDQ $0x10, SP
  x.go:5		0x1050202		c3			RET
  x.go:3		0x1050203		e83882ffff		CALL runtime.morestack_noctxt(SB)
  x.go:3		0x1050208		ebb6			JMP main.main(SB)

Constants 常量

Although the assembler takes its guidance from the Plan 9 assemblers, it is a distinct program, so there are some differences. One is in constant evaluation. Constant expressions in the assembler are parsed using Go's operator precedence, not the C-like precedence of the original. Thus 3&1<<2 is 4, not 0—it parses as (3&1)<<2 not 3&(1<<2). Also, constants are always evaluated as 64-bit unsigned integers. Thus -2 is not the integer value minus two, but the unsigned 64-bit integer with the same bit pattern. The distinction rarely matters but to avoid ambiguity, division or right shift where the right operand's high bit is set is rejected.

Symbols 符号

Some symbols, such as R1 or LR, are predefined and refer to registers. The exact set depends on the architecture.

There are four predeclared symbols that refer to pseudo-registers. These are not real registers, but rather virtual registers maintained by the toolchain, such as a frame pointer. The set of pseudo-registers is the same for all architectures:

  • FP: Frame pointer: arguments and locals.
  • PC: Program counter: jumps and branches.
  • SB: Static base pointer: global symbols.
  • SP: Stack pointer: the highest address within the local stack frame.

All user-defined symbols are written as offsets to the pseudo-registers FP (arguments and locals) and SB (globals).

The SB pseudo-register can be thought of as the origin of memory, so the symbol foo(SB) is the name foo as an address in memory. This form is used to name global functions and data. Adding <> to the name, as in foo<>(SB), makes the name visible only in the current source file, like a top-level static declaration in a C file. Adding an offset to the name refers to that offset from the symbol's address, so foo+4(SB) is four bytes past the start of foo.

The FP pseudo-register is a virtual frame pointer used to refer to function arguments. The compilers maintain a virtual frame pointer and refer to the arguments on the stack as offsets from that pseudo-register. Thus 0(FP) is the first argument to the function, 8(FP) is the second (on a 64-bit machine), and so on. However, when referring to a function argument this way, it is necessary to place a name at the beginning, as in first_arg+0(FP) and second_arg+8(FP). (The meaning of the offset—offset from the frame pointer—distinct from its use with SB, where it is an offset from the symbol.) The assembler enforces this convention, rejecting plain 0(FP) and 8(FP). The actual name is semantically irrelevant but should be used to document the argument's name. It is worth stressing that FP is always a pseudo-register, not a hardware register, even on architectures with a hardware frame pointer.

For assembly functions with Go prototypes, go vet will check that the argument names and offsets match. On 32-bit systems, the low and high 32 bits of a 64-bit value are distinguished by adding a _lo or _hi suffix to the name, as in arg_lo+0(FP) or arg_hi+4(FP). If a Go prototype does not name its result, the expected assembly name is ret.

The SP pseudo-register is a virtual stack pointer used to refer to frame-local variables and the arguments being prepared for function calls. It points to the highest address within the local stack frame, so references should use negative offsets in the range [−framesize, 0): x-8(SP), y-4(SP), and so on.

On architectures with a hardware register named SP, the name prefix distinguishes references to the virtual stack pointer from references to the architectural SP register. That is, x-8(SP) and -8(SP) are different memory locations: the first refers to the virtual stack pointer pseudo-register, while the second refers to the hardware's SP register.

On machines where SP and PC are traditionally aliases for a physical, numbered register, in the Go assembler the names SP and PC are still treated specially; for instance, references to SP require a symbol, much like FP. To access the actual hardware register use the true R name. For example, on the ARM architecture the hardware SP and PC are accessible as R13 and R15.

Branches and direct jumps are always written as offsets to the PC, or as jumps to labels:

label:
	MOVW $0, R1
	JMP label

Each label is visible only within the function in which it is defined. It is therefore permitted for multiple functions in a file to define and use the same label names. Direct jumps and call instructions can target text symbols, such as name(SB), but not offsets from symbols, such as name+4(SB).

Instructions, registers, and assembler directives are always in UPPER CASE to remind you that assembly programming is a fraught endeavor. (Exception: the g register renaming on ARM.)

In Go object files and binaries, the full name of a symbol is the package path followed by a period and the symbol name: fmt.Printf or math/rand.Int. Because the assembler's parser treats period and slash as punctuation, those strings cannot be used directly as identifier names. Instead, the assembler allows the middle dot character U+00B7 and the division slash U+2215 in identifiers and rewrites them to plain period and slash. Within an assembler source file, the symbols above are written as fmt·Printf and math∕rand·Int. The assembly listings generated by the compilers when using the -S flag show the period and slash directly instead of the Unicode replacements required by the assemblers.

Most hand-written assembly files do not include the full package path in symbol names, because the linker inserts the package path of the current object file at the beginning of any name starting with a period: in an assembly source file within the math/rand package implementation, the package's Int function can be referred to as ·Int. This convention avoids the need to hard-code a package's import path in its own source code, making it easier to move the code from one location to another.

Directives 指令

The assembler uses various directives to bind text and data to symbol names. For example, here is a simple complete function definition. The TEXT directive declares the symbol runtime·profileloop and the instructions that follow form the body of the function. The last instruction in a TEXT block must be some sort of jump, usually a RET (pseudo-)instruction. (If it's not, the linker will append a jump-to-itself instruction; there is no fallthrough in TEXTs.) After the symbol, the arguments are flags (see below) and the frame size, a constant (but see below):

TEXT runtime·profileloop(SB),NOSPLIT,$8
	MOVQ	$runtime·profileloop1(SB), CX
	MOVQ	CX, 0(SP)
	CALL	runtime·externalthreadhandler(SB)
	RET

In the general case, the frame size is followed by an argument size, separated by a minus sign. (It's not a subtraction, just idiosyncratic syntax.) The frame size $24-8 states that the function has a 24-byte frame and is called with 8 bytes of argument, which live on the caller's frame. If NOSPLIT is not specified for the TEXT, the argument size must be provided. For assembly functions with Go prototypes, go vet will check that the argument size is correct.

Note that the symbol name uses a middle dot to separate the components and is specified as an offset from the static base pseudo-register SB. This function would be called from Go source for package runtime using the simple name profileloop.

Global data symbols are defined by a sequence of initializing DATA directives followed by a GLOBL directive. Each DATA directive initializes a section of the corresponding memory. The memory not explicitly initialized is zeroed. The general form of the DATA directive is

DATA	symbol+offset(SB)/width, value

which initializes the symbol memory at the given offset and width with the given value. The DATA directives for a given symbol must be written with increasing offsets.

The GLOBL directive declares a symbol to be global. The arguments are optional flags and the size of the data being declared as a global, which will have initial value all zeros unless a DATA directive has initialized it. The GLOBL directive must follow any corresponding DATA directives.

For example,

DATA divtab&lt;&gt;+0x00(SB)/4, $0xf4f8fcff
DATA divtab&lt;&gt;+0x04(SB)/4, $0xe6eaedf0
...
DATA divtab&lt;&gt;+0x3c(SB)/4, $0x81828384
GLOBL divtab&lt;&gt;(SB), RODATA, $64

GLOBL runtime·tlsoffset(SB), NOPTR, $4

declares and initializes divtab<>, a read-only 64-byte table of 4-byte integer values, and declares runtime·tlsoffset, a 4-byte, implicitly zeroed variable that contains no pointers.

There may be one or two arguments to the directives. If there are two, the first is a bit mask of flags, which can be written as numeric expressions, added or or-ed together, or can be set symbolically for easier absorption by a human. Their values, defined in the standard #include file textflag.h, are:

  • NOPROF = 1
    (For TEXT items.) Don't profile the marked function. This flag is deprecated.
  • DUPOK = 2
    It is legal to have multiple instances of this symbol in a single binary. The linker will choose one of the duplicates to use.
  • NOSPLIT = 4
    (For TEXT items.) Don't insert the preamble to check if the stack must be split. The frame for the routine, plus anything it calls, must fit in the spare space remaining in the current stack segment. Used to protect routines such as the stack splitting code itself.
  • RODATA = 8
    (For DATA and GLOBL items.) Put this data in a read-only section.
  • NOPTR = 16
    (For DATA and GLOBL items.) This data contains no pointers and therefore does not need to be scanned by the garbage collector.
  • WRAPPER = 32
    (For TEXT items.) This is a wrapper function and should not count as disabling recover.
  • NEEDCTXT = 64
    (For TEXT items.) This function is a closure so it uses its incoming context register.
  • LOCAL = 128
    This symbol is local to the dynamic shared object.
  • TLSBSS = 256
    (For DATA and GLOBL items.) Put this data in thread local storage.
  • NOFRAME = 512
    (For TEXT items.) Do not insert instructions to allocate a stack frame and save/restore the return address, even if this is not a leaf function. Only valid on functions that declare a frame size of 0.
  • TOPFRAME = 2048
    (For TEXT items.) Function is the outermost frame of the call stack. Traceback should stop at this function.

Interacting with Go types and constants 和go交互

If a package has any .s files, then go build will direct the compiler to emit a special header called go_asm.h, which the .s files can then #include. The file contains symbolic #define constants for the offsets of Go struct fields, the sizes of Go struct types, and most Go const declarations defined in the current package. Go assembly should avoid making assumptions about the layout of Go types and instead use these constants. This improves the readability of assembly code, and keeps it robust to changes in data layout either in the Go type definitions or in the layout rules used by the Go compiler.

Constants are of the form const_name. For example, given the Go declaration const bufSize = 1024, assembly code can refer to the value of this constant as const_bufSize.

Field offsets are of the form type_field. Struct sizes are of the form type__size. For example, consider the following Go definition:

type reader struct {
	buf [bufSize]byte
	r   int
}

Assembly can refer to the size of this struct as reader__size and the offsets of the two fields as reader_buf and reader_r. Hence, if register R1 contains a pointer to a reader, assembly can reference the r field as reader_r(R1).

If any of these #define names are ambiguous (for example, a struct with a _size field), #include "go_asm.h" will fail with a "redefinition of macro" error.

Runtime Coordination 运行时

For garbage collection to run correctly, the runtime must know the location of pointers in all global data and in most stack frames. The Go compiler emits this information when compiling Go source files, but assembly programs must define it explicitly.

A data symbol marked with the NOPTR flag (see above) is treated as containing no pointers to runtime-allocated data. A data symbol with the RODATA flag is allocated in read-only memory and is therefore treated as implicitly marked NOPTR. A data symbol with a total size smaller than a pointer is also treated as implicitly marked NOPTR. It is not possible to define a symbol containing pointers in an assembly source file; such a symbol must be defined in a Go source file instead. Assembly source can still refer to the symbol by name even without DATA and GLOBL directives. A good general rule of thumb is to define all non-RODATA symbols in Go instead of in assembly.

Each function also needs annotations giving the location of live pointers in its arguments, results, and local stack frame. For an assembly function with no pointer results and either no local stack frame or no function calls, the only requirement is to define a Go prototype for the function in a Go source file in the same package. The name of the assembly function must not contain the package name component (for example, function Syscall in package syscall should use the name ·Syscall instead of the equivalent name syscall·Syscall in its TEXT directive). For more complex situations, explicit annotation is needed. These annotations use pseudo-instructions defined in the standard #include file funcdata.h.

If a function has no arguments and no results, the pointer information can be omitted. This is indicated by an argument size annotation of $n-0 on the TEXT instruction. Otherwise, pointer information must be provided by a Go prototype for the function in a Go source file, even for assembly functions not called directly from Go. (The prototype will also let go vet check the argument references.) At the start of the function, the arguments are assumed to be initialized but the results are assumed uninitialized. If the results will hold live pointers during a call instruction, the function should start by zeroing the results and then executing the pseudo-instruction GO_RESULTS_INITIALIZED. This instruction records that the results are now initialized and should be scanned during stack movement and garbage collection. It is typically easier to arrange that assembly functions do not return pointers or do not contain call instructions; no assembly functions in the standard library use GO_RESULTS_INITIALIZED.

If a function has no local stack frame, the pointer information can be omitted. This is indicated by a local frame size annotation of $0-n on the TEXT instruction. The pointer information can also be omitted if the function contains no call instructions. Otherwise, the local stack frame must not contain pointers, and the assembly must confirm this fact by executing the pseudo-instruction NO_LOCAL_POINTERS. Because stack resizing is implemented by moving the stack, the stack pointer may change during any function call: even pointers to stack data must not be kept in local variables.

Assembly functions should always be given Go prototypes, both to provide pointer information for the arguments and results and to let go vet check that the offsets being used to access them are correct.

Architecture-specific details 架构详情

It is impractical to list all the instructions and other details for each machine. To see what instructions are defined for a given machine, say ARM, look in the source for the obj support library for that architecture, located in the directory src/cmd/internal/obj/arm. In that directory is a file a.out.go; it contains a long list of constants starting with A, like this:

const (
	AAND = obj.ABaseARM + obj.A_ARCHSPECIFIC + iota
	AEOR
	ASUB
	ARSB
	AADD
	...

This is the list of instructions and their spellings as known to the assembler and linker for that architecture. Each instruction begins with an initial capital A in this list, so AAND represents the bitwise and instruction, AND (without the leading A), and is written in assembly source as AND. The enumeration is mostly in alphabetical order. (The architecture-independent AXXX, defined in the cmd/internal/obj package, represents an invalid instruction). The sequence of the A names has nothing to do with the actual encoding of the machine instructions. The cmd/internal/obj package takes care of that detail.

The instructions for both the 386 and AMD64 architectures are listed in cmd/internal/obj/x86/a.out.go.

The architectures share syntax for common addressing modes such as (R1) (register indirect), 4(R1) (register indirect with offset), and $foo(SB) (absolute address). The assembler also supports some (not necessarily all) addressing modes specific to each architecture. The sections below list these.

One detail evident in the examples from the previous sections is that data in the instructions flows from left to right: MOVQ $0, CX clears CX. This rule applies even on architectures where the conventional notation uses the opposite direction.

Here follow some descriptions of key Go-specific details for the supported architectures.

32-bit Intel 386 X86

The runtime pointer to the g structure is maintained through the value of an otherwise unused (as far as Go is concerned) register in the MMU. In the runtime package, assembly code can include go_tls.h, which defines an OS- and architecture-dependent macro get_tls for accessing this register. The get_tls macro takes one argument, which is the register to load the g pointer into.

For example, the sequence to load g and m using CX looks like this:

#include "go_tls.h"
#include "go_asm.h"
...
get_tls(CX)
MOVL	g(CX), AX     // Move g into AX.
MOVL	g_m(AX), BX   // Move g.m into BX.

The get_tls macro is also defined on amd64.

Addressing modes:

  • (DI)(BX*2): The location at address DI plus BX*2.
  • 64(DI)(BX*2): The location at address DI plus BX*2 plus 64. These modes accept only 1, 2, 4, and 8 as scale factors.

When using the compiler and assembler's -dynlink or -shared modes, any load or store of a fixed memory location such as a global variable must be assumed to overwrite CX. Therefore, to be safe for use with these modes, assembly sources should typically avoid CX except between memory references.

64-bit Intel 386 (a.k.a. amd64)

The two architectures behave largely the same at the assembler level. Assembly code to access the m and g pointers on the 64-bit version is the same as on the 32-bit 386, except it uses MOVQ rather than MOVL:

get_tls(CX)
MOVQ	g(CX), AX     // Move g into AX.
MOVQ	g_m(AX), BX   // Move g.m into BX.

Register BP is callee-save. The assembler automatically inserts BP save/restore when frame size is larger than zero. Using BP as a general purpose register is allowed, however it can interfere with sampling-based profiling.

ARM

The registers R10 and R11 are reserved by the compiler and linker.

R10 points to the g (goroutine) structure. Within assembler source code, this pointer must be referred to as g; the name R10 is not recognized.

To make it easier for people and compilers to write assembly, the ARM linker allows general addressing forms and pseudo-operations like DIV or MOD that may not be expressible using a single hardware instruction. It implements these forms as multiple instructions, often using the R11 register to hold temporary values. Hand-written assembly can use R11, but doing so requires being sure that the linker is not also using it to implement any of the other instructions in the function.

When defining a TEXT, specifying frame size $-4 tells the linker that this is a leaf function that does not need to save LR on entry.

The name SP always refers to the virtual stack pointer described earlier. For the hardware register, use R13.

Condition code syntax is to append a period and the one- or two-letter code to the instruction, as in MOVW.EQ. Multiple codes may be appended: MOVM.IA.W. The order of the code modifiers is irrelevant.

Addressing modes:

  • R0->16
    R0>>16
    R0<<16
    R0@>16: For <<, left shift R0 by 16 bits. The other codes are -> (arithmetic right shift), >> (logical right shift), and @> (rotate right).
  • R0->R1
    R0>>R1
    R0<<R1
    R0@>R1: For <<, left shift R0 by the count in R1. The other codes are -> (arithmetic right shift), >> (logical right shift), and @> (rotate right).
  • [R0,g,R12-R15]: For multi-register instructions, the set comprising R0, g, and R12 through R15 inclusive.
  • (R5, R6): Destination register pair.

ARM64

R18 is the "platform register", reserved on the Apple platform. To prevent accidental misuse, the register is named R18_PLATFORM. R27 and R28 are reserved by the compiler and linker. R29 is the frame pointer. R30 is the link register.

Instruction modifiers are appended to the instruction following a period. The only modifiers are P (postincrement) and W (preincrement): MOVW.P, MOVW.W

Addressing modes:

  • R0->16
    R0>>16
    R0<<16
    R0@>16: These are the same as on the 32-bit ARM.
  • $(8<<12): Left shift the immediate value 8 by 12 bits.
  • 8(R0): Add the value of R0 and 8.
  • (R2)(R0): The location at R0 plus R2.
  • R0.UXTB
    R0.UXTB<<imm: UXTB: extract an 8-bit value from the low-order bits of R0 and zero-extend it to the size of R0. R0.UXTB<<imm: left shift the result of R0.UXTB by imm bits. The imm value can be 0, 1, 2, 3, or 4. The other extensions include UXTH (16-bit), UXTW (32-bit), and UXTX (64-bit).
  • R0.SXTB
    R0.SXTB<<imm: SXTB: extract an 8-bit value from the low-order bits of R0 and sign-extend it to the size of R0. R0.SXTB<<imm: left shift the result of R0.SXTB by imm bits. The imm value can be 0, 1, 2, 3, or 4. The other extensions include SXTH (16-bit), SXTW (32-bit), and SXTX (64-bit).
  • (R5, R6): Register pair for LDAXP/LDP/LDXP/STLXP/STP/STP.

Reference: Go ARM64 Assembly Instructions Reference Manual

PPC64

This assembler is used by GOARCH values ppc64 and ppc64le.

Reference: Go PPC64 Assembly Instructions Reference Manual

IBM z/Architecture, a.k.a. s390x

The registers R10 and R11 are reserved. The assembler uses them to hold temporary values when assembling some instructions.

R13 points to the g (goroutine) structure. This register must be referred to as g; the name R13 is not recognized.

R15 points to the stack frame and should typically only be accessed using the virtual registers SP and FP.

Load- and store-multiple instructions operate on a range of registers. The range of registers is specified by a start register and an end register. For example, LMG (R9), R5, R7 would load R5, R6 and R7 with the 64-bit values at 0(R9), 8(R9) and 16(R9) respectively.

Storage-and-storage instructions such as MVC and XC are written with the length as the first argument. For example, XC $8, (R9), (R9) would clear eight bytes at the address specified in R9.

If a vector instruction takes a length or an index as an argument then it will be the first argument. For example, VLEIF $1, $16, V2 will load the value sixteen into index one of V2. Care should be taken when using vector instructions to ensure that they are available at runtime. To use vector instructions a machine must have both the vector facility (bit 129 in the facility list) and kernel support. Without kernel support a vector instruction will have no effect (it will be equivalent to a NOP instruction).

Addressing modes:

  • (R5)(R6*1): The location at R5 plus R6. It is a scaled mode as on the x86, but the only scale allowed is 1.

MIPS, MIPS64

General purpose registers are named R0 through R31, floating point registers are F0 through F31.

R30 is reserved to point to g. R23 is used as a temporary register.

In a TEXT directive, the frame size $-4 for MIPS or $-8 for MIPS64 instructs the linker not to save LR.

SP refers to the virtual stack pointer. For the hardware register, use R29.

Addressing modes:

  • 16(R1): The location at R1 plus 16.
  • (R1): Alias for 0(R1).

The value of GOMIPS environment variable (hardfloat or softfloat) is made available to assembly code by predefining either GOMIPS_hardfloat or GOMIPS_softfloat.

The value of GOMIPS64 environment variable (hardfloat or softfloat) is made available to assembly code by predefining either GOMIPS64_hardfloat or GOMIPS64_softfloat.

Unsupported opcodes 不支持的操作码

The assemblers are designed to support the compiler so not all hardware instructions are defined for all architectures: if the compiler doesn't generate it, it might not be there. If you need to use a missing instruction, there are two ways to proceed. One is to update the assembler to support that instruction, which is straightforward but only worthwhile if it's likely the instruction will be used again. Instead, for simple one-off cases, it's possible to use the BYTE and WORD directives to lay down explicit data into the instruction stream within a TEXT. Here's how the 386 runtime defines the 64-bit atomic load function.

// uint64 atomicload64(uint64 volatile* addr);
// so actually
// void atomicload64(uint64 *res, uint64 volatile *addr);
TEXT runtime·atomicload64(SB), NOSPLIT, $0-12
	MOVL	ptr+0(FP), AX
	TESTL	$7, AX
	JZ	2(PC)
	MOVL	0, AX // crash with nil ptr deref
	LEAL	ret_lo+4(FP), BX
	// MOVQ (%EAX), %MM0
	BYTE $0x0f; BYTE $0x6f; BYTE $0x00
	// MOVQ %MM0, 0(%EBX)
	BYTE $0x0f; BYTE $0x7f; BYTE $0x03
	// EMMS
	BYTE $0x0F; BYTE $0x77
	RET

golang 编译器(gc)

Introduction to the Go compiler

cmd/compile contains the main packages that form the Go compiler. The compiler may be logically split in four phases, which we will briefly describe alongside the list of packages that contain their code.

You may sometimes hear the terms "front-end" and "back-end" when referring to the compiler. Roughly speaking, these translate to the first two and last two phases we are going to list here. A third term, "middle-end", often refers to much of the work that happens in the second phase.

Note that the go/* family of packages, such as go/parser and go/types, are mostly unused by the compiler. Since the compiler was initially written in C, the go/* packages were developed to enable writing tools working with Go code, such as gofmt and vet. However, over time the compiler's internal APIs have slowly evolved to be more familiar to users of the go/* packages.

It should be clarified that the name "gc" stands for "Go compiler", and has little to do with uppercase "GC", which stands for garbage collection.

1. Parsing 解析

  • cmd/compile/internal/syntax (lexer, parser, syntax tree)

In the first phase of compilation, source code is tokenized (lexical analysis), parsed (syntax analysis), and a syntax tree is constructed for each source file.

Each syntax tree is an exact representation of the respective source file, with nodes corresponding to the various elements of the source such as expressions, declarations, and statements. The syntax tree also includes position information which is used for error reporting and the creation of debugging information.

2. Type checking 类型检测

  • cmd/compile/internal/types2 (type checking)

The types2 package is a port of go/types to use the syntax package's AST instead of go/ast.

3. IR construction ("noding") IR

  • cmd/compile/internal/types (compiler types)
  • cmd/compile/internal/ir (compiler AST)
  • cmd/compile/internal/typecheck (AST transformations)
  • cmd/compile/internal/noder (create compiler AST)

The compiler middle end uses its own AST definition and representation of Go types carried over from when it was written in C. All of its code is written in terms of these, so the next step after type checking is to convert the syntax and types2 representations to ir and types. This process is referred to as "noding."

There are currently two noding implementations:

  1. irgen (aka "-G=3" or sometimes "noder2") is the implementation used starting with Go 1.18, and

  2. Unified IR is another, in-development implementation (enabled with GOEXPERIMENT=unified), which also implements import/export and inlining.

Up through Go 1.18, there was a third noding implementation (just "noder" or "-G=0"), which directly converted the pre-type-checked syntax representation into IR and then invoked package typecheck's type checker. This implementation was removed after Go 1.18, so now package typecheck is only used for IR transformations.

4. Middle end 中端

  • cmd/compile/internal/deadcode (dead code elimination) 死代码消除
  • cmd/compile/internal/inline (function call inlining) 内联
  • cmd/compile/internal/devirtualize (devirtualization of known interface method calls) 去虚拟化
  • cmd/compile/internal/escape (escape analysis) 逃逸分析

Several optimization passes are performed on the IR representation: dead code elimination, (early) devirtualization, function call inlining, and escape analysis.

5. Walk

  • cmd/compile/internal/walk (order of evaluation, desugaring)

The final pass over the IR representation is "walk," which serves two purposes:

  1. It decomposes complex statements into individual, simpler statements, introducing temporary variables and respecting order of evaluation. This step is also referred to as "order."

  2. It desugars higher-level Go constructs into more primitive ones. For example, switch statements are turned into binary search or jump tables, and operations on maps and channels are replaced with runtime calls.

6. Generic SSA 生成 静态单一赋值

  • cmd/compile/internal/ssa (SSA passes and rules)
  • cmd/compile/internal/ssagen (converting IR to SSA)

In this phase, IR is converted into Static Single Assignment (SSA) form, a lower-level intermediate representation with specific properties that make it easier to implement optimizations and to eventually generate machine code from it.

During this conversion, function intrinsics are applied. These are special functions that the compiler has been taught to replace with heavily optimized code on a case-by-case basis.

Certain nodes are also lowered into simpler components during the AST to SSA conversion, so that the rest of the compiler can work with them. For instance, the copy builtin is replaced by memory moves, and range loops are rewritten into for loops. Some of these currently happen before the conversion to SSA due to historical reasons, but the long-term plan is to move all of them here.

Then, a series of machine-independent passes and rules are applied. These do not concern any single computer architecture, and thus run on all GOARCH variants. These passes include dead code elimination, removal of unneeded nil checks, and removal of unused branches. The generic rewrite rules mainly concern expressions, such as replacing some expressions with constant values, and optimizing multiplications and float operations.

7. Generating machine code 生成机器码

  • cmd/compile/internal/ssa (SSA lowering and arch-specific passes)
  • cmd/internal/obj (machine code generation)

The machine-dependent phase of the compiler begins with the "lower" pass, which rewrites generic values into their machine-specific variants. For example, on amd64 memory operands are possible, so many load-store operations may be combined.

Note that the lower pass runs all machine-specific rewrite rules, and thus it currently applies lots of optimizations too.

Once the SSA has been "lowered" and is more specific to the target architecture, the final code optimization passes are run. This includes yet another dead code elimination pass, moving values closer to their uses, the removal of local variables that are never read from, and register allocation.

Other important pieces of work done as part of this step include stack frame layout, which assigns stack offsets to local variables, and pointer liveness analysis, which computes which on-stack pointers are live at each GC safe point.

At the end of the SSA generation phase, Go functions have been transformed into a series of obj.Prog instructions. These are passed to the assembler (cmd/internal/obj), which turns them into machine code and writes out the final object file. The object file will also contain reflect data, export data, and debugging information.

延伸阅读

To dig deeper into how the SSA package works, including its passes and rules, head to cmd/compile/internal/ssa/README.md.

go internal abi 规范 (内部二进制接口规范)

Self-link: go.dev/s/regabi

This document describes Go’s internal application binary interface (ABI), known as ABIInternal. Go's ABI defines the layout of data in memory and the conventions for calling between Go functions. This ABI is unstable and will change between Go versions. If you’re writing assembly code, please instead refer to Go’s assembly documentation, which describes Go’s stable ABI, known as ABI0.

All functions defined in Go source follow ABIInternal. However, ABIInternal and ABI0 functions are able to call each other through transparent ABI wrappers, described in the internal calling convention proposal.

Go uses a common ABI design across all architectures. We first describe the common ABI, and then cover per-architecture specifics.

Rationale: For the reasoning behind using a common ABI across architectures instead of the platform ABI, see the register-based Go calling convention proposal.

Memory layout 内存布局

Go's built-in types have the following sizes and alignments. Many, though not all, of these sizes are guaranteed by the language specification. Those that aren't guaranteed may change in future versions of Go (for example, we've considered changing the alignment of int64 on 32-bit).

Type64-bit32-bit
SizeAlignSizeAlign
bool, uint8, int81111
uint16, int162222
uint32, int324444
uint64, int648884
int, uint8844
float324444
float648884
complex648484
complex128168164
uintptr, *T, unsafe.Pointer8844

The types byte and rune are aliases for uint8 and int32, respectively, and hence have the same size and alignment as these types.

The layout of map, chan, and func types is equivalent to *T.

To describe the layout of the remaining composite types, we first define the layout of a sequence S of N fields with types t1, t2, ..., tN. We define the byte offset at which each field begins relative to a base address of 0, as well as the size and alignment of the sequence as follows:

offset(S, i) = 0  if i = 1
             = align(offset(S, i-1) + sizeof(t_(i-1)), alignof(t_i))
alignof(S)   = 1  if N = 0
             = max(alignof(t_i) | 1 <= i <= N)
sizeof(S)    = 0  if N = 0
             = align(offset(S, N) + sizeof(t_N), alignof(S))

Where sizeof(T) and alignof(T) are the size and alignment of type T, respectively, and align(x, y) rounds x up to a multiple of y.

The interface{} type is a sequence of 1. a pointer to the runtime type description for the interface's dynamic type and 2. an unsafe.Pointer data field. Any other interface type (besides the empty interface) is a sequence of 1. a pointer to the runtime "itab" that gives the method pointers and the type of the data field and 2. an unsafe.Pointer data field. An interface can be "direct" or "indirect" depending on the dynamic type: a direct interface stores the value directly in the data field, and an indirect interface stores a pointer to the value in the data field. An interface can only be direct if the value consists of a single pointer word.

An array type [N]T is a sequence of N fields of type T.

The slice type []T is a sequence of a *[cap]T pointer to the slice backing store, an int giving the len of the slice, and an int giving the cap of the slice.

The string type is a sequence of a *[len]byte pointer to the string backing store, and an int giving the len of the string.

A struct type struct { f1 t1; ...; fM tM } is laid out as the sequence t1, ..., tM, tP, where tP is either:

  • Type byte if sizeof(tM) = 0 and any of sizeof(ti) ≠ 0.
  • Empty (size 0 and align 1) otherwise.

The padding byte prevents creating a past-the-end pointer by taking the address of the final, empty fN field.

Note that user-written assembly code should generally not depend on Go type layout and should instead use the constants defined in go_asm.h.

Function call argument and result passing 函数调用和结果返回

Function calls pass arguments and results using a combination of the stack and machine registers. Each argument or result is passed either entirely in registers or entirely on the stack. Because access to registers is generally faster than access to the stack, arguments and results are preferentially passed in registers. However, any argument or result that contains a non-trivial array or does not fit entirely in the remaining available registers is passed on the stack.

Each architecture defines a sequence of integer registers and a sequence of floating-point registers. At a high level, arguments and results are recursively broken down into values of base types and these base values are assigned to registers from these sequences.

Arguments and results can share the same registers, but do not share the same stack space. Beyond the arguments and results passed on the stack, the caller also reserves spill space on the stack for all register-based arguments (but does not populate this space).

The receiver, arguments, and results of function or method F are assigned to registers or the stack using the following algorithm:

  1. Let NI and NFP be the length of integer and floating-point register sequences defined by the architecture. Let I and FP be 0; these are the indexes of the next integer and floating-pointer register. Let S, the type sequence defining the stack frame, be empty.
  2. If F is a method, assign F’s receiver.
  3. For each argument A of F, assign A.
  4. Add a pointer-alignment field to S. This has size 0 and the same alignment as uintptr.
  5. Reset I and FP to 0.
  6. For each result R of F, assign R.
  7. Add a pointer-alignment field to S.
  8. For each register-assigned receiver and argument of F, let T be its type and add T to the stack sequence S. This is the argument's (or receiver's) spill space and will be uninitialized at the call.
  9. Add a pointer-alignment field to S.

Assigning a receiver, argument, or result V of underlying type T works as follows:

  1. Remember I and FP.
  2. If T has zero size, add T to the stack sequence S and return.
  3. Try to register-assign V.
  4. If step 3 failed, reset I and FP to the values from step 1, add T to the stack sequence S, and assign V to this field in S.

Register-assignment of a value V of underlying type T works as follows:

  1. If T is a boolean or integral type that fits in an integer register, assign V to register I and increment I.
  2. If T is an integral type that fits in two integer registers, assign the least significant and most significant halves of V to registers I and I+1, respectively, and increment I by 2
  3. If T is a floating-point type and can be represented without loss of precision in a floating-point register, assign V to register FP and increment FP.
  4. If T is a complex type, recursively register-assign its real and imaginary parts.
  5. If T is a pointer type, map type, channel type, or function type, assign V to register I and increment I.
  6. If T is a string type, interface type, or slice type, recursively register-assign V’s components (2 for strings and interfaces, 3 for slices).
  7. If T is a struct type, recursively register-assign each field of V.
  8. If T is an array type of length 0, do nothing.
  9. If T is an array type of length 1, recursively register-assign its one element.
  10. If T is an array type of length > 1, fail.
  11. If I > NI or FP > NFP, fail.
  12. If any recursive assignment above fails, fail.

The above algorithm produces an assignment of each receiver, argument, and result to registers or to a field in the stack sequence. The final stack sequence looks like: stack-assigned receiver, stack-assigned arguments, pointer-alignment, stack-assigned results, pointer-alignment, spill space for each register-assigned argument, pointer-alignment. The following diagram shows what this stack frame looks like on the stack, using the typical convention where address 0 is at the bottom:

+------------------------------+
|             . . .            |
| 2nd reg argument spill space |
| 1st reg argument spill space |
| <pointer-sized alignment>    |
|             . . .            |
| 2nd stack-assigned result    |
| 1st stack-assigned result    |
| <pointer-sized alignment>    |
|             . . .            |
| 2nd stack-assigned argument  |
| 1st stack-assigned argument  |
| stack-assigned receiver      |
+------------------------------+ ↓ lower addresses

To perform a call, the caller reserves space starting at the lowest address in its stack frame for the call stack frame, stores arguments in the registers and argument stack fields determined by the above algorithm, and performs the call. At the time of a call, spill space, result stack fields, and result registers are left uninitialized. Upon return, the callee must have stored results to all result registers and result stack fields determined by the above algorithm.

There are no callee-save registers, so a call may overwrite any register that doesn’t have a fixed meaning, including argument registers.

Example

Consider the function func f(a1 uint8, a2 [2]uintptr, a3 uint8) (r1 struct { x uintptr; y [2]uintptr }, r2 string) on a 64-bit architecture with hypothetical integer registers R0–R9.

On entry, a1 is assigned to R0, a3 is assigned to R1 and the stack frame is laid out in the following sequence:

a2      [2]uintptr
r1.x    uintptr
r1.y    [2]uintptr
a1Spill uint8
a3Spill uint8
_       [6]uint8  // alignment padding

In the stack frame, only the a2 field is initialized on entry; the rest of the frame is left uninitialized.

On exit, r2.base is assigned to R0, r2.len is assigned to R1, and r1.x and r1.y are initialized in the stack frame.

There are several things to note in this example. First, a2 and r1 are stack-assigned because they contain arrays. The other arguments and results are register-assigned. Result r2 is decomposed into its components, which are individually register-assigned. On the stack, the stack-assigned arguments appear at lower addresses than the stack-assigned results, which appear at lower addresses than the argument spill area. Only arguments, not results, are assigned a spill area on the stack.

Rationale 工作原理

Each base value is assigned to its own register to optimize construction and access. An alternative would be to pack multiple sub-word values into registers, or to simply map an argument's in-memory layout to registers (this is common in C ABIs), but this typically adds cost to pack and unpack these values. Modern architectures have more than enough registers to pass all arguments and results this way for nearly all functions (see the appendix), so there’s little downside to spreading base values across registers.

Arguments that can’t be fully assigned to registers are passed entirely on the stack in case the callee takes the address of that argument. If an argument could be split across the stack and registers and the callee took its address, it would need to be reconstructed in memory, a process that would be proportional to the size of the argument.

Non-trivial arrays are always passed on the stack because indexing into an array typically requires a computed offset, which generally isn’t possible with registers. Arrays in general are rare in function signatures (only 0.7% of functions in the Go 1.15 standard library and 0.2% in kubelet). We considered allowing array fields to be passed on the stack while the rest of an argument’s fields are passed in registers, but this creates the same problems as other large structs if the callee takes the address of an argument, and would benefit <0.1% of functions in kubelet (and even these very little).

We make exceptions for 0 and 1-element arrays because these don’t require computed offsets, and 1-element arrays are already decomposed in the compiler’s SSA representation.

The ABI assignment algorithm above is equivalent to Go’s stack-based ABI0 calling convention if there are zero architecture registers. This is intended to ease the transition to the register-based internal ABI and make it easy for the compiler to generate either calling convention. An architecture may still define register meanings that aren’t compatible with ABI0, but these differences should be easy to account for in the compiler.

The assignment algorithm assigns zero-sized values to the stack (assignment step 2) in order to support ABI0-equivalence. While these values take no space themselves, they do result in alignment padding on the stack in ABI0. Without this step, the internal ABI would register-assign zero-sized values even on architectures that provide no argument registers because they don't consume any registers, and hence not add alignment padding to the stack.

The algorithm reserves spill space for arguments in the caller’s frame so that the compiler can generate a stack growth path that spills into this reserved space. If the callee has to grow the stack, it may not be able to reserve enough additional stack space in its own frame to spill these, which is why it’s important that the caller do so. These slots also act as the home location if these arguments need to be spilled for any other reason, which simplifies traceback printing.

There are several options for how to lay out the argument spill space. We chose to lay out each argument according to its type's usual memory layout but to separate the spill space from the regular argument space. Using the usual memory layout simplifies the compiler because it already understands this layout. Also, if a function takes the address of a register-assigned argument, the compiler must spill that argument to memory in its usual memory layout and it's more convenient to use the argument spill space for this purpose.

Alternatively, the spill space could be structured around argument registers. In this approach, the stack growth spill path would spill each argument register to a register-sized stack word. However, if the function takes the address of a register-assigned argument, the compiler would have to reconstruct it in memory layout elsewhere on the stack.

The spill space could also be interleaved with the stack-assigned arguments so the arguments appear in order whether they are register- or stack-assigned. This would be close to ABI0, except that register-assigned arguments would be uninitialized on the stack and there's no need to reserve stack space for register-assigned results. We expect separating the spill space to perform better because of memory locality. Separating the space is also potentially simpler for reflect calls because this allows reflect to summarize the spill space as a single number. Finally, the long-term intent is to remove reserved spill slots entirely – allowing most functions to be called without any stack setup and easing the introduction of callee-save registers – and separating the spill space makes that transition easier.

Closures 闭包

A func value (e.g., var x func()) is a pointer to a closure object. A closure object begins with a pointer-sized program counter representing the entry point of the function, followed by zero or more bytes containing the closed-over environment.

Closure calls follow the same conventions as static function and method calls, with one addition. Each architecture specifies a closure context pointer register and calls to closures store the address of the closure object in the closure context pointer register prior to the call.

Software floating-point mode 软浮点模式

In "softfloat" mode, the ABI simply treats the hardware as having zero floating-point registers. As a result, any arguments containing floating-point values will be passed on the stack.

Rationale: Softfloat mode is about compatibility over performance and is not commonly used. Hence, we keep the ABI as simple as possible in this case, rather than adding additional rules for passing floating-point values in integer registers.

Architecture specifics 架构规范

This section describes per-architecture register mappings, as well as other per-architecture special cases.

amd64 architecture

The amd64 architecture uses the following sequence of 9 registers for integer arguments and results:

RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11

It uses X0 – X14 for floating-point arguments and results.

Rationale: These sequences are chosen from the available registers to be relatively easy to remember.

Registers R12 and R13 are permanent scratch registers. R15 is a scratch register except in dynamically linked binaries.

Rationale: Some operations such as stack growth and reflection calls need dedicated scratch registers in order to manipulate call frames without corrupting arguments or results.

Special-purpose registers are as follows:

RegisterCall meaningReturn meaningBody meaning
RSPStack pointerSameSame
RBPFrame pointerSameSame
RDXClosure context pointerScratchScratch
R12ScratchScratchScratch
R13ScratchScratchScratch
R14Current goroutineSameSame
R15GOT reference temporary if dynlinkSameSame
X15Zero value (*)SameScratch

(*) Except on Plan 9, where X15 is a scratch register because SSE registers cannot be used in note handlers (so the compiler avoids using them except when absolutely necessary).

Rationale: These register meanings are compatible with Go’s stack-based calling convention except for R14 and X15, which will have to be restored on transitions from ABI0 code to ABIInternal code. In ABI0, these are undefined, so transitions from ABIInternal to ABI0 can ignore these registers.

Rationale: For the current goroutine pointer, we chose a register that requires an additional REX byte. While this adds one byte to every function prologue, it is hardly ever accessed outside the function prologue and we expect making more single-byte registers available to be a net win.

Rationale: We could allow R14 (the current goroutine pointer) to be a scratch register in function bodies because it can always be restored from TLS on amd64. However, we designate it as a fixed register for simplicity and for consistency with other architectures that may not have a copy of the current goroutine pointer in TLS.

Rationale: We designate X15 as a fixed zero register because functions often have to bulk zero their stack frames, and this is more efficient with a designated zero register.

Implementation note: Registers with fixed meaning at calls but not in function bodies must be initialized by "injected" calls such as signal-based panics.

Stack layout

The stack pointer, RSP, grows down and is always aligned to 8 bytes.

The amd64 architecture does not use a link register.

A function's stack frame is laid out as follows:

+------------------------------+
| return PC                    |
| RBP on entry                 |
| ... locals ...               |
| ... outgoing arguments ...   |
+------------------------------+ ↓ lower addresses

The "return PC" is pushed as part of the standard amd64 CALL operation. On entry, a function subtracts from RSP to open its stack frame and saves the value of RBP directly below the return PC. A leaf function that does not require any stack space may omit the saved RBP.

The Go ABI's use of RBP as a frame pointer register is compatible with amd64 platform conventions so that Go can inter-operate with platform debuggers and profilers.

Flags

The direction flag (D) is always cleared (set to the “forward” direction) at a call. The arithmetic status flags are treated like scratch registers and not preserved across calls. All other bits in RFLAGS are system flags.

At function calls and returns, the CPU is in x87 mode (not MMX technology mode).

Rationale: Go on amd64 does not use either the x87 registers or MMX registers. Hence, we follow the SysV platform conventions in order to simplify transitions to and from the C ABI.

At calls, the MXCSR control bits are always set as follows:

FlagBitValueMeaning
FZ150Do not flush to zero
RC14/130 (RN)Round to nearest
PM121Precision masked
UM111Underflow masked
OM101Overflow masked
ZM91Divide-by-zero masked
DM81Denormal operations masked
IM71Invalid operations masked
DAZ60Do not zero de-normals

The MXCSR status bits are callee-save.

Rationale: Having a fixed MXCSR control configuration allows Go functions to use SSE operations without modifying or saving the MXCSR. Functions are allowed to modify it between calls (as long as they restore it), but as of this writing Go code never does. The above fixed configuration matches the process initialization control bits specified by the ELF AMD64 ABI.

The x87 floating-point control word is not used by Go on amd64.

arm64 architecture

The arm64 architecture uses R0 – R15 for integer arguments and results.

It uses F0 – F15 for floating-point arguments and results.

Rationale: 16 integer registers and 16 floating-point registers are more than enough for passing arguments and results for practically all functions (see Appendix). While there are more registers available, using more registers provides little benefit. Additionally, it will add overhead on code paths where the number of arguments are not statically known (e.g. reflect call), and will consume more stack space when there is only limited stack space available to fit in the nosplit limit.

Registers R16 and R17 are permanent scratch registers. They are also used as scratch registers by the linker (Go linker and external linker) in trampolines.

Register R18 is reserved and never used. It is reserved for the OS on some platforms (e.g. macOS).

Registers R19 – R25 are permanent scratch registers. In addition, R27 is a permanent scratch register used by the assembler when expanding instructions.

Floating-point registers F16 – F31 are also permanent scratch registers.

Special-purpose registers are as follows:

RegisterCall meaningReturn meaningBody meaning
RSPStack pointerSameSame
R30Link registerSameScratch (non-leaf functions)
R29Frame pointerSameSame
R28Current goroutineSameSame
R27ScratchScratchScratch
R26Closure context pointerScratchScratch
R18Reserved (not used)SameSame
ZRZero valueSameSame

Rationale: These register meanings are compatible with Go’s stack-based calling convention.

Rationale: The link register, R30, holds the function return address at the function entry. For functions that have frames (including most non-leaf functions), R30 is saved to stack in the function prologue and restored in the epilogue. Within the function body, R30 can be used as a scratch register.

Implementation note: Registers with fixed meaning at calls but not in function bodies must be initialized by "injected" calls such as signal-based panics.

Stack layout

The stack pointer, RSP, grows down and is always aligned to 16 bytes.

Rationale: The arm64 architecture requires the stack pointer to be 16-byte aligned.

A function's stack frame, after the frame is created, is laid out as follows:

+------------------------------+
| ... locals ...               |
| ... outgoing arguments ...   |
| return PC                    | ← RSP points to
| frame pointer on entry       |
+------------------------------+ ↓ lower addresses

The "return PC" is loaded to the link register, R30, as part of the arm64 CALL operation.

On entry, a function subtracts from RSP to open its stack frame, and saves the values of R30 and R29 at the bottom of the frame. Specifically, R30 is saved at 0(RSP) and R29 is saved at -8(RSP), after RSP is updated.

A leaf function that does not require any stack space may omit the saved R30 and R29.

The Go ABI's use of R29 as a frame pointer register is compatible with arm64 architecture requirement so that Go can inter-operate with platform debuggers and profilers.

This stack layout is used by both register-based (ABIInternal) and stack-based (ABI0) calling conventions.

Flags

The arithmetic status flags (NZCV) are treated like scratch registers and not preserved across calls. All other bits in PSTATE are system flags and are not modified by Go.

The floating-point status register (FPSR) is treated like scratch registers and not preserved across calls.

At calls, the floating-point control register (FPCR) bits are always set as follows:

FlagBitValueMeaning
DN250Propagate NaN operands
FZ240Do not flush to zero
RC23/220 (RN)Round to nearest, choose even if tied
IDE150Denormal operations trap disabled
IXE120Inexact trap disabled
UFE110Underflow trap disabled
OFE100Overflow trap disabled
DZE90Divide-by-zero trap disabled
IOE80Invalid operations trap disabled
NEP20Scalar operations do not affect higher elements in vector registers
AH10No alternate handling of de-normal inputs
FIZ00Do not zero de-normals

Rationale: Having a fixed FPCR control configuration allows Go functions to use floating-point and vector (SIMD) operations without modifying or saving the FPCR. Functions are allowed to modify it between calls (as long as they restore it), but as of this writing Go code never does.

ppc64 architecture

The ppc64 architecture uses R3 – R10 and R14 – R17 for integer arguments and results.

It uses F1 – F12 for floating-point arguments and results.

Register R31 is a permanent scratch register in Go.

Special-purpose registers used within Go generated code and Go assembly code are as follows:

RegisterCall meaningReturn meaningBody meaning
R0Zero valueSameSame
R1Stack pointerSameSame
R2TOC registerSameSame
R11Closure context pointerScratchScratch
R12Function address on indirect callsScratchScratch
R13TLS pointerSameSame
R20,R21ScratchScratchUsed by duffcopy, duffzero
R30Current goroutineSameSame
R31ScratchScratchScratch
LRLink registerLink registerScratch
Rationale: These register meanings are compatible with Go’s
stack-based calling convention.

The link register, LR, holds the function return address at the function entry and is set to the correct return address before exiting the function. It is also used in some cases as the function address when doing an indirect call.

The register R2 contains the address of the TOC (table of contents) which contains data or code addresses used when generating position independent code. Non-Go code generated when using cgo contains TOC-relative addresses which depend on R2 holding a valid TOC. Go code compiled with -shared or -dynlink initializes and maintains R2 and uses it in some cases for function calls; Go code compiled without these options does not modify R2.

When making a function call R12 contains the function address for use by the code to generate R2 at the beginning of the function. R12 can be used for other purposes within the body of the function, such as trampoline generation.

R20 and R21 are used in duffcopy and duffzero which could be generated before arguments are saved so should not be used for register arguments.

The Count register CTR can be used as the call target for some branch instructions. It holds the return address when preemption has occurred.

On PPC64 when a float32 is loaded it becomes a float64 in the register, which is different from other platforms and that needs to be recognized by the internal implementation of reflection so that float32 arguments are passed correctly.

Registers R18 - R29 and F13 - F31 are considered scratch registers.

Stack layout

The stack pointer, R1, grows down and is aligned to 8 bytes in Go, but changed to 16 bytes when calling cgo.

A function's stack frame, after the frame is created, is laid out as follows:

+------------------------------+
| ... locals ...               |
| ... outgoing arguments ...   |
| 24  TOC register R2 save     | When compiled with -shared/-dynlink
| 16  Unused in Go             | Not used in Go
|  8  CR save                  | nonvolatile CR fields
|  0  return PC                | ← R1 points to
+------------------------------+ ↓ lower addresses

The "return PC" is loaded to the link register, LR, as part of the ppc64 BL operations.

On entry to a non-leaf function, the stack frame size is subtracted from R1 to create its stack frame, and saves the value of LR at the bottom of the frame.

A leaf function that does not require any stack space does not modify R1 and does not save LR.

NOTE: We might need to save the frame pointer on the stack as in the PPC64 ELF v2 ABI so Go can inter-operate with platform debuggers and profilers.

This stack layout is used by both register-based (ABIInternal) and stack-based (ABI0) calling conventions.

Flags

The condition register consists of 8 condition code register fields CR0-CR7. Go generated code only sets and uses CR0, commonly set by compare functions and use to determine the target of a conditional branch. The generated code does not set or use CR1-CR7.

The floating point status and control register (FPSCR) is initialized to 0 by the kernel at startup of the Go program and not changed by the Go generated code.

riscv64 architecture

The riscv64 architecture uses X10 – X17, X8, X9, X18 – X23 for integer arguments and results.

It uses F10 – F17, F8, F9, F18 – F23 for floating-point arguments and results.

Special-purpose registers used within Go generated code and Go assembly code are as follows:

RegisterCall meaningReturn meaningBody meaning
X0Zero valueSameSame
X1Link registerLink registerScratch
X2Stack pointerSameSame
X3Global pointerSameUsed by dynamic linker
X4TLS (thread pointer)TLSScratch
X24,X25ScratchScratchUsed by duffcopy, duffzero
X26Closure context pointerScratchScratch
X27Current goroutineSameSame
X31ScratchScratchScratch

Rationale: These register meanings are compatible with Go’s stack-based calling convention. Context register X20 will change to X26, duffcopy, duffzero register will change to X24, X25 before this register ABI been adopted. X10 – X17, X8, X9, X18 – X23, is the same order as A0 – A7, S0 – S7 in platform ABI. F10 – F17, F8, F9, F18 – F23, is the same order as FA0 – FA7, FS0 – FS7 in platform ABI. X8 – X23, F8 – F15 are used for compressed instruction (RVC) which will benefit code size in the future.

Stack layout

The stack pointer, X2, grows down and is aligned to 8 bytes.

A function's stack frame, after the frame is created, is laid out as follows:

+------------------------------+
| ... locals ...               |
| ... outgoing arguments ...   |
| return PC                    | ← X2 points to
+------------------------------+ ↓ lower addresses

The "return PC" is loaded to the link register, X1, as part of the riscv64 CALL operation.

Flags

The riscv64 has Zicsr extension for control and status register (CSR) and treated as scratch register. All bits in CSR are system flags and are not modified by Go.

Future directions //发展方向

Spill path improvements

The ABI currently reserves spill space for argument registers so the compiler can statically generate an argument spill path before calling into runtime.morestack to grow the stack. This ensures there will be sufficient spill space even when the stack is nearly exhausted and keeps stack growth and stack scanning essentially unchanged from ABI0.

However, this wastes stack space (the median wastage is 16 bytes per call), resulting in larger stacks and increased cache footprint. A better approach would be to reserve stack space only when spilling. One way to ensure enough space is available to spill would be for every function to ensure there is enough space for the function's own frame as well as the spill space of all functions it calls. For most functions, this would change the threshold for the prologue stack growth check. For nosplit functions, this would change the threshold used in the linker's static stack size check.

Allocating spill space in the callee rather than the caller may also allow for faster reflection calls in the common case where a function takes only register arguments, since it would allow reflection to make these calls directly without allocating any frame.

The statically-generated spill path also increases code size. It is possible to instead have a generic spill path in the runtime, as part of morestack. However, this complicates reserving the spill space, since spilling all possible register arguments would, in most cases, take significantly more space than spilling only those used by a particular function. Some options are to spill to a temporary space and copy back only the registers used by the function, or to grow the stack if necessary before spilling to it (using a temporary space if necessary), or to use a heap-allocated space if insufficient stack space is available. These options all add enough complexity that we will have to make this decision based on the actual code size growth caused by the static spill paths.

Clobber sets

As defined, the ABI does not use callee-save registers. This significantly simplifies the garbage collector and the compiler's register allocator, but at some performance cost. A potentially better balance for Go code would be to use clobber sets: for each function, the compiler records the set of registers it clobbers (including those clobbered by functions it calls) and any register not clobbered by function F can remain live across calls to F.

This is generally a good fit for Go because Go's package DAG allows function metadata like the clobber set to flow up the call graph, even across package boundaries. Clobber sets would require relatively little change to the garbage collector, unlike general callee-save registers. One disadvantage of clobber sets over callee-save registers is that they don't help with indirect function calls or interface method calls, since static information isn't available in these cases.

Large aggregates go推荐用传值传递 除非特别大的参数使用 引用和延迟复制

Go encourages passing composite values by value, and this simplifies reasoning about mutation and races. However, this comes at a performance cost for large composite values. It may be possible to instead transparently pass large composite values by reference and delay copying until it is actually necessary.

Appendix: Register usage analysis 寄存器使用分析

In order to understand the impacts of the above design on register usage, we analyzed the impact of the above ABI on a large code base: cmd/kubelet from Kubernetes at tag v1.18.8.

The following table shows the impact of different numbers of available integer and floating-point registers on argument assignment:

|      |        |       |      stack args |          spills |     stack total |
| ints | floats | % fit | p50 | p95 | p99 | p50 | p95 | p99 | p50 | p95 | p99 |
|    0 |      0 |  6.3% |  32 | 152 | 256 |   0 |   0 |   0 |  32 | 152 | 256 |
|    0 |      8 |  6.4% |  32 | 152 | 256 |   0 |   0 |   0 |  32 | 152 | 256 |
|    1 |      8 | 21.3% |  24 | 144 | 248 |   8 |   8 |   8 |  32 | 152 | 256 |
|    2 |      8 | 38.9% |  16 | 128 | 224 |   8 |  16 |  16 |  24 | 136 | 240 |
|    3 |      8 | 57.0% |   0 | 120 | 224 |  16 |  24 |  24 |  24 | 136 | 240 |
|    4 |      8 | 73.0% |   0 | 120 | 216 |  16 |  32 |  32 |  24 | 136 | 232 |
|    5 |      8 | 83.3% |   0 | 112 | 216 |  16 |  40 |  40 |  24 | 136 | 232 |
|    6 |      8 | 87.5% |   0 | 112 | 208 |  16 |  48 |  48 |  24 | 136 | 232 |
|    7 |      8 | 89.8% |   0 | 112 | 208 |  16 |  48 |  56 |  24 | 136 | 232 |
|    8 |      8 | 91.3% |   0 | 112 | 200 |  16 |  56 |  64 |  24 | 136 | 232 |
|    9 |      8 | 92.1% |   0 | 112 | 192 |  16 |  56 |  72 |  24 | 136 | 232 |
|   10 |      8 | 92.6% |   0 | 104 | 192 |  16 |  56 |  72 |  24 | 136 | 232 |
|   11 |      8 | 93.1% |   0 | 104 | 184 |  16 |  56 |  80 |  24 | 128 | 232 |
|   12 |      8 | 93.4% |   0 | 104 | 176 |  16 |  56 |  88 |  24 | 128 | 232 |
|   13 |      8 | 94.0% |   0 |  88 | 176 |  16 |  56 |  96 |  24 | 128 | 232 |
|   14 |      8 | 94.4% |   0 |  80 | 152 |  16 |  64 | 104 |  24 | 128 | 232 |
|   15 |      8 | 94.6% |   0 |  80 | 152 |  16 |  64 | 112 |  24 | 128 | 232 |
|   16 |      8 | 94.9% |   0 |  16 | 152 |  16 |  64 | 112 |  24 | 128 | 232 |
|    ∞ |      8 | 99.8% |   0 |   0 |   0 |  24 | 112 | 216 |  24 | 120 | 216 |

The first two columns show the number of available integer and floating-point registers. The first row shows the results for 0 integer and 0 floating-point registers, which is equivalent to ABI0. We found that any reasonable number of floating-point registers has the same effect, so we fixed it at 8 for all other rows.

The “% fit” column gives the fraction of functions where all arguments and results are register-assigned and no arguments are passed on the stack. The three “stack args” columns give the median, 95th and 99th percentile number of bytes of stack arguments. The “spills” columns likewise summarize the number of bytes in on-stack spill space. And “stack total” summarizes the sum of stack arguments and on-stack spill slots. Note that these are three different distributions; for example, there’s no single function that takes 0 stack argument bytes, 16 spill bytes, and 24 total stack bytes.

From this, we can see that the fraction of functions that fit entirely in registers grows very slowly once it reaches about 90%, though curiously there is a small minority of functions that could benefit from a huge number of registers. Making 9 integer registers available on amd64 puts it in this realm. We also see that the stack space required for most functions is fairly small. While the increasing space required for spills largely balances out the decreasing space required for stack arguments as the number of available registers increases, there is a general reduction in the total stack space required with more available registers. This does, however, suggest that eliminating spill slots in the future would noticeably reduce stack requirements.

提案 internal calling convention

提案 register-based Go calling conventionn

Introduction

This is the reference manual for the Go programming language. The pre-Go1.18 version, without generics, can be found here. For more information and other documents, see golang.org.

Go is a general-purpose language designed with systems programming in mind. It is strongly typed and garbage-collected and has explicit support for concurrent programming. Programs are constructed from packages, whose properties allow efficient management of dependencies.

The syntax is compact and simple to parse, allowing for easy analysis by automatic tools such as integrated development environments.

Notation

The syntax is specified using a variant of Extended Backus-Naur Form (EBNF):

Syntax      = { Production } .
Production  = production_name "=" [ Expression ] "." .
Expression  = Term { "|" Term } .
Term        = Factor { Factor } .
Factor      = production_name | token [ "…" token ] | Group | Option | Repetition .
Group       = "(" Expression ")" .
Option      = "[" Expression "]" .
Repetition  = "{" Expression "}" .

Productions are expressions constructed from terms and the following operators, in increasing precedence:

|   alternation
()  grouping
[]  option (0 or 1 times)
{}  repetition (0 to n times)

Lowercase production names are used to identify lexical (terminal) tokens. Non-terminals are in CamelCase. Lexical tokens are enclosed in double quotes "" or back quotes ``.

The form a … b represents the set of characters from a through b as alternatives. The horizontal ellipsis is also used elsewhere in the spec to informally denote various enumerations or code snippets that are not further specified. The character (as opposed to the three characters ...) is not a token of the Go language.

Source code representation

Source code is Unicode text encoded in UTF-8. The text is not canonicalized, so a single accented code point is distinct from the same character constructed from combining an accent and a letter; those are treated as two code points. For simplicity, this document will use the unqualified term character to refer to a Unicode code point in the source text.

Each code point is distinct; for instance, uppercase and lowercase letters are different characters.

Implementation restriction: For compatibility with other tools, a compiler may disallow the NUL character (U+0000) in the source text.

Implementation restriction: For compatibility with other tools, a compiler may ignore a UTF-8-encoded byte order mark (U+FEFF) if it is the first Unicode code point in the source text. A byte order mark may be disallowed anywhere else in the source.

Characters 字符

The following terms are used to denote specific Unicode character categories:

newline        = /* the Unicode code point U+000A */ .
unicode_char   = /* an arbitrary Unicode code point except newline */ .
unicode_letter = /* a Unicode code point categorized as "Letter" */ .
unicode_digit  = /* a Unicode code point categorized as "Number, decimal digit" */ .

In The Unicode Standard 8.0, Section 4.5 "General Category" defines a set of character categories. Go treats all characters in any of the Letter categories Lu, Ll, Lt, Lm, or Lo as Unicode letters, and those in the Number category Nd as Unicode digits.

Letters and digits 字母和数字

The underscore character _ (U+005F) is considered a lowercase letter.

letter        = unicode_letter | "_" .
decimal_digit = "0" … "9" .
binary_digit  = "0" | "1" .
octal_digit   = "0" … "7" .
hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .

Lexical elements

Comments 注释

Comments serve as program documentation. There are two forms:

  1. Line comments start with the character sequence // and stop at the end of the line. 单行注释
  2. General comments start with the character sequence /* and stop with the first subsequent character sequence */. 多行注释

A comment cannot start inside a rune or string literal, or inside a comment. A general comment containing no newlines acts like a space. Any other comment acts like a newline.

Tokens

Tokens form the vocabulary of the Go language. There are four classes: identifiers, keywords, operators and punctuation, and literals. White space, formed from spaces (U+0020), horizontal tabs (U+0009), carriage returns (U+000D), and newlines (U+000A), is ignored except as it separates tokens that would otherwise combine into a single token. Also, a newline or end of file may trigger the insertion of a semicolon. While breaking the input into tokens, the next token is the longest sequence of characters that form a valid token.

Semicolons

The formal syntax uses semicolons ";" as terminators in a number of productions. Go programs may omit most of these semicolons using the following two rules:

  1. When the input is broken into tokens, a semicolon is automatically inserted into the token stream immediately after a line's final token if that token is
  2. To allow complex statements to occupy a single line, a semicolon may be omitted before a closing ")" or "}".

To reflect idiomatic use, code examples in this document elide semicolons using these rules.

Identifiers 标识符

Identifiers name program entities such as variables and types. An identifier is a sequence of one or more letters and digits. The first character in an identifier must be a letter.

identifier = letter { letter | unicode_digit } .
a
_x9
ThisVariableIsExported
αβ

Some identifiers are predeclared.

Keywords 关键字

The following keywords are reserved and may not be used as identifiers.

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

Operators and punctuation 运算符和符号

The following character sequences represent operators (including assignment operators) and punctuation:

+    &amp;     +=    &amp;=     &amp;&amp;    ==    !=    (    )
-    |     -=    |=     ||    &lt;     &lt;=    [    ]
*    ^     *=    ^=     &lt;-    &gt;     &gt;=    {    }
/    &lt;&lt;    /=    &lt;&lt;=    ++    =     :=    ,    ;
%    &gt;&gt;    %=    &gt;&gt;=    --    !     ...   .    :
     &amp;^          &amp;^=          ~

Integer literals 整数

An integer literal is a sequence of digits representing an integer constant. An optional prefix sets a non-decimal base: 0b or 0B for binary, 0, 0o, or 0O for octal, and 0x or 0X for hexadecimal. A single 0 is considered a decimal zero. In hexadecimal literals, letters a through f and A through F represent values 10 through 15.

For readability, an underscore character _ may appear after a base prefix or between successive digits; such underscores do not change the literal's value.

int_lit        = decimal_lit | binary_lit | octal_lit | hex_lit .
decimal_lit    = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
binary_lit     = "0" ( "b" | "B" ) [ "_" ] binary_digits .
octal_lit      = "0" [ "o" | "O" ] [ "_" ] octal_digits .
hex_lit        = "0" ( "x" | "X" ) [ "_" ] hex_digits .

decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
binary_digits  = binary_digit { [ "_" ] binary_digit } .
octal_digits   = octal_digit { [ "_" ] octal_digit } .
hex_digits     = hex_digit { [ "_" ] hex_digit } .
42
4_2
0600
0_600
0o600
0O600       // second character is capital letter 'O'
0xBadFace
0xBad_Face
0x_67_7a_2f_cc_40_c6
170141183460469231731687303715884105727
170_141183_460469_231731_687303_715884_105727

_42         // an identifier, not an integer literal
42_         // invalid: _ must separate successive digits
4__2        // invalid: only one _ at a time
0_xBadFace  // invalid: _ must separate successive digits

Floating-point literals 浮点

A floating-point literal is a decimal or hexadecimal representation of a floating-point constant.

A decimal floating-point literal consists of an integer part (decimal digits), a decimal point, a fractional part (decimal digits), and an exponent part (e or E followed by an optional sign and decimal digits). One of the integer part or the fractional part may be elided; one of the decimal point or the exponent part may be elided. An exponent value exp scales the mantissa (integer and fractional part) by 10exp.

A hexadecimal floating-point literal consists of a 0x or 0X prefix, an integer part (hexadecimal digits), a radix point, a fractional part (hexadecimal digits), and an exponent part (p or P followed by an optional sign and decimal digits). One of the integer part or the fractional part may be elided; the radix point may be elided as well, but the exponent part is required. (This syntax matches the one given in IEEE 754-2008 §5.12.3.) An exponent value exp scales the mantissa (integer and fractional part) by 2exp.

For readability, an underscore character _ may appear after a base prefix or between successive digits; such underscores do not change the literal value.

float_lit         = decimal_float_lit | hex_float_lit .

decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
                    decimal_digits decimal_exponent |
                    "." decimal_digits [ decimal_exponent ] .
decimal_exponent  = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .

hex_float_lit     = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
hex_mantissa      = [ "_" ] hex_digits "." [ hex_digits ] |
                    [ "_" ] hex_digits |
                    "." hex_digits .
hex_exponent      = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
0.
72.40
072.40       // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5.         // == 15.0
0.15e+0_2    // == 15.0

0x1p-2       // == 0.25
0x2.p10      // == 2048.0
0x1.Fp+0     // == 1.9375
0X.8p-0      // == 0.5
0X_1FFFP-16  // == 0.1249847412109375
0x15e-2      // == 0x15e - 2 (integer subtraction)

0x.p1        // invalid: mantissa has no digits
1p-2         // invalid: p exponent requires hexadecimal mantissa
0x1.5e-2     // invalid: hexadecimal mantissa requires p exponent
1_.5         // invalid: _ must separate successive digits
1._5         // invalid: _ must separate successive digits
1.5_e1       // invalid: _ must separate successive digits
1.5e_1       // invalid: _ must separate successive digits
1.5e1_       // invalid: _ must separate successive digits

Imaginary literals 虚数

An imaginary literal represents the imaginary part of a complex constant. It consists of an integer or floating-point literal followed by the lowercase letter i. The value of an imaginary literal is the value of the respective integer or floating-point literal multiplied by the imaginary unit i.

imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .

For backward compatibility, an imaginary literal's integer part consisting entirely of decimal digits (and possibly underscores) is considered a decimal integer, even if it starts with a leading 0.

0i
0123i         // == 123i for backward-compatibility
0o123i        // == 0o123 * 1i == 83i
0xabci        // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i       // == 0x1p-2 * 1i == 0.25i

Rune literals 符文

A rune literal represents a rune constant, an integer value identifying a Unicode code point. A rune literal is expressed as one or more characters enclosed in single quotes, as in 'x' or '\n'. Within the quotes, any character may appear except newline and unescaped single quote. A single quoted character represents the Unicode value of the character itself, while multi-character sequences beginning with a backslash encode values in various formats.

The simplest form represents the single character within the quotes; since Go source text is Unicode characters encoded in UTF-8, multiple UTF-8-encoded bytes may represent a single integer value. For instance, the literal 'a' holds a single byte representing a literal a, Unicode U+0061, value 0x61, while 'ä' holds two bytes (0xc3 0xa4) representing a literal a-dieresis, U+00E4, value 0xe4.

Several backslash escapes allow arbitrary values to be encoded as ASCII text. There are four ways to represent the integer value as a numeric constant: \x followed by exactly two hexadecimal digits; \u followed by exactly four hexadecimal digits; \U followed by exactly eight hexadecimal digits, and a plain backslash \ followed by exactly three octal digits. In each case the value of the literal is the value represented by the digits in the corresponding base.

Although these representations all result in an integer, they have different valid ranges. Octal escapes must represent a value between 0 and 255 inclusive. Hexadecimal escapes satisfy this condition by construction. The escapes \u and \U represent Unicode code points so within them some values are illegal, in particular those above 0x10FFFF and surrogate halves.

After a backslash, certain single-character escapes represent special values:

\a   U+0007 alert or bell
\b   U+0008 backspace
\f   U+000C form feed
\n   U+000A line feed or newline
\r   U+000D carriage return
\t   U+0009 horizontal tab
\v   U+000B vertical tab
\\   U+005C backslash
\'   U+0027 single quote  (valid escape only within rune literals)
\"   U+0022 double quote  (valid escape only within string literals)

An unrecognized character following a backslash in a rune literal is illegal.

rune_lit         = "'" ( unicode_value | byte_value ) "'" .
unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
byte_value       = octal_byte_value | hex_byte_value .
octal_byte_value = `\` octal_digit octal_digit octal_digit .
hex_byte_value   = `\` "x" hex_digit hex_digit .
little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
                           hex_digit hex_digit hex_digit hex_digit .
escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
'a'
'ä'
'本'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'
'\''         // rune literal containing single quote character
'aa'         // illegal: too many characters
'\k'         // illegal: k is not recognized after a backslash
'\xa'        // illegal: too few hexadecimal digits
'\0'         // illegal: too few octal digits
'\400'       // illegal: octal value over 255
'\uDFFF'     // illegal: surrogate half
'\U00110000' // illegal: invalid Unicode code point

String literals 字符串

A string literal represents a string constant obtained from concatenating a sequence of characters. There are two forms: raw string literals and interpreted string literals.

Raw string literals are character sequences between back quotes, as in `foo`. Within the quotes, any character may appear except back quote. The value of a raw string literal is the string composed of the uninterpreted (implicitly UTF-8-encoded) characters between the quotes; in particular, backslashes have no special meaning and the string may contain newlines. Carriage return characters ('\r') inside raw string literals are discarded from the raw string value.

Interpreted string literals are character sequences between double quotes, as in "bar". Within the quotes, any character may appear except newline and unescaped double quote. The text between the quotes forms the value of the literal, with backslash escapes interpreted as they are in rune literals (except that \' is illegal and \" is legal), with the same restrictions. The three-digit octal (\nnn) and two-digit hexadecimal (\xnn) escapes represent individual bytes of the resulting string; all other escapes represent the (possibly multi-byte) UTF-8 encoding of individual characters. Thus inside a string literal \377 and \xFF represent a single byte of value 0xFF=255, while ÿ, \u00FF, \U000000FF and \xc3\xbf represent the two bytes 0xc3 0xbf of the UTF-8 encoding of character U+00FF.

string_lit             = raw_string_lit | interpreted_string_lit .
raw_string_lit         = "`" { unicode_char | newline } "`" .
interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
`abc`                // same as "abc"
`\n
\n`                  // same as "\\n\n\\n"
"\n"
"\""                 // same as `"`
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
"\uD800"             // illegal: surrogate half
"\U00110000"         // illegal: invalid Unicode code point

These examples all represent the same string:

"日本語"                                 // UTF-8 input text
`日本語`                                 // UTF-8 input text as a raw literal
"\u65e5\u672c\u8a9e"                    // the explicit Unicode code points
"\U000065e5\U0000672c\U00008a9e"        // the explicit Unicode code points
"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"  // the explicit UTF-8 bytes

If the source code represents a character as two code points, such as a combining form involving an accent and a letter, the result will be an error if placed in a rune literal (it is not a single code point), and will appear as two code points if placed in a string literal.

Constants 常量

There are boolean constants, rune constants, integer constants, floating-point constants, complex constants, and string constants. Rune, integer, floating-point, and complex constants are collectively called numeric constants.

A constant value is represented by a rune, integer, floating-point, imaginary, or string literal, an identifier denoting a constant, a constant expression, a conversion with a result that is a constant, or the result value of some built-in functions such as unsafe.Sizeof applied to certain values, cap or len applied to some expressions, real and imag applied to a complex constant and complex applied to numeric constants. The boolean truth values are represented by the predeclared constants true and false. The predeclared identifier iota denotes an integer constant.

In general, complex constants are a form of constant expression and are discussed in that section.

Numeric constants represent exact values of arbitrary precision and do not overflow. Consequently, there are no constants denoting the IEEE-754 negative zero, infinity, and not-a-number values.

Constants may be typed or untyped. Literal constants, true, false, iota, and certain constant expressions containing only untyped constant operands are untyped.

A constant may be given a type explicitly by a constant declaration or conversion, or implicitly when used in a variable declaration or an assignment statement or as an operand in an expression. It is an error if the constant value cannot be represented as a value of the respective type. If the type is a type parameter, the constant is converted into a non-constant value of the type parameter.

An untyped constant has a default type which is the type to which the constant is implicitly converted in contexts where a typed value is required, for instance, in a short variable declaration such as i := 0 where there is no explicit type. The default type of an untyped constant is bool, rune, int, float64, complex128 or string respectively, depending on whether it is a boolean, rune, integer, floating-point, complex, or string constant.

Implementation restriction: Although numeric constants have arbitrary precision in the language, a compiler may implement them using an internal representation with limited precision. That said, every implementation must:

  • Represent integer constants with at least 256 bits.
  • Represent floating-point constants, including the parts of a complex constant, with a mantissa of at least 256 bits and a signed binary exponent of at least 16 bits.
  • Give an error if unable to represent an integer constant precisely.
  • Give an error if unable to represent a floating-point or complex constant due to overflow.
  • Round to the nearest representable constant if unable to represent a floating-point or complex constant due to limits on precision.

These requirements apply both to literal constants and to the result of evaluating constant expressions.

Variables 变量

A variable is a storage location for holding a value. The set of permissible values is determined by the variable's type.

A variable declaration or, for function parameters and results, the signature of a function declaration or function literal reserves storage for a named variable.

Calling the built-in function new or taking the address of a composite literal allocates storage for a variable at run time. Such an anonymous variable is referred to via a (possibly implicit) pointer indirection.

Structured variables of array, slice, and struct types have elements and fields that may be addressed individually. Each such element acts like a variable.

The static type (or just type) of a variable is the type given in its declaration, the type provided in the new call or composite literal, or the type of an element of a structured variable. Variables of interface type also have a distinct dynamic type, which is the (non-interface) type of the value assigned to the variable at run time (unless the value is the predeclared identifier nil, which has no type). The dynamic type may vary during execution but values stored in interface variables are always assignable to the static type of the variable.

var x interface{}  // x is nil and has static type interface{}
var v *T           // v has value nil, static type *T
x = 42             // x has value 42 and dynamic type int
x = v              // x has value (*T)(nil) and dynamic type *T

A variable's value is retrieved by referring to the variable in an expression; it is the most recent value assigned to the variable. If a variable has not yet been assigned a value, its value is the zero value for its type.

Types 类型

A type determines a set of values together with operations and methods specific to those values. A type may be denoted by a type name, if it has one, which must be followed by type arguments if the type is generic. A type may also be specified using a type literal, which composes a type from existing types.

Type      = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
TypeName  = identifier | QualifiedIdent .
TypeArgs  = "[" TypeList [ "," ] "]" .
TypeList  = Type { "," Type } .
TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
            SliceType | MapType | ChannelType .

The language predeclares certain type names. Others are introduced with type declarations or type parameter lists. Composite types—array, struct, pointer, function, interface, slice, map, and channel types—may be constructed using type literals.

Predeclared types, defined types, and type parameters are called named types. An alias denotes a named type if the type given in the alias declaration is a named type.

Boolean types 布尔类型

A boolean type represents the set of Boolean truth values denoted by the predeclared constants true and false. The predeclared boolean type is bool; it is a defined type.

Numeric types 数字类型

An integer, floating-point, or complex type represents the set of integer, floating-point, or complex values, respectively. They are collectively called numeric types. The predeclared architecture-independent numeric types are:

uint8       the set of all unsigned  8-bit integers (0 to 255)
uint16      the set of all unsigned 16-bit integers (0 to 65535)
uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)

int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

float32     the set of all IEEE-754 32-bit floating-point numbers
float64     the set of all IEEE-754 64-bit floating-point numbers

complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

byte        alias for uint8
rune        alias for int32

The value of an n-bit integer is n bits wide and represented using two's complement arithmetic.

There is also a set of predeclared integer types with implementation-specific sizes:

uint     either 32 or 64 bits
int      same size as uint
uintptr  an unsigned integer large enough to store the uninterpreted bits of a pointer value

To avoid portability issues all numeric types are defined types and thus distinct except byte, which is an alias for uint8, and rune, which is an alias for int32. Explicit conversions are required when different numeric types are mixed in an expression or assignment. For instance, int32 and int are not the same type even though they may have the same size on a particular architecture.

String types 字符类型

A string type represents the set of string values. A string value is a (possibly empty) sequence of bytes. The number of bytes is called the length of the string and is never negative. Strings are immutable: once created, it is impossible to change the contents of a string. The predeclared string type is string; it is a defined type.

The length of a string s can be discovered using the built-in function len. The length is a compile-time constant if the string is a constant. A string's bytes can be accessed by integer indices 0 through len(s)-1. It is illegal to take the address of such an element; if s[i] is the i'th byte of a string, &s[i] is invalid.

Array types 数组类型

An array is a numbered sequence of elements of a single type, called the element type. The number of elements is called the length of the array and is never negative.

ArrayType   = "[" ArrayLength "]" ElementType .
ArrayLength = Expression .
ElementType = Type .

The length is part of the array's type; it must evaluate to a non-negative constant representable by a value of type int. The length of array a can be discovered using the built-in function len. The elements can be addressed by integer indices 0 through len(a)-1. Array types are always one-dimensional but may be composed to form multi-dimensional types.

[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64  // same as [2]([2]([2]float64))

An array type T may not have an element of type T, or of a type containing T as a component, directly or indirectly, if those containing types are only array or struct types.

// invalid array types
type (
	T1 [10]T1                 // element type of T1 is T1
	T2 [10]struct{ f T2 }     // T2 contains T2 as component of a struct
	T3 [10]T4                 // T3 contains T3 as component of a struct in T4
	T4 struct{ f T3 }         // T4 contains T4 as component of array T3 in a struct
)

// valid array types
type (
	T5 [10]*T5                // T5 contains T5 as component of a pointer
	T6 [10]func() T6          // T6 contains T6 as component of a function type
	T7 [10]struct{ f []T7 }   // T7 contains T7 as component of a slice in a struct
)

Slice types 切片类型

A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array. A slice type denotes the set of all slices of arrays of its element type. The number of elements is called the length of the slice and is never negative. The value of an uninitialized slice is nil.

SliceType = "[" "]" ElementType .

The length of a slice s can be discovered by the built-in function len; unlike with arrays it may change during execution. The elements can be addressed by integer indices 0 through len(s)-1. The slice index of a given element may be less than the index of the same element in the underlying array.

A slice, once initialized, is always associated with an underlying array that holds its elements. A slice therefore shares storage with its array and with other slices of the same array; by contrast, distinct arrays always represent distinct storage.

The array underlying a slice may extend past the end of the slice. The capacity is a measure of that extent: it is the sum of the length of the slice and the length of the array beyond the slice; a slice of length up to that capacity can be created by slicing a new one from the original slice. The capacity of a slice a can be discovered using the built-in function cap(a).

A new, initialized slice value for a given element type T may be made using the built-in function make, which takes a slice type and parameters specifying the length and optionally the capacity. A slice created with make always allocates a new, hidden array to which the returned slice value refers. That is, executing

make([]T, length, capacity)

produces the same slice as allocating an array and slicing it, so these two expressions are equivalent:

make([]int, 50, 100)
new([100]int)[0:50]

Like arrays, slices are always one-dimensional but may be composed to construct higher-dimensional objects. With arrays of arrays, the inner arrays are, by construction, always the same length; however with slices of slices (or arrays of slices), the inner lengths may vary dynamically. Moreover, the inner slices must be initialized individually.

Struct types 结构体类型

A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField). Within a struct, non-blank field names must be unique.

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
Tag           = string_lit .
// An empty struct.
struct {}

// A struct with 6 fields.
struct {
	x, y int
	u float32
	_ float32  // padding
	A *[]int
	F func()
}

A field declared with a type but no explicit field name is called an embedded field. An embedded field must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name.

// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
struct {
	T1        // field name is T1
	*T2       // field name is T2
	P.T3      // field name is T3
	*P.T4     // field name is T4
	x, y int  // field names are x and y
}

The following declaration is illegal because field names must be unique in a struct type:

struct {
	T     // conflicts with embedded field *T and *P.T
	*T    // conflicts with embedded field T and *P.T
	*P.T  // conflicts with embedded field T and *T
}

A field or method f of an embedded field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

Given a struct type S and a named type T, promoted methods are included in the method set of the struct as follows:

  • If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.
  • If S contains an embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.

A field declaration may be followed by an optional string literal tag, which becomes an attribute for all the fields in the corresponding field declaration. An empty tag string is equivalent to an absent tag. The tags are made visible through a reflection interface and take part in type identity for structs but are otherwise ignored.

struct {
	x, y float64 ""  // an empty tag string is like an absent tag
	name string  "any string is permitted as a tag"
	_    [4]byte "ceci n'est pas un champ de structure"
}

// A struct corresponding to a TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers;
// they follow the convention outlined by the reflect package.
struct {
	microsec  uint64 `protobuf:"1"`
	serverIP6 uint64 `protobuf:"2"`
}

A struct type T may not contain a field of type T, or of a type containing T as a component, directly or indirectly, if those containing types are only array or struct types.

// invalid struct types
type (
	T1 struct{ T1 }            // T1 contains a field of T1
	T2 struct{ f [10]T2 }      // T2 contains T2 as component of an array
	T3 struct{ T4 }            // T3 contains T3 as component of an array in struct T4
	T4 struct{ f [10]T3 }      // T4 contains T4 as component of struct T3 in an array
)

// valid struct types
type (
	T5 struct{ f *T5 }         // T5 contains T5 as component of a pointer
	T6 struct{ f func() T6 }   // T6 contains T6 as component of a function type
	T7 struct{ f [10][]T7 }    // T7 contains T7 as component of a slice in an array
)

Pointer types 指针类型

A pointer type denotes the set of all pointers to variables of a given type, called the base type of the pointer. The value of an uninitialized pointer is nil.

PointerType = "*" BaseType .
BaseType    = Type .
*Point
*[4]int

Function types 函数类型

A function type denotes the set of all functions with the same parameter and result types. The value of an uninitialized variable of function type is nil.

FunctionType   = "func" Signature .
Signature      = Parameters [ Result ] .
Result         = Parameters | Type .
Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .

Within a list of parameters or results, the names (IdentifierList) must either all be present or all be absent. If present, each name stands for one item (parameter or result) of the specified type and all non-blank names in the signature must be unique. If absent, each type stands for one item of that type. Parameter and result lists are always parenthesized except that if there is exactly one unnamed result it may be written as an unparenthesized type.

The final incoming parameter in a function signature may have a type prefixed with .... A function with such a parameter is called variadic and may be invoked with zero or more arguments for that parameter.

func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)

Interface types 接口类型

An interface type defines a type set. A variable of interface type can store a value of any type that is in the type set of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

InterfaceType  = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem  = MethodElem | TypeElem .
MethodElem     = MethodName Signature .
MethodName     = identifier .
TypeElem       = TypeTerm { "|" TypeTerm } .
TypeTerm       = Type | UnderlyingType .
UnderlyingType = "~" Type .

An interface type is specified by a list of interface elements. An interface element is either a method or a type element, where a type element is a union of one or more type terms. A type term is either a single type or a single underlying type.

Basic interfaces 基础接口

In its most basic form an interface specifies a (possibly empty) list of methods. The type set defined by such an interface is the set of types which implement all of those methods, and the corresponding method set consists exactly of the methods specified by the interface. Interfaces whose type sets can be defined entirely by a list of methods are called basic interfaces.

// A simple File interface.
interface {
	Read([]byte) (int, error)
	Write([]byte) (int, error)
	Close() error
}

The name of each explicitly specified method must be unique and not blank.

interface {
	String() string
	String() string  // illegal: 方法不能重复
	_(x int)         // illegal: 方法不能为空
}

More than one type may implement an interface. For instance, if two types S1 and S2 have the method set

func (p T) Read(p []byte) (n int, err error)
func (p T) Write(p []byte) (n int, err error)
func (p T) Close() error

(where T stands for either S1 or S2) then the File interface is implemented by both S1 and S2, regardless of what other methods S1 and S2 may have or share.

Every type that is a member of the type set of an interface implements that interface. Any given type may implement several distinct interfaces. For instance, all types implement the empty interface which stands for the set of all (non-interface) types:

interface{}

For convenience, the predeclared type any is an alias for the empty interface.

Similarly, consider this interface specification, which appears within a type declaration to define an interface called Locker:

type Locker interface {
	Lock()
	Unlock()
}

If S1 and S2 also implement

func (p T) Lock() { … }
func (p T) Unlock() { … }

they implement the Locker interface as well as the File interface.

Embedded interfaces 内嵌接口

In a slightly more general form an interface T may use a (possibly qualified) interface type name E as an interface element. This is called embedding interface E in T. The type set of T is the intersection of the type sets defined by T's explicitly declared methods and the type sets of T’s embedded interfaces. In other words, the type set of T is the set of all types that implement all the explicitly declared methods of T and also all the methods of E.

type Reader interface {
	Read(p []byte) (n int, err error)
	Close() error
}

type Writer interface {
	Write(p []byte) (n int, err error)
	Close() error
}

// ReadWriter's methods are Read, Write, and Close.
type ReadWriter interface {
	Reader  // includes methods of Reader in ReadWriter's method set
	Writer  // includes methods of Writer in ReadWriter's method set
}

When embedding interfaces, methods with the same names must have identical signatures.

type ReadCloser interface {
	Reader   // includes methods of Reader in ReadCloser's method set
	Close()  // illegal: signatures of Reader.Close and Close are different
}

General interfaces 泛型接口

In their most general form, an interface element may also be an arbitrary type term T, or a term of the form ~T specifying the underlying type T, or a union of terms t1|t2|…|tn. Together with method specifications, these elements enable the precise definition of an interface's type set as follows:

  • The type set of the empty interface is the set of all non-interface types.
  • The type set of a non-empty interface is the intersection of the type sets of its interface elements.
  • The type set of a method specification is the set of all non-interface types whose method sets include that method.
  • The type set of a non-interface type term is the set consisting of just that type.
  • The type set of a term of the form ~T is the set of all types whose underlying type is T.
  • The type set of a union of terms t1|t2|…|tn is the union of the type sets of the terms.

The quantification "the set of all non-interface types" refers not just to all (non-interface) types declared in the program at hand, but all possible types in all possible programs, and hence is infinite. Similarly, given the set of all non-interface types that implement a particular method, the intersection of the method sets of those types will contain exactly that method, even if all types in the program at hand always pair that method with another method.

By construction, an interface's type set never contains an interface type.

// An interface representing only the type int.
interface {
	int
}

// An interface representing all types with underlying type int.
interface {
	~int
}

// An interface representing all types with underlying type int that implement the String method.
interface {
	~int
	String() string
}

// An interface representing an empty type set: there is no type that is both an int and a string.
interface {
	int
	string
}

In a term of the form ~T, the underlying type of T must be itself, and T cannot be an interface.

type MyInt int

interface {
	~[]byte  // the underlying type of []byte is itself
	~MyInt   // illegal: the underlying type of MyInt is not MyInt
	~error   // illegal: error is an interface
}

Union elements denote unions of type sets:

// The Float interface represents all floating-point types
// (including any named types whose underlying types are
// either float32 or float64).
type Float interface {
	~float32 | ~float64
}

The type T in a term of the form T or ~T cannot be a type parameter, and the type sets of all non-interface terms must be pairwise disjoint (the pairwise intersection of the type sets must be empty). Given a type parameter P:

interface {
	P                // illegal: P is a type parameter
	int | ~P         // illegal: P is a type parameter
	~int | MyInt     // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
	float32 | Float  // overlapping type sets but Float is an interface
}

Implementation restriction: A union (with more than one term) cannot contain the predeclared identifier comparable or interfaces that specify methods, or embed comparable or interfaces that specify methods.

Interfaces that are not basic may only be used as type constraints, or as elements of other interfaces used as constraints. They cannot be the types of values or variables, or components of other, non-interface types.

var x Float                     // illegal: Float is not a basic interface

var x interface{} = Float(nil)  // illegal

type Floatish struct {
	f Float                 // illegal
}

An interface type T may not embed a type element that is, contains, or embeds T, directly or indirectly.

// illegal: Bad may not embed itself
type Bad interface {
	Bad
}

// illegal: Bad1 may not embed itself using Bad2
type Bad1 interface {
	Bad2
}
type Bad2 interface {
	Bad1
}

// illegal: Bad3 may not embed a union containing Bad3
type Bad3 interface {
	~int | ~string | Bad3
}

// illegal: Bad4 may not embed an array containing Bad4 as element type
type Bad4 interface {
	[10]Bad4
}

Implementing an interface 接口实现

A type T implements an interface I if

  • T is not an interface and is an element of the type set of I; or
  • T is an interface and the type set of T is a subset of the type set of I.

A value of type T implements an interface if T implements the interface.

Map types map类型

A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type. The value of an uninitialized map is nil.

MapType     = "map" "[" KeyType "]" ElementType .
KeyType     = Type .

The comparison operators == and != must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice. If the key type is an interface type, these comparison operators must be defined for the dynamic key values; failure will cause a run-time panic.

map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}

The number of map elements is called its length. For a map m, it can be discovered using the built-in function len and may change during execution. Elements may be added during execution using assignments and retrieved with index expressions; they may be removed with the delete and clear built-in function.

A new, empty map value is made using the built-in function make, which takes the map type and an optional capacity hint as arguments:

make(map[string]int)
make(map[string]int, 100)

The initial capacity does not bound its size: maps grow to accommodate the number of items stored in them, with the exception of nil maps. A nil map is equivalent to an empty map except that no elements may be added.

Channel types channel 类型

A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type. The value of an uninitialized channel is nil.

ChannelType = ( "chan" | "chan" "&lt;-" | "&lt;-" "chan" ) ElementType .

The optional <- operator specifies the channel direction, send or receive. If a direction is given, the channel is directional, otherwise it is bidirectional. A channel may be constrained only to send or only to receive by assignment or explicit conversion.

chan T          // can be used to send and receive values of type T
chan&lt;- float64  // can only be used to send float64s
&lt;-chan int      // can only be used to receive ints

The <- operator associates with the leftmost chan possible:

chan&lt;- chan int    // same as chan&lt;- (chan int)
chan&lt;- &lt;-chan int  // same as chan&lt;- (&lt;-chan int)
&lt;-chan &lt;-chan int  // same as &lt;-chan (&lt;-chan int)
chan (&lt;-chan int)

A new, initialized channel value can be made using the built-in function make, which takes the channel type and an optional capacity as arguments:

make(chan int, 100)

The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives). A nil channel is never ready for communication.

A channel may be closed with the built-in function close. The multi-valued assignment form of the receive operator reports whether a received value was sent before the channel was closed.

A single channel may be used in send statements, receive operations, and calls to the built-in functions cap and len by any number of goroutines without further synchronization. Channels act as first-in-first-out queues. For example, if one goroutine sends values on a channel and a second goroutine receives them, the values are received in the order sent.

Properties of types and values 属性和值

Underlying types 基础类型

Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its declaration. For a type parameter that is the underlying type of its type constraint, which is always an interface.

type (
	A1 = string
	A2 = A1
)

type (
	B1 string
	B2 B1
	B3 []B1
	B4 B3
)

func f[P any](x P) { … }

The underlying type of string, A1, A2, B1, and B2 is string. The underlying type of []B1, B3, and B4 is []B1. The underlying type of P is interface{}.

Core types 核心类型

Each non-interface type T has a core type, which is the same as the underlying type of T.

An interface T has a core type if one of the following conditions is satisfied:

  1. There is a single type U which is the underlying type of all types in the type set of T; or
  2. the type set of T contains only channel types with identical element type E, and all directional channels have the same direction.

No other interfaces have a core type.

The core type of an interface is, depending on the condition that is satisfied, either:

  1. the type U; or
  2. the type chan E if T contains only bidirectional channels, or the type chan<- E or <-chan E depending on the direction of the directional channels present.

By definition, a core type is never a defined type, type parameter, or interface type.

Examples of interfaces with core types:

type Celsius float32
type Kelvin  float32

interface{ int }                          // int
interface{ Celsius|Kelvin }               // float32
interface{ ~chan int }                    // chan int
interface{ ~chan int|~chan&lt;- int }        // chan&lt;- int
interface{ ~[]*data; String() string }    // []*data

Examples of interfaces without core types:

interface{}                               // no single underlying type
interface{ Celsius|float64 }              // no single underlying type
interface{ chan int | chan&lt;- string }     // channels have different element types
interface{ &lt;-chan int | chan&lt;- int }      // directional channels have different directions

Some operations (slice expressions, append and copy) rely on a slightly more loose form of core types which accept byte slices and strings. Specifically, if there are exactly two types, []byte and string, which are the underlying types of all types in the type set of interface T, the core type of T is called bytestring.

Examples of interfaces with bytestring core types:

interface{ int }                          // int (same as ordinary core type)
interface{ []byte | string }              // bytestring
interface{ ~[]byte | myString }           // bytestring

Note that bytestring is not a real type; it cannot be used to declare variables are compose other types. It exists solely to describe the behavior of some operations that read from a sequence of bytes, which may be a byte slice or a string.

Type identity 类型定义

Two types are either identical or different.

A named type is always different from any other type. Otherwise, two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types. In detail:

  • Two array types are identical if they have identical element types and the same array length.
  • Two slice types are identical if they have identical element types.
  • Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. Non-exported field names from different packages are always different.
  • Two pointer types are identical if they have identical base types.
  • Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is. Parameter and result names are not required to match.
  • Two interface types are identical if they define the same type set.
  • Two map types are identical if they have identical key and element types.
  • Two channel types are identical if they have identical element types and the same direction.
  • Two instantiated types are identical if their defined types and all type arguments are identical.

Given the declarations

type (
	A0 = []string
	A1 = A0
	A2 = struct{ a, b int }
	A3 = int
	A4 = func(A3, float64) *A0
	A5 = func(x int, _ float64) *[]string

	B0 A0
	B1 []string
	B2 struct{ a, b int }
	B3 struct{ a, c int }
	B4 func(int, float64) *B0
	B5 func(x int, y float64) *A1

	C0 = B0
	D0[P1, P2 any] struct{ x P1; y P2 }
	E0 = D0[int, string]
)

these types are identical:

A0, A1, and []string
A2 and struct{ a, b int }
A3 and int
A4, func(int, float64) *[]string, and A5

B0 and C0
D0[int, string] and E0
[]int and []int
struct{ a, b *B5 } and struct{ a, b *B5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5

B0 and B1 are different because they are new types created by distinct type definitions; func(int, float64) *B0 and func(x int, y float64) *[]string are different because B0 is different from []string; and P1 and P2 are different because they are different type parameters. D0[int, string] and struct{ x int; y string } are different because the former is an instantiated defined type while the latter is a type literal (but they are still assignable).

Assignability 赋值

A value x of type V is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:

  • V and T are identical.
  • V and T have identical underlying types but are not type parameters and at least one of V or T is not a named type.
  • V and T are channel types with identical element types, V is a bidirectional channel, and at least one of V or T is not a named type.
  • T is an interface type, but not a type parameter, and x implements T.
  • x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type, but not a type parameter.
  • x is an untyped constant representable by a value of type T.

Additionally, if x's type V or T are type parameters, x is assignable to a variable of type T if one of the following conditions applies:

  • x is the predeclared identifier nil, T is a type parameter, and x is assignable to each type in T's type set.
  • V is not a named type, T is a type parameter, and x is assignable to each type in T's type set.
  • V is a type parameter and T is not a named type, and values of each type in V's type set are assignable to T.

Representability

A constant x is representable by a value of type T, where T is not a type parameter, if one of the following conditions applies:

  • x is in the set of values determined by T.
  • T is a floating-point type and x can be rounded to T's precision without overflow. Rounding uses IEEE 754 round-to-even rules but with an IEEE negative zero further simplified to an unsigned zero. Note that constant values never result in an IEEE negative zero, NaN, or infinity.
  • T is a complex type, and x's components real(x) and imag(x) are representable by values of T's component type (float32 or float64).

If T is a type parameter, x is representable by a value of type T if x is representable by a value of each type in T's type set.

x                   T           x is representable by a value of T because

'a'                 byte        97 is in the set of byte values
97                  rune        rune is an alias for int32, and 97 is in the set of 32-bit integers
"foo"               string      "foo" is in the set of string values
1024                int16       1024 is in the set of 16-bit integers
42.0                byte        42 is in the set of unsigned 8-bit integers
1e10                uint64      10000000000 is in the set of unsigned 64-bit integers
2.718281828459045   float32     2.718281828459045 rounds to 2.7182817 which is in the set of float32 values
-1e-1000            float64     -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0
0i                  int         0 is an integer value
(42 + 0i)           float32     42.0 (with zero imaginary part) is in the set of float32 values

x                   T           x is not representable by a value of T because

0                   bool        0 is not in the set of boolean values
'a'                 string      'a' is a rune, it is not in the set of string values
1024                byte        1024 is not in the set of unsigned 8-bit integers
-1                  uint16      -1 is not in the set of unsigned 16-bit integers
1.1                 int         1.1 is not an integer value
42i                 float32     (0 + 42i) is not in the set of float32 values
1e1000              float64     1e1000 overflows to IEEE +Inf after rounding

Method sets 方法

The method set of a type determines the methods that can be called on an operand of that type. Every type has a (possibly empty) method set associated with it:

  • The method set of a defined type T consists of all methods declared with receiver type T.
  • The method set of a pointer to a defined type T (where T is neither a pointer nor an interface) is the set of all methods declared with receiver *T or T.
  • The method set of an interface type is the intersection of the method sets of each type in the interface's type set (the resulting method set is usually just the set of declared methods in the interface).

Further rules apply to structs (and pointer to structs) containing embedded fields, as described in the section on struct types. Any other type has an empty method set.

In a method set, each method must have a unique non-blank method name.

Blocks 块

A block is a possibly empty sequence of declarations and statements within matching brace brackets.

Block = "{" StatementList "}" .
StatementList = { Statement ";" } .

In addition to explicit blocks in the source code, there are implicit blocks:

  1. The universe block encompasses all Go source text.
  2. Each package has a package block containing all Go source text for that package.
  3. Each file has a file block containing all Go source text in that file.
  4. Each "if", "for", and "switch" statement is considered to be in its own implicit block.
  5. Each clause in a "switch" or "select" statement acts as an implicit block.

Blocks nest and influence scoping.

Declarations and scope 声明 和作用域

A declaration binds a non-blank identifier to a constant, type, type parameter, variable, function, label, or package. Every identifier in a program must be declared. No identifier may be declared twice in the same block, and no identifier may be declared in both the file and package block.

The blank identifier may be used like any other identifier in a declaration, but it does not introduce a binding and thus is not declared. In the package block, the identifier init may only be used for init function declarations, and like the blank identifier it does not introduce a new binding.

Declaration   = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl  = Declaration | FunctionDecl | MethodDecl .

The scope of a declared identifier is the extent of source text in which the identifier denotes the specified constant, type, variable, function, label, or package.

Go is lexically scoped using blocks:

  1. The scope of a predeclared identifier is the universe block.
  2. The scope of an identifier denoting a constant, type, variable, or function (but not method) declared at top level (outside any function) is the package block.
  3. The scope of the package name of an imported package is the file block of the file containing the import declaration.
  4. The scope of an identifier denoting a method receiver, function parameter, or result variable is the function body.
  5. The scope of an identifier denoting a type parameter of a function or declared by a method receiver begins after the name of the function and ends at the end of the function body.
  6. The scope of an identifier denoting a type parameter of a type begins after the name of the type and ends at the end of the TypeSpec.
  7. The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.
  8. The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.

An identifier declared in a block may be redeclared in an inner block. While the identifier of the inner declaration is in scope, it denotes the entity declared by the inner declaration.

The package clause is not a declaration; the package name does not appear in any scope. Its purpose is to identify the files belonging to the same package and to specify the default package name for import declarations.

Label scopes 标签作用域

Labels are declared by labeled statements and are used in the "break", "continue", and "goto" statements. It is illegal to define a label that is never used. In contrast to other identifiers, labels are not block scoped and do not conflict with identifiers that are not labels. The scope of a label is the body of the function in which it is declared and excludes the body of any nested function.

Blank identifier 空标示

The blank identifier is represented by the underscore character _. It serves as an anonymous placeholder instead of a regular (non-blank) identifier and has special meaning in declarations, as an operand, and in assignment statements.

Predeclared identifiers

The following identifiers are implicitly declared in the universe block:

Types:
	any bool byte comparable
	complex64 complex128 error float32 float64
	int int8 int16 int32 int64 rune string
	uint uint8 uint16 uint32 uint64 uintptr

Constants:
	true false iota

Zero value:
	nil

Functions:
	append cap clear close complex copy delete imag len
	make new panic print println real recover

Exported identifiers 导出标识符

An identifier may be exported to permit access to it from another package. An identifier is exported if both:

  1. the first character of the identifier's name is a Unicode uppercase letter (Unicode character category Lu); and
  2. the identifier is declared in the package block or it is a field name or method name.

All other identifiers are not exported.

Uniqueness of identifiers 唯一标示

Given a set of identifiers, an identifier is called unique if it is different from every other in the set. Two identifiers are different if they are spelled differently, or if they appear in different packages and are not exported. Otherwise, they are the same.

Constant declarations 常量声明

A constant declaration binds a list of identifiers (the names of the constants) to the values of a list of constant expressions. The number of identifiers must be equal to the number of expressions, and the nth identifier on the left is bound to the value of the nth expression on the right.

ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .

IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .

If the type is present, all constants take the type specified, and the expressions must be assignable to that type, which must not be a type parameter. If the type is omitted, the constants take the individual types of the corresponding expressions. If the expression values are untyped constants, the declared constants remain untyped and the constant identifiers denote the constant values. For instance, if the expression is a floating-point literal, the constant identifier denotes a floating-point constant, even if the literal's fractional part is zero.

const Pi float64 = 3.14159265358979323846
const zero = 0.0         // untyped floating-point constant
const (
	size int64 = 1024
	eof        = -1  // untyped integer constant
)
const a, b, c = 3, 4, "foo"  // a = 3, b = 4, c = "foo", untyped integer and string constants
const u, v float32 = 0, 3    // u = 0.0, v = 3.0

Within a parenthesized const declaration list the expression list may be omitted from any but the first ConstSpec. Such an empty list is equivalent to the textual substitution of the first preceding non-empty expression list and its type if any. Omitting the list of expressions is therefore equivalent to repeating the previous list. The number of identifiers must be equal to the number of expressions in the previous list. Together with the iota constant generator this mechanism permits light-weight declaration of sequential values:

const (
	Sunday = iota
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Partyday
	numberOfDays  // this constant is not exported
)

Iota

Within a constant declaration, the predeclared identifier iota represents successive untyped integer constants. Its value is the index of the respective ConstSpec in that constant declaration, starting at zero. It can be used to construct a set of related constants:

const (
	c0 = iota  // c0 == 0
	c1 = iota  // c1 == 1
	c2 = iota  // c2 == 2
)

const (
	a = 1 &lt;&lt; iota  // a == 1  (iota == 0)
	b = 1 &lt;&lt; iota  // b == 2  (iota == 1)
	c = 3          // c == 3  (iota == 2, unused)
	d = 1 &lt;&lt; iota  // d == 8  (iota == 3)
)

const (
	u         = iota * 42  // u == 0     (untyped integer constant)
	v float64 = iota * 42  // v == 42.0  (float64 constant)
	w         = iota * 42  // w == 84    (untyped integer constant)
)

const x = iota  // x == 0
const y = iota  // y == 0

By definition, multiple uses of iota in the same ConstSpec all have the same value:

const (
	bit0, mask0 = 1 &lt;&lt; iota, 1&lt;&lt;iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)
	bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)
	_, _                                  //                        (iota == 2, unused)
	bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
)

This last example exploits the implicit repetition of the last non-empty expression list.

Type declarations 类型声明

A type declaration binds an identifier, the type name, to a type. Type declarations come in two forms: alias declarations and type definitions.

TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .

Alias declarations

An alias declaration binds an identifier to the given type.

AliasDecl = identifier "=" Type .

Within the scope of the identifier, it serves as an alias for the type.

type (
	nodeList = []*Node  // nodeList and []*Node are identical types
	Polar    = polar    // Polar and polar denote identical types
)

Type definitions 类型定义

A type definition creates a new, distinct type with the same underlying type and operations as the given type and binds an identifier, the type name, to it.

TypeDef = identifier [ TypeParameters ] Type .

The new type is called a defined type. It is different from any other type, including the type it is created from.

type (
	Point struct{ x, y float64 }  // Point and struct{ x, y float64 } are different types
	polar Point                   // polar and Point denote different types
)

type TreeNode struct {
	left, right *TreeNode
	value any
}

type Block interface {
	BlockSize() int
	Encrypt(src, dst []byte)
	Decrypt(src, dst []byte)
}

A defined type may have methods associated with it. It does not inherit any methods bound to the given type, but the method set of an interface type or of elements of a composite type remains unchanged:

// A Mutex is a data type with two methods, Lock and Unlock.
type Mutex struct         { /* Mutex fields */ }
func (m *Mutex) Lock()    { /* Lock implementation */ }
func (m *Mutex) Unlock()  { /* Unlock implementation */ }

// NewMutex has the same composition as Mutex but its method set is empty.
type NewMutex Mutex

// The method set of PtrMutex's underlying type *Mutex remains unchanged,
// but the method set of PtrMutex is empty.
type PtrMutex *Mutex

// The method set of *PrintableMutex contains the methods
// Lock and Unlock bound to its embedded field Mutex.
type PrintableMutex struct {
	Mutex
}

// MyBlock is an interface type that has the same method set as Block.
type MyBlock Block

Type definitions may be used to define different boolean, numeric, or string types and associate methods with them:

type TimeZone int

const (
	EST TimeZone = -(5 + iota)
	CST
	MST
	PST
)

func (tz TimeZone) String() string {
	return fmt.Sprintf("GMT%+dh", tz)
}

If the type definition specifies type parameters, the type name denotes a generic type. Generic types must be instantiated when they are used.

type List[T any] struct {
	next  *List[T]
	value T
}

In a type definition the given type cannot be a type parameter.

type T[P any] P    // illegal: P is a type parameter

func f[T any]() {
	type L T   // illegal: T is a type parameter declared by the enclosing function
}

A generic type may also have methods associated with it. In this case, the method receivers must declare the same number of type parameters as present in the generic type definition.

// The method Len returns the number of elements in the linked list l.
func (l *List[T]) Len() int  { … }

Type parameter declarations 类型参数声明

A type parameter list declares the type parameters of a generic function or type declaration. The type parameter list looks like an ordinary function parameter list except that the type parameter names must all be present and the list is enclosed in square brackets rather than parentheses.

TypeParameters  = "[" TypeParamList [ "," ] "]" .
TypeParamList   = TypeParamDecl { "," TypeParamDecl } .
TypeParamDecl   = IdentifierList TypeConstraint .

All non-blank names in the list must be unique. Each name declares a type parameter, which is a new and different named type that acts as a place holder for an (as of yet) unknown type in the declaration. The type parameter is replaced with a type argument upon instantiation of the generic function or type.

[P any]
[S interface{ ~[]byte|string }]
[S ~[]E, E any]
[P Constraint[int]]
[_ any]

Just as each ordinary function parameter has a parameter type, each type parameter has a corresponding (meta-)type which is called its type constraint.

A parsing ambiguity arises when the type parameter list for a generic type declares a single type parameter P with a constraint C such that the text P C forms a valid expression:

type T[P *C] …
type T[P (C)] …
type T[P *C|Q] …
…

In these rare cases, the type parameter list is indistinguishable from an expression and the type declaration is parsed as an array type declaration. To resolve the ambiguity, embed the constraint in an interface or use a trailing comma:

type T[P interface{*C}] …
type T[P *C,] …

Type parameters may also be declared by the receiver specification of a method declaration associated with a generic type.

Within a type parameter list of a generic type T, a type constraint may not (directly, or indirectly through the type parameter list of another generic type) refer to T.

type T1[P T1[P]] …                    // illegal: T1 refers to itself
type T2[P interface{ T2[int] }] …     // illegal: T2 refers to itself
type T3[P interface{ m(T3[int])}] …   // illegal: T3 refers to itself
type T4[P T5[P]] …                    // illegal: T4 refers to T5 and
type T5[P T4[P]] …                    //          T5 refers to T4

type T6[P int] struct{ f *T6[P] }     // ok: reference to T6 is not in type parameter list

Type constraints 类型约束

A type constraint is an interface that defines the set of permissible type arguments for the respective type parameter and controls the operations supported by values of that type parameter.

TypeConstraint = TypeElem .

If the constraint is an interface literal of the form interface{E} where E is an embedded type element (not a method), in a type parameter list the enclosing interface{ … } may be omitted for convenience:

[T []P]                      // = [T interface{[]P}]
[T ~int]                     // = [T interface{~int}]
[T int|string]               // = [T interface{int|string}]
type Constraint ~int         // illegal: ~int is not in a type parameter list

The predeclared interface type comparable denotes the set of all non-interface types that are strictly comparable.

Even though interfaces that are not type parameters are comparable, they are not strictly comparable and therefore they do not implement comparable. However, they satisfy comparable.

int                          // implements comparable (int is strictly comparable)
[]byte                       // does not implement comparable (slices cannot be compared)
interface{}                  // does not implement comparable (see above)
interface{ ~int | ~string }  // type parameter only: implements comparable (int, string types are strictly comparable)
interface{ comparable }      // type parameter only: implements comparable (comparable implements itself)
interface{ ~int | ~[]byte }  // type parameter only: does not implement comparable (slices are not comparable)
interface{ ~struct{ any } }  // type parameter only: does not implement comparable (field any is not strictly comparable)

The comparable interface and interfaces that (directly or indirectly) embed comparable may only be used as type constraints. They cannot be the types of values or variables, or components of other, non-interface types.

Satisfying a type constraint 满足类型约束

A type argument T satisfies a type constraint C if T is an element of the type set defined by C; i.e., if T implements C. As an exception, a strictly comparable type constraint may also be satisfied by a comparable (not necessarily strictly comparable) type argument. More precisely:

A type T satisfies a constraint C if

type argument      type constraint                // constraint satisfaction

int                interface{ ~int }              // satisfied: int implements interface{ ~int }
string             comparable                     // satisfied: string implements comparable (string is strictly comparable)
[]byte             comparable                     // not satisfied: slices are not comparable
any                interface{ comparable; int }   // not satisfied: any does not implement interface{ int }
any                comparable                     // satisfied: any is comparable and implements the basic interface any
struct{f any}      comparable                     // satisfied: struct{f any} is comparable and implements the basic interface any
any                interface{ comparable; m() }   // not satisfied: any does not implement the basic interface interface{ m() }
interface{ m() }   interface{ comparable; m() }   // satisfied: interface{ m() } is comparable and implements the basic interface interface{ m() }

Because of the exception in the constraint satisfaction rule, comparing operands of type parameter type may panic at run-time (even though comparable type parameters are always strictly comparable).

Variable declarations 变量声明

A variable declaration creates one or more variables, binds corresponding identifiers to them, and gives each a type and an initial value.

VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
var i int
var U, V, W float64
var k = 0
var x, y float32 = -1, -2
var (
	i       int
	u, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name]  // map lookup; only interested in "found"

If a list of expressions is given, the variables are initialized with the expressions following the rules for assignment statements. Otherwise, each variable is initialized to its zero value.

If a type is present, each variable is given that type. Otherwise, each variable is given the type of the corresponding initialization value in the assignment. If that value is an untyped constant, it is first implicitly converted to its default type; if it is an untyped boolean value, it is first implicitly converted to type bool. The predeclared value nil cannot be used to initialize a variable with no explicit type.

var d = math.Sin(0.5)  // d is float64
var i = 42             // i is int
var t, ok = x.(T)      // t is T, ok is bool
var n = nil            // illegal

Implementation restriction: A compiler may make it illegal to declare a variable inside a function body if the variable is never used.

Short variable declarations 短变量声明

A short variable declaration uses the syntax:

ShortVarDecl = IdentifierList ":=" ExpressionList .

It is shorthand for a regular variable declaration with initializer expressions but no types:

"var" IdentifierList "=" ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe()  // os.Pipe() returns a connected pair of Files and an error, if any
_, y, _ := coord(p)   // coord() returns three values; only interested in y coordinate

Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original. The non-blank variable names on the left side of := must be unique.

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset)  // redeclares offset
x, y, x := 1, 2, 3                        // illegal: x repeated on left side of :=

Short variable declarations may appear only inside functions. In some contexts such as the initializers for "if", "for", or "switch" statements, they can be used to declare local temporary variables.

Function declarations 函数声明

A function declaration binds an identifier, the function name, to a function.

FunctionDecl = "func" FunctionName [ TypeParameters ] Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .

If the function's signature declares result parameters, the function body's statement list must end in a terminating statement.

func IndexRune(s string, r rune) int {
	for i, c := range s {
		if c == r {
			return i
		}
	}
	// invalid: missing return statement
}

If the function declaration specifies type parameters, the function name denotes a generic function. A generic function must be instantiated before it can be called or used as a value.

func min[T ~int|~float64](x, y T) T {
	if x &lt; y {
		return x
	}
	return y
}

A function declaration without type parameters may omit the body. Such a declaration provides the signature for a function implemented outside Go, such as an assembly routine.

func flushICache(begin, end uintptr)  // implemented externally

Method declarations 方法声明

A method is a function with a receiver. A method declaration binds an identifier, the method name, to a method, and associates the method with the receiver's base type.

MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver   = Parameters .

The receiver is specified via an extra parameter section preceding the method name. That parameter section must declare a single non-variadic parameter, the receiver. Its type must be a defined type T or a pointer to a defined type T, possibly followed by a list of type parameter names [P1, P2, …] enclosed in square brackets. T is called the receiver base type. A receiver base type cannot be a pointer or interface type and it must be defined in the same package as the method. The method is said to be bound to its receiver base type and the method name is visible only within selectors for type T or *T.

A non-blank receiver identifier must be unique in the method signature. If the receiver's value is not referenced inside the body of the method, its identifier may be omitted in the declaration. The same applies in general to parameters of functions and methods.

For a base type, the non-blank names of methods bound to it must be unique. If the base type is a struct type, the non-blank method and field names must be distinct.

Given defined type Point the declarations

func (p *Point) Length() float64 {
	return math.Sqrt(p.x * p.x + p.y * p.y)
}

func (p *Point) Scale(factor float64) {
	p.x *= factor
	p.y *= factor
}

bind the methods Length and Scale, with receiver type *Point, to the base type Point.

If the receiver base type is a generic type, the receiver specification must declare corresponding type parameters for the method to use. This makes the receiver type parameters available to the method. Syntactically, this type parameter declaration looks like an instantiation of the receiver base type: the type arguments must be identifiers denoting the type parameters being declared, one for each type parameter of the receiver base type. The type parameter names do not need to match their corresponding parameter names in the receiver base type definition, and all non-blank parameter names must be unique in the receiver parameter section and the method signature. The receiver type parameter constraints are implied by the receiver base type definition: corresponding type parameters have corresponding constraints.

type Pair[A, B any] struct {
	a A
	b B
}

func (p Pair[A, B]) Swap() Pair[B, A]  { … }  // receiver declares A, B
func (p Pair[First, _]) First() First  { … }  // receiver declares First, corresponds to A in Pair

Expressions 表达式

An expression specifies the computation of a value by applying operators and functions to operands.

Operands 操作数

Operands denote the elementary values in an expression. An operand may be a literal, a (possibly qualified) non-blank identifier denoting a constant, variable, or function, or a parenthesized expression.

Operand     = Literal | OperandName [ TypeArgs ] | "(" Expression ")" .
Literal     = BasicLit | CompositeLit | FunctionLit .
BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent .

An operand name denoting a generic function may be followed by a list of type arguments; the resulting operand is an instantiated function.

The blank identifier may appear as an operand only on the left-hand side of an assignment statement.

Implementation restriction: A compiler need not report an error if an operand's type is a type parameter with an empty type set. Functions with such type parameters cannot be instantiated; any attempt will lead to an error at the instantiation site.

Qualified identifiers 合格的操作数

A qualified identifier is an identifier qualified with a package name prefix. Both the package name and the identifier must not be blank.

QualifiedIdent = PackageName "." identifier .

A qualified identifier accesses an identifier in a different package, which must be imported. The identifier must be exported and declared in the package block of that package.

math.Sin // denotes the Sin function in package math

Composite literals 符合文字

Composite literals construct new composite values each time they are evaluated. They consist of the type of the literal followed by a brace-bound list of elements. Each element may optionally be preceded by a corresponding key.

CompositeLit  = LiteralType LiteralValue .
LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
                SliceType | MapType | TypeName [ TypeArgs ] .
LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
ElementList   = KeyedElement { "," KeyedElement } .
KeyedElement  = [ Key ":" ] Element .
Key           = FieldName | Expression | LiteralValue .
FieldName     = identifier .
Element       = Expression | LiteralValue .

The LiteralType's core type T must be a struct, array, slice, or map type (the syntax enforces this constraint except when the type is given as a TypeName). The types of the elements and keys must be assignable to the respective field, element, and key types of type T; there is no additional conversion. The key is interpreted as a field name for struct literals, an index for array and slice literals, and a key for map literals. For map literals, all elements must have a key. It is an error to specify multiple elements with the same field name or constant key value. For non-constant map keys, see the section on evaluation order.

For struct literals the following rules apply:

  • A key must be a field name declared in the struct type.
  • An element list that does not contain any keys must list an element for each struct field in the order in which the fields are declared.
  • If any element has a key, every element must have a key.
  • An element list that contains keys does not need to have an element for each struct field. Omitted fields get the zero value for that field.
  • A literal may omit the element list; such a literal evaluates to the zero value for its type.
  • It is an error to specify an element for a non-exported field of a struct belonging to a different package.

Given the declarations

type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }

one may write

origin := Point3D{}                            // zero value for Point3D
line := Line{origin, Point3D{y: -4, z: 12.3}}  // zero value for line.q.x

For array and slice literals the following rules apply:

  • Each element has an associated integer index marking its position in the array.
  • An element with a key uses the key as its index. The key must be a non-negative constant representable by a value of type int; and if it is typed it must be of integer type.
  • An element without a key uses the previous element's index plus one. If the first element has no key, its index is zero.

Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal's value.

var pointer *Point3D = &amp;Point3D{y: 1000}

Note that the zero value for a slice or map type is not the same as an initialized but empty value of the same type. Consequently, taking the address of an empty slice or map composite literal does not have the same effect as allocating a new slice or map value with new.

p1 := &amp;[]int{}    // p1 points to an initialized, empty slice with value []int{} and length 0
p2 := new([]int)  // p2 points to an uninitialized slice with value nil and length 0

The length of an array literal is the length specified in the literal type. If fewer elements than the length are provided in the literal, the missing elements are set to the zero value for the array element type. It is an error to provide elements with index values outside the index range of the array. The notation ... specifies an array length equal to the maximum element index plus one.

buffer := [10]string{}             // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
days := [...]string{"Sat", "Sun"}  // len(days) == 2

A slice literal describes the entire underlying array literal. Thus the length and capacity of a slice literal are the maximum element index plus one. A slice literal has the form

[]T{x1, x2, … xn}

and is shorthand for a slice operation applied to an array:

tmp := [n]T{x1, x2, … xn}
tmp[0 : n]

Within a composite literal of array, slice, or map type T, elements or map keys that are themselves composite literals may elide the respective literal type if it is identical to the element or key type of T. Similarly, elements or keys that are addresses of composite literals may elide the &T when the element or key type is *T.

[...]Point{{1.5, -3.5}, {0, 0}}     // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
[][]int{{1, 2, 3}, {4, 5}}          // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
[][]Point{{{0, 1}, {1, 2}}}         // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
map[string]Point{"orig": {0, 0}}    // same as map[string]Point{"orig": Point{0, 0}}
map[Point]string{{0, 0}: "orig"}    // same as map[Point]string{Point{0, 0}: "orig"}

type PPoint *Point
[2]*Point{{1.5, -3.5}, {}}          // same as [2]*Point{&amp;Point{1.5, -3.5}, &amp;Point{}}
[2]PPoint{{1.5, -3.5}, {}}          // same as [2]PPoint{PPoint(&amp;Point{1.5, -3.5}), PPoint(&amp;Point{})}

A parsing ambiguity arises when a composite literal using the TypeName form of the LiteralType appears as an operand between the keyword and the opening brace of the block of an "if", "for", or "switch" statement, and the composite literal is not enclosed in parentheses, square brackets, or curly braces. In this rare case, the opening brace of the literal is erroneously parsed as the one introducing the block of statements. To resolve the ambiguity, the composite literal must appear within parentheses.

if x == (T{a,b,c}[i]) { … }
if (x == T{a,b,c}[i]) { … }

Examples of valid array, slice, and map literals:

// list of prime numbers
primes := []int{2, 3, 5, 7, 9, 2147483647}

// vowels[ch] is true if ch is a vowel
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}

// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}

// frequencies in Hz for equal-tempered scale (A4 = 440Hz)
noteFrequency := map[string]float32{
	"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
	"G0": 24.50, "A0": 27.50, "B0": 30.87,
}

Function literals 函数

A function literal represents an anonymous function. Function literals cannot declare type parameters.

FunctionLit = "func" Signature FunctionBody .
func(a, b int, z float64) bool { return a*b &lt; int(z) }

A function literal can be assigned to a variable or invoked directly.

f := func(x, y int) int { return x + y }
func(ch chan int) { ch &lt;- ACK }(replyChan)

Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

Primary expressions 主表达式

Primary expressions are the operands for unary and binary expressions.

PrimaryExpr =
	Operand |
	Conversion |
	MethodExpr |
	PrimaryExpr Selector |
	PrimaryExpr Index |
	PrimaryExpr Slice |
	PrimaryExpr TypeAssertion |
	PrimaryExpr Arguments .

Selector       = "." identifier .
Index          = "[" Expression [ "," ] "]" .
Slice          = "[" [ Expression ] ":" [ Expression ] "]" |
                 "[" [ Expression ] ":" Expression ":" Expression "]" .
TypeAssertion  = "." "(" Type ")" .
Arguments      = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
x
2
(s + ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
f.p[i].x()

Selectors 选择器

For a primary expression x that is not a package name, the selector expression

x.f

denotes the field or method f of the value x (or sometimes *x; see below). The identifier f is called the (field or method) selector; it must not be the blank identifier. The type of the selector expression is the type of f. If x is a package name, see the section on qualified identifiers.

A selector f may denote a field or method f of a type T, or it may refer to a field or method f of a nested embedded field of T. The number of embedded fields traversed to reach f is called its depth in T. The depth of a field or method f declared in T is zero. The depth of a field or method f declared in an embedded field A in T is the depth of f in A plus one.

The following rules apply to selectors:

  1. For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.
  2. For a value x of type I where I is an interface type, x.f denotes the actual method with name f of the dynamic value of x. If there is no method with name f in the method set of I, the selector expression is illegal.
  3. As an exception, if the type of x is a defined pointer type and (*x).f is a valid selector expression denoting a field (but not a method), x.f is shorthand for (*x).f.
  4. In all other cases, x.f is illegal.
  5. If x is of pointer type and has the value nil and x.f denotes a struct field, assigning to or evaluating x.f causes a run-time panic.
  6. If x is of interface type and has the value nil, calling or evaluating the method x.f causes a run-time panic.

For example, given the declarations:

type T0 struct {
	x int
}

func (*T0) M0()

type T1 struct {
	y int
}

func (T1) M1()

type T2 struct {
	z int
	T1
	*T0
}

func (*T2) M2()

type Q *T2

var t T2     // with t.T0 != nil
var p *T2    // with p != nil and (*p).T0 != nil
var q Q = p

one may write:

t.z          // t.z
t.y          // t.T1.y
t.x          // (*t.T0).x

p.z          // (*p).z
p.y          // (*p).T1.y
p.x          // (*(*p).T0).x

q.x          // (*(*q).T0).x        (*q).x is a valid field selector

p.M0()       // ((*p).T0).M0()      M0 expects *T0 receiver
p.M1()       // ((*p).T1).M1()      M1 expects T1 receiver
p.M2()       // p.M2()              M2 expects *T2 receiver
t.M2()       // (&amp;t).M2()           M2 expects *T2 receiver, see section on Calls

but the following is invalid:

q.M0()       // (*q).M0 is valid but not a field selector

Method expressions 方法表达式

If M is in the method set of type T, T.M is a function that is callable as a regular function with the same arguments as M prefixed by an additional argument that is the receiver of the method.

MethodExpr    = ReceiverType "." MethodName .
ReceiverType  = Type .

Consider a struct type T with two methods, Mv, whose receiver is of type T, and Mp, whose receiver is of type *T.

type T struct {
	a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

var t T

The expression

T.Mv

yields a function equivalent to Mv but with an explicit receiver as its first argument; it has signature

func(tv T, a int) int

That function may be called normally with an explicit receiver, so these five invocations are equivalent:

t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)

Similarly, the expression

(*T).Mp

yields a function value representing Mp with signature

func(tp *T, f float32) float32

For a method with a value receiver, one can derive a function with an explicit pointer receiver, so

(*T).Mv

yields a function value representing Mv with signature

func(tv *T, a int) int

Such a function indirects through the receiver to create a value to pass as the receiver to the underlying method; the method does not overwrite the value whose address is passed in the function call.

The final case, a value-receiver function for a pointer-receiver method, is illegal because pointer-receiver methods are not in the method set of the value type.

Function values derived from methods are called with function call syntax; the receiver is provided as the first argument to the call. That is, given f := T.Mv, f is invoked as f(t, 7) not t.f(7). To construct a function that binds the receiver, use a function literal or method value.

It is legal to derive a function value from a method of an interface type. The resulting function takes an explicit receiver of that interface type.

Method values 方法值

If the expression x has static type T and M is in the method set of type T, x.M is called a method value. The method value x.M is a function value that is callable with the same arguments as a method call of x.M. The expression x is evaluated and saved during the evaluation of the method value; the saved copy is then used as the receiver in any calls, which may be executed later.

type S struct { *T }
type T int
func (t T) M() { print(t) }

t := new(T)
s := S{T: t}
f := t.M                    // receiver *t is evaluated and stored in f
g := s.M                    // receiver *(s.T) is evaluated and stored in g
*t = 42                     // does not affect stored receivers in f and g

The type T may be an interface or non-interface type.

As in the discussion of method expressions above, consider a struct type T with two methods, Mv, whose receiver is of type T, and Mp, whose receiver is of type *T.

type T struct {
	a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

var t T
var pt *T
func makeT() T

The expression

t.Mv

yields a function value of type

func(int) int

These two invocations are equivalent:

t.Mv(7)
f := t.Mv; f(7)

Similarly, the expression

pt.Mp

yields a function value of type

func(float32) float32

As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.

As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.

f := t.Mv; f(7)   // like t.Mv(7)
f := pt.Mp; f(7)  // like pt.Mp(7)
f := pt.Mv; f(7)  // like (*pt).Mv(7)
f := t.Mp; f(7)   // like (&amp;t).Mp(7)
f := makeT().Mp   // invalid: result of makeT() is not addressable

Although the examples above use non-interface types, it is also legal to create a method value from a value of interface type.

var i interface { M(int) } = myVal
f := i.M; f(7)  // like i.M(7)

Index expressions 索引表达式

A primary expression of the form

a[x]

denotes the element of the array, pointer to array, slice, string or map a indexed by x. The value x is called the index or map key, respectively. The following rules apply:

If a is neither a map nor a type parameter:

  • the index x must be an untyped constant or its core type must be an integer
  • a constant index must be non-negative and representable by a value of type int
  • a constant index that is untyped is given type int
  • the index x is in range if 0 <= x < len(a), otherwise it is out of range

For a of array type A:

  • a constant index must be in range
  • if x is out of range at run time, a run-time panic occurs
  • a[x] is the array element at index x and the type of a[x] is the element type of A

For a of pointer to array type:

  • a[x] is shorthand for (*a)[x]

For a of slice type S:

  • if x is out of range at run time, a run-time panic occurs
  • a[x] is the slice element at index x and the type of a[x] is the element type of S

For a of string type:

  • a constant index must be in range if the string a is also constant
  • if x is out of range at run time, a run-time panic occurs
  • a[x] is the non-constant byte value at index x and the type of a[x] is byte
  • a[x] may not be assigned to

For a of map type M:

  • x's type must be assignable to the key type of M
  • if the map contains an entry with key x, a[x] is the map element with key x and the type of a[x] is the element type of M
  • if the map is nil or does not contain such an entry, a[x] is the zero value for the element type of M

For a of type parameter type P:

  • The index expression a[x] must be valid for values of all types in P's type set.
  • The element types of all types in P's type set must be identical. In this context, the element type of a string type is byte.
  • If there is a map type in the type set of P, all types in that type set must be map types, and the respective key types must be all identical.
  • a[x] is the array, slice, or string element at index x, or the map element with key x of the type argument that P is instantiated with, and the type of a[x] is the type of the (identical) element types.
  • a[x] may not be assigned to if P's type set includes string types.

Otherwise a[x] is illegal.

An index expression on a map a of type map[K]V used in an assignment statement or initialization of the special form

v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]

yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.

Assigning to an element of a nil map causes a run-time panic.

Slice expressions 切片表达式

Slice expressions construct a substring or slice from a string, array, pointer to array, or slice. There are two variants: a simple form that specifies a low and high bound, and a full form that also specifies a bound on the capacity.

Simple slice expressions

The primary expression

a[low : high]

constructs a substring or slice. The core type of a must be a string, array, pointer to array, slice, or a bytestring. The indices low and high select which elements of operand a appear in the result. The result has indices starting at 0 and length equal to high - low. After slicing the array a

a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]

the slice s has type []int, length 3, capacity 4, and elements

s[0] == 2
s[1] == 3
s[2] == 4

For convenience, any of the indices may be omitted. A missing low index defaults to zero; a missing high index defaults to the length of the sliced operand:

a[2:]  // same as a[2 : len(a)]
a[:3]  // same as a[0 : 3]
a[:]   // same as a[0 : len(a)]

If a is a pointer to an array, a[low : high] is shorthand for (*a)[low : high].

For arrays or strings, the indices are in range if 0 <= low <= high <= len(a), otherwise they are out of range. For slices, the upper index bound is the slice capacity cap(a) rather than the length. A constant index must be non-negative and representable by a value of type int; for arrays or constant strings, constant indices must also be in range. If both indices are constant, they must satisfy low <= high. If the indices are out of range at run time, a run-time panic occurs.

Except for untyped strings, if the sliced operand is a string or slice, the result of the slice operation is a non-constant value of the same type as the operand. For untyped string operands the result is a non-constant value of type string. If the sliced operand is an array, it must be addressable and the result of the slice operation is a slice with the same element type as the array.

If the sliced operand of a valid slice expression is a nil slice, the result is a nil slice. Otherwise, if the result is a slice, it shares its underlying array with the operand.

var a [10]int
s1 := a[3:7]   // underlying array of s1 is array a; &amp;s1[2] == &amp;a[5]
s2 := s1[1:4]  // underlying array of s2 is underlying array of s1 which is array a; &amp;s2[1] == &amp;a[5]
s2[1] = 42     // s2[1] == s1[2] == a[5] == 42; they all refer to the same underlying array element

var s []int
s3 := s[:0]    // s3 == nil

Full slice expressions

The primary expression

a[low : high : max]

constructs a slice of the same type, and with the same length and elements as the simple slice expression a[low : high]. Additionally, it controls the resulting slice's capacity by setting it to max - low. Only the first index may be omitted; it defaults to 0. The core type of a must be an array, pointer to array, or slice (but not a string). After slicing the array a

a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]

the slice t has type []int, length 2, capacity 4, and elements

t[0] == 2
t[1] == 3

As for simple slice expressions, if a is a pointer to an array, a[low : high : max] is shorthand for (*a)[low : high : max]. If the sliced operand is an array, it must be addressable.

The indices are in range if 0 <= low <= high <= max <= cap(a), otherwise they are out of range. A constant index must be non-negative and representable by a value of type int; for arrays, constant indices must also be in range. If multiple indices are constant, the constants that are present must be in range relative to each other. If the indices are out of range at run time, a run-time panic occurs.

Type assertions 类型断言

For an expression x of interface type, but not a type parameter, and a type T, the primary expression

x.(T)

asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion.

More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. In this case, T must implement the (interface) type of x; otherwise the type assertion is invalid since it is not possible for x to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T.

If the type assertion holds, the value of the expression is the value stored in x and its type is T. If the type assertion is false, a run-time panic occurs. In other words, even though the dynamic type of x is known only at run time, the type of x.(T) is known to be T in a correct program.

var x interface{} = 7          // x has dynamic type int and value 7
i := x.(int)                   // i has type int and value 7

type I interface { m() }

func f(y I) {
	s := y.(string)        // illegal: string does not implement I (missing method m)
	r := y.(io.Reader)     // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
	…
}

A type assertion used in an assignment statement or initialization of the special form

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool

yields an additional untyped boolean value. The value of ok is true if the assertion holds. Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case.

Calls 调用

Given an expression f with a core type F of function type,

f(a1, a2, … an)

calls f with arguments a1, a2, … an. Except for one special case, arguments must be single-valued expressions assignable to the parameter types of F and are evaluated before the function is called. The type of the expression is the result type of F. A method invocation is similar but the method itself is specified as a selector upon a value of the receiver type for the method.

math.Atan2(x, y)  // function call
var pt *Point
pt.Scale(3.5)     // method call with receiver pt

If f denotes a generic function, it must be instantiated before it can be called or used as a function value.

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the caller when the function returns.

Calling a nil function value causes a run-time panic.

As a special case, if the return values of a function or method g are equal in number and individually assignable to the parameters of another function or method f, then the call f(g(parameters_of_g)) will invoke f after binding the return values of g to the parameters of f in order. The call of f must contain no parameters other than the call of g, and g must have at least one return value. If f has a final ... parameter, it is assigned the return values of g that remain after assignment of regular parameters.

func Split(s string, pos int) (string, string) {
	return s[0:pos], s[pos:]
}

func Join(s, t string) string {
	return s + t
}

if Join(Split(value, len(value)/2)) != value {
	log.Panic("test fails")
}

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m():

var p Point
p.Scale(3.5)

There is no distinct method type and there are no method literals.

Passing arguments to ... parameters 参数 ...

If f is variadic with a final parameter p of type ...T, then within f the type of p is equivalent to type []T. If f is invoked with no actual arguments for p, the value passed to p is nil. Otherwise, the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T. The length and capacity of the slice is therefore the number of arguments bound to p and may differ for each call site.

Given the function and calls

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

within Greeting, who will have the value nil in the first call, and []string{"Joe", "Anna", "Eileen"} in the second.

If the final argument is assignable to a slice type []T and is followed by ..., it is passed unchanged as the value for a ...T parameter. In this case no new slice is created.

Given the slice s and call

s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

within Greeting, who will have the same value as s with the same underlying array.

Instantiations 实例化

A generic function or type is instantiated by substituting type arguments for the type parameters. Instantiation proceeds in two steps:

  1. Each type argument is substituted for its corresponding type parameter in the generic declaration. This substitution happens across the entire function or type declaration, including the type parameter list itself and any types in that list.
  2. After substitution, each type argument must satisfy the constraint (instantiated, if necessary) of the corresponding type parameter. Otherwise instantiation fails.

Instantiating a type results in a new non-generic named type; instantiating a function produces a new non-generic function.

type parameter list    type arguments    after substitution

[P any]                int               int satisfies any
[S ~[]E, E any]        []int, int        []int satisfies ~[]int, int satisfies any
[P io.Writer]          string            illegal: string doesnot satisfy io.Writer
[P comparable]         any               any satisfies (but does not implement) comparable

For a generic function, type arguments may be provided explicitly, or they may be partially or completely inferred. A generic function that is not called requires a type argument list for instantiation; if the list is partial, all remaining type arguments must be inferrable. A generic function that is called may provide a (possibly partial) type argument list, or may omit it entirely if the omitted type arguments are inferrable from the ordinary (non-type) function arguments.

func min[T ~int|~float64](x, y T) T { … }

f := min                   // illegal: min must be instantiated with type arguments when used without being called
minInt := min[int]         // minInt has type func(x, y int) int
a := minInt(2, 3)          // a has value 2 of type int
b := min[float64](2.0, 3)  // b has value 2.0 of type float64
c := min(b, -1)            // c has value -1.0 of type float64

A partial type argument list cannot be empty; at least the first argument must be present. The list is a prefix of the full list of type arguments, leaving the remaining arguments to be inferred. Loosely speaking, type arguments may be omitted from "right to left".

func apply[S ~[]E, E any](s S, f func(E) E) S { … }

f0 := apply[]                  // illegal: type argument list cannot be empty
f1 := apply[[]int]             // type argument for S explicitly provided, type argument for E inferred
f2 := apply[[]string, string]  // both type arguments explicitly provided

var bytes []byte
r := apply(bytes, func(byte) byte { … })  // both type arguments inferred from the function arguments

For a generic type, all type arguments must always be provided explicitly.

Type inference 类型接口

Missing function type arguments may be inferred by a series of steps, described below. Each step attempts to use known information to infer additional type arguments. Type inference stops as soon as all type arguments are known. After type inference is complete, it is still necessary to substitute all type arguments for type parameters and verify that each type argument implements the relevant constraint; it is possible for an inferred type argument to fail to implement a constraint, in which case instantiation fails.

Type inference is based on

  • a type parameter list
  • a substitution map M initialized with the known type arguments, if any
  • a (possibly empty) list of ordinary function arguments (in case of a function call only)

and then proceeds with the following steps:

  1. apply function argument type inference to all typed ordinary function arguments
  2. apply constraint type inference
  3. apply function argument type inference to all untyped ordinary function arguments using the default type for each of the untyped function arguments
  4. apply constraint type inference

If there are no ordinary or untyped function arguments, the respective steps are skipped. Constraint type inference is skipped if the previous step didn't infer any new type arguments, but it is run at least once if there are missing type arguments.

The substitution map M is carried through all steps, and each step may add entries to M. The process stops as soon as M has a type argument for each type parameter or if an inference step fails. If an inference step fails, or if M is still missing type arguments after the last step, type inference fails.

Type unification 类型统一 ???

Type inference is based on type unification. A single unification step applies to a substitution map and two types, either or both of which may be or contain type parameters. The substitution map tracks the known (explicitly provided or already inferred) type arguments: the map contains an entry PA for each type parameter P and corresponding known type argument A. During unification, known type arguments take the place of their corresponding type parameters when comparing types. Unification is the process of finding substitution map entries that make the two types equivalent.

For unification, two types that don't contain any type parameters from the current type parameter list are equivalent if they are identical, or if they are channel types that are identical ignoring channel direction, or if their underlying types are equivalent.

Unification works by comparing the structure of pairs of types: their structure disregarding type parameters must be identical, and types other than type parameters must be equivalent. A type parameter in one type may match any complete subtype in the other type; each successful match causes an entry to be added to the substitution map. If the structure differs, or types other than type parameters are not equivalent, unification fails.

For example, if T1 and T2 are type parameters, []map[int]bool can be unified with any of the following:

[]map[int]bool   // types are identical
T1               // adds T1 &RightArrow; []map[int]bool to substitution map
[]T1             // adds T1 &RightArrow; map[int]bool to substitution map
[]map[T1]T2      // adds T1 &RightArrow; int and T2 &RightArrow; bool to substitution map

On the other hand, []map[int]bool cannot be unified with any of

int              // int is not a slice
struct{}         // a struct is not a slice
[]struct{}       // a struct is not a map
[]map[T1]string  // map element types don't match

As an exception to this general rule, because a defined type D and a type literal L are never equivalent, unification compares the underlying type of D with L instead. For example, given the defined type

type Vector []float64

and the type literal []E, unification compares []float64 with []E and adds an entry Efloat64 to the substitution map.

Function argument type inference 方法参数类型

Function argument type inference infers type arguments from function arguments: if a function parameter is declared with a type T that uses type parameters, unifying the type of the corresponding function argument with T may infer type arguments for the type parameters used by T.

For instance, given the generic function

func scale[Number ~int64|~float64|~complex128](v []Number, s Number) []Number

and the call

var vector []float64
scaledVector := scale(vector, 42)

the type argument for Number can be inferred from the function argument vector by unifying the type of vector with the corresponding parameter type: []float64 and []Number match in structure and float64 matches with Number. This adds the entry Numberfloat64 to the substitution map. Untyped arguments, such as the second function argument 42 here, are ignored in the first round of function argument type inference and only considered if there are unresolved type parameters left.

Inference happens in two separate phases; each phase operates on a specific list of (parameter, argument) pairs:

  1. The list Lt contains all (parameter, argument) pairs where the parameter type uses type parameters and where the function argument is typed.
  2. The list Lu contains all remaining pairs where the parameter type is a single type parameter. In this list, the respective function arguments are untyped.

Any other (parameter, argument) pair is ignored.

By construction, the arguments of the pairs in Lu are untyped constants (or the untyped boolean result of a comparison). And because default types of untyped values are always predeclared non-composite types, they can never match against a composite type, so it is sufficient to only consider parameter types that are single type parameters.

Each list is processed in a separate phase:

  1. In the first phase, the parameter and argument types of each pair in Lt are unified. If unification succeeds for a pair, it may yield new entries that are added to the substitution map M. If unification fails, type inference fails.
  2. The second phase considers the entries of list Lu. Type parameters for which the type argument has already been determined are ignored in this phase. For each remaining pair, the parameter type (which is a single type parameter) and the default type of the corresponding untyped argument is unified. If unification fails, type inference fails.

While unification is successful, processing of each list continues until all list elements are considered, even if all type arguments are inferred before the last list element has been processed.

Example:

func min[T ~int|~float64](x, y T) T

var x int
min(x, 2.0)    // T is int, inferred from typed argument x; 2.0 is assignable to int
min(1.0, 2.0)  // T is float64, inferred from default type for 1.0 and matches default type for 2.0
min(1.0, 2)    // illegal: default type float64 (for 1.0) doesn't match default type int (for 2)

In the example min(1.0, 2), processing the function argument 1.0 yields the substitution map entry Tfloat64. Because processing continues until all untyped arguments are considered, an error is reported. This ensures that type inference does not depend on the order of the untyped arguments.

Constraint type inference 接口类型约束

Constraint type inference infers type arguments by considering type constraints. If a type parameter P has a constraint with a core type C, unifying P with C may infer additional type arguments, either the type argument for P, or if that is already known, possibly the type arguments for type parameters used in C.

For instance, consider the type parameter list with type parameters List and Elem:

[List ~[]Elem, Elem any]

Constraint type inference can deduce the type of Elem from the type argument for List because Elem is a type parameter in the core type []Elem of List. If the type argument is Bytes:

type Bytes []byte

unifying the underlying type of Bytes with the core type means unifying []byte with []Elem. That unification succeeds and yields the substitution map entry Elembyte. Thus, in this example, constraint type inference can infer the second type argument from the first one.

Using the core type of a constraint may lose some information: In the (unlikely) case that the constraint's type set contains a single defined type N, the corresponding core type is N's underlying type rather than N itself. In this case, constraint type inference may succeed but instantiation will fail because the inferred type is not in the type set of the constraint. Thus, constraint type inference uses the adjusted core type of a constraint: if the type set contains a single type, use that type; otherwise use the constraint's core type.

Generally, constraint type inference proceeds in two phases: Starting with a given substitution map M

  1. For all type parameters with an adjusted core type, unify the type parameter with that type. If any unification fails, constraint type inference fails.
  2. At this point, some entries in M may map type parameters to other type parameters or to types containing type parameters. For each entry PA in M where A is or contains type parameters Q for which there exist entries QB in M, substitute those Q with the respective B in A. Stop when no further substitution is possible.

The result of constraint type inference is the final substitution map M from type parameters P to type arguments A where no type parameter P appears in any of the A.

For instance, given the type parameter list

[A any, B []C, C *A]

and the single provided type argument int for type parameter A, the initial substitution map M contains the entry Aint.

In the first phase, the type parameters B and C are unified with the core type of their respective constraints. This adds the entries B[]C and C*A to M.

At this point there are two entries in M where the right-hand side is or contains type parameters for which there exists other entries in M: []C and *A. In the second phase, these type parameters are replaced with their respective types. It doesn't matter in which order this happens. Starting with the state of M after the first phase:

Aint, B[]C, C*A

Replace A on the right-hand side of → with int:

Aint, B[]C, C*int

Replace C on the right-hand side of → with *int:

Aint, B[]*int, C*int

At this point no further substitution is possible and the map is full. Therefore, M represents the final map of type parameters to type arguments for the given type parameter list.

Operators 操作符

Operators combine operands into expressions.

Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .

binary_op  = "||" | "&amp;&amp;" | rel_op | add_op | mul_op .
rel_op     = "==" | "!=" | "&lt;" | "&lt;=" | ">" | ">=" .
add_op     = "+" | "-" | "|" | "^" .
mul_op     = "*" | "/" | "%" | "&lt;&lt;" | "&gt;&gt;" | "&amp;" | "&amp;^" .

unary_op   = "+" | "-" | "!" | "^" | "*" | "&amp;" | "&lt;-" .

Comparisons are discussed elsewhere. For other binary operators, the operand types must be identical unless the operation involves shifts or untyped constants. For operations involving constants only, see the section on constant expressions.

Except for shift operations, if one operand is an untyped constant and the other operand is not, the constant is implicitly converted to the type of the other operand.

The right operand in a shift expression must have integer type or be an untyped constant representable by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.

var a [1024]byte
var s uint = 33

// The results of the following examples are given for 64-bit ints.
var i = 1&lt;&lt;s                   // 1 has type int
var j int32 = 1&lt;&lt;s             // 1 has type int32; j == 0
var k = uint64(1&lt;&lt;s)           // 1 has type uint64; k == 1&lt;&lt;33
var m int = 1.0&lt;&lt;s             // 1.0 has type int; m == 1&lt;&lt;33
var n = 1.0&lt;&lt;s == j            // 1.0 has type int32; n == true
var o = 1&lt;&lt;s == 2&lt;&lt;s           // 1 and 2 have type int; o == false
var p = 1&lt;&lt;s == 1&lt;&lt;33          // 1 has type int; p == true
var u = 1.0&lt;&lt;s                 // illegal: 1.0 has type float64, cannot shift
var u1 = 1.0&lt;&lt;s != 0           // illegal: 1.0 has type float64, cannot shift
var u2 = 1&lt;&lt;s != 1.0           // illegal: 1 has type float64, cannot shift
var v1 float32 = 1&lt;&lt;s          // illegal: 1 has type float32, cannot shift
var v2 = string(1&lt;&lt;s)          // illegal: 1 is converted to a string, cannot shift
var w int64 = 1.0&lt;&lt;33          // 1.0&lt;&lt;33 is a constant shift expression; w == 1&lt;&lt;33
var x = a[1.0&lt;&lt;s]              // panics: 1.0 has type int, but 1&lt;&lt;33 overflows array bounds
var b = make([]byte, 1.0&lt;&lt;s)   // 1.0 has type int; len(b) == 1&lt;&lt;33

// The results of the following examples are given for 32-bit ints,
// which means the shifts will overflow.
var mm int = 1.0&lt;&lt;s            // 1.0 has type int; mm == 0
var oo = 1&lt;&lt;s == 2&lt;&lt;s          // 1 and 2 have type int; oo == true
var pp = 1&lt;&lt;s == 1&lt;&lt;33         // illegal: 1 has type int, but 1&lt;&lt;33 overflows int
var xx = a[1.0&lt;&lt;s]             // 1.0 has type int; xx == a[0]
var bb = make([]byte, 1.0&lt;&lt;s)  // 1.0 has type int; len(bb) == 0

Operator precedence 操作符优先级

Unary operators have the highest precedence. As the ++ and -- operators form statements, not expressions, they fall outside the operator hierarchy. As a consequence, statement *p++ is the same as (*p)++.

There are five precedence levels for binary operators. Multiplication operators bind strongest, followed by addition operators, comparison operators, && (logical AND), and finally || (logical OR):

Precedence    Operator
    5             *  /  %  &lt;&lt;  &gt;&gt;  &amp;  &amp;^
    4             +  -  |  ^
    3             ==  !=  &lt;  &lt;=  &gt;  &gt;=
    2             &amp;&amp;
    1             ||

Binary operators of the same precedence associate from left to right. For instance, x / y * z is the same as (x / y) * z.

+x
23 + 3*x[i]
x &lt;= f()
^a &gt;&gt; b
f() || g()
x == y+1 &amp;&amp; &lt;-chanInt &gt; 0

Arithmetic operators 算数操作符

Arithmetic operators apply to numeric values and yield a result of the same type as the first operand. The four standard arithmetic operators (+, -, *, /) apply to integer, floating-point, and complex types; + also applies to strings. The bitwise logical and shift operators apply to integers only.

+    sum                    integers, floats, complex values, strings
-    difference             integers, floats, complex values
*    product                integers, floats, complex values
/    quotient               integers, floats, complex values
%    remainder              integers

&amp;    bitwise AND            integers
|    bitwise OR             integers
^    bitwise XOR            integers
&amp;^   bit clear (AND NOT)    integers

&lt;&lt;   left shift             integer &lt;&lt; integer &gt;= 0
&gt;&gt;   right shift            integer &gt;&gt; integer &gt;= 0

If the operand type is a type parameter, the operator must apply to each type in that type set. The operands are represented as values of the type argument that the type parameter is instantiated with, and the operation is computed with the precision of that type argument. For example, given the function:

func dotProduct[F ~float32|~float64](v1, v2 []F) F {
	var s F
	for i, x := range v1 {
		y := v2[i]
		s += x * y
	}
	return s
}

the product x * y and the addition s += x * y are computed with float32 or float64 precision, respectively, depending on the type argument for F.

Integer operators 整数运算符

For two integer values x and y, the integer quotient q = x / y and remainder r = x % y satisfy the following relationships:

x = q*y + r  and  |r| &lt; |y|

with x / y truncated towards zero ("truncated division").

 x     y     x / y     x % y
 5     3       1         2
-5     3      -1        -2
 5    -3      -1         2
-5    -3       1        -2

The one exception to this rule is that if the dividend x is the most negative value for the int type of x, the quotient q = x / -1 is equal to x (and r = 0) due to two's-complement integer overflow:

                         x, q
int8                     -128
int16                  -32768
int32             -2147483648
int64    -9223372036854775808

If the divisor is a constant, it must not be zero. If the divisor is zero at run time, a run-time panic occurs. If the dividend is non-negative and the divisor is a constant power of 2, the division may be replaced by a right shift, and computing the remainder may be replaced by a bitwise AND operation:

 x     x / 4     x % 4     x &gt;&gt; 2     x &amp; 3
 11      2         3         2          3
-11     -2        -3        -3          1

The shift operators shift the left operand by the shift count specified by the right operand, which must be non-negative. If the shift count is negative at run time, a run-time panic occurs. The shift operators implement arithmetic shifts if the left operand is a signed integer and logical shifts if it is an unsigned integer. There is no upper limit on the shift count. Shifts behave as if the left operand is shifted n times by 1 for a shift count of n. As a result, x << 1 is the same as x*2 and x >> 1 is the same as x/2 but truncated towards negative infinity.

For integer operands, the unary operators +, -, and ^ are defined as follows:

+x                          is 0 + x
-x    negation              is 0 - x
^x    bitwise complement    is m ^ x  with m = "all bits set to 1" for unsigned x
                                      and  m = -1 for signed x

Integer overflow 整数溢出

For unsigned integer values, the operations +, -, *, and << are computed modulo 2n, where n is the bit width of the unsigned integer's type. Loosely speaking, these unsigned integer operations discard high bits upon overflow, and programs may rely on "wrap around".

For signed integers, the operations +, -, *, /, and << may legally overflow and the resulting value exists and is deterministically defined by the signed integer representation, the operation, and its operands. Overflow does not cause a run-time panic. A compiler may not optimize code under the assumption that overflow does not occur. For instance, it may not assume that x < x + 1 is always true.

Floating-point operators 浮点运算符

For floating-point and complex numbers, +x is the same as x, while -x is the negation of x. The result of a floating-point or complex division by zero is not specified beyond the IEEE-754 standard; whether a run-time panic occurs is implementation-specific.

An implementation may combine multiple floating-point operations into a single fused operation, possibly across statements, and produce a result that differs from the value obtained by executing and rounding the instructions individually. An explicit floating-point type conversion rounds to the precision of the target type, preventing fusion that would discard that rounding.

For instance, some architectures provide a "fused multiply and add" (FMA) instruction that computes x*y + z without rounding the intermediate result x*y. These examples show when a Go implementation can use that instruction:

// FMA allowed for computing r, because x*y is not explicitly rounded:
r  = x*y + z
r  = z;   r += x*y
t  = x*y; r = t + z
*p = x*y; r = *p + z
r  = x*y + float64(z)

// FMA disallowed for computing r, because it would omit rounding of x*y:
r  = float64(x*y) + z
r  = z; r += float64(x*y)
t  = float64(x*y); r = t + z

String concatenation 字符串连接

Strings can be concatenated using the + operator or the += assignment operator:

s := "hi" + string(c)
s += " and good bye"

String addition creates a new string by concatenating the operands.

Comparison operators 比较运算符

Comparison operators compare two operands and yield an untyped boolean value.

==    equal
!=    not equal
&lt;     less
&lt;=    less or equal
&gt;     greater
&gt;=    greater or equal

In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

The equality operators == and != apply to operands of comparable types. The ordering operators <, <=, >, and >= apply to operands of ordered types. These terms and the result of the comparisons are defined as follows:

  • Boolean types are comparable. Two boolean values are equal if they are either both true or both false.
  • Integer types are comparable and ordered. Two integer values are compared in the usual way.
  • Floating-point types are comparable and ordered. Two floating-point values are compared as defined by the IEEE-754 standard.
  • Complex types are comparable. Two complex values u and v are equal if both real(u) == real(v) and imag(u) == imag(v).
  • String types are comparable and ordered. Two string values are compared lexically byte-wise.
  • Pointer types are comparable. Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.
  • Channel types are comparable. Two channel values are equal if they were created by the same call to make or if both have value nil.
  • Interface types that are not type parameters are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.
  • A value x of non-interface type X and a value t of interface type T can be compared if type X is comparable and X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x.
  • Struct types are comparable if all their field types are comparable. Two struct values are equal if their corresponding non-blank field values are equal. The fields are compared in source order, and comparison stops as soon as two field values differ (or all fields have been compared).
  • Array types are comparable if their array element types are comparable. Two array values are equal if their corresponding element values are equal. The elements are compared in ascending index order, and comparison stops as soon as two element values differ (or all elements have been compared).
  • Type parameters are comparable if they are strictly comparable (see below).

A comparison of two interface values with identical dynamic types causes a run-time panic if that type is not comparable. This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.

Slice, map, and function types are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil. Comparison of pointer, channel, and interface values to nil is also allowed and follows from the general rules above.

const c = 3 &lt; 4            // c is the untyped boolean constant true

type MyBool bool
var x, y int
var (
	// The result of a comparison is an untyped boolean.
	// The usual assignment rules apply.
	b3        = x == y // b3 has type bool
	b4 bool   = x == y // b4 has type bool
	b5 MyBool = x == y // b5 has type MyBool
)

A type is strictly comparable if it is comparable and not an interface type nor composed of interface types. Specifically:

  • Boolean, numeric, string, pointer, and channel types are strictly comparable.
  • Struct types are strictly comparable if all their field types are strictly comparable.
  • Array types are strictly comparable if their array element types are strictly comparable.
  • Type parameters are strictly comparable if all types in their type set are strictly comparable.

Logical operators 逻辑运算符

Logical operators apply to boolean values and yield a result of the same type as the operands. The right operand is evaluated conditionally.

&amp;&amp;    conditional AND    p &amp;&amp; q  is  "if p then q else false"
||    conditional OR     p || q  is  "if p then true else q"
!     NOT                !p      is  "not p"

Address operators 地址运算符

For an operand x of type T, the address operation &x generates a pointer of type *T to x. The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal. If the evaluation of x would cause a run-time panic, then the evaluation of &x does too.

For an operand x of pointer type *T, the pointer indirection *x denotes the variable of type T pointed to by x. If x is nil, an attempt to evaluate *x will cause a run-time panic.

&amp;x
&amp;a[f(2)]
&amp;Point{2, 3}
*p
*pf(x)

var x *int = nil
*x   // causes a run-time panic
&amp;*x  // causes a run-time panic

Receive operator 接收运算符

For an operand ch whose core type is a channel, the value of the receive operation <-ch is the value received from the channel ch. The channel direction must permit receive operations, and the type of the receive operation is the element type of the channel. The expression blocks until a value is available. Receiving from a nil channel blocks forever. A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.

v1 := &lt;-ch
v2 = &lt;-ch
f(&lt;-ch)
&lt;-strobe  // wait until clock pulse and discard received value

A receive expression used in an assignment statement or initialization of the special form

x, ok = &lt;-ch
x, ok := &lt;-ch
var x, ok = &lt;-ch
var x, ok T = &lt;-ch

yields an additional untyped boolean result reporting whether the communication succeeded. The value of ok is true if the value received was delivered by a successful send operation to the channel, or false if it is a zero value generated because the channel is closed and empty.

Conversions 转换

A conversion changes the type of an expression to the type specified by the conversion. A conversion may appear literally in the source, or it may be implied by the context in which an expression appears.

An explicit conversion is an expression of the form T(x) where T is a type and x is an expression that can be converted to type T.

Conversion = Type "(" Expression [ "," ] ")" .

If the type starts with the operator * or <-, or if the type starts with the keyword func and has no result list, it must be parenthesized when necessary to avoid ambiguity:

*Point(p)        // same as *(Point(p))
(*Point)(p)      // p is converted to *Point
&lt;-chan int(c)    // same as &lt;-(chan int(c))
(&lt;-chan int)(c)  // c is converted to &lt;-chan int
func()(x)        // function signature func() x
(func())(x)      // x is converted to func()
(func() int)(x)  // x is converted to func() int
func() int(x)    // x is converted to func() int (unambiguous)

A constant value x can be converted to type T if x is representable by a value of T. As a special case, an integer constant x can be explicitly converted to a string type using the same rule as for non-constant x.

Converting a constant to a type that is not a type parameter yields a typed constant.

uint(iota)               // iota value of type uint
float32(2.718281828)     // 2.718281828 of type float32
complex128(1)            // 1.0 + 0.0i of type complex128
float32(0.49999999)      // 0.5 of type float32
float64(-1e-1000)        // 0.0 of type float64
string('x')              // "x" of type string
string(0x266c)           // "♬" of type string
myString("foo" + "bar")  // "foobar" of type myString
string([]byte{'a'})      // not a constant: []byte{'a'} is not a constant
(*int)(nil)              // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type
int(1.2)                 // illegal: 1.2 cannot be represented as an int
string(65.0)             // illegal: 65.0 is not an integer constant

Converting a constant to a type parameter yields a non-constant value of that type, with the value represented as a value of the type argument that the type parameter is instantiated with. For example, given the function:

func f[P ~float32|~float64]() {
	… P(1.1) …
}

the conversion P(1.1) results in a non-constant value of type P and the value 1.1 is represented as a float32 or a float64 depending on the type argument for f. Accordingly, if f is instantiated with a float32 type, the numeric value of the expression P(1.1) + 1.2 will be computed with the same precision as the corresponding non-constant float32 addition.

A non-constant value x can be converted to type T in any of these cases:

  • x is assignable to T.
  • ignoring struct tags (see below), x's type and T are not type parameters but have identical underlying types.
  • ignoring struct tags (see below), x's type and T are pointer types that are not named types, and their pointer base types are not type parameters but have identical underlying types.
  • x's type and T are both integer or floating point types.
  • x's type and T are both complex types.
  • x is an integer or a slice of bytes or runes and T is a string type.
  • x is a string and T is a slice of bytes or runes.
  • x is a slice, T is an array or a pointer to an array, and the slice and array types have identical element types.

Additionally, if T or x's type V are type parameters, x can also be converted to type T if one of the following conditions applies:

  • Both V and T are type parameters and a value of each type in V's type set can be converted to each type in T's type set.
  • Only V is a type parameter and a value of each type in V's type set can be converted to T.
  • Only T is a type parameter and x can be converted to each type in T's type set.

Struct tags are ignored when comparing struct types for identity for the purpose of conversion:

type Person struct {
	Name    string
	Address *struct {
		Street string
		City   string
	}
}

var data *struct {
	Name    string `json:"name"`
	Address *struct {
		Street string `json:"street"`
		City   string `json:"city"`
	} `json:"address"`
}

var person = (*Person)(data)  // ignoring tags, the underlying types are identical

Specific rules apply to (non-constant) conversions between numeric types or to and from a string type. These conversions may change the representation of x and incur a run-time cost. All other conversions only change the type but not the representation of x.

There is no linguistic mechanism to convert between pointers and integers. The package unsafe implements this functionality under restricted circumstances.

Conversions between numeric types 数字转换

For the conversion of non-constant numeric values, the following rules apply:

  1. When converting between integer types, if the value is a signed integer, it is sign extended to implicit infinite precision; otherwise it is zero extended. It is then truncated to fit in the result type's size. For example, if v := uint16(0x10F0), then uint32(int8(v)) == 0xFFFFFFF0. The conversion always yields a valid value; there is no indication of overflow.
  2. When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).
  3. When converting an integer or floating-point number to a floating-point type, or a complex number to another complex type, the result value is rounded to the precision specified by the destination type. For instance, the value of a variable x of type float32 may be stored using additional precision beyond that of an IEEE-754 32-bit number, but float32(x) represents the result of rounding x's value to 32-bit precision. Similarly, x + 0.1 may use more than 32 bits of precision, but float32(x + 0.1) does not.

In all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.

Conversions to and from a string type 和字符串转换

  1. Converting a signed or unsigned integer value to a string type yields a string containing the UTF-8 representation of the integer. Values outside the range of valid Unicode code points are converted to "\uFFFD".
    string('a')       // "a"
    string(-1)        // "\ufffd" == "\xef\xbf\xbd"
    string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
    
    type myString string
    myString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"
    
  2. Converting a slice of bytes to a string type yields a string whose successive bytes are the elements of the slice.
    string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
    string([]byte{})                                     // ""
    string([]byte(nil))                                  // ""
    
    type bytes []byte
    string(bytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})    // "hellø"
    
    type myByte byte
    string([]myByte{'w', 'o', 'r', 'l', 'd', '!'})       // "world!"
    myString([]myByte{'\xf0', '\x9f', '\x8c', '\x8d'})   // "🌍"
    
  3. Converting a slice of runes to a string type yields a string that is the concatenation of the individual rune values converted to strings.
    string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    string([]rune{})                         // ""
    string([]rune(nil))                      // ""
    
    type runes []rune
    string(runes{0x767d, 0x9d6c, 0x7fd4})    // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    
    type myRune rune
    string([]myRune{0x266b, 0x266c})         // "\u266b\u266c" == "♫♬"
    myString([]myRune{0x1f30e})              // "\U0001f30e" == "🌎"
    
  4. Converting a value of a string type to a slice of bytes type yields a slice whose successive elements are the bytes of the string.
    []byte("hellø")             // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    []byte("")                  // []byte{}
    
    bytes("hellø")              // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    
    []myByte("world!")          // []myByte{'w', 'o', 'r', 'l', 'd', '!'}
    []myByte(myString("🌏"))    // []myByte{'\xf0', '\x9f', '\x8c', '\x8f'}
    
  5. Converting a value of a string type to a slice of runes type yields a slice containing the individual Unicode code points of the string.
    []rune(myString("白鵬翔"))   // []rune{0x767d, 0x9d6c, 0x7fd4}
    []rune("")                  // []rune{}
    
    runes("白鵬翔")              // []rune{0x767d, 0x9d6c, 0x7fd4}
    
    []myRune("♫♬")              // []myRune{0x266b, 0x266c}
    []myRune(myString("🌐"))    // []myRune{0x1f310}
    

Conversions from slice to array or array pointer

Converting a slice to an array yields an array containing the elements of the underlying array of the slice. Similarly, converting a slice to an array pointer yields a pointer to the underlying array of the slice. In both cases, if the length of the slice is less than the length of the array, a run-time panic occurs.

s := make([]byte, 2, 4)

a0 := [0]byte(s)
a1 := [1]byte(s[1:])     // a1[0] == s[1]
a2 := [2]byte(s)         // a2[0] == s[0]
a4 := [4]byte(s)         // panics: len([4]byte) > len(s)

s0 := (*[0]byte)(s)      // s0 != nil
s1 := (*[1]byte)(s[1:])  // &amp;s1[0] == &amp;s[1]
s2 := (*[2]byte)(s)      // &amp;s2[0] == &amp;s[0]
s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)

var t []string
t0 := [0]string(t)       // ok for nil slice t
t1 := (*[0]string)(t)    // t1 == nil
t2 := (*[1]string)(t)    // panics: len([1]string) > len(t)

u := make([]byte, 0)
u0 := (*[0]byte)(u)      // u0 != nil

Constant expressions 常量表达式

Constant expressions may contain only constant operands and are evaluated at compile time.

Untyped boolean, numeric, and string constants may be used as operands wherever it is legal to use an operand of boolean, numeric, or string type, respectively.

A constant comparison always yields an untyped boolean constant. If the left operand of a constant shift expression is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand, which must be of integer type.

Any other operation on untyped constants results in an untyped constant of the same kind; that is, a boolean, integer, floating-point, complex, or string constant. If the untyped operands of a binary operation (other than a shift) are of different kinds, the result is of the operand's kind that appears later in this list: integer, rune, floating-point, complex. For example, an untyped integer constant divided by an untyped complex constant yields an untyped complex constant.

const a = 2 + 3.0          // a == 5.0   (untyped floating-point constant)
const b = 15 / 4           // b == 3     (untyped integer constant)
const c = 15 / 4.0         // c == 3.75  (untyped floating-point constant)
const Θ float64 = 3/2      // Θ == 1.0   (type float64, 3/2 is integer division)
const Π float64 = 3/2.     // Π == 1.5   (type float64, 3/2. is float division)
const d = 1 &lt;&lt; 3.0         // d == 8     (untyped integer constant)
const e = 1.0 &lt;&lt; 3         // e == 8     (untyped integer constant)
const f = int32(1) &lt;&lt; 33   // illegal    (constant 8589934592 overflows int32)
const g = float64(2) &gt;&gt; 1  // illegal    (float64(2) is a typed floating-point constant)
const h = "foo" &gt; "bar"    // h == true  (untyped boolean constant)
const j = true             // j == true  (untyped boolean constant)
const k = 'w' + 1          // k == 'x'   (untyped rune constant)
const l = "hi"             // l == "hi"  (untyped string constant)
const m = string(k)        // m == "x"   (type string)
const Σ = 1 - 0.707i       //            (untyped complex constant)
const Δ = Σ + 2.0e-4       //            (untyped complex constant)
const Φ = iota*1i - 1/1i   //            (untyped complex constant)

Applying the built-in function complex to untyped integer, rune, or floating-point constants yields an untyped complex constant.

const ic = complex(0, c)   // ic == 3.75i  (untyped complex constant)
const iΘ = complex(0, Θ)   // iΘ == 1i     (type complex128)

Constant expressions are always evaluated exactly; intermediate values and the constants themselves may require precision significantly larger than supported by any predeclared type in the language. The following are legal declarations:

const Huge = 1 &lt;&lt; 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)
const Four int8 = Huge &gt;&gt; 98  // Four == 4                                (type int8)

The divisor of a constant division or remainder operation must not be zero:

3.14 / 0.0   // illegal: division by zero

The values of typed constants must always be accurately representable by values of the constant type. The following constant expressions are illegal:

uint(-1)     // -1 cannot be represented as a uint
int(3.14)    // 3.14 cannot be represented as an int
int64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64
Four * 300   // operand 300 cannot be represented as an int8 (type of Four)
Four * 100   // product 400 cannot be represented as an int8 (type of Four)

The mask used by the unary bitwise complement operator ^ matches the rule for non-constants: the mask is all 1s for unsigned constants and -1 for signed and untyped constants.

^1         // untyped integer constant, equal to -2
uint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8
^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1)   // same as int8(-2)
^int8(1)   // same as -1 ^ int8(1) = -2

Implementation restriction: A compiler may use rounding while computing untyped floating-point or complex constant expressions; see the implementation restriction in the section on constants. This rounding may cause a floating-point constant expression to be invalid in an integer context, even if it would be integral when calculated using infinite precision, and vice versa.

Order of evaluation 执行顺序

At package level, initialization dependencies determine the evaluation order of individual initialization expressions in variable declarations. Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.

For example, in the (function-local) assignment

y[f()], ok = g(h(), i()+x[j()], &lt;-c), k()

the function calls and communication happen in the order f(), h(), i(), j(), <-c, g(), and k(). However, the order of those events compared to the evaluation and indexing of x and the evaluation of y is not specified.

a := 1
f := func() int { a++; return a }
x := []int{a, f()}            // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
m := map[int]int{a: 1, a: 2}  // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified
n := map[int]int{a: f()}      // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified

At package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression:

var a, b, c = f() + v(), g(), sqr(u()) + v()

func f() int        { return c }
func g() int        { return a }
func sqr(x int) int { return x*x }

// functions u and v are independent of all other variables and functions

The function calls happen in the order u(), sqr(), v(), f(), v(), and g().

Floating-point operations within a single expression are evaluated according to the associativity of the operators. Explicit parentheses affect the evaluation by overriding the default associativity. In the expression x + (y + z) the addition y + z is performed before adding x.

Statements 语句

Statements control execution.

Statement =
	Declaration | LabeledStmt | SimpleStmt |
	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
	DeferStmt .

SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .

Terminating statements 终结语句

A terminating statement interrupts the regular flow of control in a block. The following statements are terminating:

  1. A "return" or "goto" statement.
  2. A call to the built-in function panic.
  3. A block in which the statement list ends in a terminating statement.
  4. An "if" statement in which:
    • the "else" branch is present, and
    • both branches are terminating statements.
  5. A "for" statement in which:
    • there are no "break" statements referring to the "for" statement, and
    • the loop condition is absent, and
    • the "for" statement does not use a range clause.
  6. A "switch" statement in which:
    • there are no "break" statements referring to the "switch" statement,
    • there is a default case, and
    • the statement lists in each case, including the default, end in a terminating statement, or a possibly labeled "fallthrough" statement.
  7. A "select" statement in which:
    • there are no "break" statements referring to the "select" statement, and
    • the statement lists in each case, including the default if present, end in a terminating statement.
  8. A labeled statement labeling a terminating statement.

All other statements are not terminating.

A statement list ends in a terminating statement if the list is not empty and its final non-empty statement is terminating.

Empty statements 空语句

The empty statement does nothing.

EmptyStmt = .

Labeled statements 标签语句

A labeled statement may be the target of a goto, break or continue statement.

LabeledStmt = Label ":" Statement .
Label       = identifier .
Error: log.Panic("error encountered")

Expression statements 表达式语句

With the exception of specific built-in functions, function and method calls and receive operations can appear in statement context. Such statements may be parenthesized.

ExpressionStmt = Expression .

The following built-in functions are not permitted in statement context:

append cap complex imag len make new real
unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice unsafe.SliceData unsafe.String unsafe.StringData
h(x+y)
f.Close()
&lt;-ch
(&lt;-ch)
len("foo")  // illegal if len is the built-in function

Send statements 发送语句

A send statement sends a value on a channel. The channel expression's core type must be a channel, the channel direction must permit send operations, and the type of the value to be sent must be assignable to the channel's element type.

SendStmt = Channel "&lt;-" Expression .
Channel  = Expression .

Both the channel and the value expression are evaluated before communication begins. Communication blocks until the send can proceed. A send on an unbuffered channel can proceed if a receiver is ready. A send on a buffered channel can proceed if there is room in the buffer. A send on a closed channel proceeds by causing a run-time panic. A send on a nil channel blocks forever.

ch &lt;- 3  // send value 3 to channel ch

IncDec statements 加减语句

The "++" and "--" statements increment or decrement their operands by the untyped constant 1. As with an assignment, the operand must be addressable or a map index expression.

IncDecStmt = Expression ( "++" | "--" ) .

The following assignment statements are semantically equivalent:

IncDec statement    Assignment
x++                 x += 1
x--                 x -= 1

Assignment statements 赋值语句

An assignment replaces the current value stored in a variable with a new value specified by an expression. An assignment statement may assign a single value to a single variable, or multiple values to a matching number of variables.

Assignment = ExpressionList assign_op ExpressionList .

assign_op = [ add_op | mul_op ] "=" .

Each left-hand side operand must be addressable, a map index expression, or (for = assignments only) the blank identifier. Operands may be parenthesized.

x = 1
*p = f()
a[i] = 23
(k) = &lt;-ch  // same as: k = &lt;-ch

An assignment operation x op= y where op is a binary arithmetic operator is equivalent to x = x op (y) but evaluates x only once. The op= construct is a single token. In assignment operations, both the left- and right-hand expression lists must contain exactly one single-valued expression, and the left-hand expression must not be the blank identifier.

a[i] &lt;&lt;= 2
i &amp;^= 1&lt;&lt;n

A tuple assignment assigns the individual elements of a multi-valued operation to a list of variables. There are two forms. In the first, the right hand operand is a single multi-valued expression such as a function call, a channel or map operation, or a type assertion. The number of operands on the left hand side must match the number of values. For instance, if f is a function returning two values,

x, y = f()

assigns the first value to x and the second to y. In the second form, the number of operands on the left must equal the number of expressions on the right, each of which must be single-valued, and the nth expression on the right is assigned to the nth operand on the left:

one, two, three = '一', '二', '三'

The blank identifier provides a way to ignore right-hand side values in an assignment:

_ = x       // evaluate x but ignore it
x, _ = f()  // evaluate f() but ignore second result value

The assignment proceeds in two phases. First, the operands of index expressions and pointer indirections (including implicit pointer indirections in selectors) on the left and the expressions on the right are all evaluated in the usual order. Second, the assignments are carried out in left-to-right order.

a, b = b, a  // exchange a and b

x := []int{1, 2, 3}
i := 0
i, x[i] = 1, 2  // set i = 1, x[0] = 2

i = 0
x[i], i = 2, 1  // set x[0] = 2, i = 1

x[0], x[0] = 1, 2  // set x[0] = 1, then x[0] = 2 (so x[0] == 2 at end)

x[1], x[3] = 4, 5  // set x[1] = 4, then panic setting x[3] = 5.

type Point struct { x, y int }
var p *Point
x[2], p.x = 6, 7  // set x[2] = 6, then panic setting p.x = 7

i = 2
x = []int{3, 5, 7}
for i, x[i] = range x {  // set i, x[2] = 0, x[0]
	break
}
// after this loop, i == 0 and x is []int{3, 5, 3}

In assignments, each value must be assignable to the type of the operand to which it is assigned, with the following special cases:

  1. Any typed value may be assigned to the blank identifier.
  2. If an untyped constant is assigned to a variable of interface type or the blank identifier, the constant is first implicitly converted to its default type.
  3. If an untyped boolean value is assigned to a variable of interface type or the blank identifier, it is first implicitly converted to type bool.

If statements 判断语句

"If" statements specify the conditional execution of two branches according to the value of a boolean expression. If the expression evaluates to true, the "if" branch is executed, otherwise, if present, the "else" branch is executed.

IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
if x &gt; max {
	x = max
}

The expression may be preceded by a simple statement, which executes before the expression is evaluated.

if x := f(); x &lt; y {
	return x
} else if x &gt; z {
	return z
} else {
	return y
}

Switch_statements 分支语句

"Switch" statements provide multi-way execution. An expression or type is compared to the "cases" inside the "switch" to determine which branch to execute.

SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .

There are two forms: expression switches and type switches. In an expression switch, the cases contain expressions that are compared against the value of the switch expression. In a type switch, the cases contain types that are compared against the type of a specially annotated switch expression. The switch expression is evaluated exactly once in a switch statement.

Expression switches 表达式切换?

In an expression switch, the switch expression is evaluated and the case expressions, which need not be constants, are evaluated left-to-right and top-to-bottom; the first one that equals the switch expression triggers execution of the statements of the associated case; the other cases are skipped. If no case matches and there is a "default" case, its statements are executed. There can be at most one default case and it may appear anywhere in the "switch" statement. A missing switch expression is equivalent to the boolean value true.

ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
ExprCaseClause = ExprSwitchCase ":" StatementList .
ExprSwitchCase = "case" ExpressionList | "default" .

If the switch expression evaluates to an untyped constant, it is first implicitly converted to its default type. The predeclared untyped value nil cannot be used as a switch expression. The switch expression type must be comparable.

If a case expression is untyped, it is first implicitly converted to the type of the switch expression. For each (possibly converted) case expression x and the value t of the switch expression, x == t must be a valid comparison.

In other words, the switch expression is treated as if it were used to declare and initialize a temporary variable t without explicit type; it is that value of t against which each case expression x is tested for equality.

In a case or default clause, the last non-empty statement may be a (possibly labeled) "fallthrough" statement to indicate that control should flow from the end of this clause to the first statement of the next clause. Otherwise control flows to the end of the "switch" statement. A "fallthrough" statement may appear as the last statement of all but the last clause of an expression switch.

The switch expression may be preceded by a simple statement, which executes before the expression is evaluated.

switch tag {
default: s3()
case 0, 1, 2, 3: s1()
case 4, 5, 6, 7: s2()
}

switch x := f(); {  // missing switch expression means "true"
case x &lt; 0: return -x
default: return x
}

switch {
case x &lt; y: f1()
case x &lt; z: f2()
case x == 4: f3()
}

Implementation restriction: A compiler may disallow multiple case expressions evaluating to the same constant. For instance, the current compilers disallow duplicate integer, floating point, or string constants in case expressions.

Type switches 类型切换

A type switch compares types rather than values. It is otherwise similar to an expression switch. It is marked by a special switch expression that has the form of a type assertion using the keyword type rather than an actual type:

switch x.(type) {
// cases
}

Cases then match actual types T against the dynamic type of the expression x. As with type assertions, x must be of interface type, but not a type parameter, and each non-interface type T listed in a case must implement the type of x. The types listed in the cases of a type switch must all be different.

TypeSwitchStmt  = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause  = TypeSwitchCase ":" StatementList .
TypeSwitchCase  = "case" TypeList | "default" .

The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause. In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.

Instead of a type, a case may use the predeclared identifier nil; that case is selected when the expression in the TypeSwitchGuard is a nil interface value. There may be at most one nil case.

Given an expression x of type interface{}, the following type switch:

switch i := x.(type) {
case nil:
	printString("x is nil")                // type of i is type of x (interface{})
case int:
	printInt(i)                            // type of i is int
case float64:
	printFloat64(i)                        // type of i is float64
case func(int) float64:
	printFunction(i)                       // type of i is func(int) float64
case bool, string:
	printString("type is bool or string")  // type of i is type of x (interface{})
default:
	printString("don't know the type")     // type of i is type of x (interface{})
}

could be rewritten:

v := x  // x is evaluated exactly once
if v == nil {
	i := v                                 // type of i is type of x (interface{})
	printString("x is nil")
} else if i, isInt := v.(int); isInt {
	printInt(i)                            // type of i is int
} else if i, isFloat64 := v.(float64); isFloat64 {
	printFloat64(i)                        // type of i is float64
} else if i, isFunc := v.(func(int) float64); isFunc {
	printFunction(i)                       // type of i is func(int) float64
} else {
	_, isBool := v.(bool)
	_, isString := v.(string)
	if isBool || isString {
		i := v                         // type of i is type of x (interface{})
		printString("type is bool or string")
	} else {
		i := v                         // type of i is type of x (interface{})
		printString("don't know the type")
	}
}

A type parameter or a generic type may be used as a type in a case. If upon instantiation that type turns out to duplicate another entry in the switch, the first matching case is chosen.

func f[P any](x any) int {
	switch x.(type) {
	case P:
		return 0
	case string:
		return 1
	case []P:
		return 2
	case []byte:
		return 3
	default:
		return 4
	}
}

var v1 = f[string]("foo")   // v1 == 0
var v2 = f[byte]([]byte{})  // v2 == 2

The type switch guard may be preceded by a simple statement, which executes before the guard is evaluated.

The "fallthrough" statement is not permitted in a type switch.

For statements 循环

A "for" statement specifies repeated execution of a block. There are three forms: The iteration may be controlled by a single condition, a "for" clause, or a "range" clause.

ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
Condition = Expression .

For statements with single condition 单个条件循环

In its simplest form, a "for" statement specifies the repeated execution of a block as long as a boolean condition evaluates to true. The condition is evaluated before each iteration. If the condition is absent, it is equivalent to the boolean value true.

for a &lt; b {
	a *= 2
}

For statements with for clause 循环

A "for" statement with a ForClause is also controlled by its condition, but additionally it may specify an init and a post statement, such as an assignment, an increment or decrement statement. The init statement may be a short variable declaration, but the post statement must not. Variables declared by the init statement are re-used in each iteration.

ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
InitStmt = SimpleStmt .
PostStmt = SimpleStmt .
for i := 0; i &lt; 10; i++ {
	f(i)
}

If non-empty, the init statement is executed once before evaluating the condition for the first iteration; the post statement is executed after each execution of the block (and only if the block was executed). Any element of the ForClause may be empty but the semicolons are required unless there is only a condition. If the condition is absent, it is equivalent to the boolean value true.

for cond { S() }    is the same as    for ; cond ; { S() }
for      { S() }    is the same as    for true     { S() }

For statements with range clause 范围循环

A "for" statement with a "range" clause iterates through all entries of an array, slice, string or map, or values received on a channel. For each entry it assigns iteration values to corresponding iteration variables if present and then executes the block.

RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .

The expression on the right in the "range" clause is called the range expression, its core type must be an array, pointer to an array, slice, string, map, or channel permitting receive operations. As with an assignment, if present the operands on the left must be addressable or map index expressions; they denote the iteration variables. If the range expression is a channel, at most one iteration variable is permitted, otherwise there may be up to two. If the last iteration variable is the blank identifier, the range clause is equivalent to the same clause without that identifier.

The range expression x is evaluated once before beginning the loop, with one exception: if at most one iteration variable is present and len(x) is constant, the range expression is not evaluated.

Function calls on the left are evaluated once per iteration. For each iteration, iteration values are produced as follows if the respective iteration variables are present:

Range expression                          1st value          2nd value

array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
string          s  string type            index    i  int    see below  rune
map             m  map[K]V                key      k  K      m[k]       V
channel         c  chan E, &lt;-chan E       element  e  E
  1. For an array, pointer to array, or slice value a, the index iteration values are produced in increasing order, starting at element index 0. If at most one iteration variable is present, the range loop produces iteration values from 0 up to len(a)-1 and does not index into the array or slice itself. For a nil slice, the number of iterations is 0.
  2. For a string value, the "range" clause iterates over the Unicode code points in the string starting at byte index 0. On successive iterations, the index value will be the index of the first byte of successive UTF-8-encoded code points in the string, and the second value, of type rune, will be the value of the corresponding code point. If the iteration encounters an invalid UTF-8 sequence, the second value will be 0xFFFD, the Unicode replacement character, and the next iteration will advance a single byte in the string.
  3. The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next. If a map entry that has not yet been reached is removed during iteration, the corresponding iteration value will not be produced. If a map entry is created during iteration, that entry may be produced during the iteration or may be skipped. The choice may vary for each entry created and from one iteration to the next. If the map is nil, the number of iterations is 0.
  4. For channels, the iteration values produced are the successive values sent on the channel until the channel is closed. If the channel is nil, the range expression blocks forever.

The iteration values are assigned to the respective iteration variables as in an assignment statement.

The iteration variables may be declared by the "range" clause using a form of short variable declaration (:=). In this case their types are set to the types of the respective iteration values and their scope is the block of the "for" statement; they are re-used in each iteration. If the iteration variables are declared outside the "for" statement, after execution their values will be those of the last iteration.

var testdata *struct {
	a *[7]int
}
for i, _ := range testdata.a {
	// testdata.a is never evaluated; len(testdata.a) is constant
	// i ranges from 0 to 6
	f(i)
}

var a [10]string
for i, s := range a {
	// type of i is int
	// type of s is string
	// s == a[i]
	g(i, s)
}

var key string
var val interface{}  // element type of m is assignable to val
m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
for key, val = range m {
	h(key, val)
}
// key == last map key encountered in iteration
// val == map[key]

var ch chan Work = producer()
for w := range ch {
	doWork(w)
}

// empty a channel
for range ch {}

Go statements go 语句

A "go" statement starts the execution of a function call as an independent concurrent thread of control, or goroutine, within the same address space.

GoStmt = "go" Expression .

The expression must be a function or method call; it cannot be parenthesized. Calls of built-in functions are restricted as for expression statements.

The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. Instead, the function begins executing independently in a new goroutine. When the function terminates, its goroutine also terminates. If the function has any return values, they are discarded when the function completes.

go Server()
go func(ch chan&lt;- bool) { for { sleep(10); ch &lt;- true }} (c)

Select statements select语句

A "select" statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a "switch" statement but with the cases all referring to communication operations.

SelectStmt = "select" "{" { CommClause } "}" .
CommClause = CommCase ":" StatementList .
CommCase   = "case" ( SendStmt | RecvStmt ) | "default" .
RecvStmt   = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
RecvExpr   = Expression .

A case with a RecvStmt may assign the result of a RecvExpr to one or two variables, which may be declared using a short variable declaration. The RecvExpr must be a (possibly parenthesized) receive operation. There can be at most one default case and it may appear anywhere in the list of cases.

Execution of a "select" statement proceeds in several steps:

  1. For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send. Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed. Expressions on the left-hand side of a RecvStmt with a short variable declaration or assignment are not yet evaluated.
  2. If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.
  3. Unless the selected case is the default case, the respective communication operation is executed.
  4. If the selected case is a RecvStmt with a short variable declaration or an assignment, the left-hand side expressions are evaluated and the received value (or values) are assigned.
  5. The statement list of the selected case is executed.

Since communication on nil channels can never proceed, a select with only nil channels and no default case blocks forever.

var a []int
var c, c1, c2, c3, c4 chan int
var i1, i2 int
select {
case i1 = &lt;-c1:
	print("received ", i1, " from c1\n")
case c2 &lt;- i2:
	print("sent ", i2, " to c2\n")
case i3, ok := (&lt;-c3):  // same as: i3, ok := &lt;-c3
	if ok {
		print("received ", i3, " from c3\n")
	} else {
		print("c3 is closed\n")
	}
case a[f()] = &lt;-c4:
	// same as:
	// case t := &lt;-c4
	//	a[f()] = t
default:
	print("no communication\n")
}

for {  // send random sequence of bits to c
	select {
	case c &lt;- 0:  // note: no statement, no fallthrough, no folding of cases
	case c &lt;- 1:
	}
}

select {}  // block forever

Return statements 返回语句

A "return" statement in a function F terminates the execution of F, and optionally provides one or more result values. Any functions deferred by F are executed before F returns to its caller.

ReturnStmt = "return" [ ExpressionList ] .

In a function without a result type, a "return" statement must not specify any result values.

func noResult() {
	return
}

There are three ways to return values from a function with a result type:

  1. The return value or values may be explicitly listed in the "return" statement. Each expression must be single-valued and assignable to the corresponding element of the function's result type. ```golang func simpleF() int { return 2 }

    func complexF1() (re float64, im float64) { return -7.0, -4.0 }

    	</li>
    	<li>The expression list in the "return" statement may be a single
    		call to a multi-valued function. The effect is as if each value
    		returned from that function were assigned to a temporary
    		variable with the type of the respective value, followed by a
    		"return" statement listing these variables, at which point the
    		rules of the previous case apply.
    ```golang
    func complexF2() (re float64, im float64) {
    	return complexF1()
    }
    
    </li>
    <li>The expression list may be empty if the function's result
    	type specifies names for its <a href="#Function_types">result parameters</a>.
    	The result parameters act as ordinary local variables
    	and the function may assign values to them as necessary.
    	The "return" statement returns the values of these variables.
    
    func complexF3() (re float64, im float64) {
    	re = 7.0
    	im = 4.0
    	return
    }
    
    func (devnull) Write(p []byte) (n int, _ error) {
    	n = len(p)
    	return
    }
    
    </li>
    

Regardless of how they are declared, all the result values are initialized to the zero values for their type upon entry to the function. A "return" statement that specifies results sets the result parameters before any deferred functions are executed.

Implementation restriction: A compiler may disallow an empty expression list in a "return" statement if a different entity (constant, type, or variable) with the same name as a result parameter is in scope at the place of the return.

func f(n int) (res int, err error) {
	if _, err := f(n-1); err != nil {
		return  // invalid return statement: err is shadowed
	}
	return
}

Break statements break 语句

A "break" statement terminates execution of the innermost "for", "switch", or "select" statement within the same function.

BreakStmt = "break" [ Label ] .

If there is a label, it must be that of an enclosing "for", "switch", or "select" statement, and that is the one whose execution terminates.

OuterLoop:
	for i = 0; i &lt; n; i++ {
		for j = 0; j &lt; m; j++ {
			switch a[i][j] {
			case nil:
				state = Error
				break OuterLoop
			case item:
				state = Found
				break OuterLoop
			}
		}
	}

Continue statements continue 语句

A "continue" statement begins the next iteration of the innermost enclosing "for" loop by advancing control to the end of the loop block. The "for" loop must be within the same function.

ContinueStmt = "continue" [ Label ] .

If there is a label, it must be that of an enclosing "for" statement, and that is the one whose execution advances.

RowLoop:
	for y, row := range rows {
		for x, data := range row {
			if data == endOfRow {
				continue RowLoop
			}
			row[x] = data + bias(x, y)
		}
	}

Goto statements goto 语句

A "goto" statement transfers control to the statement with the corresponding label within the same function.

GotoStmt = "goto" Label .
goto Error

Executing the "goto" statement must not cause any variables to come into scope that were not already in scope at the point of the goto. For instance, this example:

	goto L  // BAD
	v := 3
L:

is erroneous because the jump to label L skips the creation of v.

A "goto" statement outside a block cannot jump to a label inside that block. For instance, this example:

if n%2 == 1 {
	goto L1
}
for n &gt; 0 {
	f()
	n--
L1:
	f()
	n--
}

is erroneous because the label L1 is inside the "for" statement's block but the goto is not.

Fallthrough statements 失败语句

A "fallthrough" statement transfers control to the first statement of the next case clause in an expression "switch" statement. It may be used only as the final non-empty statement in such a clause.

FallthroughStmt = "fallthrough" .

Defer statements defer语句

A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.

DeferStmt = "defer" Expression .

The expression must be a function or method call; it cannot be parenthesized. Calls of built-in functions are restricted as for expression statements.

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. That is, if the surrounding function returns through an explicit return statement, deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the "defer" statement is executed.

For instance, if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned. If the deferred function has any return values, they are discarded when the function completes. (See also the section on handling panics.)

lock(l)
defer unlock(l)  // unlocking happens before surrounding function returns

// prints 3 2 1 0 before surrounding function returns
for i := 0; i &lt;= 3; i++ {
	defer fmt.Print(i)
}

// f returns 42
func f() (result int) {
	defer func() {
		// result is accessed after it was set to 6 by the return statement
		result *= 7
	}()
	return 6
}

Built-in functions 内建方法

Built-in functions are predeclared. They are called like any other function but some of them accept a type instead of an expression as the first argument.

The built-in functions do not have standard Go types, so they can only appear in call expressions; they cannot be used as function values.

Clear 清理

The built-in function clear takes an argument of map, slice, or type parameter type, and deletes or zeroes out all elements.

Call        Argument type     Result

clear(m)    map[K]T           deletes all entries, resulting in an
                              empty map (len(m) == 0)

clear(s)    []T               sets all elements up to the length of
                              <code>s</code> to the zero value of T

clear(t)    type parameter    see below

If the argument type is a type parameter, all types in its type set must be maps or slices, and clear performs the operation corresponding to the actual type argument.

If the map or slice is nil, clear is a no-op.

Close 关闭

For an argument ch with a core type that is a channel, the built-in function close records that no more values will be sent on the channel. It is an error if ch is a receive-only channel. Sending to or closing a closed channel causes a run-time panic. Closing the nil channel also causes a run-time panic. After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking. The multi-valued receive operation returns a received value along with an indication of whether the channel is closed.

Length and capacity 长度和容量

The built-in functions len and cap take arguments of various types and return a result of type int. The implementation guarantees that the result always fits into an int.

Call      Argument type    Result

len(s)    string type      string length in bytes
          [n]T, *[n]T      array length (== n)
          []T              slice length
          map[K]T          map length (number of defined keys)
          chan T           number of elements queued in channel buffer
          type parameter   see below

cap(s)    [n]T, *[n]T      array length (== n)
          []T              slice capacity
          chan T           channel buffer capacity
          type parameter   see below

If the argument type is a type parameter P, the call len(e) (or cap(e) respectively) must be valid for each type in P's type set. The result is the length (or capacity, respectively) of the argument whose type corresponds to the type argument with which P was instantiated.

The capacity of a slice is the number of elements for which there is space allocated in the underlying array. At any time the following relationship holds:

0 &lt;= len(s) &lt;= cap(s)

The length of a nil slice, map or channel is 0. The capacity of a nil slice or channel is 0.

The expression len(s) is constant if s is a string constant. The expressions len(s) and cap(s) are constants if the type of s is an array or pointer to an array and the expression s does not contain channel receives or (non-constant) function calls; in this case s is not evaluated. Otherwise, invocations of len and cap are not constant and s is evaluated.

const (
	c1 = imag(2i)                    // imag(2i) = 2.0 is a constant
	c2 = len([10]float64{2})         // [10]float64{2} contains no function calls
	c3 = len([10]float64{c1})        // [10]float64{c1} contains no function calls
	c4 = len([10]float64{imag(2i)})  // imag(2i) is a constant and no function call is issued
	c5 = len([10]float64{imag(z)})   // invalid: imag(z) is a (non-constant) function call
)
var z complex128

Allocation 申请

The built-in function new takes a type T, allocates storage for a variable of that type at run time, and returns a value of type *T pointing to it. The variable is initialized as described in the section on initial values.

new(T)

For instance

type S struct { a int; b float64 }
new(S)

allocates storage for a variable of type S, initializes it (a=0, b=0.0), and returns a value of type *S containing the address of the location.

Making slices, maps and channels 创建 切面 map 和 channels

The built-in function make takes a type T, optionally followed by a type-specific list of expressions. The core type of T must be a slice, map or channel. It returns a value of type T (not *T). The memory is initialized as described in the section on initial values.

Call             Core type    Result

make(T, n)       slice        slice of type T with length n and capacity n
make(T, n, m)    slice        slice of type T with length n and capacity m

make(T)          map          map of type T
make(T, n)       map          map of type T with initial space for approximately n elements

make(T)          channel      unbuffered channel of type T
make(T, n)       channel      buffered channel of type T, buffer size n

Each of the size arguments n and m must be of integer type, have a type set containing only integer types, or be an untyped constant. A constant size argument must be non-negative and representable by a value of type int; if it is an untyped constant it is given type int. If both n and m are provided and are constant, then n must be no larger than m. For slices and channels, if n is negative or larger than m at run time, a run-time panic occurs.

s := make([]int, 10, 100)       // slice with len(s) == 10, cap(s) == 100
s := make([]int, 1e3)           // slice with len(s) == cap(s) == 1000
s := make([]int, 1&lt;&lt;63)         // illegal: len(s) is not representable by a value of type int
s := make([]int, 10, 0)         // illegal: len(s) > cap(s)
c := make(chan int, 10)         // channel with a buffer size of 10
m := make(map[string]int, 100)  // map with initial space for approximately 100 elements

Calling make with a map type and size hint n will create a map with initial space to hold n map elements. The precise behavior is implementation-dependent.

Appending to and copying slices 追加和复制切片

The built-in functions append and copy assist in common slice operations. For both functions, the result is independent of whether the memory referenced by the arguments overlaps.

The variadic function append appends zero or more values x to a slice s and returns the resulting slice of the same type as s. The core type of s must be a slice of type []E. The values x are passed to a parameter of type ...E and the respective parameter passing rules apply. As a special case, if the core type of s is []byte, append also accepts a second argument with core type bytestring followed by .... This form appends the bytes of the byte slice or string.

append(s S, x ...E) S  // core type of S is []E

If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.

s0 := []int{0, 0}
s1 := append(s0, 2)                // append a single element     s1 is []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)          // append multiple elements    s2 is []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)            // append a slice              s3 is []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...)   // append overlapping slice    s4 is []int{3, 5, 7, 2, 3, 5, 7, 0, 0}

var t []interface{}
t = append(t, 42, 3.1415, "foo")   //                             t is []interface{}{42, 3.1415, "foo"}

var b []byte
b = append(b, "bar"...)            // append string contents      b is []byte{'b', 'a', 'r' }

The function copy copies slice elements from a source src to a destination dst and returns the number of elements copied. The core types of both arguments must be slices with identical element type. The number of elements copied is the minimum of len(src) and len(dst). As a special case, if the destination's core type is []byte, copy also accepts a source argument with core type bytestring. This form copies the bytes from the byte slice or string into the byte slice.

copy(dst, src []T) int
copy(dst []byte, src string) int

Examples:

var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
var b = make([]byte, 5)
n1 := copy(s, a[0:])            // n1 == 6, s is []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:])            // n2 == 4, s is []int{2, 3, 4, 5, 4, 5}
n3 := copy(b, "Hello, World!")  // n3 == 5, b is []byte("Hello")

Deletion of map elements 删除元素

The built-in function delete removes the element with key k from a map m. The value k must be assignable to the key type of m.

delete(m, k)  // remove element m[k] from map m

If the type of m is a type parameter, all types in that type set must be maps, and they must all have identical key types.

If the map m is nil or the element m[k] does not exist, delete is a no-op.

Manipulating complex numbers 复数处理

Three functions assemble and disassemble complex numbers. The built-in function complex constructs a complex value from a floating-point real and imaginary part, while real and imag extract the real and imaginary parts of a complex value.

complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT

The type of the arguments and return value correspond. For complex, the two arguments must be of the same floating-point type and the return type is the complex type with the corresponding floating-point constituents: complex64 for float32 arguments, and complex128 for float64 arguments. If one of the arguments evaluates to an untyped constant, it is first implicitly converted to the type of the other argument. If both arguments evaluate to untyped constants, they must be non-complex numbers or their imaginary parts must be zero, and the return value of the function is an untyped complex constant.

For real and imag, the argument must be of complex type, and the return type is the corresponding floating-point type: float32 for a complex64 argument, and float64 for a complex128 argument. If the argument evaluates to an untyped constant, it must be a number, and the return value of the function is an untyped floating-point constant.

The real and imag functions together form the inverse of complex, so for a value z of a complex type Z, z == Z(complex(real(z), imag(z))).

If the operands of these functions are all constants, the return value is a constant.

var a = complex(2, -2)             // complex128
const b = complex(1.0, -1.4)       // untyped complex constant 1 - 1.4i
x := float32(math.Cos(math.Pi/2))  // float32
var c64 = complex(5, -x)           // complex64
var s int = complex(1, 0)          // untyped complex constant 1 + 0i can be converted to int
_ = complex(1, 2&lt;&lt;s)               // illegal: 2 assumes floating-point type, cannot shift
var rl = real(c64)                 // float32
var im = imag(a)                   // float64
const c = imag(b)                  // untyped constant -1.4
_ = imag(3 &lt;&lt; s)                   // illegal: 3 assumes complex type, cannot shift

Arguments of type parameter type are not permitted.

Handling panics 处理恐慌

Two built-in functions, panic and recover, assist in reporting and handling run-time panics and program-defined error conditions.

func panic(interface{})
func recover() interface{}

While executing a function F, an explicit call to panic or a run-time panic terminates the execution of F. Any functions deferred by F are then executed as usual. Next, any deferred functions run by F's caller are run, and so on up to any deferred by the top-level function in the executing goroutine. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking.

panic(42)
panic("unreachable")
panic(Error("cannot parse"))

The recover function allows a program to manage behavior of a panicking goroutine. Suppose a function G defers a function D that calls recover and a panic occurs in a function on the same goroutine in which G is executing. When the running of deferred functions reaches D, the return value of D's call to recover will be the value passed to the call of panic. If D returns normally, without starting a new panic, the panicking sequence stops. In that case, the state of functions called between G and the call to panic is discarded, and normal execution resumes. Any functions deferred by G before D are then run and G's execution terminates by returning to its caller.

The return value of recover is nil when the goroutine is not panicking or recover was not called directly by a deferred function. Conversely, if a goroutine is panicking and recover was called directly by a deferred function, the return value of recover is guaranteed not to be nil. To ensure this, calling panic with a nil interface value (or an untyped nil) causes a run-time panic.

The protect function in the example below invokes the function argument g and protects callers from run-time panics raised by g.

func protect(g func()) {
	defer func() {
		log.Println("done")  // Println executes normally even if there is a panic
		if x := recover(); x != nil {
			log.Printf("run time panic: %v", x)
		}
	}()
	log.Println("start")
	g()
}

Bootstrapping 启动

Current implementations provide several built-in functions useful during bootstrapping. These functions are documented for completeness but are not guaranteed to stay in the language. They do not return a result.

Function   Behavior

print      prints all arguments; formatting of arguments is implementation-specific
println    like print but prints spaces between arguments and a newline at the end

Implementation restriction: print and println need not accept arbitrary argument types, but printing of boolean, numeric, and string types must be supported.

Packages 包

Go programs are constructed by linking together packages. A package in turn is constructed from one or more source files that together declare constants, types, variables and functions belonging to the package and which are accessible in all files of the same package. Those elements may be exported and used in another package.

Source file organization 源代码组织

Each source file consists of a package clause defining the package to which it belongs, followed by a possibly empty set of import declarations that declare packages whose contents it wishes to use, followed by a possibly empty set of declarations of functions, types, variables, and constants.

SourceFile       = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .

Package clause 包声明

A package clause begins each source file and defines the package to which the file belongs.

PackageClause  = "package" PackageName .
PackageName    = identifier .

The PackageName must not be the blank identifier.

package math

A set of files sharing the same PackageName form the implementation of a package. An implementation may require that all source files for a package inhabit the same directory.

Import declarations 导入声明

An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.

ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec       = [ "." | PackageName ] ImportPath .
ImportPath       = string_lit .

The PackageName is used in qualified identifiers to access exported identifiers of the package within the importing source file. It is declared in the file block. If the PackageName is omitted, it defaults to the identifier specified in the package clause of the imported package. If an explicit period (.) appears instead of a name, all the package's exported identifiers declared in that package's package block will be declared in the importing source file's file block and must be accessed without a qualifier.

The interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.

Implementation restriction: A compiler may restrict ImportPaths to non-empty strings using only characters belonging to Unicode's L, M, N, P, and S general categories (the Graphic characters without spaces) and may also exclude the characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD.

Consider a compiled a package containing the package clause package math, which exports function Sin, and installed the compiled package in the file identified by "lib/math". This table illustrates how Sin is accessed in files that import the package after the various types of import declaration.

Import declaration          Local name of Sin

import   "lib/math"         math.Sin
import m "lib/math"         m.Sin
import . "lib/math"         Sin

An import declaration declares a dependency relation between the importing and imported package. It is illegal for a package to import itself, directly or indirectly, or to directly import a package without referring to any of its exported identifiers. To import a package solely for its side-effects (initialization), use the blank identifier as explicit package name:

import _ "lib/math"

An example package 包 示例

Here is a complete Go package that implements a concurrent prime sieve.

package main

import "fmt"

// Send the sequence 2, 3, 4, … to channel 'ch'.
func generate(ch chan&lt;- int) {
	for i := 2; ; i++ {
		ch &lt;- i  // Send 'i' to channel 'ch'.
	}
}

// Copy the values from channel 'src' to channel 'dst',
// removing those divisible by 'prime'.
func filter(src &lt;-chan int, dst chan&lt;- int, prime int) {
	for i := range src {  // Loop over values received from 'src'.
		if i%prime != 0 {
			dst &lt;- i  // Send 'i' to channel 'dst'.
		}
	}
}

// The prime sieve: Daisy-chain filter processes together.
func sieve() {
	ch := make(chan int)  // Create a new channel.
	go generate(ch)       // Start generate() as a subprocess.
	for {
		prime := &lt;-ch
		fmt.Print(prime, "\n")
		ch1 := make(chan int)
		go filter(ch, ch1, prime)
		ch = ch1
	}
}

func main() {
	sieve()
}

Program initialization and execution 程序初始化和执行

The zero value 0值

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

These two simple declarations are equivalent:

var i int
var i int = 0

After

type T struct { i int; f float64; next *T }
t := new(T)

the following holds:

t.i == 0
t.f == 0.0
t.next == nil

The same would also be true after

var t T

Package initialization 包初始化

Within a package, package-level variable initialization proceeds stepwise, with each step selecting the variable earliest in declaration order which has no dependencies on uninitialized variables.

More precisely, a package-level variable is considered ready for initialization if it is not yet initialized and either has no initialization expression or its initialization expression has no dependencies on uninitialized variables. Initialization proceeds by repeatedly initializing the next package-level variable that is earliest in declaration order and ready for initialization, until there are no variables ready for initialization.

If any variables are still uninitialized when this process ends, those variables are part of one or more initialization cycles, and the program is not valid.

Multiple variables on the left-hand side of a variable declaration initialized by single (multi-valued) expression on the right-hand side are initialized together: If any of the variables on the left-hand side is initialized, all those variables are initialized in the same step.

var x = a
var a, b = f() // a and b are initialized together, before x is initialized

For the purpose of package initialization, blank variables are treated like any other variables in declarations.

The declaration order of variables declared in multiple files is determined by the order in which the files are presented to the compiler: Variables declared in the first file are declared before any of the variables declared in the second file, and so on.

Dependency analysis does not rely on the actual values of the variables, only on lexical references to them in the source, analyzed transitively. For instance, if a variable x's initialization expression refers to a function whose body refers to variable y then x depends on y. Specifically:

  • A reference to a variable or function is an identifier denoting that variable or function.
  • A reference to a method m is a method value or method expression of the form t.m, where the (static) type of t is not an interface type, and the method m is in the method set of t. It is immaterial whether the resulting function value t.m is invoked.
  • A variable, function, or method x depends on a variable y if x's initialization expression or body (for functions and methods) contains a reference to y or to a function or method that depends on y.

For example, given the declarations

var (
	a = c + b  // == 9
	b = f()    // == 4
	c = f()    // == 5
	d = 3      // == 5 after initialization has finished
)

func f() int {
	d++
	return d
}

the initialization order is d, b, c, a. Note that the order of subexpressions in initialization expressions is irrelevant: a = c + b and a = b + c result in the same initialization order in this example.

Dependency analysis is performed per package; only references referring to variables, functions, and (non-interface) methods declared in the current package are considered. If other, hidden, data dependencies exists between variables, the initialization order between those variables is unspecified.

For instance, given the declarations

var x = I(T{}).ab()   // x has an undetected, hidden dependency on a and b
var _ = sideEffect()  // unrelated to x, a, or b
var a = b
var b = 42

type I interface      { ab() []int }
type T struct{}
func (T) ab() []int   { return []int{a, b} }

the variable a will be initialized after b but whether x is initialized before b, between b and a, or after a, and thus also the moment at which sideEffect() is called (before or after x is initialized) is not specified.

Variables may also be initialized using functions named init declared in the package block, with no arguments and no result parameters.

func init() { … }

Multiple such functions may be defined per package, even within a single source file. In the package block, the init identifier can be used only to declare init functions, yet the identifier itself is not declared. Thus init functions cannot be referred to from anywhere in a program.

A package with no imports is initialized by assigning initial values to all its package-level variables followed by calling all init functions in the order they appear in the source, possibly in multiple files, as presented to the compiler. If a package has imports, the imported packages are initialized before initializing the package itself. If multiple packages import a package, the imported package will be initialized only once. The importing of packages, by construction, guarantees that there can be no cyclic initialization dependencies.

Package initialization—variable initialization and the invocation of init functions—happens in a single goroutine, sequentially, one package at a time. An init function may launch other goroutines, which can run concurrently with the initialization code. However, initialization always sequences the init functions: it will not invoke the next one until the previous one has returned.

To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.

Program execution 程序执行

A complete program is created by linking a single, unimported package called the main package with all the packages it imports, transitively. The main package must have package name main and declare a function main that takes no arguments and returns no value.

func main() { … }

Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

Errors 错误

The predeclared type error is defined as

type error interface {
	Error() string
}

It is the conventional interface for representing an error condition, with the nil value representing no error. For instance, a function to read data from a file might be defined:

func Read(f *File, b []byte) (n int, err error)

Run-time panics 运行时恐慌

Execution errors such as attempting to index an array out of bounds trigger a run-time panic equivalent to a call of the built-in function panic with a value of the implementation-defined interface type runtime.Error. That type satisfies the predeclared interface type error. The exact error values that represent distinct run-time error conditions are unspecified.

package runtime

type Error interface {
	error
	// and perhaps other methods
}

System considerations 注意事项

Package unsafe

unsafe

The built-in package unsafe, known to the compiler and accessible through the import path "unsafe", provides facilities for low-level programming including operations that violate the type system. A package using unsafe must be vetted manually for type safety and may not be portable. The package provides the following interface:

package unsafe

type ArbitraryType int  // shorthand for an arbitrary Go type; it is not a real type
type Pointer *ArbitraryType

func Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptr

type IntegerType int  // shorthand for an integer type; it is not a real type
func Add(ptr Pointer, len IntegerType) Pointer
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
func SliceData(slice []ArbitraryType) *ArbitraryType
func String(ptr *byte, len IntegerType) string
func StringData(str string) *byte

A Pointer is a pointer type but a Pointer value may not be dereferenced. Any pointer or value of underlying type uintptr can be converted to a type of underlying type Pointer and vice versa. The effect of converting between Pointer and uintptr is implementation-defined.

var f float64
bits = *(*uint64)(unsafe.Pointer(&amp;f))

type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&amp;f))

var p ptr = nil

The functions Alignof and Sizeof take an expression x of any type and return the alignment or size, respectively, of a hypothetical variable v as if v was declared via var v = x.

The function Offsetof takes a (possibly parenthesized) selector s.f, denoting a field f of the struct denoted by s or *s, and returns the field offset in bytes relative to the struct's address. If f is an embedded field, it must be reachable without pointer indirections through fields of the struct. For a struct s with field f:

uintptr(unsafe.Pointer(&amp;s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&amp;s.f))

Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable's type's alignment. The function Alignof takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes. For a variable x:

uintptr(unsafe.Pointer(&amp;x)) % unsafe.Alignof(x) == 0

A (variable of) type T has variable size if T is a type parameter, or if it is an array or struct type containing elements or fields of variable size. Otherwise the size is constant. Calls to Alignof, Offsetof, and Sizeof are compile-time constant expressions of type uintptr if their arguments (or the struct s in the selector expression s.f for Offsetof) are types of constant size.

The function Add adds len to ptr and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)). The len argument must be of integer type or an untyped constant. A constant len argument must be representable by a value of type int; if it is an untyped constant it is given type int. The rules for valid uses of Pointer still apply.

The function Slice returns a slice whose underlying array starts at ptr and whose length and capacity are len. Slice(ptr, len) is equivalent to

(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]

except that, as a special case, if ptr is nil and len is zero, Slice returns nil.

The len argument must be of integer type or an untyped constant. A constant len argument must be non-negative and representable by a value of type int; if it is an untyped constant it is given type int. At run time, if len is negative, or if ptr is nil and len is not zero, a run-time panic occurs.

The function SliceData returns a pointer to the underlying array of the slice argument. If the slice's capacity cap(slice) is not zero, that pointer is &slice[:1][0]. If slice is nil, the result is nil. Otherwise it is a non-nil pointer to an unspecified memory address.

The function String returns a string value whose underlying bytes start at ptr and whose length is len. The same requirements apply to the ptr and len argument as in the function Slice. If len is zero, the result is the empty string "". Since Go strings are immutable, the bytes passed to String must not be modified afterwards.

The function StringData returns a pointer to the underlying bytes of the str argument. For an empty string the return value is unspecified, and may be nil. Since Go strings are immutable, the bytes returned by StringData must not be modified.

Size and alignment guarantees 大小对齐保证?

For the numeric types, the following sizes are guaranteed:

type                                 size in bytes

byte, uint8, int8                     1
uint16, int16                         2
uint32, int32, float32                4
uint64, int64, float64, complex64     8
complex128                           16

The following minimal alignment properties are guaranteed:

  1. For a variable x of any type: unsafe.Alignof(x) is at least 1.
  2. For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1.
  3. For a variable x of array type: unsafe.Alignof(x) is the same as the alignment of a variable of the array's element type.

A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.

godebug

regexp

Package syntax parses regular expressions into parse trees and compiles parse trees into programs. Most clients of regular expressions will use the facilities of package regexp (such as Compile and Match) instead of this package.

Syntax

The regular expression syntax understood by this package when parsing with the Perl flag is as follows. Parts of the syntax can be disabled by passing alternate flags to Parse.

Single characters单个字符

.              any character, possibly including newline (flag s=true)
[xyz]          character class
[^xyz]         negated character class
\d             Perl character class
\D             negated Perl character class
[[:alpha:]]    ASCII character class
[[:^alpha:]]   negated ASCII character class
\pN            Unicode character class (one-letter name)
\p{Greek}      Unicode character class
\PN            negated Unicode character class (one-letter name)
\P{Greek}      negated Unicode character class

Composites 复合体

xy             x followed by y
x|y            x or y (prefer x)

Repetitions 重复多次

x*             zero or more x, prefer more
x+             one or more x, prefer more
x?             zero or one x, prefer one
x{n,m}         n or n+1 or ... or m x, prefer more
x{n,}          n or more x, prefer more
x{n}           exactly n x
x*?            zero or more x, prefer fewer
x+?            one or more x, prefer fewer
x??            zero or one x, prefer zero
x{n,m}?        n or n+1 or ... or m x, prefer fewer
x{n,}?         n or more x, prefer fewer
x{n}?          exactly n x

Implementation restriction: The counting forms x{n,m}, x{n,}, and x{n} reject forms that create a minimum or maximum repetition count above 1000. Unlimited repetitions are not subject to this restriction.

Grouping 分组

(re)           numbered capturing group (submatch)
(?P<name>re)   named & numbered capturing group (submatch)
(?:re)         non-capturing group
(?flags)       set flags within current group; non-capturing
(?flags:re)    set flags during re; non-capturing

Flag syntax is xyz (set) or -xyz (clear) or xy-z (set xy, clear z). The flags are:

i              case-insensitive (default false)
m              multi-line mode: ^ and $ match begin/end line in addition to begin/end text (default false)
s              let . match \n (default false)
U              ungreedy: swap meaning of x* and x*?, x+ and x+?, etc (default false)

Empty strings 空字符串

^              at beginning of text or line (flag m=true)
$              at end of text (like \z not \Z) or line (flag m=true)
\A             at beginning of text
\b             at ASCII word boundary (\w on one side and \W, \A, or \z on the other)
\B             not at ASCII word boundary
\z             at end of text

Escape sequences 转义序列

\a             bell (== \007)
\f             form feed (== \014)
\t             horizontal tab (== \011)
\n             newline (== \012)
\r             carriage return (== \015)
\v             vertical tab character (== \013)
\*             literal *, for any punctuation character *
\123           octal character code (up to three digits)
\x7F           hex character code (exactly two digits)
\x{10FFFF}     hex character code
\Q...\E        literal text ... even if ... has punctuation

Character class elements 字符类元素

x              single character
A-Z            character range (inclusive)
\d             Perl character class
[:foo:]        ASCII character class foo
\p{Foo}        Unicode character class Foo
\pF            Unicode character class F (one-letter name)

Named character classes as character class elements 命名字符:

[\d]           digits (== \d)
[^\d]          not digits (== \D)
[\D]           not digits (== \D)
[^\D]          not not digits (== \d)
[[:name:]]     named ASCII class inside character class (== [:name:])
[^[:name:]]    named ASCII class inside negated character class (== [:^name:])
[\p{Name}]     named Unicode property inside character class (== \p{Name})
[^\p{Name}]    named Unicode property inside negated character class (== \P{Name})

Perl character classes (all ASCII-only) per字符类:

\d             digits (== [0-9])
\D             not digits (== [^0-9])
\s             whitespace (== [\t\n\f\r ])
\S             not whitespace (== [^\t\n\f\r ])
\w             word characters (== [0-9A-Za-z_])
\W             not word characters (== [^0-9A-Za-z_])

ASCII character classes ascii字符类:

[[:alnum:]]    alphanumeric (== [0-9A-Za-z])
[[:alpha:]]    alphabetic (== [A-Za-z])
[[:ascii:]]    ASCII (== [\x00-\x7F])
[[:blank:]]    blank (== [\t ])
[[:cntrl:]]    control (== [\x00-\x1F\x7F])
[[:digit:]]    digits (== [0-9])
[[:graph:]]    graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
[[:lower:]]    lower case (== [a-z])
[[:print:]]    printable (== [ -~] == [ [:graph:]])
[[:punct:]]    punctuation (== [!-/:-@[-`{-~])
[[:space:]]    whitespace (== [\t\n\v\f\r ])
[[:upper:]]    upper case (== [A-Z])
[[:word:]]     word characters (== [0-9A-Za-z_])
[[:xdigit:]]   hex digit (== [0-9A-Fa-f])

Unicode character classes are those in unicode.Categories and unicode.Scripts.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

SSA Go compiler's SSA backend

golang ssa 后端

This package contains the compiler's Static Single Assignment form component. If you're not familiar with SSA, its Wikipedia article is a good starting point.

It is recommended that you first read cmd/compile/README.md if you are not familiar with the Go compiler already. That document gives an overview of the compiler, and explains what is SSA's part and purpose in it.

关键概念

The names described below may be loosely related to their Go counterparts, but note that they are not equivalent. For example, a Go block statement has a variable scope, yet SSA has no notion of variables nor variable scopes.

It may also be surprising that values and blocks are named after their unique sequential IDs. They rarely correspond to named entities in the original code, such as variables or function parameters. The sequential IDs also allow the compiler to avoid maps, and it is always possible to track back the values to Go code using debug and position information.

Values

Values are the basic building blocks of SSA. Per SSA's very definition, a value is defined exactly once, but it may be used any number of times. A value mainly consists of a unique identifier, an operator, a type, and some arguments.

An operator or Op describes the operation that computes the value. The semantics of each operator can be found in _gen/*Ops.go. For example, OpAdd8 takes two value arguments holding 8-bit integers and results in their addition. Here is a possible SSA representation of the addition of two uint8 values:

// var c uint8 = a + b
v4 = Add8 <uint8> v2 v3

A value's type will usually be a Go type. For example, the value in the example above has a uint8 type, and a constant boolean value will have a bool type. However, certain types don't come from Go and are special; below we will cover memory, the most common of them.

See value.go for more information.

内存类型

memory represents the global memory state. An Op that takes a memory argument depends on that memory state, and an Op which has the memory type impacts the state of memory. This ensures that memory operations are kept in the right order. For example:

// *a = 3
// *b = *a
v10 = Store <mem> {int} v6 v8 v1
v14 = Store <mem> {int} v7 v8 v10

Here, Store stores its second argument (of type int) into the first argument (of type *int). The last argument is the memory state; since the second store depends on the memory value defined by the first store, the two stores cannot be reordered.

See cmd/compile/internal/types/type.go for more information.

Blocks

A block represents a basic block in the control flow graph of a function. It is, essentially, a list of values that define the operation of this block. Besides the list of values, blocks mainly consist of a unique identifier, a kind, and a list of successor blocks.

The simplest kind is a plain block; it simply hands the control flow to another block, thus its successors list contains one block.

Another common block kind is the exit block. These have a final value, called control value, which must return a memory state. This is necessary for functions to return some values, for example - the caller needs some memory state to depend on, to ensure that it receives those return values correctly.

The last important block kind we will mention is the if block. It has a single control value that must be a boolean value, and it has exactly two successor blocks. The control flow is handed to the first successor if the bool is true, and to the second otherwise.

Here is a sample if-else control flow represented with basic blocks:

// func(b bool) int {
// 	if b {
// 		return 2
// 	}
// 	return 3
// }
b1:
  v1 = InitMem <mem>
  v2 = SP <uintptr>
  v5 = Addr <*int> {~r1} v2
  v6 = Arg <bool> {b}
  v8 = Const64 <int> [2]
  v12 = Const64 <int> [3]
  If v6 -> b2 b3
b2: <- b1
  v10 = VarDef <mem> {~r1} v1
  v11 = Store <mem> {int} v5 v8 v10
  Ret v11
b3: <- b1
  v14 = VarDef <mem> {~r1} v1
  v15 = Store <mem> {int} v5 v12 v14
  Ret v15

See block.go for more information.

函数

A function represents a function declaration along with its body. It mainly consists of a name, a type (its signature), a list of blocks that form its body, and the entry block within said list.

When a function is called, the control flow is handed to its entry block. If the function terminates, the control flow will eventually reach an exit block, thus ending the function call.

Note that a function may have zero or multiple exit blocks, just like a Go function can have any number of return points, but it must have exactly one entry point block.

Also note that some SSA functions are autogenerated, such as the hash functions for each type used as a map key.

For example, this is what an empty function can look like in SSA, with a single exit block that returns an uninteresting memory state:

foo func()
  b1:
    v1 = InitMem <mem>
    Ret v1

See func.go for more information.

Compiler passes

Having a program in SSA form is not very useful on its own. Its advantage lies in how easy it is to write optimizations that modify the program to make it better. The way the Go compiler accomplishes this is via a list of passes.

Each pass transforms a SSA function in some way. For example, a dead code elimination pass will remove blocks and values that it can prove will never be executed, and a nil check elimination pass will remove nil checks which it can prove to be redundant.

Compiler passes work on one function at a time, and by default run sequentially and exactly once.

The lower pass is special; it converts the SSA representation from being machine-independent to being machine-dependent. That is, some abstract operators are replaced with their non-generic counterparts, potentially reducing or increasing the final number of values.

See the passes list defined in compile.go for more information.

Playing with SSA

A good way to see and get used to the compiler's SSA in action is via GOSSAFUNC. For example, to see func Foo's initial SSA form and final generated assembly, one can run:

GOSSAFUNC=Foo go build

The generated ssa.html file will also contain the SSA func at each of the compile passes, making it easy to see what each pass does to a particular program. You can also click on values and blocks to highlight them, to help follow the control flow and values.

The value specified in GOSSAFUNC can also be a package-qualified function name, e.g.

GOSSAFUNC=blah.Foo go build

This will match any function named "Foo" within a package whose final suffix is "blah" (e.g. something/blah.Foo, anotherthing/extra/blah.Foo).

If non-HTML dumps are needed, append a "+" to the GOSSAFUNC value and dumps will be written to stdout:

GOSSAFUNC=Bar+ go build

Hacking on SSA

While most compiler passes are implemented directly in Go code, some others are code generated. This is currently done via rewrite rules, which have their own syntax and are maintained in _gen/*.rules. Simpler optimizations can be written easily and quickly this way, but rewrite rules are not suitable for more complex optimizations.

To read more on rewrite rules, have a look at the top comments in _gen/generic.rules and _gen/rulegen.go.

Similarly, the code to manage operators is also code generated from _gen/*Ops.go, as it is easier to maintain a few tables than a lot of code. After changing the rules or operators, run go generate cmd/compile/internal/ssa to generate the Go code again.

diagnostics 问题诊断

参考文档

诊断分类主要一下几个

  • (剖析)Profiling: Profiling tools analyze the complexity and costs of a Go program such as its memory usage and frequently called functions to identify the expensive sections of a Go program.
  • (跟踪)Tracing: Tracing is a way to instrument code to analyze latency throughout the lifecycle of a call or user request. Traces provide an overview of how much latency each component contributes to the overall latency in a system. Traces can span multiple Go processes.
  • (调试)Debugging: Debugging allows us to pause a Go program and examine its execution. Program state and flow can be verified with debugging.
  • (运行时分析和事件)Runtime statistics and events: Collection and analysis of runtime stats and events provides a high-level overview of the health of Go programs. Spikes/dips of metrics helps us to identify changes in throughput, utilization, and performance.

Profiling 剖析

主要的Profiling

  • cpu: CPU profile determines where a program spends its time while actively consuming CPU cycles (as opposed to while sleeping or waiting for I/O).

  • (堆分析)heap: Heap profile reports memory allocation samples; used to monitor current and historical memory usage, and to check for memory leaks. (线程的创建)threadcreate: Thread creation profile reports the sections of the program that lead the creation of new OS threads.

  • (协程)goroutine: Goroutine profile reports the stack traces of all current goroutines.

  • (阻塞)block: Block profile shows where goroutines block waiting on synchronization primitives (including timer channels). Block profile is not enabled by default; use runtime.SetBlockProfileRate to enable it.

  • (锁)mutex: Mutex profile reports the lock contentions. When you think your CPU is not fully utilized due to a mutex contention, use this profile. Mutex profile is not enabled by default, see runtime.SetMutexProfileFraction to enable it.

  • 还不够?那就上操作系统级别的工具 参考文档 [ebpf]

Tracing 跟踪

go 目前暂不提供自动的函数调用跟踪办法 需要手动添加

Debugging 调试

  • Delve: go 实现的程序调试器
  • GDB: 通用调试器 gccgo 提供对 go程序的支持

更好的调试建议 关闭编译器优化

 go build -gcflags=all="-N -l"

1.10 之后新的编译参数

$ go build -gcflags="-dwarflocationlists=true"

使用 core dump 文件 进行线上调试

Runtime statistics and events 运行时统计和事件

  • runtime.ReadMemStats
  • debug.ReadGCStats
  • debug.Stack
  • debug.WriteHeapDump
  • runtime.NumGoroutine

Execution tracer

Go comes with a runtime execution tracer to capture a wide range of runtime events. Scheduling, syscall, garbage collections, heap size, and other events are collected by runtime and available for visualization by the go tool trace. Execution tracer is a tool to detect latency and utilization problems. You can examine how well the CPU is utilized, and when networking or syscalls are a cause of preemption for the goroutines.

GODEBUG 设置debug参数

GODEBUG=gctrace=1 prints garbage collector events at each collection, summarizing the amount of memory collected and the length of the pause. GODEBUG=inittrace=1 prints a summary of execution time and memory allocation information for completed package initialization work. GODEBUG=schedtrace=X prints scheduling events every X milliseconds.

GODEBUG=cpu.all=off disables the use of all optional instruction set extensions. GODEBUG=cpu.extension=off disables use of instructions from the specified instruction set extension. extension is the lower case name for the instruction set extension such as sse41 or avx.

core dump(核心转储)

vdso (liunx 虚拟动态共享对象)

unsafe

Package unsafe contains operations that step around the type safety of Go programs.

type ArbitraryType int

ArbitraryType is here for the purposes of documentation only and is not actually part of the unsafe package. It represents the type of an arbitrary Go expression.

type IntegerType int

IntegerType is here for the purposes of documentation only and is not actually part of the unsafe package. It represents any arbitrary integer type.

type Pointer *ArbitraryType

Pointer represents a pointer to an arbitrary type. There are four special operations available for type Pointer that are not available for other types:

  • A pointer value of any type can be converted to a Pointer.
  • A Pointer can be converted to a pointer value of any type.
  • A uintptr can be converted to a Pointer.
  • A Pointer can be converted to a uintptr.

Pointer therefore allows a program to defeat the type system and read and write arbitrary memory. It should be used with extreme care.

The following patterns involving Pointer are valid. Code not using these patterns is likely to be invalid today or to become invalid in the future. Even the valid patterns below come with important caveats.

Running "go vet" can help find uses of Pointer that do not conform to these patterns, but silence from "go vet" is not a guarantee that the code is valid.

(1) Conversion of a *T1 to Pointer to *T2.

Provided that T2 is no larger than T1 and that the two share an equivalent memory layout, this conversion allows reinterpreting data of one type as data of another type. An example is the implementation of math.Float64bits:

	func Float64bits(f float64) uint64 {
		return *(*uint64)(unsafe.Pointer(&f))
	}

(2) Conversion of a Pointer to a uintptr (but not back to Pointer).

Converting a Pointer to a uintptr produces the memory address of the value pointed at, as an integer. The usual use for such a uintptr is to print it.

Conversion of a uintptr back to Pointer is not valid in general.

A uintptr is an integer, not a reference. Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.

The remaining patterns enumerate the only valid conversions from uintptr to Pointer.

(3) Conversion of a Pointer to a uintptr and back, with arithmetic.

If p points into an allocated object, it can be advanced through the object by conversion to uintptr, addition of an offset, and conversion back to Pointer.

	p = unsafe.Pointer(uintptr(p) + offset)

The most common use of this pattern is to access fields in a struct or elements of an array:

	// equivalent to f := unsafe.Pointer(&s.f)
	f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))

	// equivalent to e := unsafe.Pointer(&x[i])
	e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))

It is valid both to add and to subtract offsets from a pointer in this way. It is also valid to use &^ to round pointers, usually for alignment. In all cases, the result must continue to point into the original allocated object.

Unlike in C, it is not valid to advance a pointer just beyond the end of its original allocation:

	// INVALID: end points outside allocated space.
	var s thing
	end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s))

	// INVALID: end points outside allocated space.
	b := make([]byte, n)
	end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n))

Note that both conversions must appear in the same expression, with only the intervening arithmetic between them:

// INVALID: uintptr cannot be stored in variable
// before conversion back to Pointer.
u := uintptr(p)
p = unsafe.Pointer(u + offset)

Note that the pointer must point into an allocated object, so it may not be nil.

	// INVALID: conversion of nil pointer
	u := unsafe.Pointer(nil)
	p := unsafe.Pointer(uintptr(u) + offset)

(4) Conversion of a Pointer to a uintptr when calling syscall.Syscall.

The Syscall functions in package syscall pass their uintptr arguments directly to the operating system, which then may, depending on the details of the call, reinterpret some of them as pointers. That is, the system call implementation is implicitly converting certain arguments back from uintptr to pointer.

If a pointer argument must be converted to uintptr for use as an argument, that conversion must appear in the call expression itself:

syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))

The compiler handles a Pointer converted to a uintptr in the argument list of a call to a function implemented in assembly by arranging 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.

For the compiler to recognize this pattern, the conversion must appear in the argument list:

	// INVALID: uintptr cannot be stored in variable
	// before implicit conversion back to Pointer during system call.
	u := uintptr(unsafe.Pointer(p))
	syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))

(5) Conversion of the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr from uintptr to Pointer.

Package reflect's Value methods named Pointer and UnsafeAddr return type uintptr instead of unsafe.Pointer to keep callers from changing the result to an arbitrary type without first importing "unsafe". However, this means that the result is fragile and must be converted to Pointer immediately after making the call, in the same expression:

	p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))

As in the cases above, it is invalid to store the result before the conversion:

	// INVALID: uintptr cannot be stored in variable
	// before conversion back to Pointer.
	u := reflect.ValueOf(new(int)).Pointer()
	p := (*int)(unsafe.Pointer(u))

(6) Conversion of a reflect.SliceHeader or reflect.StringHeader Data field to or from Pointer.

As in the previous case, the reflect data structures SliceHeader and StringHeader declare the field Data as a uintptr to keep callers from changing the result to an arbitrary type without first importing "unsafe". However, this means that SliceHeader and StringHeader are only valid when interpreting the content of an actual slice or string value.

	var s string
	hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
	hdr.Data = uintptr(unsafe.Pointer(p))              // case 6 (this case)
	hdr.Len = n

In this usage hdr.Data is really an alternate way to refer to the underlying pointer in the string header, not a uintptr variable itself.

In general, reflect.SliceHeader and reflect.StringHeader should be used only as *reflect.SliceHeader and *reflect.StringHeader pointing at actual slices or strings, never as plain structs. A program should not declare or allocate variables of these struct types.

	// INVALID: a directly-declared header will not hold Data as a reference.
	var hdr reflect.StringHeader
	hdr.Data = uintptr(unsafe.Pointer(p))
	hdr.Len = n
	s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost

func Sizeof(x ArbitraryType) uintptr

Sizeof takes an expression x of any type and returns the size in bytes of a hypothetical variable v as if v was declared via var v = x. The size does not include any memory possibly referenced by x. For instance, if x is a slice, Sizeof returns the size of the slice descriptor, not the size of the memory referenced by the slice. For a struct, the size includes any padding introduced by field alignment. The return value of Sizeof is a Go constant if the type of the argument x does not have variable size. (A type has variable size if it is a type parameter or if it is an array or struct type with elements of variable size).

func Offsetof(x ArbitraryType) uintptr

Offsetof returns the offset within the struct of the field represented by x, which must be of the form structValue.field. In other words, it returns the number of bytes between the start of the struct and the start of the field. The return value of Offsetof is a Go constant if the type of the argument x does not have variable size. (See the description of [Sizeof] for a definition of variable sized types.)

func Alignof(x ArbitraryType) uintptr

Alignof takes an expression x of any type and returns the required alignment of a hypothetical variable v as if v was declared via var v = x. It is the largest value m such that the address of v is always zero mod m. It is the same as the value returned by reflect.TypeOf(x).Align(). As a special case, if a variable s is of struct type and f is a field within that struct, then Alignof(s.f) will return the required alignment of a field of that type within a struct. This case is the same as the value returned by reflect.TypeOf(s.f).FieldAlign(). The return value of Alignof is a Go constant if the type of the argument does not have variable size. (See the description of [Sizeof] for a definition of variable sized types.)

func Add(ptr Pointer, len IntegerType) Pointer

The function Add adds len to ptr and returns the updated pointer Pointer(uintptr(ptr) + uintptr(len)). The len argument must be of integer type or an untyped constant. A constant len argument must be representable by a value of type int; if it is an untyped constant it is given type int. The rules for valid uses of Pointer still apply.

func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType

The function Slice returns a slice whose underlying array starts at ptr and whose length and capacity are len. Slice(ptr, len) is equivalent to

	(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]

except that, as a special case, if ptr is nil and len is zero, Slice returns nil.

The len argument must be of integer type or an untyped constant. A constant len argument must be non-negative and representable by a value of type int; if it is an untyped constant it is given type int. At run time, if len is negative, or if ptr is nil and len is not zero, a run-time panic occurs.

func SliceData(slice []ArbitraryType) *ArbitraryType

SliceData returns a pointer to the underlying array of the argument slice.

  • If cap(slice) > 0, SliceData returns &slice[:1][0].
  • If slice == nil, SliceData returns nil.
  • Otherwise, SliceData returns a non-nil pointer to an unspecified memory address.

func String(ptr *byte, len IntegerType) string

String returns a string value whose underlying bytes start at ptr and whose length is len.

The len argument must be of integer type or an untyped constant. A constant len argument must be non-negative and representable by a value of type int; if it is an untyped constant it is given type int. At run time, if len is negative, or if ptr is nil and len is not zero, a run-time panic occurs.

Since Go strings are immutable, the bytes passed to String must not be modified afterwards.

func StringData(str string) *byte

StringData returns a pointer to the underlying bytes of str. For an empty string the return value is unspecified, and may be nil.

Since Go strings are immutable, the bytes returned by StringData must not be modified.

runtime hook (运行时)

This is a living document and at times it will be out of date. It is intended to articulate how programming in the Go runtime differs from writing normal Go. It focuses on pervasive concepts rather than details of particular interfaces.

Scheduler structures 调度器结构

The scheduler manages three types of resources that pervade the runtime: Gs, Ms, and Ps. It's important to understand these even if you're not working on the scheduler.

Gs, Ms, Ps GMP

  • G 就是一个go协程

A "G" is simply a goroutine. It's represented by type g. When a goroutine exits, its g object is returned to a pool of free gs and can later be reused for some other goroutine.

  • M 对应一个OS线程 用来执行 用户 go 代码

An "M" is an OS thread that can be executing user Go code, runtime code, a system call, or be idle. It's represented by type m. There can be any number of Ms at a time since any number of threads may be blocked in system calls.

  • P

Finally, a "P" represents the resources required to execute user Go code, such as scheduler and memory allocator state. It's represented by type p. There are exactly GOMAXPROCS Ps. A P can be thought of like a CPU in the OS scheduler and the contents of the p type like per-CPU state. This is a good place to put state that needs to be sharded for efficiency, but doesn't need to be per-thread or per-goroutine.

The scheduler's job is to match up a G (the code to execute), an M (where to execute it), and a P (the rights and resources to execute it). When an M stops executing user Go code, for example by entering a system call, it returns its P to the idle P pool. In order to resume executing user Go code, for example on return from a system call, it must acquire a P from the idle pool.

  • g m p 都是堆上分配的 从来不会被回收避免 深度 写屏障

All g, m, and p objects are heap allocated, but are never freed, so their memory remains type stable. As a result, the runtime can avoid write barriers in the depths of the scheduler.

getg() and getg().m.curg

To get the current user g, use getg().m.curg.

getg() alone returns the current g, but when executing on the system or signal stacks, this will return the current M's "g0" or "gsignal", respectively. This is usually not what you want.

To determine if you're running on the user stack or the system stack, use getg() == getg().m.curg.

Stacks 栈

Every non-dead G has a user stack associated with it, which is what user Go code executes on. User stacks start small (e.g., 2K) and grow or shrink dynamically.

Every M has a system stack associated with it (also known as the M's "g0" stack because it's implemented as a stub G) and, on Unix platforms, a signal stack (also known as the M's "gsignal" stack). System and signal stacks cannot grow, but are large enough to execute runtime and cgo code (8K in a pure Go binary; system-allocated in a cgo binary).

Runtime code often temporarily switches to the system stack using systemstack, mcall, or asmcgocall to perform tasks that must not be preempted, that must not grow the user stack, or that switch user goroutines. Code running on the system stack is implicitly non-preemptible and the garbage collector does not scan system stacks. While running on the system stack, the current user stack is not used for execution.

nosplit functions

Most functions start with a prologue that inspects the stack pointer and the current G's stack bound and calls morestack if the stack needs to grow.

Functions can be marked //go:nosplit (or NOSPLIT in assembly) to indicate that they should not get this prologue. This has several uses:

  • Functions that must run on the user stack, but must not call into stack growth, for example because this would cause a deadlock, or because they have untyped words on the stack.

  • Functions that must not be preempted on entry.

  • Functions that may run without a valid G. For example, functions that run in early runtime start-up, or that may be entered from C code such as cgo callbacks or the signal handler.

Splittable functions ensure there's some amount of space on the stack for nosplit functions to run in and the linker checks that any static chain of nosplit function calls cannot exceed this bound.

Any function with a //go:nosplit annotation should explain why it is nosplit in its documentation comment.

Error handling and reporting

Errors that can reasonably be recovered from in user code should use panic like usual. However, there are some situations where panic will cause an immediate fatal error, such as when called on the system stack or when called during mallocgc.

Most errors in the runtime are not recoverable. For these, use throw, which dumps the traceback and immediately terminates the process. In general, throw should be passed a string constant to avoid allocating in perilous situations. By convention, additional details are printed before throw using print or println and the messages are prefixed with "runtime:".

For unrecoverable errors where user code is expected to be at fault for the failure (such as racing map writes), use fatal.

For runtime error debugging, it may be useful to run with GOTRACEBACK=system or GOTRACEBACK=crash. The output of panic and fatal is as described by GOTRACEBACK. The output of throw always includes runtime frames, metadata and all goroutines regardless of GOTRACEBACK (i.e., equivalent to GOTRACEBACK=system). Whether throw crashes or not is still controlled by GOTRACEBACK.

Synchronization 同步

The runtime has multiple synchronization mechanisms. They differ in semantics and, in particular, in whether they interact with the goroutine scheduler or the OS scheduler.

The simplest is mutex, which is manipulated using lock and unlock. This should be used to protect shared structures for short periods. Blocking on a mutex directly blocks the M, without interacting with the Go scheduler. This means it is safe to use from the lowest levels of the runtime, but also prevents any associated G and P from being rescheduled. rwmutex is similar.

For one-shot notifications, use note, which provides notesleep and notewakeup. Unlike traditional UNIX sleep/wakeup, notes are race-free, so notesleep returns immediately if the notewakeup has already happened. A note can be reset after use with noteclear, which must not race with a sleep or wakeup. Like mutex, blocking on a note blocks the M. However, there are different ways to sleep on a note:notesleep also prevents rescheduling of any associated G and P, while notetsleepg acts like a blocking system call that allows the P to be reused to run another G. This is still less efficient than blocking the G directly since it consumes an M.

To interact directly with the goroutine scheduler, use gopark and goready. gopark parks the current goroutine—putting it in the "waiting" state and removing it from the scheduler's run queue—and schedules another goroutine on the current M/P. goready puts a parked goroutine back in the "runnable" state and adds it to the run queue.

In summary,

Blocks
InterfaceGMP
(rw)mutexYYY
noteYYY/N
parkYNN

Atomics 原子

The runtime uses its own atomics package at runtime/internal/atomic. This corresponds to sync/atomic, but functions have different names for historical reasons and there are a few additional functions needed by the runtime.

In general, we think hard about the uses of atomics in the runtime and try to avoid unnecessary atomic operations. If access to a variable is sometimes protected by another synchronization mechanism, the already-protected accesses generally don't need to be atomic. There are several reasons for this:

  1. Using non-atomic or atomic access where appropriate makes the code more self-documenting. Atomic access to a variable implies there's somewhere else that may concurrently access the variable.

  2. Non-atomic access allows for automatic race detection. The runtime doesn't currently have a race detector, but it may in the future. Atomic access defeats the race detector, while non-atomic access allows the race detector to check your assumptions.

  3. Non-atomic access may improve performance.

Of course, any non-atomic access to a shared variable should be documented to explain how that access is protected.

Some common patterns that mix atomic and non-atomic access are:

  • Read-mostly variables where updates are protected by a lock. Within the locked region, reads do not need to be atomic, but the write does. Outside the locked region, reads need to be atomic.

  • Reads that only happen during STW, where no writes can happen during STW, do not need to be atomic.

That said, the advice from the Go memory model stands: "Don't be [too] clever." The performance of the runtime matters, but its robustness matters more.

Unmanaged memory 手动管理内存

In general, the runtime tries to use regular heap allocation. However, in some cases the runtime must allocate objects outside of the garbage collected heap, in unmanaged memory. This is necessary if the objects are part of the memory manager itself or if they must be allocated in situations where the caller may not have a P.

There are three mechanisms for allocating unmanaged memory:

  • sysAlloc obtains memory directly from the OS. This comes in whole multiples of the system page size, but it can be freed with sysFree.

  • persistentalloc combines multiple smaller allocations into a single sysAlloc to avoid fragmentation. However, there is no way to free persistentalloced objects (hence the name).

  • fixalloc is a SLAB-style allocator that allocates objects of a fixed size. fixalloced objects can be freed, but this memory can only be reused by the same fixalloc pool, so it can only be reused for objects of the same type.

In general, types that are allocated using any of these should be marked as not in heap by embedding runtime/internal/sys.NotInHeap.

Objects that are allocated in unmanaged memory must not contain heap pointers unless the following rules are also obeyed:

  1. Any pointers from unmanaged memory to the heap must be garbage collection roots. More specifically, any pointer must either be accessible through a global variable or be added as an explicit garbage collection root in runtime.markroot.

  2. If the memory is reused, the heap pointers must be zero-initialized before they become visible as GC roots. Otherwise, the GC may observe stale heap pointers. See "Zero-initialization versus zeroing".

Zero-initialization versus zeroing 零初始化和归零

There are two types of zeroing in the runtime, depending on whether the memory is already initialized to a type-safe state.

If memory is not in a type-safe state, meaning it potentially contains "garbage" because it was just allocated and it is being initialized for first use, then it must be zero-initialized using memclrNoHeapPointers or non-pointer writes. This does not perform write barriers.

If memory is already in a type-safe state and is simply being set to the zero value, this must be done using regular writes, typedmemclr, or memclrHasPointers. This performs write barriers.

Runtime-only compiler directives 运行时指令

In addition to the "//go:" directives documented in "go doc compile", the compiler supports additional directives only in the runtime.

go:systemstack

go:systemstack indicates that a function must run on the system stack. This is checked dynamically by a special function prologue.

go:nowritebarrier 防止写屏障无线循环

go:nowritebarrier directs the compiler to emit an error if the following function contains any write barriers. (It does not suppress the generation of write barriers; it is simply an assertion.)

Usually you want go:nowritebarrierrec. go:nowritebarrier is primarily useful in situations where it's "nice" not to have write barriers, but not required for correctness.

go:nowritebarrierrec and go:yeswritebarrierrec

go:nowritebarrierrec directs the compiler to emit an error if the following function or any function it calls recursively, up to a go:yeswritebarrierrec, contains a write barrier.

Logically, the compiler floods the call graph starting from each go:nowritebarrierrec function and produces an error if it encounters a function containing a write barrier. This flood stops at go:yeswritebarrierrec functions.

go:nowritebarrierrec is used in the implementation of the write barrier to prevent infinite loops.

Both directives are used in the scheduler. The write barrier requires an active P (getg().m.p != nil) and scheduler code often runs without an active P. In this case, go:nowritebarrierrec is used on functions that release the P or may run without a P and go:yeswritebarrierrec is used when code re-acquires an active P. Since these are function-level annotations, code that releases or acquires a P may need to be split across two functions.

go:uintptrkeepalive

The //go:uintptrkeepalive directive must be followed by a function declaration.

It specifies that the function's uintptr arguments may be pointer values that have been converted to uintptr and must be kept alive for the duration of the call, even though from the types alone it would appear that the object is no longer needed during the call.

This directive is similar to //go:uintptrescapes, but it does not force arguments to escape. Since stack growth does not understand these arguments, this directive must be used with //go:nosplit (in the marked function and all transitive calls) to prevent stack growth.

The conversion from pointer to uintptr must appear in the argument list of any call to this function. This directive is used for some low-level system call implementations.

[扩展 sync. semaphore]

Semaphores in Plan 9

https://swtch.com/semaphore.pdf

metrics(指标数据)

package metrics provides a stable interface to access implementation-defined metrics exported by the Go runtime. This package is similar to existing functions like [runtime.ReadMemStats] and [debug.ReadGCStats], but significantly more general.

The set of metrics defined by this package may evolve as the runtime itself evolves, and also enables variation across Go implementations, whose relevant metric sets may not intersect.

Interface

Metrics are designated by a string key, rather than, for example, a field name in a struct. The full list of supported metrics is always available in the slice of Descriptions returned by All. Each Description also includes useful information about the metric.

Thus, users of this API are encouraged to sample supported metrics defined by the slice returned by All to remain compatible across Go versions. Of course, situations arise where reading specific metrics is critical. For these cases, users are encouraged to use build tags, and although metrics may be deprecated and removed, users should consider this to be an exceptional and rare event, coinciding with a very large change in a particular Go implementation.

Each metric key also has a "kind" that describes the format of the metric's value. In the interest of not breaking users of this package, the "kind" for a given metric is guaranteed not to change. If it must change, then a new metric will be introduced with a new key and a new "kind."

Metric key format key格式

As mentioned earlier, metric keys are strings. Their format is simple and well-defined, designed to be both human and machine readable. It is split into two components, separated by a colon: a rooted path and a unit. The choice to include the unit in the key is motivated by compatibility: if a metric's unit changes, its semantics likely did also, and a new key should be introduced.

For more details on the precise definition of the metric key's path and unit formats, see the documentation of the Name field of the Description struct.

A note about floats

This package supports metrics whose values have a floating-point representation. In order to improve ease-of-use, this package promises to never produce the following classes of floating-point values: NaN, infinity.

Supported metrics 支持的指标

Below is the full list of supported metrics, ordered lexicographically.

/cgo/go-to-c-calls:calls
	Count of calls made from Go to C by the current process.

/cpu/classes/gc/mark/assist:cpu-seconds
	Estimated total CPU time goroutines spent performing GC
	tasks to assist the GC and prevent it from falling behind the
	application. This metric is an overestimate, and not directly
	comparable to system CPU time measurements. Compare only with
	other /cpu/classes metrics.

/cpu/classes/gc/mark/dedicated:cpu-seconds
	Estimated total CPU time spent performing GC tasks on processors
	(as defined by GOMAXPROCS) dedicated to those tasks. This
	includes time spent with the world stopped due to the GC. This
	metric is an overestimate, and not directly comparable to system
	CPU time measurements. Compare only with other /cpu/classes
	metrics.

/cpu/classes/gc/mark/idle:cpu-seconds
	Estimated total CPU time spent performing GC tasks on spare CPU
	resources that the Go scheduler could not otherwise find a use
	for. This should be subtracted from the total GC CPU time to
	obtain a measure of compulsory GC CPU time. This metric is an
	overestimate, and not directly comparable to system CPU time
	measurements. Compare only with other /cpu/classes metrics.

/cpu/classes/gc/pause:cpu-seconds
	Estimated total CPU time spent with the application paused by
	the GC. Even if only one thread is running during the pause,
	this is computed as GOMAXPROCS times the pause latency because
	nothing else can be executing. This is the exact sum of samples
	in /gc/pause:seconds if each sample is multiplied by GOMAXPROCS
	at the time it is taken. This metric is an overestimate,
	and not directly comparable to system CPU time measurements.
	Compare only with other /cpu/classes metrics.

/cpu/classes/gc/total:cpu-seconds
	Estimated total CPU time spent performing GC tasks. This metric
	is an overestimate, and not directly comparable to system CPU
	time measurements. Compare only with other /cpu/classes metrics.
	Sum of all metrics in /cpu/classes/gc.

/cpu/classes/idle:cpu-seconds
	Estimated total available CPU time not spent executing
	any Go or Go runtime code. In other words, the part of
	/cpu/classes/total:cpu-seconds that was unused. This metric is
	an overestimate, and not directly comparable to system CPU time
	measurements. Compare only with other /cpu/classes metrics.

/cpu/classes/scavenge/assist:cpu-seconds
	Estimated total CPU time spent returning unused memory to the
	underlying platform in response eagerly in response to memory
	pressure. This metric is an overestimate, and not directly
	comparable to system CPU time measurements. Compare only with
	other /cpu/classes metrics.

/cpu/classes/scavenge/background:cpu-seconds
	Estimated total CPU time spent performing background tasks to
	return unused memory to the underlying platform. This metric is
	an overestimate, and not directly comparable to system CPU time
	measurements. Compare only with other /cpu/classes metrics.

/cpu/classes/scavenge/total:cpu-seconds
	Estimated total CPU time spent performing tasks that return
	unused memory to the underlying platform. This metric is an
	overestimate, and not directly comparable to system CPU time
	measurements. Compare only with other /cpu/classes metrics.
	Sum of all metrics in /cpu/classes/scavenge.

/cpu/classes/total:cpu-seconds
	Estimated total available CPU time for user Go code or the Go
	runtime, as defined by GOMAXPROCS. In other words, GOMAXPROCS
	integrated over the wall-clock duration this process has been
	executing for. This metric is an overestimate, and not directly
	comparable to system CPU time measurements. Compare only with
	other /cpu/classes metrics. Sum of all metrics in /cpu/classes.

/cpu/classes/user:cpu-seconds
	Estimated total CPU time spent running user Go code. This may
	also include some small amount of time spent in the Go runtime.
	This metric is an overestimate, and not directly comparable
	to system CPU time measurements. Compare only with other
	/cpu/classes metrics.

/gc/cycles/automatic:gc-cycles
	Count of completed GC cycles generated by the Go runtime.

/gc/cycles/forced:gc-cycles
	Count of completed GC cycles forced by the application.

/gc/cycles/total:gc-cycles
	Count of all completed GC cycles.

/gc/heap/allocs-by-size:bytes
	Distribution of heap allocations by approximate size.
	Note that this does not include tiny objects as defined by
	/gc/heap/tiny/allocs:objects, only tiny blocks.

/gc/heap/allocs:bytes
	Cumulative sum of memory allocated to the heap by the
	application.

/gc/heap/allocs:objects
	Cumulative count of heap allocations triggered by the
	application. Note that this does not include tiny objects as
	defined by /gc/heap/tiny/allocs:objects, only tiny blocks.

/gc/heap/frees-by-size:bytes
	Distribution of freed heap allocations by approximate size.
	Note that this does not include tiny objects as defined by
	/gc/heap/tiny/allocs:objects, only tiny blocks.

/gc/heap/frees:bytes
	Cumulative sum of heap memory freed by the garbage collector.

/gc/heap/frees:objects
	Cumulative count of heap allocations whose storage was freed
	by the garbage collector. Note that this does not include tiny
	objects as defined by /gc/heap/tiny/allocs:objects, only tiny
	blocks.

/gc/heap/goal:bytes
	Heap size target for the end of the GC cycle.

/gc/heap/objects:objects
	Number of objects, live or unswept, occupying heap memory.

/gc/heap/tiny/allocs:objects
	Count of small allocations that are packed together into blocks.
	These allocations are counted separately from other allocations
	because each individual allocation is not tracked by the
	runtime, only their block. Each block is already accounted for
	in allocs-by-size and frees-by-size.

/gc/limiter/last-enabled:gc-cycle
	GC cycle the last time the GC CPU limiter was enabled.
	This metric is useful for diagnosing the root cause of an
	out-of-memory error, because the limiter trades memory for CPU
	time when the GC's CPU time gets too high. This is most likely
	to occur with use of SetMemoryLimit. The first GC cycle is cycle
	1, so a value of 0 indicates that it was never enabled.

/gc/pauses:seconds
	Distribution individual GC-related stop-the-world pause
	latencies.

/gc/stack/starting-size:bytes
	The stack size of new goroutines.

/godebug/non-default-behavior/execerrdot:events
	The number of non-default behaviors executed by the os/exec
	package due to a non-default GODEBUG=execerrdot=... setting.

/godebug/non-default-behavior/http2client:events
	The number of non-default behaviors executed by the net/http
	package due to a non-default GODEBUG=http2client=... setting.

/godebug/non-default-behavior/http2server:events
	The number of non-default behaviors executed by the net/http
	package due to a non-default GODEBUG=http2server=... setting.

/godebug/non-default-behavior/installgoroot:events
	The number of non-default behaviors executed by the go/build
	package due to a non-default GODEBUG=installgoroot=... setting.

/godebug/non-default-behavior/jstmpllitinterp:events
	The number of non-default behaviors executed by
	the html/template package due to a non-default
	GODEBUG=jstmpllitinterp=... setting.

/godebug/non-default-behavior/multipartmaxheaders:events
	The number of non-default behaviors executed by
	the mime/multipart package due to a non-default
	GODEBUG=multipartmaxheaders=... setting.

/godebug/non-default-behavior/multipartmaxparts:events
	The number of non-default behaviors executed by
	the mime/multipart package due to a non-default
	GODEBUG=multipartmaxparts=... setting.

/godebug/non-default-behavior/panicnil:events
	The number of non-default behaviors executed by the runtime
	package due to a non-default GODEBUG=panicnil=... setting.

/godebug/non-default-behavior/randautoseed:events
	The number of non-default behaviors executed by the math/rand
	package due to a non-default GODEBUG=randautoseed=... setting.

/godebug/non-default-behavior/tarinsecurepath:events
	The number of non-default behaviors executed by the archive/tar
	package due to a non-default GODEBUG=tarinsecurepath=...
	setting.

/godebug/non-default-behavior/x509sha1:events
	The number of non-default behaviors executed by the crypto/x509
	package due to a non-default GODEBUG=x509sha1=... setting.

/godebug/non-default-behavior/x509usefallbackroots:events
	The number of non-default behaviors executed by the crypto/x509
	package due to a non-default GODEBUG=x509usefallbackroots=...
	setting.

/godebug/non-default-behavior/zipinsecurepath:events
	The number of non-default behaviors executed by the archive/zip
	package due to a non-default GODEBUG=zipinsecurepath=...
	setting.

/memory/classes/heap/free:bytes
	Memory that is completely free and eligible to be returned to
	the underlying system, but has not been. This metric is the
	runtime's estimate of free address space that is backed by
	physical memory.

/memory/classes/heap/objects:bytes
	Memory occupied by live objects and dead objects that have not
	yet been marked free by the garbage collector.

/memory/classes/heap/released:bytes
	Memory that is completely free and has been returned to the
	underlying system. This metric is the runtime's estimate of free
	address space that is still mapped into the process, but is not
	backed by physical memory.

/memory/classes/heap/stacks:bytes
	Memory allocated from the heap that is reserved for stack space,
	whether or not it is currently in-use.

/memory/classes/heap/unused:bytes
	Memory that is reserved for heap objects but is not currently
	used to hold heap objects.

/memory/classes/metadata/mcache/free:bytes
	Memory that is reserved for runtime mcache structures, but not
	in-use.

/memory/classes/metadata/mcache/inuse:bytes
	Memory that is occupied by runtime mcache structures that are
	currently being used.

/memory/classes/metadata/mspan/free:bytes
	Memory that is reserved for runtime mspan structures, but not
	in-use.

/memory/classes/metadata/mspan/inuse:bytes
	Memory that is occupied by runtime mspan structures that are
	currently being used.

/memory/classes/metadata/other:bytes
	Memory that is reserved for or used to hold runtime metadata.

/memory/classes/os-stacks:bytes
	Stack memory allocated by the underlying operating system.

/memory/classes/other:bytes
	Memory used by execution trace buffers, structures for debugging
	the runtime, finalizer and profiler specials, and more.

/memory/classes/profiling/buckets:bytes
	Memory that is used by the stack trace hash map used for
	profiling.

/memory/classes/total:bytes
	All memory mapped by the Go runtime into the current process
	as read-write. Note that this does not include memory mapped
	by code called via cgo or via the syscall package. Sum of all
	metrics in /memory/classes.

/sched/gomaxprocs:threads
	The current runtime.GOMAXPROCS setting, or the number of
	operating system threads that can execute user-level Go code
	simultaneously.

/sched/goroutines:goroutines
	Count of live goroutines.

/sched/latencies:seconds
	Distribution of the time goroutines have spent in the scheduler
	in a runnable state before actually running.

/sync/mutex/wait/total:seconds
	Approximate cumulative time goroutines have spent blocked
	on a sync.Mutex or sync.RWMutex. This metric is useful for
	identifying global changes in lock contention. Collect a mutex
	or block profile using the runtime/pprof package for more
	detailed contention data.

archive

tar

zip

arena

bufio

bufio

scan

buildin(内建)

bytes

buffer

bytes

reader

cmd

addr2line

api

asm

go tool asm [flags] file
Flags:

	-D name[=value]
		Predefine symbol name with an optional simple value.
		Can be repeated to define multiple symbols.
	-I dir1 -I dir2
		Search for #include files in dir1, dir2, etc,
		after consulting $GOROOT/pkg/$GOOS_$GOARCH.
	-S
		Print assembly and machine code.
	-V
		Print assembler version and exit.
	-debug
		Dump instructions as they are parsed.
	-dynlink
		Support references to Go symbols defined in other shared libraries.
	-gensymabis
		Write symbol ABI information to output file. Don't assemble.
	-o file
		Write output to file. The default is foo.o for /a/b/c/foo.s.
	-p pkgpath
		Set expected package import to pkgpath.
	-shared
		Generate code that can be linked into a shared library.
	-spectre list
		Enable spectre mitigations in list (all, ret).
	-trimpath prefix
		Remove prefix from recorded source file paths.
Flags:

	-D name[=value]
		Predefine symbol name with an optional simple value.
		Can be repeated to define multiple symbols.
	-I dir1 -I dir2
		Search for #include files in dir1, dir2, etc,
		after consulting $GOROOT/pkg/$GOOS_$GOARCH.
	-S
		打印汇编和机器码
	-V
		打印版本·
	-debug
		Dump instructions as they are parsed.
	-dynlink
		Support references to Go symbols defined in other shared libraries.
	-gensymabis
		Write symbol ABI information to output file. Don't assemble.
	-o file
		Write output to file. The default is foo.o for /a/b/c/foo.s.
	-p pkgpath
		Set expected package import to pkgpath.
	-shared
		Generate code that can be linked into a shared library.
	-spectre list
		Enable spectre mitigations in list (all, ret).
	-trimpath prefix
		Remove prefix from recorded source file paths.

buildid

cgo

compile

covdata

cover

dist

doc

fix

go

gofmt

objdump

nm

objdump

pack

pprof

trace

vet

compress

container

heap

list

ring

context

crypto

aes

boring

cipher

des

dsa

ecdh

ecdsa

ed25519

elliptic

hmac

md5

rand

rc4

rsa

sha1

sha256

sha512

subtle

tls

x509

database

sql

debug

pe(windows)

elf(liunx)

pe(windows)

macho (mac)

embed

encoding

encoding

encoding

encoding

encoding

encoding

encoding

encoding

encoding

encoding

encoding

encoding

errors

expvar

flag

fmt

go

ast

build

constant

doc

format

importer

internael

parser

pointer

scanner

token

types

hash

adler

crc32

crc64

fnv

html

image

index(后缀数组)

suffixarray

internal

io

log

maps

  • Keys 获取map的key

  • Values 获取map的values

  • Equal map进行比较

  • EqualFunc map通过func 进行比较

  • Clone map浅拷贝

  • Copy src 复制到 dst map

  • DeleteFunc 通过func 进行删除

math

mime

net

os

path

plugin

reflect

regexp

runtime

asan

cgo

coverage

debug

metric

msan

pprof

race

proc.go (golang调度)

slice.go (切片)

chan.go (通道 实现)

map.go (map 实现)

mem.go (内存管理抽象层)

mgc.go (垃圾回收)

mbarriers.go (写屏障)

slices

sort

strconv

strings

builder

clone

compare

reader

replace

search

strings

sync

atomic (原子)

map (原子)

once (只执行一次)

mutex (锁)

rwmutex (读写锁)

pool (池化)

waitgroup ()

syscall

testing

text

scanner

tabwriter

template(模版)

time

unicode

unsafe

misc

api

对各个版本api的变更列表

lib

当前只包含 time lib 提供对时区相关数据的支持

附录

更新日志

主要变化宝库

  • 语言变更
  • 工具
  • 运行时
  • 编译器
  • 连接器
  • 启动器
  • 核心类库
  • 类库变更

各个版本发布时间点

  • go1.20 (released 2023-02-01)

  • go1.19 (released 2022-08-02)

  • go1.18 (released 2022-03-15)

  • go1.17 (released 2021-08-16)

  • go1.16 (released 2021-02-16)

  • go1.15 (released 2020-08-11)

  • go1.14 (released 2020-02-25)

  • go1.13 (released 2019-09-03)

  • go1.12 (released 2019-02-25)

  • go1.11 (released 2018-08-24)

  • go1.10 (released 2018-02-16)

  • go1.9 (released 2017-08-24)

  • go1.8 (released 2017-02-16)

  • go1.7 (released 2016-08-15)

  • go1.6 (released 2016-02-17)

  • go1.5 (released 2015-08-19)

  • go1.4 (released 2014-12-10)

  • go1.3 (released 2014-06-18)

  • go1.2 (released 2013-12-01)

  • go1.1 (released 2013-05-13)

  • go1 (released 2012-03-28)

Changes to the language 语言变化

Go 1.20 includes four changes to the language.

Go 1.17 added conversions from slice to an array pointer. Go 1.20 extends this to allow conversions from a slice to an array: given a slice x, [4]byte(x) can now be written instead of ([4]byte)(x).

The unsafe package defines three new functions SliceData, String, and StringData. Along with Go 1.17's Slice, these functions now provide the complete ability to construct and deconstruct slice and string values, without depending on their exact representation.

The specification now defines that struct values are compared one field at a time, considering fields in the order they appear in the struct type definition, and stopping at the first mismatch. The specification could previously have been read as if all fields needed to be compared beyond the first mismatch. Similarly, the specification now defines that array values are compared one element at a time, in increasing index order. In both cases, the difference affects whether certain comparisons must panic. Existing programs are unchanged: the new spec wording describes what the implementations have always done.

Comparable types (such as ordinary interfaces) may now satisfy comparable constraints, even if the type arguments are not strictly comparable (comparison may panic at runtime). This makes it possible to instantiate a type parameter constrained by comparable (e.g., a type parameter for a user-defined generic map key) with a non-strictly comparable type argument such as an interface type, or a composite type containing an interface type.

Ports

Windows

Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016.

Darwin and iOS

Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later.

FreeBSD/RISC-V

Go 1.20 adds experimental support for FreeBSD on RISC-V (GOOS=freebsd, GOARCH=riscv64).

Tools

Go command

The directory $GOROOT/pkg no longer stores pre-compiled package archives for the standard library: go install no longer writes them, the go build no longer checks for them, and the Go distribution no longer ships them. Instead, packages in the standard library are built as needed and cached in the build cache, just like packages outside GOROOT. This change reduces the size of the Go distribution and also avoids C toolchain skew for packages that use cgo.

The implementation of go test -json has been improved to make it more robust. Programs that run go test -json do not need any updates. Programs that invoke go tool test2json directly should now run the test binary with -v=test2json (for example, go test -v=test2json or ./pkg.test -test.v=test2json) instead of plain -v.

A related change to go test -json is the addition of an event with Action set to start at the beginning of each test program's execution. When running multiple tests using the go command, these start events are guaranteed to be emitted in the same order as the packages named on the command line.

The go command now defines architecture feature build tags, such as amd64.v2, to allow selecting a package implementation file based on the presence or absence of a particular architecture feature. See go help buildconstraint for details.

The go subcommands now accept -C

to change directory to before performing the command, which may be useful for scripts that need to execute commands in multiple different modules.

The go build and go test commands no longer accept the -i flag, which has been deprecated since Go 1.16.

The go generate command now accepts -skip to skip //go:generate directives matching .

The go test command now accepts -skip to skip tests, subtests, or examples matching .

When the main module is located within GOPATH/src, go install no longer installs libraries for non-main packages to GOPATH/pkg, and go list no longer reports a Target field for such packages. (In module mode, compiled packages are stored in the build cache only, but a bug had caused the GOPATH install targets to unexpectedly remain in effect.)

The go build, go install, and other build-related commands now support a -pgo flag that enables profile-guided optimization, which is described in more detail in the Compiler section below. The -pgo flag specifies the file path of the profile. Specifying -pgo=auto causes the go command to search for a file named default.pgo in the main package's directory and use it if present. This mode currently requires a single main package to be specified on the command line, but we plan to lift this restriction in a future release. Specifying -pgo=off turns off profile-guided optimization.

The go build, go install, and other build-related commands now support a -cover flag that builds the specified target with code coverage instrumentation. This is described in more detail in the Cover section below.

go version The go version -m command now supports reading more types of Go binaries, most notably, Windows DLLs built with go build -buildmode=c-shared and Linux binaries without execute permission.

Cgo

The go command now disables cgo by default on systems without a C toolchain. More specifically, when the CGO_ENABLED environment variable is unset, the CC environment variable is unset, and the default C compiler (typically clang or gcc) is not found in the path, CGO_ENABLED defaults to 0. As always, you can override the default by setting CGO_ENABLED explicitly.

The most important effect of the default change is that when Go is installed on a system without a C compiler, it will now use pure Go builds for packages in the standard library that use cgo, instead of using pre-distributed package archives (which have been removed, as noted above) or attempting to use cgo and failing. This makes Go work better in some minimal container environments as well as on macOS, where pre-distributed package archives have not been used for cgo-based packages since Go 1.16.

The packages in the standard library that use cgo are net, os/user, and plugin. On macOS, the net and os/user packages have been rewritten not to use cgo: the same code is now used for cgo and non-cgo builds as well as cross-compiled builds. On Windows, the net and os/user packages have never used cgo. On other systems, builds with cgo disabled will use a pure Go version of these packages.

A consequence is that, on macOS, if Go code that uses the net package is built with -buildmode=c-archive, linking the resulting archive into a C program requires passing -lresolv when linking the C code.

On macOS, the race detector has been rewritten not to use cgo: race-detector-enabled programs can be built and run without Xcode. On Linux and other Unix systems, and on Windows, a host C toolchain is required to use the race detector.

Cover

Go 1.20 supports collecting code coverage profiles for programs (applications and integration tests), as opposed to just unit tests.

To collect coverage data for a program, build it with go build's -cover flag, then run the resulting binary with the environment variable GOCOVERDIR set to an output directory for coverage profiles. See the 'coverage for integration tests' landing page for more on how to get started. For details on the design and implementation, see the proposal.

Vet

Improved detection of loop variable capture by nested functions The vet tool now reports references to loop variables following a call to T.Parallel() within subtest function bodies. Such references may observe the value of the variable from a different iteration (typically causing test cases to be skipped) or an invalid state due to unsynchronized concurrent access.

The tool also detects reference mistakes in more places. Previously it would only consider the last statement of the loop body, but now it recursively inspects the last statements within if, switch, and select statements.

New diagnostic for incorrect time formats The vet tool now reports use of the time format 2006-02-01 (yyyy-dd-mm) with Time.Format and time.Parse. This format does not appear in common date standards, but is frequently used by mistake when attempting to use the ISO 8601 date format (yyyy-mm-dd).

Runtime

Some of the garbage collector's internal data structures were reorganized to be both more space and CPU efficient. This change reduces memory overheads and improves overall CPU performance by up to 2%.

The garbage collector behaves less erratically with respect to goroutine assists in some circumstances.

Go 1.20 adds a new runtime/coverage package containing APIs for writing coverage profile data at runtime from long-running and/or server programs that do not terminate via os.Exit().

Compiler

Go 1.20 adds preview support for profile-guided optimization (PGO). PGO enables the toolchain to perform application- and workload-specific optimizations based on run-time profile information. Currently, the compiler supports pprof CPU profiles, which can be collected through usual means, such as the runtime/pprof or net/http/pprof packages. To enable PGO, pass the path of a pprof profile file via the -pgo flag to go build, as mentioned above. Go 1.20 uses PGO to more aggressively inline functions at hot call sites. Benchmarks for a representative set of Go programs show enabling profile-guided inlining optimization improves performance about 3–4%. See the PGO user guide for detailed documentation. We plan to add more profile-guided optimizations in future releases. Note that profile-guided optimization is a preview, so please use it with appropriate caution.

The Go 1.20 compiler upgraded its front-end to use a new way of handling the compiler's internal data, which fixes several generic-types issues and enables type declarations within generic functions and methods.

The compiler now rejects anonymous interface cycles with a compiler error by default. These arise from tricky uses of embedded interfaces and have always had subtle correctness issues, yet we have no evidence that they're actually used in practice. Assuming no reports from users adversely affected by this change, we plan to update the language specification for Go 1.22 to formally disallow them so tools authors can stop supporting them too.

Go 1.18 and 1.19 saw regressions in build speed, largely due to the addition of support for generics and follow-on work. Go 1.20 improves build speeds by up to 10%, bringing it back in line with Go 1.17. Relative to Go 1.19, generated code performance is also generally slightly improved.

Linker

On Linux, the linker now selects the dynamic interpreter for glibc or musl at link time.

On Windows, the Go linker now supports modern LLVM-based C toolchains.

Go 1.20 uses go: and type: prefixes for compiler-generated symbols rather than go. and type.. This avoids confusion for user packages whose name starts with go.. The debug/gosym package understands this new naming convention for binaries built with Go 1.20 and newer.

Bootstrap

When building a Go release from source and GOROOT_BOOTSTRAP is not set, previous versions of Go looked for a Go 1.4 or later bootstrap toolchain in the directory $HOME/go1.4 (%HOMEDRIVE%%HOMEPATH%\go1.4 on Windows). Go 1.18 and Go 1.19 looked first for $HOME/go1.17 or $HOME/sdk/go1.17 before falling back to $HOME/go1.4, in anticipation of requiring Go 1.17 for use when bootstrapping Go 1.20. Go 1.20 does require a Go 1.17 release for bootstrapping, but we realized that we should adopt the latest point release of the bootstrap toolchain, so it requires Go 1.17.13. Go 1.20 looks for $HOME/go1.17.13 or $HOME/sdk/go1.17.13 before falling back to $HOME/go1.4 (to support systems that hard-coded the path $HOME/go1.4 but have installed a newer Go toolchain there). In the future, we plan to move the bootstrap toolchain forward approximately once a year, and in particular we expect that Go 1.22 will require the final point release of Go 1.20 for bootstrap.

Core library

New crypto/ecdh package

Go 1.20 adds a new crypto/ecdh package to provide explicit support for Elliptic Curve Diffie-Hellman key exchanges over NIST curves and Curve25519.

Programs should use crypto/ecdh instead of the lower-level functionality in crypto/elliptic for ECDH, and third-party modules for more advanced use cases.

Wrapping multiple errors

Go 1.20 expands support for error wrapping to permit an error to wrap multiple other errors.

An error e can wrap more than one error by providing an Unwrap method that returns a []error.

The errors.Is and errors.As functions have been updated to inspect multiply wrapped errors.

The fmt.Errorf function now supports multiple occurrences of the %w format verb, which will cause it to return an error that wraps all of those error operands.

The new function errors.Join returns an error wrapping a list of errors.

HTTP ResponseController

The new "net/http".ResponseController type provides access to extended per-request functionality not handled by the "net/http".ResponseWriter interface.

Previously, we have added new per-request functionality by defining optional interfaces which a ResponseWriter can implement, such as Flusher. These interfaces are not discoverable and clumsy to use.

The ResponseController type provides a clearer, more discoverable way to add per-handler controls. Two such controls also added in Go 1.20 are SetReadDeadline and SetWriteDeadline, which allow setting per-request read and write deadlines. For example:

func RequestHandler(w ResponseWriter, r *Request) { rc := http.NewResponseController(w) rc.SetWriteDeadline(time.Time{}) // disable Server.WriteTimeout when sending a large response io.Copy(w, bigData) }

New ReverseProxy Rewrite hook

The httputil.ReverseProxy forwarding proxy includes a new Rewrite hook function, superseding the previous Director hook.

The Rewrite hook accepts a ProxyRequest parameter, which includes both the inbound request received by the proxy and the outbound request that it will send. Unlike Director hooks, which only operate on the outbound request, this permits Rewrite hooks to avoid certain scenarios where a malicious inbound request may cause headers added by the hook to be removed before forwarding. See issue #50580.

The ProxyRequest.SetURL method routes the outbound request to a provided destination and supersedes the NewSingleHostReverseProxy function. Unlike NewSingleHostReverseProxy, SetURL also sets the Host header of the outbound request.

The ProxyRequest.SetXForwarded method sets the X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto headers of the outbound request. When using a Rewrite, these headers are not added by default.

An example of a Rewrite hook using these features is:

proxyHandler := &httputil.ReverseProxy{ Rewrite: func(r httputil.ProxyRequest) { r.SetURL(outboundURL) // Forward request to outboundURL. r.SetXForwarded() // Set X-Forwarded- headers. r.Out.Header.Set("X-Additional-Header", "header set by the proxy") }, } ReverseProxy no longer adds a User-Agent header to forwarded requests when the incoming request does not have one.

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind. There are also various performance improvements, not enumerated here.

archive/tar

When the GODEBUG=tarinsecurepath=0 environment variable is set, Reader.Next method will now return the error ErrInsecurePath for an entry with a file name that is an absolute path, refers to a location outside the current directory, contains invalid characters, or (on Windows) is a reserved name such as NUL. A future version of Go may disable insecure paths by default.

archive/zip

When the GODEBUG=zipinsecurepath=0 environment variable is set, NewReader will now return the error ErrInsecurePath when opening an archive which contains any file name that is an absolute path, refers to a location outside the current directory, contains invalid characters, or (on Windows) is a reserved names such as NUL. A future version of Go may disable insecure paths by default.

Reading from a directory file that contains file data will now return an error. The zip specification does not permit directory files to contain file data, so this change only affects reading from invalid archives.

bytes

The new CutPrefix and CutSuffix functions are like TrimPrefix and TrimSuffix but also report whether the string was trimmed.

The new Clone function allocates a copy of a byte slice.

context

The new WithCancelCause function provides a way to cancel a context with a given error. That error can be retrieved by calling the new Cause function.

crypto/ecdsa

When using supported curves, all operations are now implemented in constant time. This led to an increase in CPU time between 5% and 30%, mostly affecting P-384 and P-521.

The new PrivateKey.ECDH method converts an ecdsa.PrivateKey to an ecdh.PrivateKey.

crypto/ed25519

The PrivateKey.Sign method and the VerifyWithOptions function now support signing pre-hashed messages with Ed25519ph, indicated by an Options.HashFunc that returns crypto.SHA512. They also now support Ed25519ctx and Ed25519ph with context, indicated by setting the new Options.Context field.

crypto/rsa

The new field OAEPOptions.MGFHash allows configuring the MGF1 hash separately for OAEP decryption.

crypto/rsa now uses a new, safer, constant-time backend. This causes a CPU runtime increase for decryption operations between approximately 15% (RSA-2048 on amd64) and 45% (RSA-4096 on arm64), and more on 32-bit architectures. Encryption operations are approximately 20x slower than before (but still 5-10x faster than decryption). Performance is expected to improve in future releases. Programs must not modify or manually generate the fields of PrecomputedValues.

crypto/subtle

The new function XORBytes XORs two byte slices together.

crypto/tls

Parsed certificates are now shared across all clients actively using that certificate. The memory savings can be significant in programs that make many concurrent connections to a server or collection of servers sharing any part of their certificate chains.

For a handshake failure due to a certificate verification failure, the TLS client and server now return an error of the new type CertificateVerificationError, which includes the presented certificates.

crypto/x509

ParsePKCS8PrivateKey and MarshalPKCS8PrivateKey now support keys of type *crypto/ecdh.PrivateKey. ParsePKIXPublicKey and MarshalPKIXPublicKey now support keys of type *crypto/ecdh.PublicKey. Parsing NIST curve keys still returns values of type *ecdsa.PublicKey and *ecdsa.PrivateKey. Use their new ECDH methods to convert to the crypto/ecdh types.

The new SetFallbackRoots function allows a program to define a set of fallback root certificates in case an operating system verifier or standard platform root bundle is unavailable at runtime. It will most commonly be used with a new package, golang.org/x/crypto/x509roots/fallback, which will provide an up to date root bundle.

debug/elf

Attempts to read from a SHT_NOBITS section using Section.Data or the reader returned by Section.Open now return an error.

Additional R_LARCH_* constants are defined for use with LoongArch systems.

Additional R_PPC64_* constants are defined for use with PPC64 ELFv2 relocations.

The constant value for R_PPC64_SECTOFF_LO_DS is corrected, from 61 to 62.

debug/gosym

Due to a change of Go's symbol naming conventions, tools that process Go binaries should use Go 1.20's debug/gosym package to transparently handle both old and new binaries.

debug/pe

Additional IMAGE_FILE_MACHINE_RISCV* constants are defined for use with RISC-V systems.

encoding/binary

The ReadVarint and ReadUvarint functions will now return io.ErrUnexpectedEOF after reading a partial value, rather than io.EOF.

encoding/xml

The new Encoder.Close method can be used to check for unclosed elements when finished encoding.

The decoder now rejects element and attribute names with more than one colon, such as <a:b:c>, as well as namespaces that resolve to an empty string, such as xmlns:a="".

The decoder now rejects elements that use different namespace prefixes in the opening and closing tag, even if those prefixes both denote the same namespace.

errors

The new Join function returns an error wrapping a list of errors.

fmt

The Errorf function supports multiple occurrences of the %w format verb, returning an error that unwraps to the list of all arguments to %w.

The new FormatString function recovers the formatting directive corresponding to a State, which can be useful in Formatter. implementations.

go/ast

The new RangeStmt.Range field records the position of the range keyword in a range statement.

The new File.FileStart and File.FileEnd fields record the position of the start and end of the entire source file.

go/token

The new FileSet.RemoveFile method removes a file from a FileSet. Long-running programs can use this to release memory associated with files they no longer need.

go/types

The new Satisfies function reports whether a type satisfies a constraint. This change aligns with the new language semantics that distinguish satisfying a constraint from implementing an interface.

tml/template

Go 1.20.3 and later disallow actions in ECMAScript 6 template literals. This behavior can be reverted by the GODEBUG=jstmpllitinterp=1 setting.

io

The new OffsetWriter wraps an underlying WriterAt and provides Seek, Write, and WriteAt methods that adjust their effective file offset position by a fixed amount.

io/fs

The new error SkipAll terminates a WalkDir immediately but successfully.

math/big

The math/big package's wide scope and input-dependent timing make it ill-suited for implementing cryptography. The cryptography packages in the standard library no longer call non-trivial Int methods on attacker-controlled inputs. In the future, the determination of whether a bug in math/big is considered a security vulnerability will depend on its wider impact on the standard library.

math/rand

The math/rand package now automatically seeds the global random number generator (used by top-level functions like Float64 and Int) with a random value, and the top-level Seed function has been deprecated. Programs that need a reproducible sequence of random numbers should prefer to allocate their own random source, using rand.New(rand.NewSource(seed)).

Programs that need the earlier consistent global seeding behavior can set GODEBUG=randautoseed=0 in their environment.

The top-level Read function has been deprecated. In almost all cases, crypto/rand.Read is more appropriate.

mime

The ParseMediaType function now allows duplicate parameter names, so long as the values of the names are the same.

mime/multipart

Methods of the Reader type now wrap errors returned by the underlying io.Reader.

In Go 1.19.8 and later, this package sets limits the size of the MIME data it processes to protect against malicious inputs. Reader.NextPart and Reader.NextRawPart limit the number of headers in a part to 10000 and Reader.ReadForm limits the total number of headers in all FileHeaders to 10000. These limits may be adjusted with the GODEBUG=multipartmaxheaders setting. Reader.ReadForm further limits the number of parts in a form to 1000. This limit may be adjusted with the GODEBUG=multipartmaxparts setting.

net

The LookupCNAME function now consistently returns the contents of a CNAME record when one exists. Previously on Unix systems and when using the pure Go resolver, LookupCNAME would return an error if a CNAME record referred to a name that with no A, AAAA, or CNAME record. This change modifies LookupCNAME to match the previous behavior on Windows, allowing LookupCNAME to succeed whenever a CNAME exists.

Interface.Flags now includes the new flag FlagRunning, indicating an operationally active interface. An interface which is administratively configured but not active (for example, because the network cable is not connected) will have FlagUp set but not FlagRunning.

The new Dialer.ControlContext field contains a callback function similar to the existing Dialer.Control hook, that additionally accepts the dial context as a parameter. Control is ignored when ControlContext is not nil.

The Go DNS resolver recognizes the trust-ad resolver option. When options trust-ad is set in resolv.conf, the Go resolver will set the AD bit in DNS queries. The resolver does not make use of the AD bit in responses.

DNS resolution will detect changes to /etc/nsswitch.conf and reload the file when it changes. Checks are made at most once every five seconds, matching the previous handling of /etc/hosts and /etc/resolv.conf.

net/http

The ResponseWriter.WriteHeader function now supports sending 1xx status codes.

The new Server.DisableGeneralOptionsHandler configuration setting allows disabling the default OPTIONS * handler.

The new Transport.OnProxyConnectResponse hook is called when a Transport receives an HTTP response from a proxy for a CONNECT request.

The HTTP server now accepts HEAD requests containing a body, rather than rejecting them as invalid.

HTTP/2 stream errors returned by net/http functions may be converted to a golang.org/x/net/http2.StreamError using errors.As.

Leading and trailing spaces are trimmed from cookie names, rather than being rejected as invalid. For example, a cookie setting of "name =value" is now accepted as setting the cookie "name".

A Cookie with an empty Expires field is now considered valid. Cookie.Valid only checks Expires when it is set.

net/netip

The new IPv6LinkLocalAllRouters and IPv6Loopback functions are the net/netip equivalents of net.IPv6loopback and net.IPv6linklocalallrouters.

os

On Windows, the name NUL is no longer treated as a special case in Mkdir and Stat.

On Windows, File.Stat now uses the file handle to retrieve attributes when the file is a directory. Previously it would use the path passed to Open, which may no longer be the file represented by the file handle if the file has been moved or replaced. This change modifies Open to open directories without the FILE_SHARE_DELETE access, which match the behavior of regular files.

On Windows, File.Seek now supports seeking to the beginning of a directory.

os/exec

The new Cmd fields Cancel and WaitDelay specify the behavior of the Cmd when its associated Context is canceled or its process exits with I/O pipes still held open by a child process.

path/filepath

The new error SkipAll terminates a Walk immediately but successfully.

The new IsLocal function reports whether a path is lexically local to a directory. For example, if IsLocal(p) is true, then Open(p) will refer to a file that is lexically within the subtree rooted at the current directory.

reflect

The new Value.Comparable and Value.Equal methods can be used to compare two Values for equality. Comparable reports whether Equal is a valid operation for a given Value receiver.

The new Value.Grow method extends a slice to guarantee space for another n elements.

The new Value.SetZero method sets a value to be the zero value for its type.

Go 1.18 introduced Value.SetIterKey and Value.SetIterValue methods. These are optimizations: v.SetIterKey(it) is meant to be equivalent to v.Set(it.Key()). The implementations incorrectly omitted a check for use of unexported fields that was present in the unoptimized forms. Go 1.20 corrects these methods to include the unexported field check.

regexp

Go 1.19.2 and Go 1.18.7 included a security fix to the regular expression parser, making it reject very large expressions that would consume too much memory. Because Go patch releases do not introduce new API, the parser returned syntax.ErrInternalError in this case. Go 1.20 adds a more specific error, syntax.ErrLarge, which the parser now returns instead.

runtime/cgo

Go 1.20 adds new Incomplete marker type. Code generated by cgo will use cgo.Incomplete to mark an incomplete C type.

runtime/metrics

Go 1.20 adds new supported metrics, including the current GOMAXPROCS setting (/sched/gomaxprocs:threads), the number of cgo calls executed (/cgo/go-to-c-calls:calls), total mutex block time (/sync/mutex/wait/total:seconds), and various measures of time spent in garbage collection.

Time-based histogram metrics are now less precise, but take up much less memory.

runtime/pprof

Mutex profile samples are now pre-scaled, fixing an issue where old mutex profile samples would be scaled incorrectly if the sampling rate changed during execution.

Profiles collected on Windows now include memory mapping information that fixes symbolization issues for position-independent binaries.

runtime/trace

The garbage collector's background sweeper now yields less frequently, resulting in many fewer extraneous events in execution traces.

strings

The new CutPrefix and CutSuffix functions are like TrimPrefix and TrimSuffix but also report whether the string was trimmed.

sync

The new Map methods Swap, CompareAndSwap, and CompareAndDelete allow existing map entries to be updated atomically.

syscall

On FreeBSD, compatibility shims needed for FreeBSD 11 and earlier have been removed.

On Linux, additional CLONE_* constants are defined for use with the SysProcAttr.Cloneflags field.

On Linux, the new SysProcAttr.CgroupFD and SysProcAttr.UseCgroupFD fields provide a way to place a child process into a specific cgroup.

testing

The new method B.Elapsed reports the current elapsed time of the benchmark, which may be useful for calculating rates to report with ReportMetric.

Calling T.Run from a function passed to T.Cleanup was never well-defined, and will now panic.

time

The new time layout constants DateTime, DateOnly, and TimeOnly provide names for three of the most common layout strings used in a survey of public Go source code.

The new Time.Compare method compares two times.

Parse now ignores sub-nanosecond precision in its input, instead of reporting those digits as an error.

The Time.MarshalJSON method is now more strict about adherence to RFC 3339.

unicode/utf16

The new AppendRune function appends the UTF-16 encoding of a given rune to a uint16 slice, analogous to utf8.AppendRune.

Changes to the language 语言变化

There is only one small change to the language, a very small correction to the scope of type parameters in method declarations. Existing programs are unaffected.

Memory Model 修改后和其他语言保持一致

The Go memory model has been revised to align Go with the memory model used by C, C++, Java, JavaScript, Rust, and Swift. Go only provides sequentially consistent atomics, not any of the more relaxed forms found in other languages. Along with the memory model update, Go 1.19 introduces new types in the sync/atomic package that make it easier to use atomic values, such as atomic.Int64 and atomic.Pointer[T].

Ports 增加对龙芯的支持

LoongArch 64-bit Go 1.19 adds support for the Loongson 64-bit architecture LoongArch on Linux (GOOS=linux, GOARCH=loong64). The implemented ABI is LP64D. Minimum kernel version supported is 5.19.

Note that most existing commercial Linux distributions for LoongArch come with older kernels, with a historical incompatible system call ABI. Compiled binaries will not work on these systems, even if statically linked. Users on such unsupported systems are limited to the distribution-provided Go package.

RISC-V The riscv64 port now supports passing function arguments and result using registers. Benchmarking shows typical performance improvements of 10% or more on riscv64.

Tools 工具

Doc Comments

Go 1.19 adds support for links, lists, and clearer headings in doc comments. As part of this change, gofmt now reformats doc comments to make their rendered meaning clearer. See “Go Doc Comments” for syntax details and descriptions of common mistakes now highlighted by gofmt. As another part of this change, the new package go/doc/comment provides parsing and reformatting of doc comments as well as support for rendering them to HTML, Markdown, and text.

New unix build constraint

The build constraint unix is now recognized in //go:build lines. The constraint is satisfied if the target operating system, also known as GOOS, is a Unix or Unix-like system. For the 1.19 release it is satisfied if GOOS is one of aix, android, darwin, dragonfly, freebsd, hurd, illumos, ios, linux, netbsd, openbsd, or solaris. In future releases the unix constraint may match additional newly supported operating systems.

Go command

The -trimpath flag, if set, is now included in the build settings stamped into Go binaries by go build, and can be examined using go version -m or debug.ReadBuildInfo.

go generate now sets the GOROOT environment variable explicitly in the generator's environment, so that generators can locate the correct GOROOT even if built with -trimpath.

go test and go generate now place GOROOT/bin at the beginning of the PATH used for the subprocess, so tests and generators that execute the go command will resolve it to same GOROOT.

go env now quotes entries that contain spaces in the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS, CGO_LDFLAGS, and GOGCCFLAGS variables it reports.

go list -json now accepts a comma-separated list of JSON fields to populate. If a list is specified, the JSON output will include only those fields, and go list may avoid work to compute fields that are not included. In some cases, this may suppress errors that would otherwise be reported.

The go command now caches information necessary to load some modules, which should result in a speed-up of some go list invocations.

Vet

The vet checker “errorsas” now reports when errors.As is called with a second argument of type *error, a common mistake.

Runtime 运行时

增加软内存限制 The runtime now includes support for a soft memory limit. This memory limit includes the Go heap and all other memory managed by the runtime, and excludes external memory sources such as mappings of the binary itself, memory managed in other languages, and memory held by the operating system on behalf of the Go program. This limit may be managed via runtime/debug.SetMemoryLimit or the equivalent GOMEMLIMIT environment variable. The limit works in conjunction with runtime/debug.SetGCPercent / GOGC, and will be respected even if GOGC=off, allowing Go programs to always make maximal use of their memory limit, improving resource efficiency in some cases. See the GC guide for a detailed guide explaining the soft memory limit in more detail, as well as a variety of common use-cases and scenarios. Please note that small memory limits, on the order of tens of megabytes or less, are less likely to be respected due to external latency factors, such as OS scheduling. See issue 52433 for more details. Larger memory limits, on the order of hundreds of megabytes or more, are stable and production-ready.

In order to limit the effects of GC thrashing when the program's live heap size approaches the soft memory limit, the Go runtime also attempts to limit total GC CPU utilization to 50%, excluding idle time, choosing to use more memory over preventing application progress. In practice, we expect this limit to only play a role in exceptional cases, and the new runtime metric /gc/limiter/last-enabled:gc-cycle reports when this last occurred.

The runtime now schedules many fewer GC worker goroutines on idle operating system threads when the application is idle enough to force a periodic GC cycle.

The runtime will now allocate initial goroutine stacks based on the historic average stack usage of goroutines. This avoids some of the early stack growth and copying needed in the average case in exchange for at most 2x wasted space on below-average goroutines.

On Unix operating systems, Go programs that import package os now automatically increase the open file limit (RLIMIT_NOFILE) to the maximum allowed value; that is, they change the soft limit to match the hard limit. This corrects artificially low limits set on some systems for compatibility with very old C programs using the select system call. Go programs are not helped by that limit, and instead even simple programs like gofmt often ran out of file descriptors on such systems when processing many files in parallel. One impact of this change is that Go programs that in turn execute very old C programs in child processes may run those programs with too high a limit. This can be corrected by setting the hard limit before invoking the Go program.

Unrecoverable fatal errors (such as concurrent map writes, or unlock of unlocked mutexes) now print a simpler traceback excluding runtime metadata (equivalent to a fatal panic) unless GOTRACEBACK=system or crash. Runtime-internal fatal error tracebacks always include full metadata regardless of the value of GOTRACEBACK

Support for debugger-injected function calls has been added on ARM64, enabling users to call functions from their binary in an interactive debugging session when using a debugger that is updated to make use of this functionality.

The address sanitizer support added in Go 1.18 now handles function arguments and global variables more precisely.

Compiler 编译器

The compiler now uses a jump table to implement large integer and string switch statements. Performance improvements for the switch statement vary but can be on the order of 20% faster. (GOARCH=amd64 and GOARCH=arm64 only)

The Go compiler now requires the -p=importpath flag to build a linkable object file. This is already supplied by the go command and by Bazel. Any other build systems that invoke the Go compiler directly will need to make sure they pass this flag as well.

The Go compiler no longer accepts the -importmap flag. Build systems that invoke the Go compiler directly must use the -importcfg flag instead.

Assembler 汇编器

Like the compiler, the assembler now requires the -p=importpath flag to build a linkable object file. This is already supplied by the go command. Any other build systems that invoke the Go assembler directly will need to make sure they pass this flag as well.

Linker 连接器

On ELF platforms, the linker now emits compressed DWARF sections in the standard gABI format (SHF_COMPRESSED), instead of the legacy .zdebug format.

Core library 核心库

New atomic types

The sync/atomic package defines new atomic types Bool, Int32, Int64, Uint32, Uint64, Uintptr, and Pointer. These types hide the underlying values so that all accesses are forced to use the atomic APIs. Pointer also avoids the need to convert to unsafe.Pointer at call sites. Int64 and Uint64 are automatically aligned to 64-bit boundaries in structs and allocated data, even on 32-bit systems.

PATH lookups

Command and LookPath no longer allow results from a PATH search to be found relative to the current directory. This removes a common source of security problems but may also break existing programs that depend on using, say, exec.Command("prog") to run a binary named prog (or, on Windows, prog.exe) in the current directory. See the os/exec package documentation for information about how best to update such programs.

On Windows, Command and LookPath now respect the NoDefaultCurrentDirectoryInExePath environment variable, making it possible to disable the default implicit search of “.” in PATH lookups on Windows systems.

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind. There are also various performance improvements, not enumerated here.

archive/zip

Reader now ignores non-ZIP data at the start of a ZIP file, matching most other implementations. This is necessary to read some Java JAR files, among other uses.

crypto/elliptic

Operating on invalid curve points (those for which the IsOnCurve method returns false, and which are never returned by Unmarshal or by a Curve method operating on a valid point) has always been undefined behavior and can lead to key recovery attacks. If an invalid point is supplied to Marshal, MarshalCompressed, Add, Double, or ScalarMult, they will now panic.

ScalarBaseMult operations on the P224, P384, and P521 curves are now up to three times faster, leading to similar speedups in some ECDSA operations. The generic (not platform optimized) P256 implementation was replaced with one derived from a formally verified model; this might lead to significant slowdowns on 32-bit platforms.

crypto/rand

Read no longer buffers random data obtained from the operating system between calls. Applications that perform many small reads at high frequency might choose to wrap Reader in a bufio.Reader for performance reasons, taking care to use io.ReadFull to ensure no partial reads occur.

On Plan 9, Read has been reimplemented, replacing the ANSI X9.31 algorithm with a fast key erasure generator.

The Prime implementation was changed to use only rejection sampling, which removes a bias when generating small primes in non-cryptographic contexts, removes one possible minor timing leak, and better aligns the behavior with BoringSSL, all while simplifying the implementation. The change does produce different outputs for a given random source stream compared to the previous implementation, which can break tests written expecting specific results from specific deterministic random sources. To help prevent such problems in the future, the implementation is now intentionally non-deterministic with respect to the input stream.

crypto/tls

The GODEBUG option tls10default=1 has been removed. It is still possible to enable TLS 1.0 client-side by setting Config.MinVersion.

The TLS server and client now reject duplicate extensions in TLS handshakes, as required by RFC 5246, Section 7.4.1.4 and RFC 8446, Section 4.2.

crypto/x509

CreateCertificate no longer supports creating certificates with SignatureAlgorithm set to MD5WithRSA.

CreateCertificate no longer accepts negative serial numbers.

CreateCertificate will not emit an empty SEQUENCE anymore when the produced certificate has no extensions.

Removal of the GODEBUG optionx509sha1=1, originally planned for Go 1.19, has been rescheduled to a future release. Applications using it should work on migrating. Practical attacks against SHA-1 have been demonstrated since 2017 and publicly trusted Certificate Authorities have not issued SHA-1 certificates since 2015.

ParseCertificate and ParseCertificateRequest now reject certificates and CSRs which contain duplicate extensions.

The new CertPool.Clone and CertPool.Equal methods allow cloning a CertPool and checking the equivalence of two CertPools respectively.

The new function ParseRevocationList provides a faster, safer to use CRL parser which returns a RevocationList. Parsing a CRL also populates the new RevocationList fields RawIssuer, Signature, AuthorityKeyId, and Extensions, which are ignored by CreateRevocationList.

The new method RevocationList.CheckSignatureFrom checks that the signature on a CRL is a valid signature from a Certificate.

The ParseCRL and ParseDERCRL functions are now deprecated in favor of ParseRevocationList. The Certificate.CheckCRLSignature method is deprecated in favor of RevocationList.CheckSignatureFrom.

The path builder of Certificate.Verify was overhauled and should now produce better chains and/or be more efficient in complicated scenarios. Name constraints are now also enforced on non-leaf certificates.

crypto/x509/pkix

The types CertificateList and TBSCertificateList have been deprecated. The new crypto/x509 CRL functionality should be used instead.

debug/elf

The new EM_LOONGARCH and R_LARCH_* constants support the loong64 port.

debug/pe

The new File.COFFSymbolReadSectionDefAux method, which returns a COFFSymbolAuxFormat5, provides access to COMDAT information in PE file sections. These are supported by new IMAGE_COMDAT_* and IMAGE_SCN_* constants.

encoding/binary

The new interface AppendByteOrder provides efficient methods for appending a uint16, uint32, or uint64 to a byte slice. BigEndian and LittleEndian now implement this interface.

Similarly, the new functions AppendUvarint and AppendVarint are efficient appending versions of PutUvarint and PutVarint.

encoding/csv

The new method Reader.InputOffset reports the reader's current input position as a byte offset, analogous to encoding/json's Decoder.InputOffset.

encoding/xml

The new method Decoder.InputPos reports the reader's current input position as a line and column, analogous to encoding/csv's Decoder.FieldPos.

flag

The new function TextVar defines a flag with a value implementing encoding.TextUnmarshaler, allowing command-line flag variables to have types such as big.Int, netip.Addr, and time.Time.

fmt

The new functions Append, Appendf, and Appendln append formatted data to byte slices.

go/parser

The parser now recognizes ~x as a unary expression with operator token.TILDE, allowing better error recovery when a type constraint such as ~int is used in an incorrect context.

go/types

The new methods Func.Origin and Var.Origin return the corresponding Object of the generic type for synthetic Func and Var objects created during type instantiation.

It is no longer possible to produce an infinite number of distinct-but-identical Named type instantiations via recursive calls to Named.Underlying or Named.Method.

hash/maphash

The new functions Bytes and String provide an efficient way hash a single byte slice or string. They are equivalent to using the more general Hash with a single write, but they avoid setup overhead for small inputs.

html/template

The type FuncMap is now an alias for text/template's FuncMap instead of its own named type. This allows writing code that operates on a FuncMap from either setting.

Go 1.19.8 and later disallow actions in ECMAScript 6 template literals. This behavior can be reverted by the GODEBUG=jstmpllitinterp=1 setting.

image/draw

Draw with the Src operator preserves non-premultiplied-alpha colors when destination and source images are both image.NRGBA or both image.NRGBA64. This reverts a behavior change accidentally introduced by a Go 1.18 library optimization; the code now matches the behavior in Go 1.17 and earlier.

io

NopCloser's result now implements WriterTo whenever its input does.

MultiReader's result now implements WriterTo unconditionally. If any underlying reader does not implement WriterTo, it is simulated appropriately.

mime

On Windows only, the mime package now ignores a registry entry recording that the extension .js should have MIME type text/plain. This is a common unintentional misconfiguration on Windows systems. The effect is that .js will have the default MIME type text/javascript; charset=utf-8. Applications that expect text/plain on Windows must now explicitly call AddExtensionType.

mime/multipart

In Go 1.19.8 and later, this package sets limits the size of the MIME data it processes to protect against malicious inputs. Reader.NextPart and Reader.NextRawPart limit the number of headers in a part to 10000 and Reader.ReadForm limits the total number of headers in all FileHeaders to 10000. These limits may be adjusted with the GODEBUG=multipartmaxheaders setting. Reader.ReadForm further limits the number of parts in a form to 1000. This limit may be adjusted with the GODEBUG=multipartmaxparts setting.

net

The pure Go resolver will now use EDNS(0) to include a suggested maximum reply packet length, permitting reply packets to contain up to 1232 bytes (the previous maximum was 512). In the unlikely event that this causes problems with a local DNS resolver, setting the environment variable GODEBUG=netdns=cgo to use the cgo-based resolver should work. Please report any such problems on the issue tracker.

When a net package function or method returns an "I/O timeout" error, the error will now satisfy errors.Is(err, context.DeadlineExceeded). When a net package function returns an "operation was canceled" error, the error will now satisfy errors.Is(err, context.Canceled). These changes are intended to make it easier for code to test for cases in which a context cancellation or timeout causes a net package function or method to return an error, while preserving backward compatibility for error messages.

Resolver.PreferGo is now implemented on Windows and Plan 9. It previously only worked on Unix platforms. Combined with Dialer.Resolver and Resolver.Dial, it's now possible to write portable programs and be in control of all DNS name lookups when dialing.

The net package now has initial support for the netgo build tag on Windows. When used, the package uses the Go DNS client (as used by Resolver.PreferGo) instead of asking Windows for DNS results. The upstream DNS server it discovers from Windows may not yet be correct with complex system network configurations, however.

net/http

ResponseWriter.WriteHeader now supports sending user-defined 1xx informational headers.

The io.ReadCloser returned by MaxBytesReader will now return the defined error type MaxBytesError when its read limit is exceeded.

The HTTP client will handle a 3xx response without a Location header by returning it to the caller, rather than treating it as an error.

net/url

The new JoinPath function and URL.JoinPath method create a new URL by joining a list of path elements.

The URL type now distinguishes between URLs with no authority and URLs with an empty authority. For example, http:///path has an empty authority (host), while http:/path has none.

The new URL field OmitHost is set to true when a URL has an empty authority.

os/exec

A Cmd with a non-empty Dir field and nil Env now implicitly sets the PWD environment variable for the subprocess to match Dir.

The new method Cmd.Environ reports the environment that would be used to run the command, including the implicitly set PWD variable.

reflect

The method Value.Bytes now accepts addressable arrays in addition to slices.

The methods Value.Len and Value.Cap now successfully operate on a pointer to an array and return the length of that array, to match what the builtin len and cap functions do.

regexp/syntax

Go 1.18 release candidate 1, Go 1.17.8, and Go 1.16.15 included a security fix to the regular expression parser, making it reject very deeply nested expressions. Because Go patch releases do not introduce new API, the parser returned syntax.ErrInternalError in this case. Go 1.19 adds a more specific error, syntax.ErrNestingDepth, which the parser now returns instead.

runtime

The GOROOT function now returns the empty string (instead of "go") when the binary was built with the -trimpath flag set and the GOROOT variable is not set in the process environment.

runtime/metrics

The new /sched/gomaxprocs:threads metric reports the current runtime.GOMAXPROCS value.

The new /cgo/go-to-c-calls:calls metric reports the total number of calls made from Go to C. This metric is identical to the runtime.NumCgoCall function.

The new /gc/limiter/last-enabled:gc-cycle metric reports the last GC cycle when the GC CPU limiter was enabled. See the runtime notes for details about the GC CPU limiter.

runtime/pprof

Stop-the-world pause times have been significantly reduced when collecting goroutine profiles, reducing the overall latency impact to the application.

MaxRSS is now reported in heap profiles for all Unix operating systems (it was previously only reported for GOOS=android, darwin, ios, and linux).

runtime/race race

The race detector has been upgraded to use thread sanitizer version v3 on all supported platforms except windows/amd64 and openbsd/amd64, which remain on v2. Compared to v2, it is now typically 1.5x to 2x faster, uses half as much memory, and it supports an unlimited number of goroutines. On Linux, the race detector now requires at least glibc version 2.17 and GNU binutils 2.26.

The race detector is now supported on GOARCH=s390x.

Race detector support for openbsd/amd64 has been removed from thread sanitizer upstream, so it is unlikely to ever be updated from v2.

runtime/trace

When tracing and the CPU profiler are enabled simultaneously, the execution trace includes CPU profile samples as instantaneous events.

sort

The sorting algorithm has been rewritten to use pattern-defeating quicksort, which is faster for several common scenarios.

The new function Find is like Search but often easier to use: it returns an additional boolean reporting whether an equal value was found.

strconv

Quote and related functions now quote the rune U+007F as \x7f, not \u007f, for consistency with other ASCII values.

syscall

On PowerPC (GOARCH=ppc64, ppc64le), Syscall, Syscall6, RawSyscall, and RawSyscall6 now always return 0 for return value r2 instead of an undefined value.

On AIX and Solaris, Getrusage is now defined.

time

The new method Duration.Abs provides a convenient and safe way to take the absolute value of a duration, converting −2⁶³ to 2⁶³−1. (This boundary case can happen as the result of subtracting a recent time from the zero time.)

The new method Time.ZoneBounds returns the start and end times of the time zone in effect at a given time. It can be used in a loop to enumerate all the known time zone transitions at a given location.

Changes to the language 语言变化

Generics 泛型

Go 1.18 includes an implementation of generic features as described by the Type Parameters Proposal. This includes major - but fully backward-compatible - changes to the language.

These new language changes required a large amount of new code that has not had significant testing in production settings. That will only happen as more people write and use generic code. We believe that this feature is well implemented and high quality. However, unlike most aspects of Go, we can't back up that belief with real world experience. Therefore, while we encourage the use of generics where it makes sense, please use appropriate caution when deploying generic code in production.

While we believe that the new language features are well designed and clearly specified, it is possible that we have made mistakes. We want to stress that the Go 1 compatibility guarantee says "If it becomes necessary to address an inconsistency or incompleteness in the specification, resolving the issue could affect the meaning or legality of existing programs. We reserve the right to address such issues, including updating the implementations." It also says "If a compiler or library has a bug that violates the specification, a program that depends on the buggy behavior may break if the bug is fixed. We reserve the right to fix such bugs." In other words, it is possible that there will be code using generics that will work with the 1.18 release but break in later releases. We do not plan or expect to make any such change. However, breaking 1.18 programs in future releases may become necessary for reasons that we cannot today foresee. We will minimize any such breakage as much as possible, but we can't guarantee that the breakage will be zero.

The following is a list of the most visible changes. For a more comprehensive overview, see the proposal. For details see the language spec.

The syntax for function and type declarations now accepts type parameters. Parameterized functions and types can be instantiated by following them with a list of type arguments in square brackets. The new token ~ has been added to the set of operators and punctuation. The syntax for Interface types now permits the embedding of arbitrary types (not just type names of interfaces) as well as union and ~T type elements. Such interfaces may only be used as type constraints. An interface now defines a set of types as well as a set of methods. The new predeclared identifier any is an alias for the empty interface. It may be used instead of interface{}. The new predeclared identifier comparable is an interface that denotes the set of all types which can be compared using == or !=. It may only be used as (or embedded in) a type constraint. There are three experimental packages using generics that may be useful. These packages are in x/exp repository; their API is not covered by the Go 1 guarantee and may change as we gain more experience with generics.

golang.org/x/exp/constraints Constraints that are useful for generic code, such as constraints.Ordered.

golang.org/x/exp/slices A collection of generic functions that operate on slices of any element type.

golang.org/x/exp/maps A collection of generic functions that operate on maps of any key or element type.

The current generics implementation has the following known limitations:

The Go compiler cannot handle type declarations inside generic functions or methods. We hope to provide support for this feature in a future release. The Go compiler does not accept arguments of type parameter type with the predeclared functions real, imag, and complex. We hope to remove this restriction in a future release. The Go compiler only supports calling a method m on a value x of type parameter type P if m is explicitly declared by P's constraint interface. Similarly, method values x.m and method expressions P.m also are only supported if m is explicitly declared by P, even though m might be in the method set of P by virtue of the fact that all types in P implement m. We hope to remove this restriction in a future release. The Go compiler does not support accessing a struct field x.f where x is of type parameter type even if all types in the type parameter's type set have a field f. We may remove this restriction in a future release. Embedding a type parameter, or a pointer to a type parameter, as an unnamed field in a struct type is not permitted. Similarly, embedding a type parameter in an interface type is not permitted. Whether these will ever be permitted is unclear at present. A union element with more than one term may not contain an interface type with a non-empty method set. Whether this will ever be permitted is unclear at present. Generics also represent a large change for the Go ecosystem. While we have updated several core tools with generics support, there is much more to do. It will take time for remaining tools, documentation, and libraries to catch up with these language changes.

bug修复

The Go 1.18 compiler now correctly reports declared but not used errors for variables that are set inside a function literal but are never used. Before Go 1.18, the compiler did not report an error in such cases. This fixes long-outstanding compiler issue #8560. As a result of this change, (possibly incorrect) programs may not compile anymore. The necessary fix is straightforward: fix the program if it was in fact incorrect, or use the offending variable, for instance by assigning it to the blank identifier _. Since go vet always pointed out this error, the number of affected programs is likely very small.

The Go 1.18 compiler now reports an overflow when passing a rune constant expression such as '1' << 32 as an argument to the predeclared functions print and println, consistent with the behavior of user-defined functions. Before Go 1.18, the compiler did not report an error in such cases but silently accepted such constant arguments if they fit into an int64. As a result of this change, (possibly incorrect) programs may not compile anymore. The necessary fix is straightforward: fix the program if it was in fact incorrect, or explicitly convert the offending argument to the correct type. Since go vet always pointed out this error, the number of affected programs is likely very small.

Ports

AMD64

Go 1.18 introduces the new GOAMD64 environment variable, which selects at compile time a minimum target version of the AMD64 architecture. Allowed values are v1, v2, v3, or v4. Each higher level requires, and takes advantage of, additional processor features. A detailed description can be found here.

The GOAMD64 environment variable defaults to v1.

RISC-V

The 64-bit RISC-V architecture on Linux (the linux/riscv64 port) now supports the c-archive and c-shared build modes.

Linux

Go 1.18 requires Linux kernel version 2.6.32 or later.

Windows

The windows/arm and windows/arm64 ports now support non-cooperative preemption, bringing that capability to all four Windows ports, which should hopefully address subtle bugs encountered when calling into Win32 functions that block for extended periods of time.

iOS

On iOS (the ios/arm64 port) and iOS simulator running on AMD64-based macOS (the ios/amd64 port), Go 1.18 now requires iOS 12 or later; support for previous versions has been discontinued.

FreeBSD

Go 1.18 is the last release that is supported on FreeBSD 11.x, which has already reached end-of-life. Go 1.19 will require FreeBSD 12.2+ or FreeBSD 13.0+. FreeBSD 13.0+ will require a kernel with the COMPAT_FREEBSD12 option set (this is the default).

Tools 工具

  • Fuzzing Go 1.18 includes an implementation of fuzzing as described by the fuzzing proposal.

See the fuzzing landing page to get started.

Please be aware that fuzzing can consume a lot of memory and may impact your machine’s performance while it runs. Also be aware that the fuzzing engine writes values that expand test coverage to a fuzz cache directory within $GOCACHE/fuzz while it runs. There is currently no limit to the number of files or total bytes that may be written to the fuzz cache, so it may occupy a large amount of storage (possibly several GBs).

  • Go command go get go get no longer builds or installs packages in module-aware mode. go get is now dedicated to adjusting dependencies in go.mod. Effectively, the -d flag is always enabled. To install the latest version of an executable outside the context of the current module, use go install example.com/cmd@latest. Any version query may be used instead of latest. This form of go install was added in Go 1.16, so projects supporting older versions may need to provide install instructions for both go install and go get. go get now reports an error when used outside a module, since there is no go.mod file to update. In GOPATH mode (with GO111MODULE=off), go get still builds and installs packages, as before.

Automatic go.mod and go.sum updates The go mod graph, go mod vendor, go mod verify, and go mod why subcommands no longer automatically update the go.mod and go.sum files. (Those files can be updated explicitly using go get, go mod tidy, or go mod download.)

go version The go command now embeds version control information in binaries. It includes the currently checked-out revision, commit time, and a flag indicating whether edited or untracked files are present. Version control information is embedded if the go command is invoked in a directory within a Git, Mercurial, Fossil, or Bazaar repository, and the main package and its containing main module are in the same repository. This information may be omitted using the flag -buildvcs=false.

Additionally, the go command embeds information about the build, including build and tool tags (set with -tags), compiler, assembler, and linker flags (like -gcflags), whether cgo was enabled, and if it was, the values of the cgo environment variables (like CGO_CFLAGS). Both VCS and build information may be read together with module information using go version -m file or runtime/debug.ReadBuildInfo (for the currently running binary) or the new debug/buildinfo package.

The underlying data format of the embedded build information can change with new go releases, so an older version of go may not handle the build information produced with a newer version of go. To read the version information from a binary built with go 1.18, use the go version command and the debug/buildinfo package from go 1.18+.

go mod download If the main module's go.mod file specifies go 1.17 or higher, go mod download without arguments now downloads source code for only the modules explicitly required in the main module's go.mod file. (In a go 1.17 or higher module, that set already includes all dependencies needed to build the packages and tests in the main module.) To also download source code for transitive dependencies, use go mod download all.

go mod vendor The go mod vendor subcommand now supports a -o flag to set the output directory. (Other go commands still read from the vendor directory at the module root when loading packages with -mod=vendor, so the main use for this flag is for third-party tools that need to collect package source code.)

go mod tidy The go mod tidy command now retains additional checksums in the go.sum file for modules whose source code is needed to verify that each imported package is provided by only one module in the build list. Because this condition is rare and failure to apply it results in a build error, this change is not conditioned on the go version in the main module's go.mod file.

go work The go command now supports a "Workspace" mode. If a go.work file is found in the working directory or a parent directory, or one is specified using the GOWORK environment variable, it will put the go command into workspace mode. In workspace mode, the go.work file will be used to determine the set of main modules used as the roots for module resolution, instead of using the normally-found go.mod file to specify the single main module. For more information see the go work documentation.

go build -asan The go build command and related commands now support an -asan flag that enables interoperation with C (or C++) code compiled with the address sanitizer (C compiler option -fsanitize=address).

go test The go command now supports additional command line options for the new fuzzing support described above:

go test supports -fuzz, -fuzztime, and -fuzzminimizetime options. For documentation on these see go help testflag. go clean supports a -fuzzcache option. For documentation see go help clean. //go:build lines Go 1.17 introduced //go:build lines as a more readable way to write build constraints, instead of // +build lines. As of Go 1.17, gofmt adds //go:build lines to match existing +build lines and keeps them in sync, while go vet diagnoses when they are out of sync.

Since the release of Go 1.18 marks the end of support for Go 1.16, all supported versions of Go now understand //go:build lines. In Go 1.18, go fix now removes the now-obsolete // +build lines in modules declaring go 1.18 or later in their go.mod files.

For more information, see https://go.dev/design/draft-gobuild.

Gofmt gofmt now reads and formats input files concurrently, with a memory limit proportional to GOMAXPROCS. On a machine with multiple CPUs, gofmt should now be significantly faster.

Vet Updates for Generics The vet tool is updated to support generic code. In most cases, it reports an error in generic code whenever it would report an error in the equivalent non-generic code after substituting for type parameters with a type from their type set. For example, vet reports a format error in

func Print[T ~int|~string](t T) { fmt.Printf("%d", t) } because it would report a format error in the non-generic equivalent of Print[string]: func PrintString(x string) { fmt.Printf("%d", x) } Precision improvements for existing checkers The cmd/vet checkers copylock, printf, sortslice, testinggoroutine, and tests have all had moderate precision improvements to handle additional code patterns. This may lead to newly reported errors in existing packages. For example, the printf checker now tracks formatting strings created by concatenating string constants. So vet will report an error in:

// fmt.Printf formatting directive %d is being passed to Println. fmt.Println("%d"+ ≡ x (mod 2)+"\n", x%2)

Runtime 运行时

The garbage collector now includes non-heap sources of garbage collector work (e.g., stack scanning) when determining how frequently to run. As a result, garbage collector overhead is more predictable when these sources are significant. For most applications these changes will be negligible; however, some Go applications may now use less memory and spend more time on garbage collection, or vice versa, than before. The intended workaround is to tweak GOGC where necessary.

The runtime now returns memory to the operating system more efficiently and has been tuned to work more aggressively as a result.

Go 1.17 generally improved the formatting of arguments in stack traces, but could print inaccurate values for arguments passed in registers. This is improved in Go 1.18 by printing a question mark (?) after each value that may be inaccurate.

The built-in function append now uses a slightly different formula when deciding how much to grow a slice when it must allocate a new underlying array. The new formula is less prone to sudden transitions in allocation behavior.

Compiler 编译器

Go 1.17 implemented a new way of passing function arguments and results using registers instead of the stack on 64-bit x86 architecture on selected operating systems. Go 1.18 expands the supported platforms to include 64-bit ARM (GOARCH=arm64), big- and little-endian 64-bit PowerPC (GOARCH=ppc64, ppc64le), as well as 64-bit x86 architecture (GOARCH=amd64) on all operating systems. On 64-bit ARM and 64-bit PowerPC systems, benchmarking shows typical performance improvements of 10% or more.

As mentioned in the Go 1.17 release notes, this change does not affect the functionality of any safe Go code and is designed to have no impact on most assembly code. See the Go 1.17 release notes for more details.

The compiler now can inline functions that contain range loops or labeled for loops.

The new -asan compiler option supports the new go command -asan option.

Because the compiler's type checker was replaced in its entirety to support generics, some error messages now may use different wording than before. In some cases, pre-Go 1.18 error messages provided more detail or were phrased in a more helpful way. We intend to address these cases in Go 1.19.

Because of changes in the compiler related to supporting generics, the Go 1.18 compile speed can be roughly 15% slower than the Go 1.17 compile speed. The execution time of the compiled code is not affected. We intend to improve the speed of the compiler in future releases.

Linker 连接器

The linker emits far fewer relocations. As a result, most codebases will link faster, require less memory to link, and generate smaller binaries. Tools that process Go binaries should use Go 1.18's debug/gosym package to transparently handle both old and new binaries.

The new -asan linker option supports the new go command -asan option.

Bootstrap 启动器

When building a Go release from source and GOROOT_BOOTSTRAP is not set, previous versions of Go looked for a Go 1.4 or later bootstrap toolchain in the directory $HOME/go1.4 (%HOMEDRIVE%%HOMEPATH%\go1.4 on Windows). Go now looks first for $HOME/go1.17 or $HOME/sdk/go1.17 before falling back to $HOME/go1.4. We intend for Go 1.19 to require Go 1.17 or later for bootstrap, and this change should make the transition smoother. For more details, see go.dev/issue/44505.

Core library 核心类库

New debug/buildinfo package

The new debug/buildinfo package provides access to module versions, version control information, and build flags embedded in executable files built by the go command. The same information is also available via runtime/debug.ReadBuildInfo for the currently running binary and via go version -m on the command line.

New net/netip package

The new net/netip package defines a new IP address type, Addr. Compared to the existing net.IP type, the netip.Addr type takes less memory, is immutable, and is comparable so it supports == and can be used as a map key.

In addition to Addr, the package defines AddrPort, representing an IP and port, and Prefix, representing a network CIDR prefix.

The package also defines several functions to create and examine these new types: AddrFrom4, AddrFrom16, AddrFromSlice, AddrPortFrom, IPv4Unspecified, IPv6LinkLocalAllNodes, IPv6Unspecified, MustParseAddr, MustParseAddrPort, MustParsePrefix, ParseAddr, ParseAddrPort, ParsePrefix, PrefixFrom.

The net package includes new methods that parallel existing methods, but return netip.AddrPort instead of the heavier-weight net.IP or *net.UDPAddr types: Resolver.LookupNetIP, UDPConn.ReadFromUDPAddrPort, UDPConn.ReadMsgUDPAddrPort, UDPConn.WriteToUDPAddrPort, UDPConn.WriteMsgUDPAddrPort. The new UDPConn methods support allocation-free I/O.

The net package also now includes functions and methods to convert between the existing TCPAddr/UDPAddr types and netip.AddrPort: TCPAddrFromAddrPort, UDPAddrFromAddrPort, TCPAddr.AddrPort, UDPAddr.AddrPort.

TLS 1.0 and 1.1 disabled by default client-side

If Config.MinVersion is not set, it now defaults to TLS 1.2 for client connections. Any safely up-to-date server is expected to support TLS 1.2, and browsers have required it since 2020. TLS 1.0 and 1.1 are still supported by setting Config.MinVersion to VersionTLS10. The server-side default is unchanged at TLS 1.0.

The default can be temporarily reverted to TLS 1.0 by setting the GODEBUG=tls10default=1 environment variable. This option will be removed in Go 1.19.

Rejecting SHA-1 certificates

crypto/x509 will now reject certificates signed with the SHA-1 hash function. This doesn't apply to self-signed root certificates. Practical attacks against SHA-1 have been demonstrated since 2017 and publicly trusted Certificate Authorities have not issued SHA-1 certificates since 2015.

This can be temporarily reverted by setting the GODEBUG=x509sha1=1 environment variable. This option will be removed in a future release.

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind.

bufio

The new Writer.AvailableBuffer method returns an empty buffer with a possibly non-empty capacity for use with append-like APIs. After appending, the buffer can be provided to a succeeding Write call and possibly avoid any copying.

The Reader.Reset and Writer.Reset methods now use the default buffer size when called on objects with a nil buffer.

bytes

The new Cut function slices a []byte around a separator. It can replace and simplify many common uses of Index, IndexByte, IndexRune, and SplitN.

Trim, TrimLeft, and TrimRight are now allocation free and, especially for small ASCII cutsets, up to 10 times faster.

The Title function is now deprecated. It doesn't handle Unicode punctuation and language-specific capitalization rules, and is superseded by the golang.org/x/text/cases package.

crypto/elliptic

The P224, P384, and P521 curve implementations are now all backed by code generated by the addchain and fiat-crypto projects, the latter of which is based on a formally-verified model of the arithmetic operations. They now use safer complete formulas and internal APIs. P-224 and P-384 are now approximately four times faster. All specific curve implementations are now constant-time.

Operating on invalid curve points (those for which the IsOnCurve method returns false, and which are never returned by Unmarshal or a Curve method operating on a valid point) has always been undefined behavior, can lead to key recovery attacks, and is now unsupported by the new backend. If an invalid point is supplied to a P224, P384, or P521 method, that method will now return a random point. The behavior might change to an explicit panic in a future release.

crypto/tls

The new Conn.NetConn method allows access to the underlying net.Conn.

crypto/x509 Certificate.Verify now uses platform APIs to verify certificate validity on macOS and iOS when it is called with a nil VerifyOpts.Roots or when using the root pool returned from SystemCertPool.

SystemCertPool is now available on Windows.

On Windows, macOS, and iOS, when a CertPool returned by SystemCertPool has additional certificates added to it, Certificate.Verify will do two verifications: one using the platform verifier APIs and the system roots, and one using the Go verifier and the additional roots. Chains returned by the platform verifier APIs will be prioritized.

CertPool.Subjects is deprecated. On Windows, macOS, and iOS the CertPool returned by SystemCertPool will return a pool which does not include system roots in the slice returned by Subjects, as a static list can't appropriately represent the platform policies and might not be available at all from the platform APIs.

Support for signing certificates using signature algorithms that depend on the MD5 hash (MD5WithRSA) may be removed in Go 1.19.

debug/dwarf

The StructField and BasicType structs both now have a DataBitOffset field, which holds the value of the DW_AT_data_bit_offset attribute if present.

debug/elf

The R_PPC64_RELATIVE constant has been added.

debug/plan9obj

The File.Symbols method now returns the new exported error value ErrNoSymbols if the file has no symbol section.

embed

A go:embed directive may now start with all: to include files whose names start with dot or underscore.

go/ast

Per the proposal Additions to go/ast and go/token to support parameterized functions and types the following additions are made to the go/ast package:

the FuncType and TypeSpec nodes have a new field TypeParams to hold type parameters, if any. The new expression node IndexListExpr represents index expressions with multiple indices, used for function and type instantiations with more than one explicit type argument. go/constant The new Kind.String method returns a human-readable name for the receiver kind.

go/token

The new constant TILDE represents the ~ token per the proposal Additions to go/ast and go/token to support parameterized functions and types .

go/types

The new Config.GoVersion field sets the accepted Go language version.

Per the proposal Additions to go/types to support type parameters the following additions are made to the go/types package:

The new type TypeParam, factory function NewTypeParam, and associated methods are added to represent a type parameter. The new type TypeParamList holds a list of type parameters. The new type TypeList holds a list of types. The new factory function NewSignatureType allocates a Signature with (receiver or function) type parameters. To access those type parameters, the Signature type has two new methods Signature.RecvTypeParams and Signature.TypeParams. Named types have four new methods: Named.Origin to get the original parameterized types of instantiated types, Named.TypeArgs and Named.TypeParams to get the type arguments or type parameters of an instantiated or parameterized type, and Named.SetTypeParams to set the type parameters (for instance, when importing a named type where allocation of the named type and setting of type parameters cannot be done simultaneously due to possible cycles). The Interface type has four new methods: Interface.IsComparable and Interface.IsMethodSet to query properties of the type set defined by the interface, and Interface.MarkImplicit and Interface.IsImplicit to set and test whether the interface is an implicit interface around a type constraint literal. The new types Union and Term, factory functions NewUnion and NewTerm, and associated methods are added to represent type sets in interfaces. The new function Instantiate instantiates a parameterized type. The new Info.Instances map records function and type instantiations through the new Instance type. The new type ArgumentError and associated methods are added to represent an error related to a type argument. The new type Context and factory function NewContext are added to facilitate sharing of identical type instances across type-checked packages, via the new Config.Context field. The predicates AssignableTo, ConvertibleTo, Implements, Identical, IdenticalIgnoreTags, and AssertableTo now also work with arguments that are or contain generalized interfaces, i.e. interfaces that may only be used as type constraints in Go code. Note that the behavior of AssignableTo, ConvertibleTo, Implements, and AssertableTo is undefined with arguments that are uninstantiated generic types, and AssertableTo is undefined if the first argument is a generalized interface.

html/template

Within a range pipeline the new {{break}} command will end the loop early and the new {{continue}} command will immediately start the next loop iteration.

The and function no longer always evaluates all arguments; it stops evaluating arguments after the first argument that evaluates to false. Similarly, the or function now stops evaluating arguments after the first argument that evaluates to true. This makes a difference if any of the arguments is a function call.

image/draw

The Draw and DrawMask fallback implementations (used when the arguments are not the most common image types) are now faster when those arguments implement the optional draw.RGBA64Image and image.RGBA64Image interfaces that were added in Go 1.17.

net

net.Error.Temporary has been deprecated.

net/http

On WebAssembly targets, the Dial, DialContext, DialTLS and DialTLSContext method fields in Transport will now be correctly used, if specified, for making HTTP requests.

The new Cookie.Valid method reports whether the cookie is valid.

The new MaxBytesHandler function creates a Handler that wraps its ResponseWriter and Request.Body with a MaxBytesReader.

When looking up a domain name containing non-ASCII characters, the Unicode-to-ASCII conversion is now done in accordance with Nontransitional Processing as defined in the Unicode IDNA Compatibility Processing standard (UTS #46). The interpretation of four distinct runes are changed: ß, ς, zero-width joiner U+200D, and zero-width non-joiner U+200C. Nontransitional Processing is consistent with most applications and web browsers.

os/user

User.GroupIds now uses a Go native implementation when cgo is not available.

reflect

The new Value.SetIterKey and Value.SetIterValue methods set a Value using a map iterator as the source. They are equivalent to Value.Set(iter.Key()) and Value.Set(iter.Value()), but do fewer allocations.

The new Value.UnsafePointer method returns the Value's value as an unsafe.Pointer. This allows callers to migrate from Value.UnsafeAddr and Value.Pointer to eliminate the need to perform uintptr to unsafe.Pointer conversions at the callsite (as unsafe.Pointer rules require).

The new MapIter.Reset method changes its receiver to iterate over a different map. The use of MapIter.Reset allows allocation-free iteration over many maps.

A number of methods ( Value.CanInt, Value.CanUint, Value.CanFloat, Value.CanComplex ) have been added to Value to test if a conversion is safe.

Value.FieldByIndexErr has been added to avoid the panic that occurs in Value.FieldByIndex when stepping through a nil pointer to an embedded struct.

reflect.Ptr and reflect.PtrTo have been renamed to reflect.Pointer and reflect.PointerTo, respectively, for consistency with the rest of the reflect package. The old names will continue to work, but will be deprecated in a future Go release.

regexp

regexp now treats each invalid byte of a UTF-8 string as U+FFFD.

runtime/debug

The BuildInfo struct has two new fields, containing additional information about how the binary was built:

GoVersion holds the version of Go used to build the binary. Settings is a slice of BuildSettings structs holding key/value pairs describing the build. runtime/pprof The CPU profiler now uses per-thread timers on Linux. This increases the maximum CPU usage that a profile can observe, and reduces some forms of bias.

strconv

strconv.Unquote now rejects Unicode surrogate halves.

strings

The new Cut function slices a string around a separator. It can replace and simplify many common uses of Index, IndexByte, IndexRune, and SplitN.

The new Clone function copies the input string without the returned cloned string referencing the input string's memory.

Trim, TrimLeft, and TrimRight are now allocation free and, especially for small ASCII cutsets, up to 10 times faster.

The Title function is now deprecated. It doesn't handle Unicode punctuation and language-specific capitalization rules, and is superseded by the golang.org/x/text/cases package.

sync

The new methods Mutex.TryLock, RWMutex.TryLock, and RWMutex.TryRLock, will acquire the lock if it is not currently held.

syscall

The new function SyscallN has been introduced for Windows, allowing for calls with arbitrary number of arguments. As a result, Syscall, Syscall6, Syscall9, Syscall12, Syscall15, and Syscall18 are deprecated in favor of SyscallN.

SysProcAttr.Pdeathsig is now supported in FreeBSD.

syscall/js

The Wrapper interface has been removed.

testing

The precedence of / in the argument for -run and -bench has been increased. A/B|C/D used to be treated as A/(B|C)/D and is now treated as (A/B)|(C/D).

If the -run option does not select any tests, the -count option is ignored. This could change the behavior of existing tests in the unlikely case that a test changes the set of subtests that are run each time the test function itself is run.

The new testing.F type is used by the new fuzzing support described above. Tests also now support the command line options -test.fuzz, -test.fuzztime, and -test.fuzzminimizetime.

text/template

Within a range pipeline the new {{break}} command will end the loop early and the new {{continue}} command will immediately start the next loop iteration.

The and function no longer always evaluates all arguments; it stops evaluating arguments after the first argument that evaluates to false. Similarly, the or function now stops evaluating arguments after the first argument that evaluates to true. This makes a difference if any of the arguments is a function call.

text/template/parse

The package supports the new text/template and html/template {{break}} command via the new constant NodeBreak and the new type BreakNode, and similarly supports the new {{continue}} command via the new constant NodeContinue and the new type ContinueNode.

unicode/utf8

The new AppendRune function appends the UTF-8 encoding of a rune to a []byte.

Introduction to Go 1.17

The latest Go release, version 1.17, arrives six months after Go 1.16. Most of its changes are in the implementation of the toolchain, runtime, and libraries. As always, the release maintains the Go 1 promise of compatibility. We expect almost all Go programs to continue to compile and run as before.

Changes to the language

Go 1.17 includes three small enhancements to the language.

Conversions from slice to array pointer: An expression s of type []T may now be converted to array pointer type *[N]T. If a is the result of such a conversion, then corresponding indices that are in range refer to the same underlying elements: &a[i] == &s[i] for 0 <= i < N. The conversion panics if len(s) is less than N. unsafe.Add: unsafe.Add(ptr, len) adds len to ptr and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)). unsafe.Slice: For expression ptr of type *T, unsafe.Slice(ptr, len) returns a slice of type []T whose underlying array starts at ptr and whose length and capacity are len. The package unsafe enhancements were added to simplify writing code that conforms to unsafe.Pointer's safety rules, but the rules remain unchanged. In particular, existing programs that correctly use unsafe.Pointer remain valid, and new programs must still follow the rules when using unsafe.Add or unsafe.Slice.

Note that the new conversion from slice to array pointer is the first case in which a type conversion can panic at run time. Analysis tools that assume type conversions can never panic should be updated to consider this possibility.

Ports

Darwin As announced in the Go 1.16 release notes, Go 1.17 requires macOS 10.13 High Sierra or later; support for previous versions has been discontinued.

Windows Go 1.17 adds support of 64-bit ARM architecture on Windows (the windows/arm64 port). This port supports cgo.

OpenBSD The 64-bit MIPS architecture on OpenBSD (the openbsd/mips64 port) now supports cgo.

In Go 1.16, on the 64-bit x86 and 64-bit ARM architectures on OpenBSD (the openbsd/amd64 and openbsd/arm64 ports) system calls are made through libc, instead of directly using machine instructions. In Go 1.17, this is also done on the 32-bit x86 and 32-bit ARM architectures on OpenBSD (the openbsd/386 and openbsd/arm ports). This ensures compatibility with OpenBSD 6.9 onwards, which require system calls to be made through libc for non-static Go binaries.

ARM64 Go programs now maintain stack frame pointers on the 64-bit ARM architecture on all operating systems. Previously, stack frame pointers were only enabled on Linux, macOS, and iOS.

loong64 GOARCH value reserved The main Go compiler does not yet support the LoongArch architecture, but we've reserved the GOARCH value "loong64". This means that Go files named *_loong64.go will now be ignored by Go tools except when that GOARCH value is being used.

Tools

Go command

Pruned module graphs in go 1.17 modules If a module specifies go 1.17 or higher, the module graph includes only the immediate dependencies of other go 1.17 modules, not their full transitive dependencies. (See Module graph pruning for more detail.)

For the go command to correctly resolve transitive imports using the pruned module graph, the go.mod file for each module needs to include more detail about the transitive dependencies relevant to that module. If a module specifies go 1.17 or higher in its go.mod file, its go.mod file now contains an explicit require directive for every module that provides a transitively-imported package. (In previous versions, the go.mod file typically only included explicit requirements for directly-imported packages.)

Since the expanded go.mod file needed for module graph pruning includes all of the dependencies needed to load the imports of any package in the main module, if the main module specifies go 1.17 or higher the go tool no longer reads (or even downloads) go.mod files for dependencies if they are not needed in order to complete the requested command. (See Lazy loading.)

Because the number of explicit requirements may be substantially larger in an expanded Go 1.17 go.mod file, the newly-added requirements on indirect dependencies in a go 1.17 module are maintained in a separate require block from the block containing direct dependencies.

To facilitate the upgrade to Go 1.17 pruned module graphs, the go mod tidy subcommand now supports a -go flag to set or change the go version in the go.mod file. To convert the go.mod file for an existing module to Go 1.17 without changing the selected versions of its dependencies, run:

go mod tidy -go=1.17 By default, go mod tidy verifies that the selected versions of dependencies relevant to the main module are the same versions that would be used by the prior Go release (Go 1.16 for a module that specifies go 1.17), and preserves the go.sum entries needed by that release even for dependencies that are not normally needed by other commands.

The -compat flag allows that version to be overridden to support older (or only newer) versions, up to the version specified by the go directive in the go.mod file. To tidy a go 1.17 module for Go 1.17 only, without saving checksums for (or checking for consistency with) Go 1.16:

go mod tidy -compat=1.17 Note that even if the main module is tidied with -compat=1.17, users who require the module from a go 1.16 or earlier module will still be able to use it, provided that the packages use only compatible language and library features.

The go mod graph subcommand also supports the -go flag, which causes it to report the graph as seen by the indicated Go version, showing dependencies that may otherwise be pruned out.

Module deprecation comments Module authors may deprecate a module by adding a // Deprecated: comment to go.mod, then tagging a new version. go get now prints a warning if a module needed to build packages named on the command line is deprecated. go list -m -u prints deprecations for all dependencies (use -f or -json to show the full message). The go command considers different major versions to be distinct modules, so this mechanism may be used, for example, to provide users with migration instructions for a new major version.

go get The go get -insecure flag is deprecated and has been removed. To permit the use of insecure schemes when fetching dependencies, please use the GOINSECURE environment variable. The -insecure flag also bypassed module sum validation, use GOPRIVATE or GONOSUMDB if you need that functionality. See go help environment for details.

go get prints a deprecation warning when installing commands outside the main module (without the -d flag). go install cmd@version should be used instead to install a command at a specific version, using a suffix like @latest or @v1.2.3. In Go 1.18, the -d flag will always be enabled, and go get will only be used to change dependencies in go.mod.

go.mod files missing go directives If the main module's go.mod file does not contain a go directive and the go command cannot update the go.mod file, the go command now assumes go 1.11 instead of the current release. (go mod init has added go directives automatically since Go 1.12.)

If a module dependency lacks an explicit go.mod file, or its go.mod file does not contain a go directive, the go command now assumes go 1.16 for that dependency instead of the current release. (Dependencies developed in GOPATH mode may lack a go.mod file, and the vendor/modules.txt has to date never recorded the go versions indicated by dependencies' go.mod files.)

vendor contents If the main module specifies go 1.17 or higher, go mod vendor now annotates vendor/modules.txt with the go version indicated by each vendored module in its own go.mod file. The annotated version is used when building the module's packages from vendored source code.

If the main module specifies go 1.17 or higher, go mod vendor now omits go.mod and go.sum files for vendored dependencies, which can otherwise interfere with the ability of the go command to identify the correct module root when invoked within the vendor tree.

Password prompts The go command by default now suppresses SSH password prompts and Git Credential Manager prompts when fetching Git repositories using SSH, as it already did previously for other Git password prompts. Users authenticating to private Git repos with password-protected SSH may configure an ssh-agent to enable the go command to use password-protected SSH keys.

go mod download When go mod download is invoked without arguments, it will no longer save sums for downloaded module content to go.sum. It may still make changes to go.mod and go.sum needed to load the build list. This is the same as the behavior in Go 1.15. To save sums for all modules, use go mod download all.

//go:build lines The go command now understands //go:build lines and prefers them over // +build lines. The new syntax uses boolean expressions, just like Go, and should be less error-prone. As of this release, the new syntax is fully supported, and all Go files should be updated to have both forms with the same meaning. To aid in migration, gofmt now automatically synchronizes the two forms. For more details on the syntax and migration plan, see https://golang.org/design/draft-gobuild.

go run go run now accepts arguments with version suffixes (for example, go run example.com/cmd@v1.0.0). This causes go run to build and run packages in module-aware mode, ignoring the go.mod file in the current directory or any parent directory, if there is one. This is useful for running executables without installing them or without changing dependencies of the current module.

Gofmt

gofmt (and go fmt) now synchronizes //go:build lines with // +build lines. If a file only has // +build lines, they will be moved to the appropriate location in the file, and matching //go:build lines will be added. Otherwise, // +build lines will be overwritten based on any existing //go:build lines. For more information, see https://golang.org/design/draft-gobuild.

Vet

New warning for mismatched //go:build and // +build lines The vet tool now verifies that //go:build and // +build lines are in the correct part of the file and synchronized with each other. If they aren't, gofmt can be used to fix them. For more information, see https://golang.org/design/draft-gobuild.

New warning for calling signal.Notify on unbuffered channels The vet tool now warns about calls to signal.Notify with incoming signals being sent to an unbuffered channel. Using an unbuffered channel risks missing signals sent on them as signal.Notify does not block when sending to a channel. For example:

c := make(chan os.Signal) // signals are sent on c before the channel is read from. // This signal may be dropped as c is unbuffered. signal.Notify(c, os.Interrupt) Users of signal.Notify should use channels with sufficient buffer space to keep up with the expected signal rate.

New warnings for Is, As and Unwrap methods The vet tool now warns about methods named As, Is or Unwrap on types implementing the error interface that have a different signature than the one expected by the errors package. The errors.{As,Is,Unwrap} functions expect such methods to implement either Is(error) bool, As(interface{}) bool, or Unwrap() error respectively. The functions errors.{As,Is,Unwrap} will ignore methods with the same names but a different signature. For example:

type MyError struct { hint string } func (m MyError) Error() string { ... } // MyError implements error. func (MyError) Is(target interface{}) bool { ... } // target is interface{} instead of error. func Foo() bool { x, y := MyError{"A"}, MyError{"B"} return errors.Is(x, y) // returns false as x != y and MyError does not have an Is(error) bool function. }

Cover

The cover tool now uses an optimized parser from golang.org/x/tools/cover, which may be noticeably faster when parsing large coverage profiles.

Compiler

Go 1.17 implements a new way of passing function arguments and results using registers instead of the stack. Benchmarks for a representative set of Go packages and programs show performance improvements of about 5%, and a typical reduction in binary size of about 2%. This is currently enabled for Linux, macOS, and Windows on the 64-bit x86 architecture (the linux/amd64, darwin/amd64, and windows/amd64 ports).

This change does not affect the functionality of any safe Go code and is designed to have no impact on most assembly code. It may affect code that violates the unsafe.Pointer rules when accessing function arguments, or that depends on undocumented behavior involving comparing function code pointers. To maintain compatibility with existing assembly functions, the compiler generates adapter functions that convert between the new register-based calling convention and the previous stack-based calling convention. These adapters are typically invisible to users, except that taking the address of a Go function in assembly code or taking the address of an assembly function in Go code using reflect.ValueOf(fn).Pointer() or unsafe.Pointer will now return the address of the adapter. Code that depends on the value of these code pointers may no longer behave as expected. Adapters also may cause a very small performance overhead in two cases: calling an assembly function indirectly from Go via a func value, and calling Go functions from assembly.

The format of stack traces from the runtime (printed when an uncaught panic occurs, or when runtime.Stack is called) is improved. Previously, the function arguments were printed as hexadecimal words based on the memory layout. Now each argument in the source code is printed separately, separated by commas. Aggregate-typed (struct, array, string, slice, interface, and complex) arguments are delimited by curly braces. A caveat is that the value of an argument that only lives in a register and is not stored to memory may be inaccurate. Function return values (which were usually inaccurate) are no longer printed.

Functions containing closures can now be inlined. One effect of this change is that a function with a closure may produce a distinct closure code pointer for each place that the function is inlined. Go function values are not directly comparable, but this change could reveal bugs in code that uses reflect or unsafe.Pointer to bypass this language restriction and compare functions by code pointer.

Linker

When the linker uses external linking mode, which is the default when linking a program that uses cgo, and the linker is invoked with a -I option, the option will now be passed to the external linker as a -Wl,--dynamic-linker option.

Core library

Cgo

The runtime/cgo package now provides a new facility that allows to turn any Go values to a safe representation that can be used to pass values between C and Go safely. See runtime/cgo.Handle for more information.

URL query parsing

The net/url and net/http packages used to accept ";" (semicolon) as a setting separator in URL queries, in addition to "&" (ampersand). Now, settings with non-percent-encoded semicolons are rejected and net/http servers will log a warning to Server.ErrorLog when encountering one in a request URL.

For example, before Go 1.17 the Query method of the URL example?a=1;b=2&c=3 would have returned map[a:[1] b:[2] c:[3]], while now it returns map[c:[3]].

When encountering such a query string, URL.Query and Request.FormValue ignore any settings that contain a semicolon, ParseQuery returns the remaining settings and an error, and Request.ParseForm and Request.ParseMultipartForm return an error but still set Request fields based on the remaining settings.

net/http users can restore the original behavior by using the new AllowQuerySemicolons handler wrapper. This will also suppress the ErrorLog warning. Note that accepting semicolons as query separators can lead to security issues if different systems interpret cache keys differently. See issue 25192 for more information.

TLS strict ALPN

When Config.NextProtos is set, servers now enforce that there is an overlap between the configured protocols and the ALPN protocols advertised by the client, if any. If there is no mutually supported protocol, the connection is closed with the no_application_protocol alert, as required by RFC 7301. This helps mitigate the ALPACA cross-protocol attack.

As an exception, when the value "h2" is included in the server's Config.NextProtos, HTTP/1.1 clients will be allowed to connect as if they didn't support ALPN. See issue 46310 for more information.

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind.

archive/zip

The new methods File.OpenRaw, Writer.CreateRaw, Writer.Copy provide support for cases where performance is a primary concern.

bufio

The Writer.WriteRune method now writes the replacement character U+FFFD for negative rune values, as it does for other invalid runes.

bytes

The Buffer.WriteRune method now writes the replacement character U+FFFD for negative rune values, as it does for other invalid runes.

compress/lzw

The NewReader function is guaranteed to return a value of the new type Reader, and similarly NewWriter is guaranteed to return a value of the new type Writer. These new types both implement a Reset method (Reader.Reset, Writer.Reset) that allows reuse of the Reader or Writer.

crypto/ed25519

The crypto/ed25519 package has been rewritten, and all operations are now approximately twice as fast on amd64 and arm64. The observable behavior has not otherwise changed.

crypto/elliptic

CurveParams methods now automatically invoke faster and safer dedicated implementations for known curves (P-224, P-256, and P-521) when available. Note that this is a best-effort approach and applications should avoid using the generic, not constant-time CurveParams methods and instead use dedicated Curve implementations such as P256.

The P521 curve implementation has been rewritten using code generated by the fiat-crypto project, which is based on a formally-verified model of the arithmetic operations. It is now constant-time and three times faster on amd64 and arm64. The observable behavior has not otherwise changed.

crypto/rand

The crypto/rand package now uses the getentropy syscall on macOS and the getrandom syscall on Solaris, Illumos, and DragonFlyBSD.

crypto/tls

The new Conn.HandshakeContext method allows the user to control cancellation of an in-progress TLS handshake. The provided context is accessible from various callbacks through the new ClientHelloInfo.Context and CertificateRequestInfo.Context methods. Canceling the context after the handshake has finished has no effect.

Cipher suite ordering is now handled entirely by the crypto/tls package. Currently, cipher suites are sorted based on their security, performance, and hardware support taking into account both the local and peer's hardware. The order of the Config.CipherSuites field is now ignored, as well as the Config.PreferServerCipherSuites field. Note that Config.CipherSuites still allows applications to choose what TLS 1.0–1.2 cipher suites to enable.

The 3DES cipher suites have been moved to InsecureCipherSuites due to fundamental block size-related weakness. They are still enabled by default but only as a last resort, thanks to the cipher suite ordering change above.

Beginning in the next release, Go 1.18, the Config.MinVersion for crypto/tls clients will default to TLS 1.2, disabling TLS 1.0 and TLS 1.1 by default. Applications will be able to override the change by explicitly setting Config.MinVersion. This will not affect crypto/tls servers.

crypto/x509

CreateCertificate now returns an error if the provided private key doesn't match the parent's public key, if any. The resulting certificate would have failed to verify.

The temporary GODEBUG=x509ignoreCN=0 flag has been removed.

ParseCertificate has been rewritten, and now consumes ~70% fewer resources. The observable behavior when processing WebPKI certificates has not otherwise changed, except for error messages.

On BSD systems, /etc/ssl/certs is now searched for trusted roots. This adds support for the new system trusted certificate store in FreeBSD 12.2+.

Beginning in the next release, Go 1.18, crypto/x509 will reject certificates signed with the SHA-1 hash function. This doesn't apply to self-signed root certificates. Practical attacks against SHA-1 have been demonstrated in 2017 and publicly trusted Certificate Authorities have not issued SHA-1 certificates since 2015.

database/sql

The DB.Close method now closes the connector field if the type in this field implements the io.Closer interface.

The new NullInt16 and NullByte structs represent the int16 and byte values that may be null. These can be used as destinations of the Scan method, similar to NullString.

debug/elf

The SHT_MIPS_ABIFLAGS constant has been added.

encoding/binary

binary.Uvarint will stop reading after 10 bytes to avoid wasted computations. If more than 10 bytes are needed, the byte count returned is -11. Previous Go versions could return larger negative counts when reading incorrectly encoded varints.

encoding/csv

The new Reader.FieldPos method returns the line and column corresponding to the start of a given field in the record most recently returned by Read.

encoding/xml

When a comment appears within a Directive, it is now replaced with a single space instead of being completely elided.

Invalid element or attribute names with leading, trailing, or multiple colons are now stored unmodified into the Name.Local field.

flag

Flag declarations now panic if an invalid name is specified.

go/build

The new Context.ToolTags field holds the build tags appropriate to the current Go toolchain configuration.

go/format

The Source and Node functions now synchronize //go:build lines with // +build lines. If a file only has // +build lines, they will be moved to the appropriate location in the file, and matching //go:build lines will be added. Otherwise, // +build lines will be overwritten based on any existing //go:build lines. For more information, see https://golang.org/design/draft-gobuild.

go/parser

The new SkipObjectResolution Mode value instructs the parser not to resolve identifiers to their declaration. This may improve parsing speed.

image

The concrete image types (RGBA, Gray16 and so on) now implement a new RGBA64Image interface. The concrete types that previously implemented draw.Image now also implement draw.RGBA64Image, a new interface in the image/draw package.

io/fs

The new FileInfoToDirEntry function converts a FileInfo to a DirEntry.

math

The math package now defines three more constants: MaxUint, MaxInt and MinInt. For 32-bit systems their values are 2^32 - 1, 2^31 - 1 and -2^31, respectively. For 64-bit systems their values are 2^64 - 1, 2^63 - 1 and -2^63, respectively.

ime

On Unix systems, the table of MIME types is now read from the local system's Shared MIME-info Database when available.

mime/multipart

Part.FileName now applies filepath.Base to the return value. This mitigates potential path traversal vulnerabilities in applications that accept multipart messages, such as net/http servers that call Request.FormFile.

net

The new method IP.IsPrivate reports whether an address is a private IPv4 address according to RFC 1918 or a local IPv6 address according RFC 4193.

The Go DNS resolver now only sends one DNS query when resolving an address for an IPv4-only or IPv6-only network, rather than querying for both address families.

The ErrClosed sentinel error and ParseError error type now implement the net.Error interface.

The ParseIP and ParseCIDR functions now reject IPv4 addresses which contain decimal components with leading zeros. These components were always interpreted as decimal, but some operating systems treat them as octal. This mismatch could hypothetically lead to security issues if a Go application was used to validate IP addresses which were then used in their original form with non-Go applications which interpreted components as octal. Generally, it is advisable to always re-encode values after validation, which avoids this class of parser misalignment issues.

net/http

The net/http package now uses the new (*tls.Conn).HandshakeContext with the Request context when performing TLS handshakes in the client or server.

Setting the Server ReadTimeout or WriteTimeout fields to a negative value now indicates no timeout rather than an immediate timeout.

The ReadRequest function now returns an error when the request has multiple Host headers.

When producing a redirect to the cleaned version of a URL, ServeMux now always uses relative URLs in the Location header. Previously it would echo the full URL of the request, which could lead to unintended redirects if the client could be made to send an absolute request URL.

When interpreting certain HTTP headers handled by net/http, non-ASCII characters are now ignored or rejected.

If Request.ParseForm returns an error when called by Request.ParseMultipartForm, the latter now continues populating Request.MultipartForm before returning it.

net/http/httptest

ResponseRecorder.WriteHeader now panics when the provided code is not a valid three-digit HTTP status code. This matches the behavior of ResponseWriter implementations in the net/http package.

net/url

The new method Values.Has reports whether a query parameter is set.

os

The File.WriteString method has been optimized to not make a copy of the input string.

reflect

The new Value.CanConvert method reports whether a value can be converted to a type. This may be used to avoid a panic when converting a slice to an array pointer type if the slice is too short. Previously it was sufficient to use Type.ConvertibleTo for this, but the newly permitted conversion from slice to array pointer type can panic even if the types are convertible.

The new StructField.IsExported and Method.IsExported methods report whether a struct field or type method is exported. They provide a more readable alternative to checking whether PkgPath is empty.

The new VisibleFields function returns all the visible fields in a struct type, including fields inside anonymous struct members.

The ArrayOf function now panics when called with a negative length.

Checking the Type.ConvertibleTo method is no longer sufficient to guarantee that a call to Value.Convert will not panic. It may panic when converting []T to *[N]T if the slice's length is less than N. See the language changes section above.

The Value.Convert and Type.ConvertibleTo methods have been fixed to not treat types in different packages with the same name as identical, to match what the language allows.

runtime/metrics

New metrics were added that track total bytes and objects allocated and freed. A new metric tracking the distribution of goroutine scheduling latencies was also added.

runtime/pprof

Block profiles are no longer biased to favor infrequent long events over frequent short events.

strconv

The strconv package now uses Ulf Adams's Ryū algorithm for formatting floating-point numbers. This algorithm improves performance on most inputs and is more than 99% faster on worst-case inputs.

The new QuotedPrefix function returns the quoted string (as understood by Unquote) at the start of input.

strings

The Builder.WriteRune method now writes the replacement character U+FFFD for negative rune values, as it does for other invalid runes.

sync/atomic

atomic.Value now has Swap and CompareAndSwap methods that provide additional atomic operations.

yscall

The GetQueuedCompletionStatus and PostQueuedCompletionStatus functions are now deprecated. These functions have incorrect signatures and are superseded by equivalents in the golang.org/x/sys/windows package.

On Unix-like systems, the process group of a child process is now set with signals blocked. This avoids sending a SIGTTOU to the child when the parent is in a background process group.

The Windows version of SysProcAttr has two new fields. AdditionalInheritedHandles is a list of additional handles to be inherited by the new child process. ParentProcess permits specifying the parent process of the new process.

The constant MSG_CMSG_CLOEXEC is now defined on DragonFly and all OpenBSD systems (it was already defined on some OpenBSD systems and all FreeBSD, NetBSD, and Linux systems).

The constants SYS_WAIT6 and WEXITED are now defined on NetBSD systems (SYS_WAIT6 was already defined on DragonFly and FreeBSD systems; WEXITED was already defined on Darwin, DragonFly, FreeBSD, Linux, and Solaris systems).

testing

Added a new testing flag -shuffle which controls the execution order of tests and benchmarks.

The new T.Setenv and B.Setenv methods support setting an environment variable for the duration of the test or benchmark.

text/template/parse

The new SkipFuncCheck Mode value changes the template parser to not verify that functions are defined.

time

The Time type now has a GoString method that will return a more useful value for times when printed with the %#v format specifier in the fmt package.

The new Time.IsDST method can be used to check whether the time is in Daylight Savings Time in its configured location.

The new Time.UnixMilli and Time.UnixMicro methods return the number of milliseconds and microseconds elapsed since January 1, 1970 UTC respectively. The new UnixMilli and UnixMicro functions return the local Time corresponding to the given Unix time.

The package now accepts comma "," as a separator for fractional seconds when parsing and formatting time. For example, the following time layouts are now accepted:

2006-01-02 15:04:05,999999999 -0700 MST Mon Jan _2 15:04:05,000000 2006 Monday, January 2 15:04:05,000 2006 The new constant Layout defines the reference time.

unicode

The Is, IsGraphic, IsLetter, IsLower, IsMark, IsNumber, IsPrint, IsPunct, IsSpace, IsSymbol, and IsUpper functions now return false on negative rune values, as they do for other invalid runes.

go1.16(添加泛型)

go1.15(添加泛型)

go1.14(添加泛型)

go1.13(添加泛型)

go1.12(添加泛型)

go1.11(添加泛型)

go1.10(添加泛型)

go1.9(添加泛型)

go1.8(添加泛型)

go1.17(添加泛型)

Introduction to Go 1.7

The latest Go release, version 1.7, arrives six months after 1.6. Most of its changes are in the implementation of the toolchain, runtime, and libraries. There is one minor change to the language specification. As always, the release maintains the Go 1 promise of compatibility. We expect almost all Go programs to continue to compile and run as before.

The release adds a port to IBM LinuxOne; updates the x86-64 compiler back end to generate more efficient code; includes the context package, promoted from the x/net subrepository and now used in the standard library; and adds support in the testing package for creating hierarchies of tests and benchmarks. The release also finalizes the vendoring support started in Go 1.5, making it a standard feature.

Changes to the language There is one tiny language change in this release. The section on terminating statements clarifies that to determine whether a statement list ends in a terminating statement, the “final non-empty statement” is considered the end, matching the existing behavior of the gc and gccgo compiler toolchains. In earlier releases the definition referred only to the “final statement,” leaving the effect of trailing empty statements at the least unclear. The go/types package has been updated to match the gc and gccgo compiler toolchains in this respect. This change has no effect on the correctness of existing programs.

Ports

Go 1.7 adds support for macOS 10.12 Sierra. Binaries built with versions of Go before 1.7 will not work correctly on Sierra.

Go 1.7 adds an experimental port to Linux on z Systems (linux/s390x) and the beginning of a port to Plan 9 on ARM (plan9/arm).

The experimental ports to Linux on 64-bit MIPS (linux/mips64 and linux/mips64le) added in Go 1.6 now have full support for cgo and external linking.

The experimental port to Linux on little-endian 64-bit PowerPC (linux/ppc64le) now requires the POWER8 architecture or later. Big-endian 64-bit PowerPC (linux/ppc64) only requires the POWER5 architecture.

The OpenBSD port now requires OpenBSD 5.6 or later, for access to the getentropy(2) system call.

Known Issues

There are some instabilities on FreeBSD that are known but not understood. These can lead to program crashes in rare cases. See issue 16136, issue 15658, and issue 16396. Any help in solving these FreeBSD-specific issues would be appreciated.

Tools

Assembler

For 64-bit ARM systems, the vector register names have been corrected to V0 through V31; previous releases incorrectly referred to them as V32 through V63.

For 64-bit x86 systems, the following instructions have been added: PCMPESTRI, RORXL, RORXQ, VINSERTI128, VPADDD, VPADDQ, VPALIGNR, VPBLENDD, VPERM2F128, VPERM2I128, VPOR, VPSHUFB, VPSHUFD, VPSLLD, VPSLLDQ, VPSLLQ, VPSRLD, VPSRLDQ, and VPSRLQ.

Compiler Toolchain

This release includes a new code generation back end for 64-bit x86 systems, following a proposal from 2015 that has been under development since then. The new back end, based on SSA, generates more compact, more efficient code and provides a better platform for optimizations such as bounds check elimination. The new back end reduces the CPU time required by our benchmark programs by 5-35%.

For this release, the new back end can be disabled by passing -ssa=0 to the compiler. If you find that your program compiles or runs successfully only with the new back end disabled, please file a bug report.

The format of exported metadata written by the compiler in package archives has changed: the old textual format has been replaced by a more compact binary format. This results in somewhat smaller package archives and fixes a few long-standing corner case bugs.

For this release, the new export format can be disabled by passing -newexport=0 to the compiler. If you find that your program compiles or runs successfully only with the new export format disabled, please file a bug report.

The linker's -X option no longer supports the unusual two-argument form -X name value, as announced in the Go 1.6 release and in warnings printed by the linker. Use -X name=value instead.

The compiler and linker have been optimized and run significantly faster in this release than in Go 1.6, although they are still slower than we would like and will continue to be optimized in future releases.

Due to changes across the compiler toolchain and standard library, binaries built with this release should typically be smaller than binaries built with Go 1.6, sometimes by as much as 20-30%.

On x86-64 systems, Go programs now maintain stack frame pointers as expected by profiling tools like Linux's perf and Intel's VTune, making it easier to analyze and optimize Go programs using these tools. The frame pointer maintenance has a small run-time overhead that varies but averages around 2%. We hope to reduce this cost in future releases. To build a toolchain that does not use frame pointers, set GOEXPERIMENT=noframepointer when running make.bash, make.bat, or make.rc.

Cgo

Packages using cgo may now include Fortran source files (in addition to C, C++, Objective C, and SWIG), although the Go bindings must still use C language APIs.

Go bindings may now use a new helper function C.CBytes. In contrast to C.CString, which takes a Go string and returns a C.byte (a C char), C.CBytes takes a Go []byte and returns an unsafe.Pointer (a C void*).

Packages and binaries built using cgo have in past releases produced different output on each build, due to the embedding of temporary directory names. When using this release with new enough versions of GCC or Clang (those that support the -fdebug-prefix-map option), those builds should finally be deterministic.

Gccgo

Due to the alignment of Go's semiannual release schedule with GCC's annual release schedule, GCC release 6 contains the Go 1.6.1 version of gccgo. The next release, GCC 7, will likely have the Go 1.8 version of gccgo.

Go command

The go command's basic operation is unchanged, but there are a number of changes worth noting.

This release removes support for the GO15VENDOREXPERIMENT environment variable, as announced in the Go 1.6 release. Vendoring support is now a standard feature of the go command and toolchain.

The Package data structure made available to “go list” now includes a StaleReason field explaining why a particular package is or is not considered stale (in need of rebuilding). This field is available to the -f or -json options and is useful for understanding why a target is being rebuilt.

The “go get” command now supports import paths referring to git.openstack.org.

This release adds experimental, minimal support for building programs using binary-only packages, packages distributed in binary form without the corresponding source code. This feature is needed in some commercial settings but is not intended to be fully integrated into the rest of the toolchain. For example, tools that assume access to complete source code will not work with such packages, and there are no plans to support such packages in the “go get” command.

Go doc

The “go doc” command now groups constructors with the type they construct, following godoc.

Go vet

The “go vet” command has more accurate analysis in its -copylock and -printf checks, and a new -tests check that checks the name and signature of likely test functions. To avoid confusion with the new -tests check, the old, unadvertised -test option has been removed; it was equivalent to -all -shadow.

The vet command also has a new check, -lostcancel, which detects failure to call the cancellation function returned by the WithCancel, WithTimeout, and WithDeadline functions in Go 1.7's new context package (see below). Failure to call the function prevents the new Context from being reclaimed until its parent is canceled. (The background context is never canceled.)

Go tool dist

The new subcommand “go tool dist list” prints all supported operating system/architecture pairs.

Go tool trace

The “go tool trace” command, introduced in Go 1.5, has been refined in various ways.

First, collecting traces is significantly more efficient than in past releases. In this release, the typical execution-time overhead of collecting a trace is about 25%; in past releases it was at least 400%. Second, trace files now include file and line number information, making them more self-contained and making the original executable optional when running the trace tool. Third, the trace tool now breaks up large traces to avoid limits in the browser-based viewer.

Although the trace file format has changed in this release, the Go 1.7 tools can still read traces from earlier releases.

Performance

As always, the changes are so general and varied that precise statements about performance are difficult to make. Most programs should run a bit faster, due to speedups in the garbage collector and optimizations in the core library. On x86-64 systems, many programs will run significantly faster, due to improvements in generated code brought by the new compiler back end. As noted above, in our own benchmarks, the code generation changes alone typically reduce program CPU time by 5-35%.

There have been significant optimizations bringing more than 10% improvements to implementations in the crypto/sha1, crypto/sha256, encoding/binary, fmt, hash/adler32, hash/crc32, hash/crc64, image/color, math/big, strconv, strings, unicode, and unicode/utf16 packages.

Garbage collection pauses should be significantly shorter than they were in Go 1.6 for programs with large numbers of idle goroutines, substantial stack size fluctuation, or large package-level variables.

Core library

Context

Go 1.7 moves the golang.org/x/net/context package into the standard library as context. This allows the use of contexts for cancellation, timeouts, and passing request-scoped data in other standard library packages, including net, net/http, and os/exec, as noted below.

For more information about contexts, see the package documentation and the Go blog post “Go Concurrent Patterns: Context.”

HTTP Tracing

Go 1.7 introduces net/http/httptrace, a package that provides mechanisms for tracing events within HTTP requests.

Testing

The testing package now supports the definition of tests with subtests and benchmarks with sub-benchmarks. This support makes it easy to write table-driven benchmarks and to create hierarchical tests. It also provides a way to share common setup and tear-down code. See the package documentation for details.

Runtime

All panics started by the runtime now use panic values that implement both the builtin error, and runtime.Error, as required by the language specification.

During panics, if a signal's name is known, it will be printed in the stack trace. Otherwise, the signal's number will be used, as it was before Go1.7.

The new function KeepAlive provides an explicit mechanism for declaring that an allocated object must be considered reachable at a particular point in a program, typically to delay the execution of an associated finalizer.

The new function CallersFrames translates a PC slice obtained from Callers into a sequence of frames corresponding to the call stack. This new API should be preferred instead of direct use of FuncForPC, because the frame sequence can more accurately describe call stacks with inlined function calls.

The new function SetCgoTraceback facilitates tighter integration between Go and C code executing in the same process called using cgo.

On 32-bit systems, the runtime can now use memory allocated by the operating system anywhere in the address space, eliminating the “memory allocated by OS not in usable range” failure common in some environments.

The runtime can now return unused memory to the operating system on all architectures. In Go 1.6 and earlier, the runtime could not release memory on ARM64, 64-bit PowerPC, or MIPS.

On Windows, Go programs in Go 1.5 and earlier forced the global Windows timer resolution to 1ms at startup by calling timeBeginPeriod(1). Changing the global timer resolution caused problems on some systems, and testing suggested that the call was not needed for good scheduler performance, so Go 1.6 removed the call. Go 1.7 brings the call back: under some workloads the call is still needed for good scheduler performance.

Minor changes to the library

As always, there are various minor changes and updates to the library, made with the Go 1 promise of compatibility in mind.

bufio

In previous releases of Go, if Reader's Peek method were asked for more bytes than fit in the underlying buffer, it would return an empty slice and the error ErrBufferFull. Now it returns the entire underlying buffer, still accompanied by the error ErrBufferFull.

bytes

The new functions ContainsAny and ContainsRune have been added for symmetry with the strings package.

In previous releases of Go, if Reader's Read method were asked for zero bytes with no data remaining, it would return a count of 0 and no error. Now it returns a count of 0 and the error io.EOF.

The Reader type has a new method Reset to allow reuse of a Reader.

compress/flate

There are many performance optimizations throughout the package. Decompression speed is improved by about 10%, while compression for DefaultCompression is twice as fast.

In addition to those general improvements, the BestSpeed compressor has been replaced entirely and uses an algorithm similar to Snappy, resulting in about a 2.5X speed increase, although the output can be 5-10% larger than with the previous algorithm.

There is also a new compression level HuffmanOnly that applies Huffman but not Lempel-Ziv encoding. Forgoing Lempel-Ziv encoding means that HuffmanOnly runs about 3X faster than the new BestSpeed but at the cost of producing compressed outputs that are 20-40% larger than those generated by the new BestSpeed.

It is important to note that both BestSpeed and HuffmanOnly produce a compressed output that is RFC 1951 compliant. In other words, any valid DEFLATE decompressor will continue to be able to decompress these outputs.

Lastly, there is a minor change to the decompressor's implementation of io.Reader. In previous versions, the decompressor deferred reporting io.EOF until exactly no more bytes could be read. Now, it reports io.EOF more eagerly when reading the last set of bytes.

crypto/tls

The TLS implementation sends the first few data packets on each connection using small record sizes, gradually increasing to the TLS maximum record size. This heuristic reduces the amount of data that must be received before the first packet can be decrypted, improving communication latency over low-bandwidth networks. Setting Config's DynamicRecordSizingDisabled field to true forces the behavior of Go 1.6 and earlier, where packets are as large as possible from the start of the connection.

The TLS client now has optional, limited support for server-initiated renegotiation, enabled by setting the Config's Renegotiation field. This is needed for connecting to many Microsoft Azure servers.

The errors returned by the package now consistently begin with a tls: prefix. In past releases, some errors used a crypto/tls: prefix, some used a tls: prefix, and some had no prefix at all.

When generating self-signed certificates, the package no longer sets the “Authority Key Identifier” field by default.

crypto/x509

The new function SystemCertPool provides access to the entire system certificate pool if available. There is also a new associated error type SystemRootsError.

debug/dwarf

The Reader type's new SeekPC method and the Data type's new Ranges method help to find the compilation unit to pass to a LineReader and to identify the specific function for a given program counter.

debug/elf

The new R_390 relocation type and its many predefined constants support the S390 port.

encoding/asn1

The ASN.1 decoder now rejects non-minimal integer encodings. This may cause the package to reject some invalid but formerly accepted ASN.1 data.

encoding/json

The Encoder's new SetIndent method sets the indentation parameters for JSON encoding, like in the top-level Indent function.

The Encoder's new SetEscapeHTML method controls whether the &, <, and > characters in quoted strings should be escaped as \u0026, \u003c, and \u003e, respectively. As in previous releases, the encoder defaults to applying this escaping, to avoid certain problems that can arise when embedding JSON in HTML.

In earlier versions of Go, this package only supported encoding and decoding maps using keys with string types. Go 1.7 adds support for maps using keys with integer types: the encoding uses a quoted decimal representation as the JSON key. Go 1.7 also adds support for encoding maps using non-string keys that implement the MarshalText (see encoding.TextMarshaler) method, as well as support for decoding maps using non-string keys that implement the UnmarshalText (see encoding.TextUnmarshaler) method. These methods are ignored for keys with string types in order to preserve the encoding and decoding used in earlier versions of Go.

When encoding a slice of typed bytes, Marshal now generates an array of elements encoded using that byte type's MarshalJSON or MarshalText method if present, only falling back to the default base64-encoded string data if neither method is available. Earlier versions of Go accept both the original base64-encoded string encoding and the array encoding (assuming the byte type also implements UnmarshalJSON or UnmarshalText as appropriate), so this change should be semantically backwards compatible with earlier versions of Go, even though it does change the chosen encoding.

go/build

To implement the go command's new support for binary-only packages and for Fortran code in cgo-based packages, the Package type adds new fields BinaryOnly, CgoFFLAGS, and FFiles.

go/doc

To support the corresponding change in go test described above, Example struct adds an Unordered field indicating whether the example may generate its output lines in any order.

io

The package adds new constants SeekStart, SeekCurrent, and SeekEnd, for use with Seeker implementations. These constants are preferred over os.SEEK_SET, os.SEEK_CUR, and os.SEEK_END, but the latter will be preserved for compatibility.

math/big

The Float type adds GobEncode and GobDecode methods, so that values of type Float can now be encoded and decoded using the encoding/gob package.

math/rand

The Read function and Rand's Read method now produce a pseudo-random stream of bytes that is consistent and not dependent on the size of the input buffer.

The documentation clarifies that Rand's Seed and Read methods are not safe to call concurrently, though the global functions Seed and Read are (and have always been) safe.

mime/multipart

The Writer implementation now emits each multipart section's header sorted by key. Previously, iteration over a map caused the section header to use a non-deterministic order.

net

As part of the introduction of context, the Dialer type has a new method DialContext, like Dial but adding the context.Context for the dial operation. The context is intended to obsolete the Dialer's Cancel and Deadline fields, but the implementation continues to respect them, for backwards compatibility.

The IP type's String method has changed its result for invalid IP addresses. In past releases, if an IP byte slice had length other than 0, 4, or 16, String returned "?". Go 1.7 adds the hexadecimal encoding of the bytes, as in "?12ab".

The pure Go name resolution implementation now respects nsswitch.conf's stated preference for the priority of DNS lookups compared to local file (that is, /etc/hosts) lookups.

net/http

ResponseWriter's documentation now makes clear that beginning to write the response may prevent future reads on the request body. For maximal compatibility, implementations are encouraged to read the request body completely before writing any part of the response.

As part of the introduction of context, the Request has a new methods Context, to retrieve the associated context, and WithContext, to construct a copy of Request with a modified context.

In the Server implementation, Serve records in the request context both the underlying *Server using the key ServerContextKey and the local address on which the request was received (a Addr) using the key LocalAddrContextKey. For example, the address on which a request received is req.Context().Value(http.LocalAddrContextKey).(net.Addr).

The server's Serve method now only enables HTTP/2 support if the Server.TLSConfig field is nil or includes "h2" in its TLSConfig.NextProtos.

The server implementation now pads response codes less than 100 to three digits as required by the protocol, so that w.WriteHeader(5) uses the HTTP response status 005, not just 5.

The server implementation now correctly sends only one "Transfer-Encoding" header when "chunked" is set explicitly, following RFC 7230.

The server implementation is now stricter about rejecting requests with invalid HTTP versions. Invalid requests claiming to be HTTP/0.x are now rejected (HTTP/0.9 was never fully supported), and plaintext HTTP/2 requests other than the "PRI * HTTP/2.0" upgrade request are now rejected as well. The server continues to handle encrypted HTTP/2 requests.

In the server, a 200 status code is sent back by the timeout handler on an empty response body, instead of sending back 0 as the status code.

In the client, the Transport implementation passes the request context to any dial operation connecting to the remote server. If a custom dialer is needed, the new Transport field DialContext is preferred over the existing Dial field, to allow the transport to supply a context.

The Transport also adds fields IdleConnTimeout, MaxIdleConns, and MaxResponseHeaderBytes to help control client resources consumed by idle or chatty servers.

A Client's configured CheckRedirect function can now return ErrUseLastResponse to indicate that the most recent redirect response should be returned as the result of the HTTP request. That response is now available to the CheckRedirect function as req.Response.

Since Go 1, the default behavior of the HTTP client is to request server-side compression using the Accept-Encoding request header and then to decompress the response body transparently, and this behavior is adjustable using the Transport's DisableCompression field. In Go 1.7, to aid the implementation of HTTP proxies, the Response's new Uncompressed field reports whether this transparent decompression took place.

DetectContentType adds support for a few new audio and video content types.

net/http/cgi

The Handler adds a new field Stderr that allows redirection of the child process's standard error away from the host process's standard error.

net/http/httptest

The new function NewRequest prepares a new http.Request suitable for passing to an http.Handler during a test.

The ResponseRecorder's new Result method returns the recorded http.Response. Tests that need to check the response's headers or trailers should call Result and inspect the response fields instead of accessing ResponseRecorder's HeaderMap directly.

net/http/httputil

The ReverseProxy implementation now responds with “502 Bad Gateway” when it cannot reach a back end; in earlier releases it responded with “500 Internal Server Error.”

Both ClientConn and ServerConn have been documented as deprecated. They are low-level, old, and unused by Go's current HTTP stack and will no longer be updated. Programs should use http.Client, http.Transport, and http.Server instead.

net/http/pprof

The runtime trace HTTP handler, installed to handle the path /debug/pprof/trace, now accepts a fractional number in its seconds query parameter, allowing collection of traces for intervals smaller than one second. This is especially useful on busy servers.

net/mail

The address parser now allows unescaped UTF-8 text in addresses following RFC 6532, but it does not apply any normalization to the result. For compatibility with older mail parsers, the address encoder, namely Address's String method, continues to escape all UTF-8 text following RFC 5322.

The ParseAddress function and the AddressParser.Parse method are stricter. They used to ignore any characters following an e-mail address, but will now return an error for anything other than whitespace.

net/url

The URL's new ForceQuery field records whether the URL must have a query string, in order to distinguish URLs without query strings (like /search) from URLs with empty query strings (like /search?).

os

IsExist now returns true for syscall.ENOTEMPTY, on systems where that error exists.

On Windows, Remove now removes read-only files when possible, making the implementation behave as on non-Windows systems.

os/exec

As part of the introduction of context, the new constructor CommandContext is like Command but includes a context that can be used to cancel the command execution.

os/user

The Current function is now implemented even when cgo is not available.

The new Group type, along with the lookup functions LookupGroup and LookupGroupId and the new field GroupIds in the User struct, provides access to system-specific user group information.

reflect

Although Value's Field method has always been documented to panic if the given field number i is out of range, it has instead silently returned a zero Value. Go 1.7 changes the method to behave as documented.

The new StructOf function constructs a struct type at run time. It completes the set of type constructors, joining ArrayOf, ChanOf, FuncOf, MapOf, PtrTo, and SliceOf.

StructTag's new method Lookup is like Get but distinguishes the tag not containing the given key from the tag associating an empty string with the given key.

The Method and NumMethod methods of Type and Value no longer return or count unexported methods.

strings

In previous releases of Go, if Reader's Read method were asked for zero bytes with no data remaining, it would return a count of 0 and no error. Now it returns a count of 0 and the error io.EOF.

The Reader type has a new method Reset to allow reuse of a Reader.

time

Duration's time.Duration.String method now reports the zero duration as "0s", not "0". ParseDuration continues to accept both forms.

The method call time.Local.String() now returns "Local" on all systems; in earlier releases, it returned an empty string on Windows.

The time zone database in $GOROOT/lib/time has been updated to IANA release 2016d. This fallback database is only used when the system time zone database cannot be found, for example on Windows. The Windows time zone abbreviation list has also been updated.

syscall

On Linux, the SysProcAttr struct (as used in os/exec.Cmd's SysProcAttr field) has a new Unshareflags field. If the field is nonzero, the child process created by ForkExec (as used in exec.Cmd's Run method) will call the unshare(2) system call before executing the new program.

unicode

The unicode package and associated support throughout the system has been upgraded from version 8.0 to Unicode 9.0.

go1.6(添加泛型)

go1.5(添加泛型)

go1.4(添加泛型)

相关博客/书籍

官方博客

Code coverage for Go integration tests

All your comparable types

Profile-guided optimization preview (配置文件引导加速)

When you build a Go binary, the Go compiler performs optimizations to try to generate the best performing binary it can. For example, constant propagation can evaluate constant expressions at compile time, avoiding runtime evaluation cost. Escape analysis avoids heap allocations for locally-scoped objects, avoiding GC overheads. Inlining copies the body of simple functions into callers, often enabling further optimization in the caller (such as additional constant propagation or better escape analysis).

Go improves optimizations from release to release, but this is not always an easy task. Some optimizations are tunable, but the compiler can’t just “turn it up to 11” on every function because overly aggressive optimizations can actually hurt performance or cause excessive build times. Other optimizations require the compiler to make a judgment call about what the “common” and “uncommon” paths in a function are. The compiler must make a best guess based on static heuristics because it can’t know which cases will be common at run time.

Or can it?

With no definitive information about how the code is used in a production environment, the compiler can operate only on the source code of packages. But we do have a tool to evaluate production behavior: profiling. If we provide a profile to the compiler, it can make more informed decisions: more aggressively optimizing the most frequently used functions, or more accurately selecting common cases.

Using profiles of application behavior for compiler optimization is known as Profile-Guided Optimization (PGO) (also known as Feedback-Directed Optimization (FDO)).

Go 1.20 includes initial support for PGO as a preview. See the profile-guided optimization user guide for complete documentation. There are still some rough edges that may prevent production use, but we would love for you to try it out and send us any feedback or issues you encounter.

Example 示例

Let’s build a service that converts Markdown to HTML: users upload Markdown source to /render, which returns the HTML conversion. We can use gitlab.com/golang-commonmark/markdown to implement this easily.

Set up 创建

$ go mod init example.com/markdown
$ go get gitlab.com/golang-commonmark/markdown@bf3e522c626a

In main.go:

package main

import (
    "bytes"
    "io"
    "log"
    "net/http"
    _ "net/http/pprof"

    "gitlab.com/golang-commonmark/markdown"
)

func render(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
        return
    }

    src, err := io.ReadAll(r.Body)
    if err != nil {
        log.Printf("error reading body: %v", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }

    md := markdown.New(
        markdown.XHTMLOutput(true),
        markdown.Typographer(true),
        markdown.Linkify(true),
        markdown.Tables(true),
    )

    var buf bytes.Buffer
    if err := md.Render(&buf, src); err != nil {
        log.Printf("error converting markdown: %v", err)
        http.Error(w, "Malformed markdown", http.StatusBadRequest)
        return
    }

    if _, err := io.Copy(w, &buf); err != nil {
        log.Printf("error writing response: %v", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
}

func main() {
    http.HandleFunc("/render", render)
    log.Printf("Serving on port 8080...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Build and run the server:

$ go build -o markdown.nopgo.exe
$ ./markdown.nopgo.exe
2023/01/19 14:26:24 Serving on port 8080...
Let’s try sending some Markdown from another terminal. We can use the README from the Go project as a sample document:
$ curl -o README.md -L "https://raw.githubusercontent.com/golang/go/c16c2c49e2fa98ae551fc6335215fadd62d33542/README.md"
$ curl --data-binary @README.md http://localhost:8080/render
<h1>The Go Programming Language</h1>
<p>Go is an open source programming language that makes it easy to build simple,
reliable, and efficient software.</p>

...

Profiling 分析

Now that we have a working service, let’s collect a profile and rebuild with PGO to see if we get better performance.

In main.go, we imported net/http/pprof which automatically adds a /debug/pprof/profile endpoint to the server for fetching a CPU profile.

Normally you want to collect a profile from your production environment so that the compiler gets a representative view of behavior in production. Since this example doesn’t have a “production” environment, we will create a simple program to generate load while we collect a profile. Copy the source of this program to load/main.go and start the load generator (make sure the server is still running!).

$ go run example.com/markdown/load

While that is running, download a profile from the server:

$ curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30"

Once this completes, kill the load generator and the server.

Using the profile We can ask the Go toolchain to build with PGO using the -pgo flag to go build. -pgo takes either the path to the profile to use, or auto, which will use the default.pgo file in the main package directory.

We recommending commiting default.pgo profiles to your repository. Storing profiles alongside your source code ensures that users automatically have access to the profile simply by fetching the repository (either via the version control system, or via go get) and that builds remain reproducible. In Go 1.20, -pgo=off is the default, so users still need to add -pgo=auto, but a future version of Go is expected to change the default to -pgo=auto, automatically giving anyone that builds the binary the benefit of PGO.

Let’s build:

$ mv cpu.pprof default.pgo
$ go build -pgo=auto -o markdown.withpgo.exe

Evaluation 评估

We will use a Go benchmark version of the load generator to evaluate the effect of PGO on performance. Copy this benchmark to load/bench_test.go.

First, we will benchmark the server without PGO. Start that server:

$ ./markdown.nopgo.exe

While that is running, run several benchmark iterations:

$ go test example.com/markdown/load -bench=. -count=20 -source ../README.md > nopgo.txt

Once that completes, kill the original server and start the version with PGO:

$ ./markdown.withpgo.exe

While that is running, run several benchmark iterations:

$ go test example.com/markdown/load -bench=. -count=20 -source ../README.md > withpgo.txt

Once that completes, let’s compare the results:

$ go install golang.org/x/perf/cmd/benchstat@latest
$ benchstat nopgo.txt withpgo.txt
goos: linux
goarch: amd64
pkg: example.com/markdown/load
cpu: Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz
        │  nopgo.txt  │            withpgo.txt             │
        │   sec/op    │   sec/op     vs base               │
Load-12   393.8µ ± 1%   383.6µ ± 1%  -2.59% (p=0.000 n=20)

The new version is around 2.6% faster! In Go 1.20, workloads typically get between 2% and 4% CPU usage improvements from enabling PGO. Profiles contain a wealth of information about application behavior and Go 1.20 just begins to crack the surface by using this information for inlining. Future releases will continue improving performance as more parts of the compiler take advantage of PGO.

Next steps 下一步

In this example, after collecting a profile, we rebuilt our server using the exact same source code used in the original build. In a real-world scenario, there is always ongoing development. So we may collect a profile from production, which is running last week’s code, and use it to build with today’s source code. That is perfectly fine! PGO in Go can handle minor changes to source code without issue.

For much more information on using PGO, best practices and caveats to be aware of, please see the profile-guided optimization user guide.

Please send us your feedback! PGO is still in preview and we’d love to hear about anything that is difficult to use, doesn’t work correctly, etc. Please file issues at https://go.dev/issue/new.

Go runtime: 4 years later