The "amd64" architecture is what Go calls 64-bit x86 (often referred to as the more vendor neutral x86-64, although AMD did create it); it's the most common architecture on Unix servers these days. As we all know, the (64-bit) x86 architecture has evolved over the years to add more and more instructions that cover more and more situations. Some of these are various generations of vector/SIMD extensions ( eg ) and some of them are for common operations, like POPCNT and other bit manipulation instructions . In 2020, a cross-vendor collaboration established a series of x86-64 architecture levels .
Up through Go 1.17, Go's AMD64 support targeted the baseline architecture level that all ax86-64 CPUs support. The Go compiler only generated these baseline instructions, although some internal runtime functions would check for more advanced CPUs and switch to more efficient implementations on them. In Go 1.18, Go has added support for a new environment variable,
$GOAMD64
, that's described this way in the current (draft) release notes:
Go 1.18 introduces the new
GOAMD64environment variable which selects a version of the AMD64 architecture. Allowed values arev1,v2,v3, orv4. Each higher level requires, and takes advantage of, additional processor features. A detailed description of the versions is here .The
GOAMD64environment variable defaults tov1.
(What Go calls
v1
is the baseline level. For more background and discussion, see issue #45453 , and perhaps CL 349595 .)
Although it's not mentioned in the release notes, a program compiled with a higher GOAMD64 level than the current CPU supports will fail on startup with a message about this.
If you're a curious person, you might be inclined to wonder both what GOAMD64 affects and which specific versions make the most difference. As of today (mid-November 2021), the answer is that much of the differences currently happen at
v3
, although
v2
also makes some difference, and there seem to be two forms of changes.
First, there are some compiler intrinsic functions (such as math.FMA ) that normally compile to a guarded use of instructions like
FMA
that's conditional on the CPU being capable enough. If you build with an appropriate GOAMD64 value, the guard is omitted and the compiler just directly emits the applicable instructions. You can see this in ssagen/ssa.go by searching for uses of
buildcfg.GOAMD64
. At the moment, math.FMA changes for
v3
, while math.bits.OnesCount* and some math rounding functions change for
v2
.
Second, in code generation setting a GOAMD64 level can change some instructions used for various operations to what are presumably more efficient versions. This happens in ssa/gen/AMD64.rules , which will do things like generate
TZCNTQ
and
TZCNTL
instructions; again you can see this by searching for
buildcfg.GOAMD64
. At the moment, all of these changes happen for
v3
. These conditional instruction selections appear to ripple through to functions like some of the math.bits.TrailingZeros* family (see ssagen/ssa.go again).
(I don't know enough about Go's compiler to know if these SSA pseudo-instructions can be generated through things other than those intrinsic functions.)