Skip to content

Go specification makes very few address alignment guarantees

Go101 edited this page Oct 24, 2022 · 10 revisions

Go specification only specifies a little on address alignment guarantees:

The following minimal alignment properties are guaranteed:

  1. For a variable x of any type: unsafe.Alignof(x) is at least 1.
  2. For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1.
  3. For a variable x of array type: unsafe.Alignof(x) is the same as the alignment of a variable of the array's element type.

A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.

But the popular Go compilers, gc (the one in Go Toolchain) and gccgo, make more address alignment guarantees. They both make the following guarantees:

type                      alignment guarantee (bytes)
------                    ------
bool, byte, uint8, int8   1
uint16, int16             2
uint32, int32             4
float32, complex64        4
arrays                    depend on element types
structs                   depend on field types
other types               size of a native word (4 for 32-bit arch, 8 for 64-bit arch)

Some standard packages comply with the guarantees made by gc and gccgo instead of Go specification. For example, the implementation of sync.WaitGroup assume the address alignment guarantee of uint32 values is 4.

At least up to Go 1.13, the 64-bit atomic functions provided in the sync/atomic standard package require the operated arguments must be 8-byte aligned, so the following program might panic on 32-bit OSes, because the address of v.y is not guaranteed to be 8-bytes aligned.

package main

import "sync/atomic"

type T struct {
    x int32
    y int64
}

func main() {
    var v T
    _ = atomic.LoadInt64(&v.y)
}

For the same reason, the following program might also panic in running. The reason is the addresses of the i field of v.y and the f field of v.z might not be 8-byte aligned on 32-bit OSes, but their Add method applies 64-bit atomic functions to the two fields.

package main

import "expvar"

type T struct {
    x int32
    y expvar.Int
    z expvar.Float
}

func main() {
    var v T
    v.y.Add(1)
    v.z.Add(0.1)
}

There are some situations compilers should guarantee 64-bit numeric values are 8-byte aligned on any arch/OS.

There is a proposal which proposes 64-bit numeric values should be always 8-byte aligned. At present (Go 1.13), the proposal has not been accepted yet.

(Update: although the just mentioned proposal is not accepted yet, since Go 1.19, two types, atomic.Int64 and atomic.Uint64, has been added to Go. Values of the two types are always 8-byte aligned, even on 32-bit architectures.)

Clone this wiki locally