Wandering Thoughts archives

2021-11-14

Go 1.18 will let you set the version of the "AMD64" architecture to target

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 GOAMD64 environment variable which selects a 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 of the versions is here.

The GOAMD64 environment variable defaults to v1.

(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.)

programming/GoAmd64ArchitectureLevels written at 23:43:54; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.