Skip to content

const function arguments for intrinstrics/simd #47980

@gnzlbg

Description

@gnzlbg

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions