简介
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
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
The go test command now accepts -skip
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
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
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
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
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/
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:
- w happens before r.
- 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<>+0x00(SB)/4, $0xf4f8fcff
DATA divtab<>+0x04(SB)/4, $0xe6eaedf0
...
DATA divtab<>+0x3c(SB)/4, $0x81828384
GLOBL divtab<>(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
(ForTEXT
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
(ForTEXT
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
(ForDATA
andGLOBL
items.) Put this data in a read-only section. -
NOPTR
= 16
(ForDATA
andGLOBL
items.) This data contains no pointers and therefore does not need to be scanned by the garbage collector. -
WRAPPER
= 32
(ForTEXT
items.) This is a wrapper function and should not count as disablingrecover
. -
NEEDCTXT
= 64
(ForTEXT
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
(ForDATA
andGLOBL
items.) Put this data in thread local storage. -
NOFRAME
= 512
(ForTEXT
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
(ForTEXT
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 addressDI
plusBX*2
. -
64(DI)(BX*2)
: The location at addressDI
plusBX*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 shiftR0
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 shiftR0
by the count inR1
. The other codes are->
(arithmetic right shift),>>
(logical right shift), and@>
(rotate right). -
[R0,g,R12-R15]
: For multi-register instructions, the set comprisingR0
,g
, andR12
throughR15
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 value8
by12
bits. -
8(R0)
: Add the value ofR0
and8
. -
(R2)(R0)
: The location atR0
plusR2
. -
R0.UXTB
R0.UXTB<<imm
:UXTB
: extract an 8-bit value from the low-order bits ofR0
and zero-extend it to the size ofR0
.R0.UXTB<<imm
: left shift the result ofR0.UXTB
byimm
bits. Theimm
value can be 0, 1, 2, 3, or 4. The other extensions includeUXTH
(16-bit),UXTW
(32-bit), andUXTX
(64-bit). -
R0.SXTB
R0.SXTB<<imm
:SXTB
: extract an 8-bit value from the low-order bits ofR0
and sign-extend it to the size ofR0
.R0.SXTB<<imm
: left shift the result ofR0.SXTB
byimm
bits. Theimm
value can be 0, 1, 2, 3, or 4. The other extensions includeSXTH
(16-bit),SXTW
(32-bit), andSXTX
(64-bit). -
(R5, R6)
: Register pair forLDAXP
/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 atR5
plusR6
. It is a scaled mode as on the x86, but the only scale allowed is1
.
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 atR1
plus 16. -
(R1)
: Alias for0(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:
-
irgen (aka "-G=3" or sometimes "noder2") is the implementation used starting with Go 1.18, and
-
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:
-
It decomposes complex statements into individual, simpler statements, introducing temporary variables and respecting order of evaluation. This step is also referred to as "order."
-
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).
Type | 64-bit | 32-bit | ||
---|---|---|---|---|
Size | Align | Size | Align | |
bool, uint8, int8 | 1 | 1 | 1 | 1 |
uint16, int16 | 2 | 2 | 2 | 2 |
uint32, int32 | 4 | 4 | 4 | 4 |
uint64, int64 | 8 | 8 | 8 | 4 |
int, uint | 8 | 8 | 4 | 4 |
float32 | 4 | 4 | 4 | 4 |
float64 | 8 | 8 | 8 | 4 |
complex64 | 8 | 4 | 8 | 4 |
complex128 | 16 | 8 | 16 | 4 |
uintptr, *T, unsafe.Pointer | 8 | 8 | 4 | 4 |
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:
- 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.
- If F is a method, assign F’s receiver.
- For each argument A of F, assign A.
- Add a pointer-alignment field to S. This has size 0 and the same
alignment as
uintptr
. - Reset I and FP to 0.
- For each result R of F, assign R.
- Add a pointer-alignment field to S.
- 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.
- Add a pointer-alignment field to S.
Assigning a receiver, argument, or result V of underlying type T works as follows:
- Remember I and FP.
- If T has zero size, add T to the stack sequence S and return.
- Try to register-assign V.
- 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:
- If T is a boolean or integral type that fits in an integer register, assign V to register I and increment I.
- 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
- 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.
- If T is a complex type, recursively register-assign its real and imaginary parts.
- If T is a pointer type, map type, channel type, or function type, assign V to register I and increment I.
- 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).
- If T is a struct type, recursively register-assign each field of V.
- If T is an array type of length 0, do nothing.
- If T is an array type of length 1, recursively register-assign its one element.
- If T is an array type of length > 1, fail.
- If I > NI or FP > NFP, fail.
- 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:
Register | Call meaning | Return meaning | Body meaning |
---|---|---|---|
RSP | Stack pointer | Same | Same |
RBP | Frame pointer | Same | Same |
RDX | Closure context pointer | Scratch | Scratch |
R12 | Scratch | Scratch | Scratch |
R13 | Scratch | Scratch | Scratch |
R14 | Current goroutine | Same | Same |
R15 | GOT reference temporary if dynlink | Same | Same |
X15 | Zero value (*) | Same | Scratch |
(*) 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:
Flag | Bit | Value | Meaning |
---|---|---|---|
FZ | 15 | 0 | Do not flush to zero |
RC | 14/13 | 0 (RN) | Round to nearest |
PM | 12 | 1 | Precision masked |
UM | 11 | 1 | Underflow masked |
OM | 10 | 1 | Overflow masked |
ZM | 9 | 1 | Divide-by-zero masked |
DM | 8 | 1 | Denormal operations masked |
IM | 7 | 1 | Invalid operations masked |
DAZ | 6 | 0 | Do 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:
Register | Call meaning | Return meaning | Body meaning |
---|---|---|---|
RSP | Stack pointer | Same | Same |
R30 | Link register | Same | Scratch (non-leaf functions) |
R29 | Frame pointer | Same | Same |
R28 | Current goroutine | Same | Same |
R27 | Scratch | Scratch | Scratch |
R26 | Closure context pointer | Scratch | Scratch |
R18 | Reserved (not used) | Same | Same |
ZR | Zero value | Same | Same |
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:
Flag | Bit | Value | Meaning |
---|---|---|---|
DN | 25 | 0 | Propagate NaN operands |
FZ | 24 | 0 | Do not flush to zero |
RC | 23/22 | 0 (RN) | Round to nearest, choose even if tied |
IDE | 15 | 0 | Denormal operations trap disabled |
IXE | 12 | 0 | Inexact trap disabled |
UFE | 11 | 0 | Underflow trap disabled |
OFE | 10 | 0 | Overflow trap disabled |
DZE | 9 | 0 | Divide-by-zero trap disabled |
IOE | 8 | 0 | Invalid operations trap disabled |
NEP | 2 | 0 | Scalar operations do not affect higher elements in vector registers |
AH | 1 | 0 | No alternate handling of de-normal inputs |
FIZ | 0 | 0 | Do 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:
Register | Call meaning | Return meaning | Body meaning |
---|---|---|---|
R0 | Zero value | Same | Same |
R1 | Stack pointer | Same | Same |
R2 | TOC register | Same | Same |
R11 | Closure context pointer | Scratch | Scratch |
R12 | Function address on indirect calls | Scratch | Scratch |
R13 | TLS pointer | Same | Same |
R20,R21 | Scratch | Scratch | Used by duffcopy, duffzero |
R30 | Current goroutine | Same | Same |
R31 | Scratch | Scratch | Scratch |
LR | Link register | Link register | Scratch |
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:
Register | Call meaning | Return meaning | Body meaning |
---|---|---|---|
X0 | Zero value | Same | Same |
X1 | Link register | Link register | Scratch |
X2 | Stack pointer | Same | Same |
X3 | Global pointer | Same | Used by dynamic linker |
X4 | TLS (thread pointer) | TLS | Scratch |
X24,X25 | Scratch | Scratch | Used by duffcopy, duffzero |
X26 | Closure context pointer | Scratch | Scratch |
X27 | Current goroutine | Same | Same |
X31 | Scratch | Scratch | Scratch |
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:
-
Line comments start with the character sequence
//
and stop at the end of the line. 单行注释 -
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:
-
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
- an identifier
- an integer, floating-point, imaginary, rune, or string literal
- one of the keywords
break
,continue
,fallthrough
, orreturn
- one of the operators and punctuation
++
,--
,)
,]
, or}
-
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:
+ & += &= && == != ( )
- | -= |= || < <= [ ]
* ^ *= ^= <- > >= { }
/ << /= <<= ++ = := , ;
% >> %= >>= -- ! ... . :
&^ &^= ~
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 (\x
nn) 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 fieldT
, the method sets ofS
and*S
both include promoted methods with receiverT
. The method set of*S
also includes promoted methods with receiver*T
. -
If
S
contains an embedded field*T
, the method sets ofS
and*S
both include promoted methods with receiverT
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 isT
. - 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 ofI
; or -
T
is an interface and the type set ofT
is a subset of the type set ofI
.
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" "<-" | "<-" "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<- float64 // can only be used to send float64s
<-chan int // can only be used to receive ints
The <-
operator associates with the leftmost chan
possible:
chan<- chan int // same as chan<- (chan int)
chan<- <-chan int // same as chan<- (<-chan int)
<-chan <-chan int // same as <-chan (<-chan int)
chan (<-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:
-
There is a single type
U
which is the underlying type of all types in the type set ofT
; or -
the type set of
T
contains only channel types with identical element typeE
, 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:
-
the type
U
; or -
the type
chan E
ifT
contains only bidirectional channels, or the typechan<- 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<- int } // chan<- 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<- string } // channels have different element types
interface{ <-chan int | chan<- 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
andT
are identical. -
V
andT
have identical underlying types but are not type parameters and at least one ofV
orT
is not a named type. -
V
andT
are channel types with identical element types,V
is a bidirectional channel, and at least one ofV
orT
is not a named type. -
T
is an interface type, but not a type parameter, andx
implementsT
. -
x
is the predeclared identifiernil
andT
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 typeT
.
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 identifiernil
,T
is a type parameter, andx
is assignable to each type inT
's type set. -
V
is not a named type,T
is a type parameter, andx
is assignable to each type inT
's type set. -
V
is a type parameter andT
is not a named type, and values of each type inV
's type set are assignable toT
.
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 byT
. -
T
is a floating-point type andx
can be rounded toT
'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, andx
's componentsreal(x)
andimag(x)
are representable by values ofT
's component type (float32
orfloat64
).
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 typeT
. -
The method set of a pointer to a defined type
T
(whereT
is neither a pointer nor an interface) is the set of all methods declared with receiver*T
orT
. - 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:
- The universe block encompasses all Go source text.
- Each package has a package block containing all Go source text for that package.
- Each file has a file block containing all Go source text in that file.
- Each "if", "for", and "switch" statement is considered to be in its own implicit block.
- 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:
- The scope of a predeclared identifier is the universe block.
- 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.
- The scope of the package name of an imported package is the file block of the file containing the import declaration.
- The scope of an identifier denoting a method receiver, function parameter, or result variable is the function body.
- 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.
- 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.
- 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.
- 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:
- the first character of the identifier's name is a Unicode uppercase letter (Unicode character category Lu); and
- 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 << iota // a == 1 (iota == 0)
b = 1 << iota // b == 2 (iota == 1)
c = 3 // c == 3 (iota == 2, unused)
d = 1 << 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 << iota, 1<<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
-
T
implementsC
; or -
C
can be written in the forminterface{ comparable; E }
, whereE
is a basic interface andT
is comparable and implementsE
.
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 < 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 = &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 := &[]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{&Point{1.5, -3.5}, &Point{}}
[2]PPoint{{1.5, -3.5}, {}} // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&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 < 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 <- 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:
-
For a value
x
of typeT
or*T
whereT
is not a pointer or interface type,x.f
denotes the field or method at the shallowest depth inT
where there is such anf
. If there is not exactly onef
with shallowest depth, the selector expression is illegal. -
For a value
x
of typeI
whereI
is an interface type,x.f
denotes the actual method with namef
of the dynamic value ofx
. If there is no method with namef
in the method set ofI
, the selector expression is illegal. -
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
. -
In all other cases,
x.f
is illegal. -
If
x
is of pointer type and has the valuenil
andx.f
denotes a struct field, assigning to or evaluatingx.f
causes a run-time panic. -
If
x
is of interface type and has the valuenil
, calling or evaluating the methodx.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() // (&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 (&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 if0 <= 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 indexx
and the type ofa[x]
is the element type ofA
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 indexx
and the type ofa[x]
is the element type ofS
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 indexx
and the type ofa[x]
isbyte
a[x]
may not be assigned to
For a
of map type M
:
x
's type must be assignable to the key type ofM
- if the map contains an entry with key
x
,a[x]
is the map element with keyx
and the type ofa[x]
is the element type ofM
- if the map is
nil
or does not contain such an entry,a[x]
is the zero value for the element type ofM
For a
of type parameter type P
:
- The index expression
a[x]
must be valid for values of all types inP
'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 isbyte
. - 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 indexx
, or the map element with keyx
of the type argument thatP
is instantiated with, and the type ofa[x]
is the type of the (identical) element types.a[x]
may not be assigned to ifP
'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; &s1[2] == &a[5]
s2 := s1[1:4] // underlying array of s2 is underlying array of s1 which is array a; &s2[1] == &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:
- 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.
- 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:
- apply function argument type inference to all typed ordinary function arguments
- apply constraint type inference
- apply function argument type inference to all untyped ordinary function arguments using the default type for each of the untyped function arguments
- 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 P
→ A
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 → []map[int]bool to substitution map
[]T1 // adds T1 → map[int]bool to substitution map
[]map[T1]T2 // adds T1 → int and T2 → 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 E
→ float64
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 Number
→ float64
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:
- The list Lt contains all (parameter, argument) pairs where the parameter type uses type parameters and where the function argument is typed.
- 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:
- 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.
- 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 T
→ float64
. 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
Elem
→ byte
.
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
- For all type parameters with an adjusted core type, unify the type parameter with that type. If any unification fails, constraint type inference fails.
-
At this point, some entries in M may map type parameters to other
type parameters or to types containing type parameters. For each entry
P
→A
in M whereA
is or contains type parametersQ
for which there exist entriesQ
→B
in M, substitute thoseQ
with the respectiveB
inA
. 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 A
→ int
.
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:
A
→ int
,
B
→ []C
,
C
→ *A
Replace A
on the right-hand side of → with int
:
A
→ int
,
B
→ []C
,
C
→ *int
Replace C
on the right-hand side of → with *int
:
A
→ int
,
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 = "||" | "&&" | rel_op | add_op | mul_op .
rel_op = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op = "+" | "-" | "|" | "^" .
mul_op = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
unary_op = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .
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<<s // 1 has type int
var j int32 = 1<<s // 1 has type int32; j == 0
var k = uint64(1<<s) // 1 has type uint64; k == 1<<33
var m int = 1.0<<s // 1.0 has type int; m == 1<<33
var n = 1.0<<s == j // 1.0 has type int32; n == true
var o = 1<<s == 2<<s // 1 and 2 have type int; o == false
var p = 1<<s == 1<<33 // 1 has type int; p == true
var u = 1.0<<s // illegal: 1.0 has type float64, cannot shift
var u1 = 1.0<<s != 0 // illegal: 1.0 has type float64, cannot shift
var u2 = 1<<s != 1.0 // illegal: 1 has type float64, cannot shift
var v1 float32 = 1<<s // illegal: 1 has type float32, cannot shift
var v2 = string(1<<s) // illegal: 1 is converted to a string, cannot shift
var w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression; w == 1<<33
var x = a[1.0<<s] // panics: 1.0 has type int, but 1<<33 overflows array bounds
var b = make([]byte, 1.0<<s) // 1.0 has type int; len(b) == 1<<33
// The results of the following examples are given for 32-bit ints,
// which means the shifts will overflow.
var mm int = 1.0<<s // 1.0 has type int; mm == 0
var oo = 1<<s == 2<<s // 1 and 2 have type int; oo == true
var pp = 1<<s == 1<<33 // illegal: 1 has type int, but 1<<33 overflows int
var xx = a[1.0<<s] // 1.0 has type int; xx == a[0]
var bb = make([]byte, 1.0<<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 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
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 <= f()
^a >> b
f() || g()
x == y+1 && <-chanInt > 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
& bitwise AND integers
| bitwise OR integers
^ bitwise XOR integers
&^ bit clear (AND NOT) integers
<< left shift integer << integer >= 0
>> right shift integer >> integer >= 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| < |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 >> 2 x & 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
< less
<= less or equal
> greater
>= 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 bothfalse
. - 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
andv
are equal if bothreal(u) == real(v)
andimag(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 valuenil
. -
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 typeX
and a valuet
of interface typeT
can be compared if typeX
is comparable andX
implementsT
. They are equal ift
's dynamic type is identical toX
andt
's dynamic value is equal tox
. - 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 < 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.
&& conditional AND p && 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.
&x
&a[f(2)]
&Point{2, 3}
*p
*pf(x)
var x *int = nil
*x // causes a run-time panic
&*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 := <-ch
v2 = <-ch
f(<-ch)
<-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 = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-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
<-chan int(c) // same as <-(chan int(c))
(<-chan int)(c) // c is converted to <-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 toT
. -
ignoring struct tags (see below),
x
's type andT
are not type parameters but have identical underlying types. -
ignoring struct tags (see below),
x
's type andT
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 andT
are both integer or floating point types. -
x
's type andT
are both complex types. -
x
is an integer or a slice of bytes or runes andT
is a string type. -
x
is a string andT
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
andT
are type parameters and a value of each type inV
's type set can be converted to each type inT
's type set. -
Only
V
is a type parameter and a value of each type inV
's type set can be converted toT
. -
Only
T
is a type parameter andx
can be converted to each type inT
'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:
-
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)
, thenuint32(int8(v)) == 0xFFFFFFF0
. The conversion always yields a valid value; there is no indication of overflow. - When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).
-
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 typefloat32
may be stored using additional precision beyond that of an IEEE-754 32-bit number, but float32(x) represents the result of roundingx
's value to 32-bit precision. Similarly,x + 0.1
may use more than 32 bits of precision, butfloat32(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 和字符串转换
-
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"
-
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'}) // "🌍"
-
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" == "🌎"
-
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'}
-
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:]) // &s1[0] == &s[1]
s2 := (*[2]byte)(s) // &s2[0] == &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 << 3.0 // d == 8 (untyped integer constant)
const e = 1.0 << 3 // e == 8 (untyped integer constant)
const f = int32(1) << 33 // illegal (constant 8589934592 overflows int32)
const g = float64(2) >> 1 // illegal (float64(2) is a typed floating-point constant)
const h = "foo" > "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 << 100 // Huge == 1267650600228229401496703205376 (untyped integer constant)
const Four int8 = Huge >> 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()], <-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:
-
A "return" or
"goto" statement.
-
A call to the built-in function
panic
. -
A block in which the statement list ends in a terminating statement.
-
An "if" statement in which:
- the "else" branch is present, and
- both branches are terminating statements.
-
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.
-
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.
-
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.
- 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()
<-ch
(<-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 "<-" 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 <- 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) = <-ch // same as: k = <-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] <<= 2
i &^= 1<<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:
- Any typed value may be assigned to the blank identifier.
- 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.
-
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 > max {
x = max
}
The expression may be preceded by a simple statement, which executes before the expression is evaluated.
if x := f(); x < y {
return x
} else if x > 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 < 0: return -x
default: return x
}
switch {
case x < y: f1()
case x < 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 < 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 < 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, <-chan E element e E
-
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 tolen(a)-1
and does not index into the array or slice itself. For anil
slice, the number of iterations is 0. -
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 be0xFFFD
, the Unicode replacement character, and the next iteration will advance a single byte in the string. -
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. -
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<- bool) { for { sleep(10); ch <- 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:
- 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.
- 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.
- Unless the selected case is the default case, the respective communication operation is executed.
- 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.
- 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 = <-c1:
print("received ", i1, " from c1\n")
case c2 <- i2:
print("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
print("received ", i3, " from c3\n")
} else {
print("c3 is closed\n")
}
case a[f()] = <-c4:
// same as:
// case t := <-c4
// a[f()] = t
default:
print("no communication\n")
}
for { // send random sequence of bits to c
select {
case c <- 0: // note: no statement, no fallthrough, no folding of cases
case c <- 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:
- 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 < n; i++ {
for j = 0; j < 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 > 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 <= 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 <= len(s) <= 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<<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<<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 << 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<- int) {
for i := 2; ; i++ {
ch <- i // Send 'i' to channel 'ch'.
}
}
// Copy the values from channel 'src' to channel 'dst',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
for i := range src { // Loop over values received from 'src'.
if i%prime != 0 {
dst <- 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 := <-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 formt.m
, where the (static) type oft
is not an interface type, and the methodm
is in the method set oft
. It is immaterial whether the resulting function valuet.m
is invoked. -
A variable, function, or method
x
depends on a variabley
ifx
's initialization expression or body (for functions and methods) contains a reference toy
or to a function or method that depends ony
.
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(&f))
type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&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(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&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(&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:
- For a variable
x
of any type:unsafe.Alignof(x)
is at least 1. - For a variable
x
of struct type:unsafe.Alignof(x)
is the largest of all the valuesunsafe.Alignof(x.f)
for each fieldf
ofx
, but at least 1. - 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 g
s 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
, note
s 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 | |||
---|---|---|---|
Interface | G | M | P |
(rw)mutex | Y | Y | Y |
note | Y | Y | Y/N |
park | Y | N | N |
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:
-
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.
-
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.
-
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:
-
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
. -
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
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
The go test command now accepts -skip
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.