Go: cmd/compile: make 64-bit fields 64-bit aligned on 32-bit systems

Created on 12 Feb 2010  ·  36Comments  ·  Source: golang/go

Even though the 386 has a 4-byte word size, some instructions move 
data in larger chunks.  In particular float64 loads move 8 bytes and
some SSE2 operations (as yet unused) move even larger amounts.
A float64 load from an only-4-byte-aligned address is significantly
more expensive than one from a 8-byte-aligned address.  The same
is probably true of SSE2 too.

We should make sure that data symbols >32 bits (e.g., float64 constants)
are aligned on 8-byte boundaries in the data segment.  (Fairly easy.)

We should also make sure that the stack pointer can be relied upon to be
8-byte aligned (harder), by making stack frame sizes = -4 mod 8 
(the caller PC will add 4 more) and starting new goroutines with a
properly aligned stack pointer.

It might be worth using 16 bytes for the stack alignment.  OS X requires
that for their own ABI, presumably because it matters for SSE2.
NeedsInvestigation

Most helpful comment

Since I was already running into this issue on 32-bit architectures when working on a different change, and then @aclements pointed me at this issue and #19057, I decided to do a prototype change for this issue, and see the problems that I discovered. Actually, there were surprisingly few problems -- some of this was helped out by some of the code that had to be added for amd64p32 a few years. Note that I did not change the alignment of 64-bit stack variables (int64, uint64, float64), since that would change the calling ABI and break a bunch of assembly language functions.

Prototype change is here

We knew that if we changed the default alignment for 64-bit fields on 32-bit architecture, we would have to have some exception/annotation, etc. for Cgo-generated Go structs, so that we could mimic the alignment of C structs. The main unforeseen problem that I ran into was that two Syscall structures on 32-bit architectures, Stat_t and Flock_t (see ztypes_linux_386.go), have a 64-bit field which is not aligned to 64-bits naturally. Therefore, changing the alignment rules according to this issue would change the layout of those structs. That does not work, since those structs are used in Linux system calls. (This may be related to the Apr 15th comment by @schlamar)

So, if we change the default alignment rules, it seems that, for both Cgo purposes and for several system call structs, we will need some kind of solution that specifies a "packed" layout for a struct, which means the layout follows the current rules. This could be a "//go:packed" annotation on a struct type.

What do people think? I believe that many people would like to fix this issue, so that folks on 32-bit architectures do not keep running into the unaligned atomic problem. Are there any suggestions on how to deal with the syscall and Cgo issues other than an annotation on a struct type (or maybe on a struct field)? Is it worth fixing this issue at this point if we have to add a new kind of annotation
to allow for the original semantics?

All 36 comments

Comment 1:

_Labels changed: added priority-someday._

Comment 2:

_Issue #3799 has been merged into this issue._

Comment 3:

This is necessary for atomics to work correctly.

Comment 4:

_Labels changed: added priority-soon, removed priority-someday._

_Status changed to Accepted._

Comment 5:

_Labels changed: added go1.1._

Comment 7:

_Labels changed: added size-l._

Comment 8:

Too hard. I tried a few different things and failed miserably. The gc toolchain expects
that the struct and function argument frame alignments match. The stack frame 
barely matters, since we don't take pointers of stack variables anymore, but that's
hard too.
This only really matters for atomics, and we do guarantee 64-bit alignment for the
base of an allocated struct or slice, so that will have to be good enough for now.

_Labels changed: added priority-someday, removed priority-soon, go1.1._

Comment 9:

_Labels changed: added size-xl, removed size-l._

Comment 10:

_Labels changed: added go1.3maybe._

Comment 11:

_Labels changed: removed go1.3maybe._

Comment 13:

I have had an opportunity to revisit this problem recently in a different context. I
think we can do it for Go 1.3.

_Labels changed: added go1.3._

Comment 14:

_Labels changed: added release-go1.3._

Comment 15:

_Labels changed: removed go1.3._

Comment 16:

_Labels changed: added repo-main._

Comment 17:

We didn't do it for 1.3. My guess is that in comment 13 I was referring to NaCl having
unusual alignment requirements, so perhaps that would pave the way.

_Labels changed: added release-go1.4, removed release-go1.3._

Comment 18:

_Labels changed: added release-none, removed release-go1.4._

