For all other operators, we use wrapping logic where applicable.
This is another case it applies. Per rust-lang/rust#91237, we may
wish to specify this as the natural behavior of `simd_{shl,shr}`.
Remove Select trait
I realized that our `select` implementation predated `Simd` being generic over element type, and we don't really need the `Select` trait at all. The function signature is much simpler now (generic over element type, rather than over the entire vector). This did require changing mask select to be a different function, but I think that's fine considering they're not necessarily vectors.
Generic `core::ops` for `Simd<T, _>`
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:
- assign.rs: `core::ops::*Assign`
- deref.rs: `core::ops` impls which "deref" borrowed versions of the arguments
- unary.rs: encloses the logic for unary operators on `Simd`, as unary ops are much simpler
This 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.
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.
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.
Resolves my comment in #197, at least for now; #187 is pending but since these are already here, just commented, it seemed to make sense to me to re-enable them anyway.
Instead of implementing {Op}Assign traits for individual scalar type args
to Simd<_, _>, use parametric impls that reassert the bounds of the binary op.
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.
We would like to check for errors with AVX512,
but we don't pick our CPU. So, detect available features.
This variance in checks stochastically reveals issues.
Nondeterminism is acceptable as our goal is protecting downstream.
* add `Simd::from_slice`
uses a zeroed initial array and loops so that it can be const.
unfortunately, parameterizing the assert with slice length
needs `#![feature(const_fn_fn_ptr_basics)]` to work.
This changes simd_swizzle! to a decl_macro to give it a path,
so it can be imported using a path and not the crate root.
It also adds various uses that were missed and adjusts paths.
This unsafe variant allows the thinnest API, in case LLVM cannot
perform loop-invariant code motion on a hot loop when the safe
form is used.
An unchecked variant could be added to other forms, but doesn't
seem likely to improve anything, since it would just add heavier
codegen.