-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Rust cannot pass a vector constant directly to LLVM #118209
Comments
Cc @Amanieu @workingjubilee @oli-obk (once you're back from vacation) Generally our policy was that we encode such arguments as const generics, which forces them to be constants. Otherwise this seems impossible to do reliably, after all the user could easily write a function like fn myfun(x: int8x8_t) {
vqshlu_n_s8::<2>(x);
} What is supposed to happen for such functions? Certainly we cannot require Is there a list of intrinsics this affects? I only know of |
Looking at the stdarch code here, maybe I misunderstood the description: pub unsafe fn vqshlu_n_s8<const N: i32>(a: int8x8_t) -> uint8x8_t {
static_assert_uimm_bits!(N, 3);
#[allow(improper_ctypes)]
extern "unadjusted" {
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vqshiftsu.v8i8")]
fn vqshlu_n_s8_(a: int8x8_t, n: int8x8_t) -> uint8x8_t;
}
vqshlu_n_s8_(a, int8x8_t(N as i8, N as i8, N as i8, N as i8, N as i8, N as i8, N as i8, N as i8))
} It's not the argument to Does it work if we use an inline const block, like I don't think we have to equip the entire Rust compiler with support for vector immediates. |
Yes sorry, the issue is in the call to That inline const block only works with the patch I have written. I don't believe that a constant vector is currently created/propagated as an inline const block or as a const generic. If there is precedent for this kind of special casing and you are happy for us to special case a decent number of them then I can do that. |
I don't know if this situation is similar to Long-term, we want shufflevector to take a const generic instead of what looks like a regular argument but must actually be known at compile-time. So ideally we'd implement that instead of expanding the use of the existing shufflevector hack. Either way the tricky part will be properly declaring this in the intrinsic import. The signature of extern "unadjusted" {
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vqshiftsu.v8i8")]
#[rustc_intrinsic_const_vector_arg(1)] // the argument with index 1 must be a constant vector
fn vqshlu_n_s8_(a: int8x8_t, n: int8x8_t) -> uint8x8_t;
} or extern "unadjusted" {
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vqshiftsu.v8i8")]
#[rustc_intrinsic_const_generic_arg(1)] // the const generic should be passed to the intrinsic as argument index 1
fn vqshlu_n_s8_<N: int8x8_t>(a: int8x8_t) -> uint8x8_t;
} The latter being the more principled approach, the former closer to the current shufflevector hack. |
One complication here is that the AArch64 Neon intrinsics are stable, and the first |
How that? |
Oh, of course, sorry. Yes, we could do that with a few changes to the generator. |
Do you have a suggestion for how a constant vector could be constructed and passed as a generic const parameter without turning on |
Hm, I was imagining something like this... #![feature(adt_const_params)]
#![feature(generic_const_exprs)]
use std::marker::ConstParamTy;
#[derive(ConstParamTy, PartialEq, Eq)]
struct int8x2_t(i8, i8);
fn vqshlu_n_s8_<const N: int8x2_t>(a: int8x2_t) -> int8x2_t { todo!() }
fn vqshlu_n_s8<const N: i32>(a: int8x2_t) -> int8x2_t {
vqshlu_n_s8_::<{ int8x2_t(N as i8, N as i8) }>(a)
} But that requires a bunch of features still marked as "incomplete". @lcnr what do you think is the best way to handle intrinsics that need as argument a vector that must be known at compile-time? Given the current status of const generics, maybe extending the shufflevector hack is a more realistic plan. |
I think internally depending on with that, could the following work?
idk how simd intrinsics are implemented, but could we convert |
Okay, good to know. adt_const_params is also marked incomplete so I wasn't sure if this is usable yet. Is there any way to avoid depending on generic_const_exprs by using inline const blocks or so? I tried Sadly, even the associated-const trick seems to require generic_const_exprs: fn vqshlu_n_s8<const N: i32>(a: int8x2_t) -> int8x2_t {
struct S<const N: i32>;
impl<const N: i32> S<N> {
const B: int8x2_t = int8x2_t(N as i8, N as i8);
}
vqshlu_n_s8_::<{ S::<N>::B }>(a)
} And even then it doesn't work since it says it needs a
I don't know if all callers of |
I think that warning can be removed by now. I am not working on const generics myself anymore and it that work has generally pretty much stalled, so I don't know the exact status there.
once a constant is used in the type system, you need gce if it's not concrete of a generic parameter by itself. This means you cannot have generic expressions as generic args for functions without gce. One hack would be the following: #[derive(PartialEq, Eq)]
struct int8x2_t(i8, i8);
#[lang = "ToValueTrait"]
trait ToValue<T> {
#[lang = "ToValueValue"]
const VALUE: T;
}
extern "rust-intrinsics" {
fn vqshlu_n_s8_<T: ToValue<int8x2_t>>(a: int8x2_t) -> int8x2_t;
}
fn vqshlu_n_s8<const N: i32>(a: int8x2_t) -> int8x2_t {
struct Converter<const N: i32>;
impl<const N: i32> ToValue<int8x2_t> for Converter<N> {
const VALUE: int8x2_t = int8x2_t(N as i8, N as i8);
}
vqshlu_n_s8_::<Converter<N>>(a)
} this means we don't have to emulate the computation in the compiler, we can lookup the associated type and compute it. It doesn't need gce as the associated const is never used in the type system. Still have to decide how to deal with evaluation failures when evaluating the associated const, but 🤷 |
Yeah at that point I think I'd prefer building on the shufflevector hack.^^ |
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
… as constants This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic. fix rust-lang#118209
I've opened a fresh pull request for this: #128537 which incoporates feedback from a previous pull request |
…r=RalfJung,nikic const vector passed through to codegen This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic rust-lang#118209 r? RalfJung
Rollup merge of rust-lang#128537 - Jamesbarford:118980-const-vector, r=RalfJung,nikic const vector passed through to codegen This allows constant vectors using a repr(simd) type to be propagated through to the backend by reusing the functionality used to do a similar thing for the simd_shuffle intrinsic rust-lang#118209 r? RalfJung
This issue is referenced in stdarch here in missing_aarch64.txt. under "LLVM select error in debug builds".
Certain intrinsics in LLVM require a vector constant to be passed directly as an argument, one of the reasons for this might be that it needs to reduce that vector down to a duplicated scalar immediate that can be encoded in the generated instruction. Rust cannot pass vector constants directly and so relies on the CSE LLVM pass to propagate the loaded constant vector to the intrinsic call. This pass is not used at
-O0
and so compilation fails withLLVM ERROR: Cannot select: intrinsic %llvm.aarch64.neon.sqshlu
for example.This is an example snippet that fails to compile:
There are a number of options to resolve this issue when it comes to Neon intrinsics specifically.
Meta
rustc --version --verbose
:The text was updated successfully, but these errors were encountered: