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
base: master
Are you sure you want to change the base?
Conversation
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.
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() { |
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.
This needs a TODO to make sure it's not accidentally committed.
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.
This should allow scalable simd parameters without requiring params_can_be_unsized
.
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.
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() { |
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.
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. |
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.
Needs a TODO or a very detailed explanation here
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.
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() { |
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.
Why is this true?
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, for the record, this is unsound. For a type to implement Copy
, it must implement Sized
, since Sized
is one of Copy
's supertraits.
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, 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.
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.
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.
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.
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.
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 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.
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.
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?
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.
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), |
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.
This should get a tracking issue and moved to the feature-group-start: actual feature gates
section.
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.
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], |
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.
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), |
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.
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?
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.
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>` |
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'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), |
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.
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 => {} |
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.
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) { |
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.
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() { |
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.
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. |
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.
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 => { |
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.
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, |
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.
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 { .. } => {} |
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.
Unreachable.
} else if let ty::Slice(e_ty) = f0_ty.kind() | ||
&& def.repr().scalable() | ||
{ | ||
(*e_ty, 1, false) |
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.
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"); |
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.
This check is incorrect.
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