-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Changes from 2 commits
c3b4a8d
ad25344
60926a6
e019825
4c4423f
a4b68d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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}`. | ||
|
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'll break all the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
||
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 |
There was a problem hiding this comment.
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.