-
Notifications
You must be signed in to change notification settings - Fork 13.9k
Description
Thist issue lifts rust-lang/stdarch#248 into rust-lang/rust
Problem
Many intrinsics, in particular, many SIMD intrinsics, work only on immediate-mode registers. We can currently implement them in the compiler directly, handling constant function arguments in the implementation.
This doesn't allow users to easily build abstractions on top of these intrinsics because they can't do what the compiler can do. Abstractions can, however, be built (I show what the stdsimd
crate does below).
Currently, for many intrinsics, we don't do this in the compiler, but in libraries, like the stdsimd
crate (check out, for example, _mm_i32gather_epi32).
In the stdsimd
crate, new contributors typically clash against our workarounds for dealing with these intrinsics pretty early on, often in their first pull-request (e.g. see rust-lang/stdarch#311).
The API of these intrinsics in the stdsimd
crate uses run-time values
fn foo(x: u8, imm8: u8) { ... }
and then it uses constify_xxx!
macros like constify_imm8!
to map these run-time values into compile-time constants:
fn foo(x: u8, imm8: u8) {
macro_rules! call {
($imm8:expr) => (foo_intrinsic(x, $imm8))
}
constify_imm8!(imm8, call)
}
These constify
macros just match against every possible run-time value, and call a function with a constant:
macro_rules! constify_imm8 {
($imm8:expr, $expand:ident) => {
#[allow(overflowing_literals)]
match ($imm8) & 0b1111_1111 {
0 => $expand!(0),
1 => $expand!(1),
2 => $expand!(2),
...
254 => $expand!(254),
_ => $expand!(255),
}
}
}
The status quo does not scale, neither for those wanting to write higher-level wrappers over the stdsimd
crate, nor for some of the intrinsics that require huge match arms (e.g. bextri
is currently disabled because constify_imm16!
makes the compile-times of the stdsimd
crate explode from 1-3 minutes to ~30 minutes).
Ideal solution
I would like to be able to specify that a function argument must be const:
/// This function takes a run-time argument `x`
/// and a constant argument `y`.
fn foo(x: u8, const y: u8) { ... }
This is what the Intel spec tries to specify when it uses const int
. It fails because in C const
does not mean the same thing as in Rust, but this makes it an opportunity: Rust could follow the C spec better than C does.
Alternatives
We could just implement these intrinsics in rustc. There are many many of them, but this can be done. Rustc actually already does this for many intrinsics, and errors of arguments not being constants are already reported.
The main issues I see with this is that
- users can't do this in their own libraries
- the function types do not express that the arguments must be constants which is confusing for users going through the documentation (this is mitigated by good compiler errors though)
Moving these intrinsics into the compiler would allow us to stabilize simd without having to wait for const
function arguments or equivalent language features.