LLVM bug means it treats I128/U128 as 8B-aligned
Apparently for at least 5 years, LLVM has had a bug where 128-bit integers are given an alignment of 8 instead of 16. This is why Rust treats i128 and u128 as not FFI-safe - because C defines them to be 16B-aligned.
This causes a few problems for us:
- For dev backends, we'd benefit from 16B alignment, since otherwise various SIMD operations (e.g. on
Dec) will get a lot slower - might even cause crashes on some CPUs. - All backends, including LLVM, need to pick the same alignment - or else you might get different runtime behavior when running with
--optimize. So if dev backend uses 16 and LLVM uses 8, that's a problem. - Different hosts may give different alignments. So for example, if we accept an
i128from Rust, and we're building with the dev backend, we may get something 8B-aligned our generated code assumes is 16B-aligned.
To fix this, we can add a roc_std wrapper around i128/u128 that enforces an alignment of 16 (already done in 37bfa4506c6b5d84f9cbc99546da8cbf4471daf4), and use that for the host boundary in languages (like Rust at least, not sure about Zig) where the alignment for the native i128/u128 type is less than 16. This way, the host will always pass 16B-aligned I128/U128 values to Roc, even if the host itself doesn't enforce that alignment on its own 128-bit numeric operations. This means the dev backend can assume an alignment of 16B without a problem.
Separately, we should manually tell LLVM to use an alignment of 16 for all I128/U128 numbers. This way, when hosts receive these values (including C hosts, which really will assume 16B alignment for them!) it will get the correct 16B alignment no matter which Roc backend was used. This can be done for alloca values by calling set_alignment, but since set_alignment works on InstructionValues, we need to call PointerValue::as_instruction on the PointerValue returned by build_alloca.
Also, we should make sure represent these as structs with one field and _Alignas in the C implementation of roc_std, so that we get the right alignment kind (a struct rather than a number). If we use __i128, things may get passed in registers in C but in pointers in roc's code gen, which could cause problems across the host boundary.
Was this fixed by the recent update to LLVM 18?