Is this still an issue on go 1.5?

Is this still an issue on go 1.5?

Yes. This issue is still open, and has not been assigned a release milestone.

Status: right now there does not appear to be motivation to remove the BUG in the docs and close the issue or ensure struct alignment by the compiler: https://groups.google.com/forum/#!topic/golang-dev/cUju6W3nWEU

CL https://golang.org/cl/34193 mentions this issue.

@davecheney, what's the background for reopening this?

9fe2291 closes issue #18140, not this one

cmd/internal/obj/mips: replace MOVD with MOVF on 32-bit to avoid unaligned memory access

This is the simplest CL that I can make for Go 1.8. For Go 1.9, we can revisit it
and optimize the redundant address generation instructions or just fix #599 instead.

Fixes #18140.

Hah, thanks.

For the record, this is still an open issue on linux/arm and android/arm for go 1.8 (we've just been bitten by it) when doing atomic.AddUint64.

Yes that's true. Go 1.8 is the same as all previous Go versions in this regard. I assume "bitten" means that atomic.AddUint64 panicked. (That's the intended behavior, as opposed to silently doing the wrong thing.)

Panicking is indeed better than silently failing, although the reason/message is obscure (we found out why because of this issue and GroupCache's).
Perhaps the panic message should be more descriptive?

Here was ours:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x4 pc=0x9c74983c]

Since it's just a stop-gap until this is fixed and any additional work would slow down the common case, I think the message is probably fine as is.

My question might be off-topic, but I'd like to know other than plan struct carefully for sync/atomic, what else need to take care of in order to run on ARM32 platforms? It seems all arm-related problems lead to this issue...
Also, is this issue relevant to ARM64 platforms? The doc seems a little outdated:
On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically.

ARM means ARM32. ARM64 platforms have no this issue. On 32bit archs, there are several ways to guarantee 64-bit aligned at compile time and a trick to do this at run time. Please read this article for more explanations.

If I see this correctly, there is another issue besides sync/atomic.

You cannot use a syscall ABI with structs containing 64bit values on 32bit (see https://stackoverflow.com/q/55664457/851737 for details), at least not in a portable way.

Change https://golang.org/cl/186919 mentions this issue: runtime: align allocations harder in GODEBUG=sbrk=1 mode

This was incorrectly closed by a bot.

Change https://golang.org/cl/210637 mentions this issue: cmd/compile: change uint64/int64/float64 in structs to be 8-byte aligned on 32-bit architectures

Since I was already running into this issue on 32-bit architectures when working on a different change, and then @aclements pointed me at this issue and #19057, I decided to do a prototype change for this issue, and see the problems that I discovered. Actually, there were surprisingly few problems -- some of this was helped out by some of the code that had to be added for amd64p32 a few years. Note that I did not change the alignment of 64-bit stack variables (int64, uint64, float64), since that would change the calling ABI and break a bunch of assembly language functions.

Prototype change is here

We knew that if we changed the default alignment for 64-bit fields on 32-bit architecture, we would have to have some exception/annotation, etc. for Cgo-generated Go structs, so that we could mimic the alignment of C structs. The main unforeseen problem that I ran into was that two Syscall structures on 32-bit architectures, Stat_t and Flock_t (see ztypes_linux_386.go), have a 64-bit field which is not aligned to 64-bits naturally. Therefore, changing the alignment rules according to this issue would change the layout of those structs. That does not work, since those structs are used in Linux system calls. (This may be related to the Apr 15th comment by @schlamar)

So, if we change the default alignment rules, it seems that, for both Cgo purposes and for several system call structs, we will need some kind of solution that specifies a "packed" layout for a struct, which means the layout follows the current rules. This could be a "//go:packed" annotation on a struct type.

What do people think? I believe that many people would like to fix this issue, so that folks on 32-bit architectures do not keep running into the unaligned atomic problem. Are there any suggestions on how to deal with the syscall and Cgo issues other than an annotation on a struct type (or maybe on a struct field)? Is it worth fixing this issue at this point if we have to add a new kind of annotation
to allow for the original semantics?

Change https://golang.org/cl/237317 mentions this issue: design/36606-64-bit-field-alignment: add proposal

Duplicate of #36606.

Was this page helpful?
0 / 5 - 0 ratings