128-bit integers are underaligned on x86-64
Consider the following:
@[Extern]
struct Foo
@x = 0_i64
@y = 0_i128
end
foo = Foo.new
pointerof(foo.@x) # => Pointer(Int128)@0x7fffd4e08e60
pointerof(foo.@y) # => Pointer(Int128)@0x7fffd4e08e68
offsetof(Foo, @y) # => 8
sizeof(Foo) # => 24
alignof(Foo) # => 8
Instead, the address of foo.@y should end with a 0 instead, offsetof(Foo, @y) should be 16, sizeof(Foo) should be 32, and alignof(Foo) should be 16. Underalignment could lead to suboptimal code generation or totally incorrect lib bindings.
This is known to happen on x86-64 Windows and Linux. The Apple M2 (AArch64) does not have this issue:
pointerof(foo.@x) # => Pointer(Int64)@0x16f6d3340
pointerof(foo.@y) # => Pointer(Int128)@0x16f6d3350
offsetof(Foo, @y) # => 16
sizeof(Foo) # => 32
alignof(Foo) # => 16
This is fixed in LLVM 18, however alignof(Foo | Int32) or even alignof(Foo | Int128) is still 8, since the data storage for mixed unions has the same alignment as size_t:
https://github.com/crystal-lang/crystal/blob/f66312174d0b7dfa30b8795e356c30b9c23e0649/src/compiler/crystal/codegen/unions.cr#L44-L46
Rust also backported this to LLVM 17 and below: https://blog.rust-lang.org/2024/03/30/i128-layout-update.html