Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i128 / u128 are not compatible with C's definition. #54341

Closed
emilio opened this issue Sep 19, 2018 · 75 comments
Closed

i128 / u128 are not compatible with C's definition. #54341

emilio opened this issue Sep 19, 2018 · 75 comments
Labels
A-ABI Area: Concerning the application binary interface (ABI) A-FFI Area: Foreign function interface (FFI) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@emilio
Copy link
Contributor

emilio commented Sep 19, 2018

While fixing various bindgen bugs related to long double, int128, (rust-lang/rust-bindgen#1370, etc).

I realized that the following Rust program, in my x86_64 Linux machine:

#[repr(C)]
struct Foo {
    f: u128,
}

fn main() {
    println!("Align: {}", ::std::mem::align_of::<Foo>());
}

Prints 8.

While the following C program:

#include <stdio.h>

struct foo {
  unsigned __int128 t;
};

int main() {
  printf("Align: %ld\n", _Alignof(struct foo));
}

Prints 16 on the same system. This is pretty unexpected, and means that i128 / u128 are not really usable for FFI / alignment purposes.

@vext01
Copy link
Contributor

vext01 commented Sep 19, 2018

I think the correct fix would be to add long_double type to libc with the correct alignment.

I guess that's probably not hard? I could have a go at that if it sounds like the right thing to do.

@nagisa
Copy link
Member

nagisa commented Sep 19, 2018

This is a bug in llvm. There was an attempt to fix this in the past, but it got reverted because of reasons.

The sysV __int128 should be 16-byte aligned and rust follows that ABI.

@nagisa
Copy link
Member

nagisa commented Sep 19, 2018

Rather, this is blocked on LLVM data-layouts getting fixed, because rustc expects them to be matched last time I checked.

@emilio
Copy link
Contributor Author

emilio commented Sep 19, 2018

Ugh, thanks @nagisa... I think this is also a problem for long double, reading that LLVM patch they'd align it to 64 bits. Sigh.

#include <stdio.h>

struct foo {
  long double bar;
};

int main() {
  printf("%ld\n", _Alignof(struct foo)); // 16
}

emilio added a commit to emilio/rust-bindgen that referenced this issue Sep 19, 2018
To work-around some cases of rust-lang/rust#54341.

Other cases where u128 and u64 are mixed in fields might not behave correctly,
but the field offset assertions would catch them.

Fixes rust-lang#1370
@strega-nil
Copy link
Contributor

strega-nil commented Oct 10, 2018

Copying from the last thread, since this thread seems to mostly be about long double.

In C and C++, on x86_64 Linux, alignof(__int128) is equal to 16. However, in Rust, align_of::() is equal to 8.

C++: https://gcc.godbolt.org/z/YAq1XC
Rust: https://gcc.godbolt.org/z/QvZeqK

This will cause subtle UB if you ever try to use i128s across FFI boundaries; for example:

C++: https://gcc.godbolt.org/z/PrtHlp
Rust: https://gcc.godbolt.org/z/_SdVqD

@pnkfelix pnkfelix added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Oct 11, 2018
@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 24, 2019

The improper_ctypes warning diagnoses usage of these types in C FFI, so at least users are alerted that this does not work. Playground

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 28, 2019

@Centril @eddyb this should be I-unsound.

@RalfJung
Copy link
Member

Why that? Rust itself is sound. The types are not FFI-safe, but FFI needs unsafe code.

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 28, 2019

Using i128 and u128 on FFI is sound. It currently isn't due to a bug on our end, therefore, this is IMO a soundness bug.

@nagisa
Copy link
Member

nagisa commented Jun 28, 2019

Using i128 and u128 on FFI is sound.

This can be generalized for any T. The complications arise with getting layout right on both sides of the FFI (more specifically it is sound to use repr(Rust) type as long as you manage to get layout of T right on the other side). Since only a few targets have a defined layout for 128-bit integer types and many don’t even have an implementation defined behaviour, it makes perfect sense to delegate them to the same level of FFI support as we do for other repr(Rust) types.

Just to be entirely clear: even if we fixed this particular issue for x86_64 SysV targets, it will only make u128 matching with other x86_64 SysV-abiding artifacts. For most other targets the fact that i/u128 ABI is not specified or is implementation-specific will remain. And so this type can never get any more guarantees than what it currently gets.

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 28, 2019

Since only a few targets have a defined layout for 128-bit integer types

AFAIK all 64-bit targets except for MSVC have a defined __int128. That's a lot of targets.

Since only a few targets have a defined layout for 128-bit integer types and many don’t even have an implementation defined behaviour, it makes perfect sense to delegate them to the same level of FFI support as we do for other repr(Rust) types.

That means that the layout of these types is unspecified, which prevents users from using i128 to interface with C, requiring us to add a libc::__int128 on those platforms that's incompatible with the Rust type, and requiring users to use that in C FFI.

What's the rationale for not providing a layout that's compatible with __int128 on those platforms?

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 28, 2019

For all other integer types and bool, we match the C layout of the platform when the platform has the integer type - the stdint.h header is optional, so not all platforms are required to have it.

EDIT: NVM, for the integer types, stdint.h spec matches our, so if it exists, ours match. For bool we do specify it as being equal to C's _Bool, iff the platform has a _Bool. We could do the same for i128.

@RalfJung
Copy link
Member

RalfJung commented Jun 28, 2019

What's the rationale for not providing a layout that's compatible with __int128 on those platforms?

Nobody argued that we should have a different layout. I agree using a different layout is a bug. I just don't agree it's a soundness bug.

u128/i128 are not FFI-safe when they should be. That's a bug. That's why this issue exists and is still open. We also don't claim that they are FFI safe, so it's not a soundness bug.

Ultimately where to draw the line for soundness bugs can be arbitrary though.

@elichai
Copy link
Contributor

elichai commented Aug 8, 2019

Is anyone working on fixing this?
Trying to understand why does clang and rust have different u128 types https://godbolt.org/z/WyzeRz

@gnzlbg
Copy link
Contributor

gnzlbg commented Aug 9, 2019

Trying to understand why does clang and rust have different u128 types

The reason is that u128 is not FFI safe, so you can't use it on FFI, and therefore it doesn't matter whether it has the same layout as some other clang type.

@elichai
Copy link
Contributor

elichai commented Aug 9, 2019

@gnzlbg but why isn't it FFI safe?
I would've thought they would use the same llvm type

@gnzlbg
Copy link
Contributor

gnzlbg commented Aug 9, 2019

@gnzlbg but why isn't it FFI safe?

See: #54341 (comment)

@RalfJung
Copy link
Member

RalfJung commented Aug 9, 2019

So which targets do have a defined call ABI for passing 128-bit integers? You said everything except the MSVC targets, at least. (EDIT: corrected.)

It was my understanding that this is blocked on LLVM bugs. Not sure if there is an LLVM issue tracking this. But now it sounds more like this is blocked on Rust needing a target-dependent notion of "FFI safe"?

@gnzlbg
Copy link
Contributor

gnzlbg commented Aug 9, 2019

So which targets do have a defined call ABI for passing 128-bit integers?

I don't know of any target that doesn't. I also can't imagine a target for which not having this documented on its ABI wouldn't be considered an ABI spec bug.

You said all the MSVC targets, at least.

I said that the msvc compiler is the only widely-used C compiler I know that does not have an __int128 type, but that's not the only C compiler for this target, and other compilers for this target like clang do have a __int128 type and support using it on the windows msvc targets (https://gcc.godbolt.org/z/vivo9o). I'd suspect this agrees with what GCC does.

If all these other compilers for this target agree, then there is an ABI for __int128 that everybody agrees on for this target. The fact that MSVC doesn't support it just means that MSVC is not able to export symbols or call other symbols that use this type, but that's it.

@gnzlbg
Copy link
Contributor

gnzlbg commented Aug 9, 2019

There might be targets without an ABI spec and/or without C compilers, in which case, we can try to agree with whatever is used on the target, but if that doesn't support 128-bit integers, does it even matter what we do?

@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented Aug 9, 2019

Most 32 bit C toolchains don't have __int128 AFAIK. Definitely not 32 bit x86 Linux.

bridiver pushed a commit to bridiver/rust that referenced this issue Dec 12, 2023
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341

add missing windows targets
maurer added a commit to maurer/rust that referenced this issue Dec 13, 2023
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
maurer added a commit to maurer/rust that referenced this issue Dec 13, 2023
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
maurer added a commit to maurer/rust that referenced this issue Dec 13, 2023
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
maurer added a commit to maurer/rust that referenced this issue Dec 13, 2023
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
maurer added a commit to maurer/rust that referenced this issue Dec 13, 2023
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
maurer added a commit to maurer/rust that referenced this issue Dec 14, 2023
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
maurer added a commit to maurer/rust that referenced this issue Dec 15, 2023
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

This alignment change was discussed in rust-lang/compiler-team#683

See rust-lang#54341 for additional information about why this is happening and
where this will be useful in the future.

This *does not* stabilize `i128`/`u128` for FFI.
bors added a commit to rust-lang-ci/rust that referenced this issue Dec 15, 2023
LLVM 18 x86 data layout update

With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to 16-bytes on x86 based platforms. This will be in LLVM-18. This patch updates all our spec targets to be 16-byte aligned, and removes the alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
maurer added a commit to maurer/rust that referenced this issue Jan 5, 2024
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
maurer added a commit to maurer/rust that referenced this issue Jan 5, 2024
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

This alignment change was discussed in rust-lang/compiler-team#683

See rust-lang#54341 for additional information about why this is happening and
where this will be useful in the future.

This *does not* stabilize `i128`/`u128` for FFI.
bors added a commit to rust-lang-ci/rust that referenced this issue Jan 9, 2024
LLVM 18 x86 data layout update

With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to 16-bytes on x86 based platforms. This will be in LLVM-18. This patch updates all our spec targets to be 16-byte aligned, and removes the alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
nikic pushed a commit to nikic/rust that referenced this issue Jan 17, 2024
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

This alignment change was discussed in rust-lang/compiler-team#683

See rust-lang#54341 for additional information about why this is happening and
where this will be useful in the future.

This *does not* stabilize `i128`/`u128` for FFI.
cuviper pushed a commit to cuviper/rust that referenced this issue Jan 18, 2024
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

This alignment change was discussed in rust-lang/compiler-team#683

See rust-lang#54341 for additional information about why this is happening and
where this will be useful in the future.

This *does not* stabilize `i128`/`u128` for FFI.
bors added a commit to rust-lang-ci/rust that referenced this issue Jan 19, 2024
LLVM 18 x86 data layout update

With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to 16-bytes on x86 based platforms. This will be in LLVM-18. This patch updates all our spec targets to be 16-byte aligned, and removes the alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

See rust-lang#54341
nikic pushed a commit to maurer/rust that referenced this issue Jan 19, 2024
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and removes the
alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

This alignment change was discussed in rust-lang/compiler-team#683

See rust-lang#54341 for additional information about why this is happening and
where this will be useful in the future.

This *does not* stabilize `i128`/`u128` for FFI.
bors added a commit to rust-lang-ci/rust that referenced this issue Jan 20, 2024
LLVM 18 x86 data layout update

With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to 16-bytes on x86 based platforms. This will be in LLVM-18. This patch updates all our spec targets to be 16-byte aligned, and removes the alignment when speaking to older LLVM.

This results in Rust overaligning things relative to LLVM on older LLVMs.

This implements MCP rust-lang/compiler-team#683.

See rust-lang#54341
kxxt pushed a commit to kxxt/rust that referenced this issue Feb 2, 2024
With https://reviews.llvm.org/D86310 LLVM now has i128 aligned to
16-bytes on x86 based platforms. This will be in LLVM-18. This patch
updates all our spec targets to be 16-byte aligned, and ignores the
alignment change when checking what the LLVM's default spec is on
the relevant targets for older LLVMs.

* This is a potentially breaking change, as u128 and i128 will have
  their alignment increase on some platforms.
* This results in Rust overaligning things relative to LLVM on older
  LLVMs.

See rust-lang#54341
@tgross35
Copy link
Contributor

Latest status: this is fixed in latest Rust with the latest LLVM (default starting with 1.78), see https://blog.rust-lang.org/2024/03/30/i128-layout-update.html. We still need to figure out what to do with the improper_ctypes lint since there are broken conditions with older LLVM versions, see rust-lang/lang-team#255.

I think it is okay to close this huge issue and just track the lint changes at at the lang team issue.

@RalfJung
Copy link
Member

RalfJung commented Aug 3, 2024

I think it is okay to close this huge issue and just track the lint changes at at the lang team issue.

All right, let's close this then. :) Thanks a lot to everyone who worked on fixing this long-standing problem!

@RalfJung RalfJung closed this as completed Aug 3, 2024
bocharov added a commit to bocharov/entropy-map that referenced this issue Oct 18, 2024
The `aarch64` and `x86_64` architectures have alignment differences for `u128` type:
* `aarch64` has 16 bytes alignment
* `x86_64` has 8 bytes aligment

In order to fix the issue we're going to use `[u8; 16]` instead of `u128`, which will always have alignment of 8 bytes and have backwards compatibility with existing `x86_64` serialized data.

See the following issues for more details:
* rust-lang/rust#54949
* rkyv/rkyv#409
* rust-lang/rust#54341
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ABI Area: Concerning the application binary interface (ABI) A-FFI Area: Foreign function interface (FFI) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests