-
Notifications
You must be signed in to change notification settings - Fork 82
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
Generic core::ops
for Simd<T, _>
#195
Conversation
We discussed this and decided impl<T, const LANES: usize> BitAnd for Simd<T, LANES>
where
T: SimdElement + BitAnd<Output = T>,
LaneCount<LANES>: SupportedLaneCount,
{
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self::Output {
unsafe { intrinsics::simd_and(self, rhs) }
}
} is a bridge too far, but impl<T, const LANES: usize> BitAnd<T> for Simd<T, LANES>
where
T: SimdElement + BitAnd<Output = T>,
LaneCount<LANES>: SupportedLaneCount,
{
type Output = Self;
#[inline]
fn bitand(self, rhs: T) -> Self::Output {
unsafe { intrinsics::simd_and(self, Simd::splat(rhs)) }
}
} is fine, since if a vector can be added or whatever then we also want the implied promotion for binary ops. |
I think that second example is still making the same assumptions as the first. I suggested something more like this, since it uses the explicitly defined operation. impl<T, const LANES: usize> BitAnd<T> for Simd<T, LANES>
where
T: SimdElement,
Self: BitAnd,
LaneCount<LANES>: SupportedLaneCount,
{
type Output = Self;
#[inline]
fn bitand(self, rhs: T) -> Self::Output {
self & Simd::splat(rhs)
}
} This covers the same cases, but doesn't make any assumptions about what |
Ah, I wasn't actually paying that close attention to the trait bounds because I am adjusting the way all of those are written to fit the new revision anyways. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
3f2e316
to
2ac3763
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
dd1f800
to
6138468
Compare
Instead of implementing each "deref" pattern for every single scalar, we can use type parameters for Simd operating on &Self. We can use a macro, but keep it cleaner and more explicit.
Instead of implementing {Op}Assign traits for individual scalar type args to Simd<_, _>, use parametric impls that reassert the bounds of the binary op.
6138468
to
0d76eca
Compare
I pulled back some of the changes I had been experimenting with for later review and consideration. This version still has significant wins in terms of reducing the weight of the docs and in general making our implementation a bit cleaner. Right now, at the tip of this branch, the |
core::ops
for Simd<T, _>
Hmm, I'm not in favor of the splatting this way — I think having |
They both work, @thomcc. I simply didn't parameterize the LHS splat impls, That is, the ops are symmetric in actuality. |
Ah, sorry! My bad, I misread the patch then. |
0d76eca
to
ccae0b0
Compare
Mmm, that's understandable. I'll adjust the wording on that commit to reflect that. |
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.
lgtm
In order to assure type soundness, these "base" impls need to go directly on Simd<T, _> for every scalar type argument. A bit of cleanup of ops.rs is still warranted.
Unfortunately, splatting impls currently break several crates. Rust needs more time to review possible mitigations, so drop the impls for the `impl Add<T> for Simd<T, _>` pattern, for now.
ccae0b0
to
853e41d
Compare
All review remarks have been addressed. Specifically, the conversation about splats has been resolved by removing splats entirely, although this is primarily due to autosplatting creating unacceptable levels of type inference related breakage at the moment, per rust-lang/rust#90904. Thus, this now carries a fix for a regression currently on beta. |
853e41d
to
8003b04
Compare
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.
lgtm, just the remaining stylistic comment which could go either way
also, worth noting that we probably shouldn't update nightly with these changes right away, since there are some other changes on master that are somewhat half-complete |
In order to maintain type soundness, we need to be sure we only implement an operation for
Simd<T, _> where T: SimdElement
... and also valid for that operation in general. While we could do this purely parametrically, it is more sound to implement the operators directly for the base scalar type arguments and then use type parameters to extend the operators to the "higher order" operations.This implements that strategy and cleans up
simd::ops
into a few submodules:core::ops::*Assign
core::ops
impls which "deref" borrowed versions of the argumentsSimd
, as unary ops are much simplerThis is possible since everything need not be nested in a single maze of macros anymore. The result simplifies the logic and allows reasoning about what operators are valid based on the expressed trait bounds, and also reduces the size of the trait implementation output in rustdoc, for a huge win of 4 MB off the size of
struct.Simd.html
! This addresses a common user complaint, as the original was over 5.5 MB and capable of crashing browsers!This also carries a fix for a type-inference-related breakage, by removing the autosplatting (vector + scalar binop) impls, as unfortunately the presence of autosplatting was capable of busting type inference. We will likely need to see results from a Crater run before we can understand how to re-land autosplatting.