Skip to content
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

Support for a scalable simd representation #118917

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

JamieCunliffe
Copy link
Contributor

As requested here the changes required to allow for scalable vector types to be represented.

A few of the restrictions on the types that are stated in the RFC haven't yet been implemented. I'll make a start on those, but I thought it would be worth getting some comments on some of the fundamental changes in here sooner rather than later.

r? @Amanieu

The representation of the element type has been changed to be a slice
rather than a zero length array. Two feature gates are now required in
core_arch unsized fn params and unsized locals.

This still leaves unsized return types being an issue. For this we are
currently bypassing some of the `Sized` trait checking to pass when
the type is scalable simd.

This still leaves the copy issue. For that we have marked scalable
simd types as trivally pure clone copy. We have still had to remove
some trait checks for the copy trait with this though as they are
still performed in certain situations.

The implementation of `transmute` is also an issue for us. For this a
new SIMD intrinsic has been created simd_reinterpret which performs a
transmute on SIMD vectors. A few intrinsics need to be able to produce
an LLVM `undef` this intrinsic will also produce that when given a
zero sized input.
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Dec 13, 2023
@Nilstrieb
Copy link
Member

cc @rust-lang/types for awareness about proposed scalable SIMD types, which are proposed to contain several major type system special cases. See the RFC linked in the description for more details.

@@ -122,7 +122,14 @@ pub(super) fn check_fn<'a, 'tcx>(
hir::FnRetTy::DefaultReturn(_) => body.value.span,
hir::FnRetTy::Return(ty) => ty.span,
};
fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::SizedReturnType);

if !declared_ret_ty.is_scalable_simd() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a TODO to make sure it's not accidentally committed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should allow scalable simd parameters without requiring params_can_be_unsized.

Copy link
Member

@compiler-errors compiler-errors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some questions that I had, I know this is still a draft -- just want to not forget + also be subscribed to this PR 😜

@@ -583,7 +583,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
infer::BoundRegionConversionTime::FnCall,
fn_sig.output(),
);
self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType);
if !output.is_scalable_simd() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

@@ -265,7 +265,10 @@ fn typeck_with_fallback<'tcx>(

for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
let ty = fcx.normalize(span, ty);
fcx.require_type_is_sized(ty, span, code);
// ScalableSIMD: Justify this.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a TODO or a very detailed explanation here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should be moved to the call sites of require_type_is_sized_deferred.

@@ -2874,6 +2888,10 @@ impl<'tcx> Ty<'tcx> {
/// This is mostly useful for optimizations, as these are the types
/// on which we can replace cloning with dereferencing.
pub fn is_trivially_pure_clone_copy(self) -> bool {
if self.is_scalable_simd() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this true?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, for the record, this is unsound. For a type to implement Copy, it must implement Sized, since Sized is one of Copy's supertraits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's one of the problems we have with these types. We need them to be Copy but we can't implement the trait due to the bounds. These types should be trivially copyable as they are just a register and (for SVE anyway) can be copied with a mov instruction. It was a discussion with @Amanieu a while ago that lead to this approach, I'm definitely open to alternative suggestions as to how this could be done though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be moved to is_copy_modulo_regions. Possibly further up, since it might depend on what the callers of that function are checking.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_copy_modulo_regions must agree with the implementation of Copy. As I said before, these types cannot implement Copy soundly while also being unsized.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then perhaps this exception should be moved up to the callers of is_copy_modulo_regions where we can get a better idea of what special handling is needed.

Copy link
Contributor

@lcnr lcnr Apr 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that these types also have the requirements to only be used in registers, I would expect that trying to use too many if these variables at the same time is likely also not supported 🤔 Looking at the RFC I don't fully understand the requirements for these types, both the questions by RalfJ, e.g. https://github.com/rust-lang/rfcs/pull/3268/files#r1500263066, but also why is it ok to reference them if they may only be stored in registers?

How does LLVM model svfloat64_t types? I would expect them to encounter many of the same issues? edit: I found https://llvm.org/devmtg/2021-11/slides/2021-OptimizingCodeForScalableVectorArchitectures.pdf 🤔 they made a breaking change to how they store type sizes?

Copy link
Contributor

@lcnr lcnr Apr 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if references to these simd registers are apparently ok and you also want their scope to be limited, could something like teh following work (where these simd registers are extern types with special behavior during codegen)

// An invariant token,similar to `scoped_threads` in std
#[derive(Copy, Clone)
struct Scope<'scope>(Phantomdata<*mut &'scope ()>);
fn sve_scope<F: for<'scope> Fn(Scope<'scope>) -> R>(f: F) -> R;

