-
Notifications
You must be signed in to change notification settings - Fork 1.6k
rustc_scalable_vector
#3838
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
base: master
Are you sure you want to change the base?
rustc_scalable_vector
#3838
Conversation
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.
I do not think this is the correct approach nor implementation. It is invested in reusing an existing framework in repr(simd)
which has many undesirable features.
Thanks all for the feedback, I got a little bit busy after posting this, so I'll respond as soon as I can. |
Co-authored-by: Jamie Cunliffe <[email protected]>
5f5f7d2
to
9630780
Compare
Thanks everyone for the feedback, I've pushed a bunch of changes: Notable changes:
Small changes:
Hopefully these address many of the concerns raised so far. I don't expect it to address all of them, there are still big unresolved questions to be resolved. My intent with this is just to indicate the rough direction we plan to take so that I can justify an experimental implementation to try and find solutions to some of the issues raised. |
- Cannot be instantiated into generic functions (see | ||
[*Target features*][target-features]) |
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.
Does this also exclude e.g. size_of::<svfloat32_t>()
?
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.
It does, which is unfortunate and I'd prefer to support it. This restriction is only present as requiring the target feature be present when the type is used seems like it won't be possible, and I've not had time to experiment with using an indirect ABI (as per #3838 (comment)), so a restriction here makes the RFC, as written, technically feasible in the meantime.
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.
How do C and C++ compilers deal with this dilemma? Do they support sizeof(svfloat32_t)
as runtime value, and if so, do they either require the target feature to be available for that or implicitly enable it?
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.
It looks like sizeless types are prohibited from being used with sizeof
:
<source>:9:5: error: invalid application of 'sizeof' to sizeless type 'svuint8x3_t' (aka '__clang_svuint8x3_t')
9 | sizeof(svuint8x3_t);
| ^ ~~~~~~~~~~~~~
1 error generated.
- Cannot have trait implementations (see [*Target features*][target-features]) | ||
|
||
- Including blanket implementations (i.e. `impl<T> Foo for T` is not a valid | ||
candidate for a scalable vector) |
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.
I guess this is intended to prevent a scalable vector value and types from bleeding into generic code, without relying entirely on post-mono errors? If so, note that they can also be smuggled in via associated types (slight modification of Ralf's earlier example):
trait Tr { type Assoc; }
fn foo<T: Tr>() {
size_of::<T::Assoc>();
}
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.
Not just avoiding post-mono errors, but just avoiding any potential issues with target features needing to be be applied to trait methods but not the trait definition (#3820 is related to this). I'd expect that smuggling in instantiation via associated types to be prohibited by the existing restriction on generic instantiation - I don't think it's worth elaborating on that too much as I do intend to remove this restriction once investigating using an indirect ABI and hopefully finding that feasible.
- It may be possible to support scalable vector types without the target feature | ||
being enabled by using an indirect ABI similarly to fixed length vectors. | ||
|
||
- This would enable these restrictions to be lifted and for scalable vector | ||
types to be the same as fixed length vectors with respect to interactions | ||
with the `target_feature` attribute. | ||
|
||
- As with fixed length vectors, it would still be desirable for them to | ||
avoid needing to be passed indirectly between annotated functions, but | ||
this could be addressed in a follow-up. | ||
|
||
- Experimentation is required to determine if this is feasible. |
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.
With an indirect ABI it should be possible to make use of scalable vector types without the sve
feature. You will need to avoid emitting any vscale
in the LLVM IR and use variable-length alloca/memcpy for any data movement involving such types. You will need to call a helper function to actually read VL to figure out how big the vectors are.
This could be done in rustc, but it's also possible that this support could be implemented directly in LLVM.
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.
imo the ABI for determining vscale
should be like:
static VSCALE: AtomicUsize = AtomicUsize::new(0);
#[cold]
fn vscale_slow() -> usize {
let mut vscale = 1; // default if no features are available
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))]
if is_aarch64_feature_detected!("sve") {
vscale = svcntb() as usize / 16;
}
// add other arches here
VSCALE.store(vscale, Ordering::Relaxed);
NonZeroUsize::new(vscale).unwrap()
}
#[inline(always)]
pub fn vscale() -> NonZeroUsize {
NonZeroUsize::new(VSCALE.load(Ordering::Relaxed)).unwrap_or_else(vscale_slow)
}
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.
Actually I think you can get away with just a function containing svcntb
if you assume that the presence of an SVE type implies that the sve
feature is enabled since that is required to create an instance of such a type in the first place.
Unfortunately this doesn't work for MaybeUninit<svint32_t>
since you then have to know the size without the guarantee that an sve
-feature function has previously been executed. Is MaybeUninit
expected to support scalable vector types?
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.
Then again, this doesn't need to be super optimized since this is for the slow path: we will most likely lint against any use of a scalable vector type in a function without the appropriate features, and consider it a user mistake to do such a thing.
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.
Actually I think you can get away with just a function containing
svcntb
if you assume that the presence of an SVE type implies that thesve
feature is enabled since that is required to create an instance of such a type in the first place.
no you can't -- you can make a sve type even without the sve feature with something like (avoiding zeroed
, read_unaligned
, etc. since they would require putting sve types in generics):
fn zeroed() -> svuint8_t {
#[repr(C, align(16)] // I'm assuming 16 is enough
struct Zeros([u8; 2048]);
const ZEROS: &'static Zeros = &Zeros([0; 2048]); // long enough for max vl
unsafe { *(ZEROS as *const _ as *const svuint8_t) }
}
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.
With an indirect ABI it should be possible to make use of scalable vector types without the sve feature. You will need to avoid emitting any vscale in the LLVM IR and use variable-length alloca/memcpy for any data movement involving such types. You will need to call a helper function to actually read VL to figure out how big the vectors are.
I expect this will be the only way to make these types feasible, as requiring the target feature be present when the type is used is looking like it isn't. I intend to experiment with this or something like it and see what problems we run into.
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.
Even if the restrictions in the current RFC are extended to plug this hole, I believe the end goal is closer to what’s described in the Sized Hierarchy RFC: scalable vectors should be
Copy
andSized
, just notconst Sized
. So things likesize_of::<svfloat32_t>()
andMaybeUninit<svfloat32_t>
ought to work, and don’t require any values of scalable vector type to have ever existed.
I think this would be ideal but I'll settle for whatever is feasible, if that means that these types exist and you can only really use them with the vendor intrinsics, then that's fine. I think using an indirect ABI will get us at least that far, but I expect we'd need something more to support generic types and functions that have no notion of the relevant target features.
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.
FWIW we make all other vector types indirect in the Rust ABI (and require the corresponding target feature to be present for other ABIs), so it seems not surprising that scalable vectors would work the same.
However it is worth noting that we ended up in that spot kind of by accident, or at least I am not aware of an RFC or much up-front discussion from before stdarch vector types got stabilized.
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.
IIRC that was done because on x86 passing a 256/512-bit SIMD type causes LLVM to use a different mutually-incompatible ABI based on whether or not AVX/AVX512 is enabled, so to avoid that problem and whatever similar problems other architectures might have, the decision was made to always pass SIMD types in memory, with the intention that we might fix that in the future.
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.
Yes. But that was an afterthought, there are old issues where people did manual work-arounds for these ABI differences. The original SIMD support was added to the compiler without considering any of these problems, it seems.
And then actually rejecting those types in non-Rust functions is fairly recent.
9630780
to
442beea
Compare
Supercedes #3268.
Introduces a new attribute,
#[rustc_scalable_vector(N)]
, which can be used to define new scalable vector types, such as those in Arm's Scalable Vector Extension (SVE), or RISC-V's Vector Extension (RVV).rustc_scalable_vector(N)
is internal compiler infrastructure that will be used only in the standard library to introduce scalable vector types which can then be stabilised. Only the infrastructure to define these types are introduced in this RFC, not the types or intrinsics that use it.This RFC has a dependency on #3729 as scalable vectors are necessarily non-const
Sized
.There are some large unresolved questions in this RFC, the current purpose of the RFC is to indicate a intended direction for this work to justify an experimental implementation to help resolve those unresolved questions.
Rendered