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

Add support for 128-bit integers #1504

Merged
merged 6 commits into from
Jul 29, 2016
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions text/0000-int128.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
- Feature Name: int128
- Start Date: 21-02-2016
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

This RFC adds the `i128` and `u128` types to Rust. The `i128` and `u128` are not added to the prelude, and must instead be explicitly imported with `use core::{i128, u128}`.

# Motivation
[motivation]: #motivation

Some algorithms need to work with very large numbers that don't fit in 64 bits, such as certain cryptographic algorithms. One possibility would be to use a BigNum library, but these use heap allocation and tend to have high overhead. LLVM has support for very efficient 128-bit integers, which are exposed by Clang in C as the `__int128` type.

# Detailed design
[design]: #detailed-design

The `i128` and `u128` types are not added to the Rust prelude since that would break compatibility. Instead they must be explicitly imported with `use core::{i128, u128}` or `use std::{i128, u128}`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After rust-lang/rust#32131 addition of new primitive types is not a breaking change, so I think everything related to name resolution can be removed from this RFC and u128/i128 can be resolved as all other primitive types.


Implementation-wise, this should just be a matter of adding a new primitive type to the compiler and adding trait implementations for `i128`/`u128` in libcore. Literals will need to be extended to support `i128`/`u128`.

LLVM fully supports 128-bit integers on all architectures, however it will emit calls to functions in `compiler-rt` for many operations such as multiplication and division (addition and subtraction are implemented natively). However, `compiler-rt` only provides the functions for 128-bit integers on 64-bit platforms (`#ifdef __LP64__`). We will need to provide our own implementations of the following functions to allow `i128`/`u128` to be available on all architectures:

```c
// si_int = i32
// su_int = u32
// ti_int = i128
// tu_int = u128
ti_int __absvti2(ti_int a);
ti_int __addvti3(ti_int a, ti_int b);
ti_int __ashlti3(ti_int a, si_int b);
ti_int __ashrti3(ti_int a, si_int b);
si_int __clzti2(ti_int a);
si_int __cmpti2(ti_int a, ti_int b);
si_int __ctzti2(ti_int a);
ti_int __divti3(ti_int a, ti_int b);
si_int __ffsti2(ti_int a);
ti_int __fixdfti(double a);
ti_int __fixsfti(float a);
tu_int __fixunsdfti(double a);
tu_int __fixunssfti(float a);
double __floattidf(ti_int a);
float __floattisf(ti_int a);
double __floatuntidf(tu_int a);
float __floatuntisf(tu_int a);
ti_int __lshrti3(ti_int a, si_int b);
ti_int __modti3(ti_int a, ti_int b);
ti_int __muloti4(ti_int a, ti_int b, int* overflow);
ti_int __multi3(ti_int a, ti_int b);
ti_int __mulvti3(ti_int a, ti_int b);
ti_int __negti2(ti_int a);
ti_int __negvti2(ti_int a);
si_int __parityti2(ti_int a);
si_int __popcountti2(ti_int a);
ti_int __subvti3(ti_int a, ti_int b);
si_int __ucmpti2(tu_int a, tu_int b);
tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem);
tu_int __udivti3(tu_int a, tu_int b);
tu_int __umodti3(tu_int a, tu_int b);
```

# Drawbacks
[drawbacks]: #drawbacks

One possible complication is that primitive types aren't currently part of the prelude, instead they are directly added to the global namespace by the compiler. The new `i128` and `u128` types will behave differently and will need to be explicitly imported.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still possible to improve name resolution for primitive types and prelude backward compatibly.
I'd define the new i128/u128 in some dedicated module like core::primitives, there's a good chance other primitive types will be defined there as well in some time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding primitive types is a backwards compatible thing to do (for 99.999% of all code).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll break all the i128/u128 emulations using the same names, like https://crates.io/crates/extprim, and their dependencies. (Also, using playpen often prevents posting misinformation.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

u128, i128 are not keywords, so that one is ruled out. However, defining types shadowing primitive types results in E0317, however I think this is justified, since the crates depending on crates using these emulations can easily drop the dependencies, without changing any code (except use and extern crate).


Another possible issue is that a `u128` can hold a very large number that doesn't fit in a `f32`. We need to make sure this doesn't lead to any `undef`s from LLVM. See [this comment](https://github.com/rust-lang/rust/issues/10185#issuecomment-110955148), and [this example code](https://gist.github.com/Amanieu/f87da5f0599b343c5500).

# Alternatives
[alternatives]: #alternatives

There have been several attempts to create `u128`/`i128` wrappers based on two `u64` values, but these can't match the performance of LLVM's native 128-bit integers.

# Unresolved questions
[unresolved]: #unresolved-questions

None