extern {
    type svbool<'scope>;
}

impl<'scope> Scope<'scope> {
    fn whilelt_b64(self, ..) -> &'scope mut svbool<'scope>;
    // ...
}

Given that I don't fully understand the constraints here there may definitely be issues with this approach

@@ -199,6 +199,8 @@ declare_features! (
(internal, prelude_import, "1.2.0", None, None),
/// Used to identify crates that contain the profiler runtime.
(internal, profiler_runtime, "1.18.0", None, None),
/// Allows the use of scalable SIMD types.
(unstable, repr_scalable, "CURRENT_RUSTC_VERSION", None, None),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should get a tracking issue and moved to the feature-group-start: actual feature gates section.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if the version you end up landing is not entirely complete, it should be marked as incomplete instead of unstable.


#[repr(simd, scalable(16))] //~ error: Scalable SIMD types are experimental
struct Foo {
_ty: [i8; 0],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type is a lie and will give issues for Foo { _ty: [] }. Maybe use _ty: [i8] instead? That will also automatically mark it as !Sized.

@@ -422,6 +426,11 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
}))
}

Abi::ScalableVector { .. } => Ok(HomogeneousAggregate::Homogeneous(Reg {
kind: RegKind::ScalableVector,
size: Size::from_bits(128),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This size is a lie. Would panicking or returning Err instead work? Or is it genuinely possible for a struct that is passed as argument to contain a scalable vector?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the RFC I did state that these types can't be inside a struct (currently no checking to enforce that though). We might want to relax that in the future though. Currently we could panic or return an error here, I'll make this an error for now.

@@ -370,6 +370,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
return;
}
// FIXME: Don't spill scalable simd, this works for most of them however,
// some intermediate types can't be spilled e.g. `<vscale x 4 x i1>`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure even with this change, we do end up putting every value that cg_ssa couldn't determine as having a single assignment on the stack anyway.

@@ -124,6 +124,7 @@ impl LlvmType for Reg {
_ => bug!("unsupported float: {:?}", self),
},
RegKind::Vector => cx.type_vector(cx.type_i8(), self.size.bytes()),
RegKind::ScalableVector => cx.type_scalable_vector(cx.type_i8(), 16),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use a comment.

@@ -1176,12 +1197,16 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
);
match m_elem_ty.kind() {
ty::Int(_) => {}
ty::Int(_) | ty::Bool => {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use a comment explaining this is for svbool_t.

@@ -831,13 +831,13 @@ fn check_impl_items_against_trait<'tcx>(
}
}

pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId, is_scalable: bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be reworked to check for _ty: [T] which must be an unsized slice.

@@ -122,7 +122,14 @@ pub(super) fn check_fn<'a, 'tcx>(
hir::FnRetTy::DefaultReturn(_) => body.value.span,
hir::FnRetTy::Return(ty) => ty.span,
};
fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::SizedReturnType);

if !declared_ret_ty.is_scalable_simd() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should allow scalable simd parameters without requiring params_can_be_unsized.

@@ -265,7 +265,10 @@ fn typeck_with_fallback<'tcx>(

for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
let ty = fcx.normalize(span, ty);
fcx.require_type_is_sized(ty, span, code);
// ScalableSIMD: Justify this.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should be moved to the call sites of require_type_is_sized_deferred.

@@ -76,7 +76,9 @@ where
}
}
},
Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv),
Abi::Vector { .. } | Abi::ScalableVector { .. } | Abi::Uninhabited => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this unreachable! or bug!.

@@ -35,6 +35,7 @@ where
RegKind::Integer => false,
RegKind::Float => true,
RegKind::Vector => arg.layout.size.bits() == 128,
RegKind::ScalableVector => true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreachable.

@@ -18,6 +18,7 @@ pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
// FIXME(eddyb) there should be a size cap here
// (probably what clang calls "illegal vectors").
}
Abi::ScalableVector { .. } => {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreachable.

} else if let ty::Slice(e_ty) = f0_ty.kind()
&& def.repr().scalable()
{
(*e_ty, 1, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make e_len an Option.

if def.repr().scalable()
&& variants[FIRST_VARIANT].iter().all(|field| !field.0.is_zst())
{
bug!("Fields for a Scalable vector should be a ZST");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is incorrect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants