Skip to content

wasm u128/i128 is improperly aligned #133991

Closed
llvm/llvm-project
#119204

Description

@V0ldek

The u128 and i128 types on the wasm targets has alignment of 8. This is inconsistent with the alignment on all Tier-1 targets.

Compile this code:

#![crate_type = "cdylib"]
#[no_mangle]
pub extern "C" fn align_of_u128() -> usize {
    std::mem::align_of::<u128>()
}

Godbolt link.

The body of this function on all Tier-1 targets is "return 16", while on wasm32-unknown-unknown, wasm32-unknown-emscripten, and wasm32-wasi it compiles to "return 8".

This causes arrays that are aligned on wasm to no longer be aligned when pointers are passed through FFI to the host. For example, creating a Vec<u128> in wasm and returning it as slice to the host causes misalignment.

I am not sure if this is a compiler bug or if this is for some reason intentional, but it is very annoying. A workaround is to declare your own this wrapper to force alignment and use it throughout:

#[repr(C, align(16))]
struct au128(u128);

however that doesn't play well with external libraries that use u128.

Meta

This happens both on stable 1.83 (see the Godbolt link above) and my nightly from 2024-12-05

rustc --version --verbose:

rustc 1.85.0-nightly (c94848c04 2024-12-05)
binary: rustc
commit-hash: c94848c046d29f9a80c09aae758e27e418a289f2
commit-date: 2024-12-05
host: x86_64-unknown-linux-gnu
release: 1.85.0-nightly
LLVM version: 19.1.5

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Dec 7, 2024
  2. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    O-wasmTarget: WASM (WebAssembly), http://webassembly.org/
    A-alignArea: alignment control (`repr(align(N))` and so on)
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Dec 7, 2024
  3. hanna-kruppe commented on Dec 7, 2024

    @hanna-kruppe
    Contributor

    Quite a few non-wasm targets have eight byte alignment for u128 and i128 (e.g., 32-bit Arm and RISC-V targets). I don't know of any that are tier 1 right now, but tiers can change over time, tier 1 is a very high bar, and many tier 2 targets are widely used. In fact, some tier 1 targets had 8-byte-aligned u128 until earlier this year. So I would strongly advice against assuming any particular alignment for those types.

    That said, in general i128 and u128 should be interoperable with C's 128-bit integer types when they exist. I don't know when Clang started supporting these types in wasm, but WebAssembly/tool-conventions#240 indicates that they're supported now with 16 byte alignment. So Rust may have to increase the alignment of these types on the wasm targets anyway for interop with C code that's also compiled to wasm. That doesn't change the fact that non-wasm code shouldn't assume it agrees with wasm code on the alignment.

    A workaround is to declare your own this wrapper to force alignment and use it throughout:

    Another solution is to use u128 on the wasm side but not on the host side, at least not for talking about slices pointing into wasm memory. You can avoid assuming any alignment for things in wasm memory and do unaligned reads via uN::from_le_bytes. Explicitly using little endian byte order also makes the host code portable to big endian platforms (wasm is always little-endian).

  4. V0ldek commented on Dec 7, 2024

    @V0ldek
    ContributorAuthor

    The reason I mention Tier 1 targets is that wasm is a bit "special", especially wasi, in that it often needs to interop with host functions. Different alignments between x86 and idk PowerPC are unlikely to matter, whereas wasm is by necessity embedded in another target.

  5. hanna-kruppe commented on Dec 7, 2024

    @hanna-kruppe
    Contributor

    Sure, but the host code isn't limited to tier 1 Rust targets. As a concrete example, wasmtime explicitly supports several targets that are or were tier 2 in rustc (currently Linux s390x and Linux riscv64, also macOS aarch64 was only promoted to Rust tier 1 recently). Rust target tiers are basically never relevant for "do I need to worry about this target when writing portable code?" because the tiers don't have any inherent meaning, they only reflect how much a target is currently tested in rust-lang/rust CI.

  6. workingjubilee commented on Dec 8, 2024

    @workingjubilee
    Member

    Yes.

    We do not intend to alter our wasm ABI conformance based on non-wasm targets.

    We do have to alter the alignment to match the ABI documented by tool-conventions.

  7. workingjubilee commented on Dec 8, 2024

    @workingjubilee
    Member

    cc @sunfishcode and @alexcrichton Should this be done upstream in LLVM? Like was done in llvm/llvm-project@dbad963 for SPARC.

  8. sunfishcode commented on Dec 9, 2024

    @sunfishcode
    Member

    It seems the situation is that Clang defaults to aligning __int128_t to 16 bytes, while LLVM datalayout strings default to aligning i128 to 8 bytes. Wasm is currently using the defaults for both, so it's inconsistent.

    It appears the fix for SPARC was to add an explicit i128 entry to the datalayout string to override the default. That sounds like it would be a reasonable fix for Wasm too.

    Another option would be to change clang, to align __int128_t to 8 bytes. Wasm doesn't have a i128 type and seems unlikely to add one for the foreseeable future (there is a discussion about i128 arithmetic, but that would use pairs of i64s). That would be a C ABI break, but __int128_t isn't that widely used, and technically it's not documented ABI yet, as WebAssembly/tool-conventions#240 hasn't landed yet. However, if Wasm ever adds a 128-bit atomic, it seems likely to require 16-byte alignment, since aarch64 and x86_64 both do, and clang would want to use it for _Atomic __int128_t and std::atomic<__int128_t>, and I don't know of any precedent for having alignment differ between atomic and non-atomic types.

    So overall, adding -i128:128 to Wasm's datalayout string sounds like the best path to me. I've now posted llvm/llvm-project#119204 with a patch similar to llvm/llvm-project@dbad963, for WebAssembly.

  9. workingjubilee commented on Dec 9, 2024

    @workingjubilee
    Member

    Thanks!

  10. added 2 commits that reference this issue on Dec 9, 2024
    c45088c
    dd2939a
  11. added a commit that references this issue on Dec 10, 2024
    c5ab70c
  12. added a commit that references this issue on Jul 19, 2026
    eaaeef7
  13. added a commit that references this issue on Oct 7, 2026
    6254ff1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-alignArea: alignment control (`repr(align(N))` and so on)C-bugCategory: This is a bug.O-wasmTarget: WASM (WebAssembly), http://webassembly.org/T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions