Skip to content
1,028 changes: 58 additions & 970 deletions fearless_simd/src/generated/avx2.rs

Large diffs are not rendered by default.

1,770 changes: 679 additions & 1,091 deletions fearless_simd/src/generated/fallback.rs

Large diffs are not rendered by default.

1,188 changes: 32 additions & 1,156 deletions fearless_simd/src/generated/neon.rs

Large diffs are not rendered by default.

720 changes: 0 additions & 720 deletions fearless_simd/src/generated/ops.rs

Large diffs are not rendered by default.

904 changes: 316 additions & 588 deletions fearless_simd/src/generated/simd_trait.rs

Large diffs are not rendered by default.

1,346 changes: 183 additions & 1,163 deletions fearless_simd/src/generated/simd_types.rs

Large diffs are not rendered by default.

970 changes: 39 additions & 931 deletions fearless_simd/src/generated/sse4_2.rs

Large diffs are not rendered by default.

946 changes: 23 additions & 923 deletions fearless_simd/src/generated/wasm.rs

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions fearless_simd/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ use crate::{Level, Simd, SimdBase, seal::Seal};

/// Element-wise selection between two SIMD vectors using `self`.
pub trait Select<T: Seal>: Seal {
/// For each element of this mask, select the first operand if the element is all ones, and select the second
/// operand if the element is all zeroes.
/// For each logical lane of this mask, select the first operand if the lane is true, and select the second
/// operand if the lane is false.
///
/// If a mask element is *not* all ones or all zeroes, the result is unspecified. It may vary depending on
/// architecture, feature level, the mask elements' width, the mask vector's width, or library version.
/// Masks may be converted to and from signed integer lane arrays for compatibility with older APIs. For those
/// conversions, false is encoded as all zeroes (integer value 0) and true is encoded as all ones (integer value -1).
/// If a mask is constructed from any other integer bit pattern, the result of this operation is unspecified.
fn select(self, if_true: T, if_false: T) -> T;
}

Expand Down
13 changes: 13 additions & 0 deletions fearless_simd_gen/src/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ pub(crate) fn generic_op_name(op: &str, ty: &VecType) -> Ident {
Ident::new(&format!("{op}_{}", ty.rust_name()), Span::call_site())
}

/// For backends that store masks as all-zero/all-one integer lanes, convert the public
/// `bool` mask splat argument into the backend's lane representation.
pub(crate) fn integer_lane_mask_splat_arg(vec_ty: &VecType) -> TokenStream {
if vec_ty.scalar != ScalarType::Mask {
return TokenStream::new();
}

let scalar = vec_ty.scalar.rust(vec_ty.scalar_bits);
quote! {
let val: #scalar = if val { !0 } else { 0 };
}
}

/// Implementation based on split/combine
///
/// Only suitable for lane-wise and block-wise operations
Expand Down
60 changes: 40 additions & 20 deletions fearless_simd_gen/src/mk_fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::arch::fallback;
use crate::generic::{generic_from_bytes, generic_op_name, generic_to_bytes};
use crate::generic::{
generic_from_bytes, generic_op_name, generic_to_bytes, integer_lane_mask_splat_arg,
};
use crate::level::Level;
use crate::ops::{Op, OpSig, RefKind, valid_reinterpret};
use crate::types::{ScalarType, VecType};
Expand Down Expand Up @@ -136,8 +138,10 @@ impl Level for Fallback {
match sig {
OpSig::Splat => {
let num_elements = vec_ty.len;
let normalize_mask = integer_lane_mask_splat_arg(vec_ty);
quote! {
#method_sig {
#normalize_mask
[val; #num_elements].simd_into(self)
}
}
Expand All @@ -146,7 +150,7 @@ impl Level for Fallback {
let items = make_list(
(0..vec_ty.len)
.map(|idx| {
let args = [quote! { a[#idx] }];
let args = [lane(quote! { a }, vec_ty, idx)];
let expr = fallback::expr(method, vec_ty, &args);
quote! { #expr }
})
Expand All @@ -164,7 +168,8 @@ impl Level for Fallback {
(0..vec_ty.len)
.map(|idx| {
let scalar_ty = target_ty.scalar.rust(target_ty.scalar_bits);
quote! { a[#idx] as #scalar_ty }
let a = lane(quote! { a }, vec_ty, idx);
quote! { #a as #scalar_ty }
})
.collect::<Vec<_>>(),
);
Expand All @@ -179,19 +184,20 @@ impl Level for Fallback {
let items = make_list(
(0..vec_ty.len)
.map(|idx| {
let b_lane = lane(quote! { b }, vec_ty, idx);
let b = if fallback::translate_op(
method,
vec_ty.scalar == ScalarType::Float,
)
.map(rhs_reference)
.unwrap_or(true)
{
quote! { &b[#idx] }
quote! { &#b_lane }
} else {
quote! { b[#idx] }
b_lane
};

let args = [quote! { a[#idx] }, quote! { #b }];
let args = [lane(quote! { a }, vec_ty, idx), quote! { #b }];
let expr = fallback::expr(method, vec_ty, &args);
quote! { #expr }
})
Expand All @@ -208,7 +214,7 @@ impl Level for Fallback {
let items = make_list(
(0..vec_ty.len)
.map(|idx| {
let args = [quote! { a[#idx] }, quote! { shift }];
let args = [lane(quote! { a }, vec_ty, idx), quote! { shift }];
let expr = fallback::expr(method, vec_ty, &args);
quote! { #expr }
})
Expand Down Expand Up @@ -254,7 +260,9 @@ impl Level for Fallback {
let items = make_list(
(0..vec_ty.len)
.map(|idx: usize| {
let args = [quote! { &a[#idx] }, quote! { &b[#idx] }];
let a = lane(quote! { a }, vec_ty, idx);
let b = lane(quote! { b }, vec_ty, idx);
let args = [quote! { &#a }, quote! { &#b }];
let expr = fallback::expr(method, vec_ty, &args);
let mask_ty = mask_type.scalar.rust(vec_ty.scalar_bits);
quote! { -(#expr as #mask_ty) }
Expand All @@ -269,10 +277,14 @@ impl Level for Fallback {
}
}
OpSig::Select => {
let mask_type = vec_ty.mask_ty();
let items = make_list(
(0..vec_ty.len)
.map(|idx| {
quote! { if a[#idx] != 0 { b[#idx] } else { c[#idx] } }
let a = lane(quote! { a }, &mask_type, idx);
let b = lane(quote! { b }, vec_ty, idx);
let c = lane(quote! { c }, vec_ty, idx);
quote! { if #a != 0 { #b } else { #c } }
})
.collect::<Vec<_>>(),
);
Expand Down Expand Up @@ -326,7 +338,9 @@ impl Level for Fallback {
let zip = make_list(
indices
.map(|idx| {
quote! {a[#idx], b[#idx] }
let a = lane(quote! { a }, vec_ty, idx);
let b = lane(quote! { b }, vec_ty, idx);
quote! { #a, #b }
})
.collect::<Vec<_>>(),
);
Expand All @@ -347,12 +361,8 @@ impl Level for Fallback {
let unzip = make_list(
indices
.clone()
.map(|idx| {
quote! {a[#idx]}
})
.chain(indices.map(|idx| {
quote! {b[#idx]}
}))
.map(|idx| lane(quote! { a }, vec_ty, idx))
.chain(indices.map(|idx| lane(quote! { b }, vec_ty, idx)))
.collect::<Vec<_>>(),
);

Expand Down Expand Up @@ -392,7 +402,8 @@ impl Level for Fallback {
let items = make_list(
(0..vec_ty.len)
.map(|idx| {
quote! { a[#idx] as #scalar }
let a = lane(quote! { a }, vec_ty, idx);
quote! { #a as #scalar }
})
.collect::<Vec<_>>(),
);
Expand Down Expand Up @@ -421,7 +432,6 @@ impl Level for Fallback {
quantifier,
condition,
} => {
let indices = (0..vec_ty.len).map(|idx| quote! { #idx });
let check = if condition {
quote! { != }
} else {
Expand All @@ -430,10 +440,12 @@ impl Level for Fallback {

let expr = match quantifier {
crate::ops::Quantifier::Any => {
quote! { #(a[#indices] #check 0)||* }
let lanes = (0..vec_ty.len).map(|idx| lane(quote! { a }, vec_ty, idx));
quote! { #(#lanes #check 0)||* }
}
crate::ops::Quantifier::All => {
quote! { #(a[#indices] #check 0)&&* }
let lanes = (0..vec_ty.len).map(|idx| lane(quote! { a }, vec_ty, idx));
quote! { #(#lanes #check 0)&&* }
}
};

Expand Down Expand Up @@ -540,6 +552,14 @@ fn interleave_indices(
make_list(indices.into_iter().map(func).collect::<Vec<_>>())
}

fn lane(value: TokenStream, vec_ty: &VecType, idx: usize) -> TokenStream {
if vec_ty.scalar == ScalarType::Mask {
quote! { #value.val.0[#idx] }
} else {
quote! { #value[#idx] }
}
}

/// Whether the second argument of the function needs to be passed by reference.
fn rhs_reference(method: &str) -> bool {
!matches!(
Expand Down
4 changes: 3 additions & 1 deletion fearless_simd_gen/src/mk_neon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use quote::{ToTokens as _, format_ident, quote};

use crate::generic::{
generic_as_array, generic_from_array, generic_from_bytes, generic_op_name, generic_store_array,
generic_to_bytes,
generic_to_bytes, integer_lane_mask_splat_arg,
};
use crate::level::Level;
use crate::ops::{Op, SlideGranularity, valid_reinterpret};
Expand Down Expand Up @@ -82,9 +82,11 @@ impl Level for Neon {
match sig {
OpSig::Splat => {
let expr = neon::expr(method, vec_ty, &[quote! { val }]);
let normalize_mask = integer_lane_mask_splat_arg(vec_ty);
quote! {
#method_sig {
unsafe {
#normalize_mask
#expr.simd_into(self)
}
}
Expand Down
51 changes: 28 additions & 23 deletions fearless_simd_gen/src/mk_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use quote::{format_ident, quote};
use crate::{
generic::generic_op_name,
ops::{CoreOpTrait, OpKind, OpSig, TyFlavor, overloaded_ops_for},
types::{SIMD_TYPES, type_imports},
types::{SIMD_TYPES, ScalarType, type_imports},
};

pub(crate) fn mk_ops() -> TokenStream {
Expand Down Expand Up @@ -85,6 +85,32 @@ pub(crate) fn mk_ops() -> TokenStream {
}
_ => {
let scalar = ty.scalar.rust(ty.scalar_bits);
let scalar_overloads = (ty.scalar != ScalarType::Mask).then(|| {
quote! {
impl<S: Simd> core::ops::#trait_id<#scalar> for #simd<S> {
type Output = Self;
#[inline(always)]
fn #opfn(self, rhs: #scalar) -> Self::Output {
self.simd.#simd_fn(self, rhs.simd_into(self.simd))
}
}

impl<S: Simd> core::ops::#trait_assign_id<#scalar> for #simd<S> {
#[inline(always)]
fn #op_assign_fn(&mut self, rhs: #scalar) {
*self = self.simd.#simd_fn(*self, rhs.simd_into(self.simd));
}
}

impl<S: Simd> core::ops::#trait_id<#simd<S>> for #scalar {
type Output = #simd<S>;
#[inline(always)]
fn #opfn(self, rhs: #simd<S>) -> Self::Output {
rhs.simd.#simd_fn(self.simd_into(rhs.simd), rhs)
}
}
}
});
impls.push(quote! {
impl<S: Simd> core::ops::#trait_id for #simd<S> {
type Output = Self;
Expand All @@ -103,28 +129,7 @@ pub(crate) fn mk_ops() -> TokenStream {
}
}

impl<S: Simd> core::ops::#trait_id<#scalar> for #simd<S> {
type Output = Self;
#[inline(always)]
fn #opfn(self, rhs: #scalar) -> Self::Output {
self.simd.#simd_fn(self, rhs.simd_into(self.simd))
}
}

impl<S: Simd> core::ops::#trait_assign_id<#scalar> for #simd<S> {
#[inline(always)]
fn #op_assign_fn(&mut self, rhs: #scalar) {
*self = self.simd.#simd_fn(*self, rhs.simd_into(self.simd));
}
}

impl<S: Simd> core::ops::#trait_id<#simd<S>> for #scalar {
type Output = #simd<S>;
#[inline(always)]
fn #opfn(self, rhs: #simd<S>) -> Self::Output {
rhs.simd.#simd_fn(self.simd_into(rhs.simd), rhs)
}
}
#scalar_overloads
});
}
}
Expand Down
Loading
Loading