Skip to content

Commit d26ca59

Browse files
committed
Introduce SimdCvt trait family
1 parent b722722 commit d26ca59

File tree

9 files changed

+158
-13
lines changed

9 files changed

+158
-13
lines changed

fearless_simd/src/generated/simd_trait.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
// This file is autogenerated by fearless_simd_gen
22

3-
use crate::{Level, SimdElement, SimdInto, seal::Seal};
3+
use crate::{Level, SimdCvtFloat, SimdCvtTruncate, SimdElement, SimdInto, seal::Seal};
44
use crate::{
55
f32x4, f32x8, f32x16, i8x16, i8x32, i8x64, i16x8, i16x16, i16x32, i32x4, i32x8, i32x16,
66
mask8x16, mask8x32, mask8x64, mask16x8, mask16x16, mask16x32, mask32x4, mask32x8, mask32x16,
77
u8x16, u8x32, u8x64, u16x8, u16x16, u16x32, u32x4, u32x8, u32x16,
88
};
99
#[doc = r" TODO: docstring"]
1010
pub trait Simd: Sized + Clone + Copy + Send + Sync + Seal + 'static {
11-
type f32s: SimdFloat<f32, Self, Block = f32x4<Self>>;
11+
type f32s: SimdFloat<f32, Self, Block = f32x4<Self>>
12+
+ SimdCvtFloat<Self::u32s>
13+
+ SimdCvtFloat<Self::i32s>;
1214
type u8s: SimdInt<u8, Self, Block = u8x16<Self>>;
1315
type i8s: SimdInt<i8, Self, Block = i8x16<Self>>;
1416
type u16s: SimdInt<u16, Self, Block = u16x8<Self>>;
1517
type i16s: SimdInt<i16, Self, Block = i16x8<Self>>;
16-
type u32s: SimdInt<u32, Self, Block = u32x4<Self>>;
17-
type i32s: SimdInt<i32, Self, Block = i32x4<Self>>;
18+
type u32s: SimdInt<u32, Self, Block = u32x4<Self>> + SimdCvtTruncate<Self::f32s>;
19+
type i32s: SimdInt<i32, Self, Block = i32x4<Self>> + SimdCvtTruncate<Self::f32s>;
1820
type mask8s: SimdMask<i8, Self, Block = mask8x16<Self>>;
1921
type mask16s: SimdMask<i16, Self, Block = mask16x8<Self>>;
2022
type mask32s: SimdMask<i32, Self, Block = mask32x4<Self>>;
@@ -667,6 +669,10 @@ pub trait SimdFloat<Element: SimdElement, S: Simd>:
667669
+ core::ops::Div<Output = Self>
668670
+ core::ops::Div<Element, Output = Self>
669671
{
672+
#[inline(always)]
673+
fn to_int<T: SimdCvtTruncate<Self>>(self) -> T {
674+
T::truncate_from(self)
675+
}
670676
fn abs(self) -> Self;
671677
fn sqrt(self) -> Self;
672678
fn copysign(self, rhs: impl SimdInto<Self, S>) -> Self;
@@ -702,6 +708,10 @@ pub trait SimdInt<Element: SimdElement, S: Simd>:
702708
+ core::ops::BitXor<Output = Self>
703709
+ core::ops::BitXor<Element, Output = Self>
704710
{
711+
#[inline(always)]
712+
fn to_float<T: SimdCvtFloat<Self>>(self) -> T {
713+
T::float_from(self)
714+
}
705715
fn simd_eq(self, rhs: impl SimdInto<Self, S>) -> Self::Mask;
706716
fn simd_lt(self, rhs: impl SimdInto<Self, S>) -> Self::Mask;
707717
fn simd_le(self, rhs: impl SimdInto<Self, S>) -> Self::Mask;

fearless_simd/src/generated/simd_types.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This file is autogenerated by fearless_simd_gen
22

3-
use crate::{Bytes, Select, Simd, SimdFrom, SimdInto};
3+
use crate::{Bytes, Select, Simd, SimdCvtFloat, SimdCvtTruncate, SimdFrom, SimdInto};
44
#[derive(Clone, Copy, Debug)]
55
#[repr(C, align(16))]
66
pub struct f32x4<S: Simd> {
@@ -269,6 +269,16 @@ impl<S: Simd> crate::SimdFloat<f32, S> for f32x4<S> {
269269
self.simd.trunc_f32x4(self)
270270
}
271271
}
272+
impl<S: Simd> SimdCvtFloat<u32x4<S>> for f32x4<S> {
273+
fn float_from(x: u32x4<S>) -> Self {
274+
x.simd.cvt_f32_u32x4(x)
275+
}
276+
}
277+
impl<S: Simd> SimdCvtFloat<i32x4<S>> for f32x4<S> {
278+
fn float_from(x: i32x4<S>) -> Self {
279+
x.simd.cvt_f32_i32x4(x)
280+
}
281+
}
272282
#[derive(Clone, Copy, Debug)]
273283
#[repr(C, align(16))]
274284
pub struct i8x16<S: Simd> {
@@ -1597,6 +1607,11 @@ impl<S: Simd> crate::SimdInt<i32, S> for i32x4<S> {
15971607
self.simd.max_i32x4(self, rhs.simd_into(self.simd))
15981608
}
15991609
}
1610+
impl<S: Simd> SimdCvtTruncate<f32x4<S>> for i32x4<S> {
1611+
fn truncate_from(x: f32x4<S>) -> Self {
1612+
x.simd.cvt_i32_f32x4(x)
1613+
}
1614+
}
16001615
#[derive(Clone, Copy, Debug)]
16011616
#[repr(C, align(16))]
16021617
pub struct u32x4<S: Simd> {
@@ -1803,6 +1818,11 @@ impl<S: Simd> crate::SimdInt<u32, S> for u32x4<S> {
18031818
self.simd.max_u32x4(self, rhs.simd_into(self.simd))
18041819
}
18051820
}
1821+
impl<S: Simd> SimdCvtTruncate<f32x4<S>> for u32x4<S> {
1822+
fn truncate_from(x: f32x4<S>) -> Self {
1823+
x.simd.cvt_u32_f32x4(x)
1824+
}
1825+
}
18061826
#[derive(Clone, Copy, Debug)]
18071827
#[repr(C, align(16))]
18081828
pub struct mask32x4<S: Simd> {
@@ -2200,6 +2220,16 @@ impl<S: Simd> crate::SimdFloat<f32, S> for f32x8<S> {
22002220
self.simd.trunc_f32x8(self)
22012221
}
22022222
}
2223+
impl<S: Simd> SimdCvtFloat<u32x8<S>> for f32x8<S> {
2224+
fn float_from(x: u32x8<S>) -> Self {
2225+
x.simd.cvt_f32_u32x8(x)
2226+
}
2227+
}
2228+
impl<S: Simd> SimdCvtFloat<i32x8<S>> for f32x8<S> {
2229+
fn float_from(x: i32x8<S>) -> Self {
2230+
x.simd.cvt_f32_i32x8(x)
2231+
}
2232+
}
22032233
#[derive(Clone, Copy, Debug)]
22042234
#[repr(C, align(32))]
22052235
pub struct i8x32<S: Simd> {
@@ -3609,6 +3639,11 @@ impl<S: Simd> crate::SimdInt<i32, S> for i32x8<S> {
36093639
self.simd.max_i32x8(self, rhs.simd_into(self.simd))
36103640
}
36113641
}
3642+
impl<S: Simd> SimdCvtTruncate<f32x8<S>> for i32x8<S> {
3643+
fn truncate_from(x: f32x8<S>) -> Self {
3644+
x.simd.cvt_i32_f32x8(x)
3645+
}
3646+
}
36123647
#[derive(Clone, Copy, Debug)]
36133648
#[repr(C, align(32))]
36143649
pub struct u32x8<S: Simd> {
@@ -3824,6 +3859,11 @@ impl<S: Simd> crate::SimdInt<u32, S> for u32x8<S> {
38243859
self.simd.max_u32x8(self, rhs.simd_into(self.simd))
38253860
}
38263861
}
3862+
impl<S: Simd> SimdCvtTruncate<f32x8<S>> for u32x8<S> {
3863+
fn truncate_from(x: f32x8<S>) -> Self {
3864+
x.simd.cvt_u32_f32x8(x)
3865+
}
3866+
}
38273867
#[derive(Clone, Copy, Debug)]
38283868
#[repr(C, align(32))]
38293869
pub struct mask32x8<S: Simd> {
@@ -4235,6 +4275,16 @@ impl<S: Simd> crate::SimdFloat<f32, S> for f32x16<S> {
42354275
self.simd.trunc_f32x16(self)
42364276
}
42374277
}
4278+
impl<S: Simd> SimdCvtFloat<u32x16<S>> for f32x16<S> {
4279+
fn float_from(x: u32x16<S>) -> Self {
4280+
x.simd.cvt_f32_u32x16(x)
4281+
}
4282+
}
4283+
impl<S: Simd> SimdCvtFloat<i32x16<S>> for f32x16<S> {
4284+
fn float_from(x: i32x16<S>) -> Self {
4285+
x.simd.cvt_f32_i32x16(x)
4286+
}
4287+
}
42384288
#[derive(Clone, Copy, Debug)]
42394289
#[repr(C, align(64))]
42404290
pub struct i8x64<S: Simd> {
@@ -5775,6 +5825,11 @@ impl<S: Simd> crate::SimdInt<i32, S> for i32x16<S> {
57755825
self.simd.max_i32x16(self, rhs.simd_into(self.simd))
57765826
}
57775827
}
5828+
impl<S: Simd> SimdCvtTruncate<f32x16<S>> for i32x16<S> {
5829+
fn truncate_from(x: f32x16<S>) -> Self {
5830+
x.simd.cvt_i32_f32x16(x)
5831+
}
5832+
}
57785833
#[derive(Clone, Copy, Debug)]
57795834
#[repr(C, align(64))]
57805835
pub struct u32x16<S: Simd> {
@@ -5995,6 +6050,11 @@ impl<S: Simd> crate::SimdInt<u32, S> for u32x16<S> {
59956050
self.simd.max_u32x16(self, rhs.simd_into(self.simd))
59966051
}
59976052
}
6053+
impl<S: Simd> SimdCvtTruncate<f32x16<S>> for u32x16<S> {
6054+
fn truncate_from(x: f32x16<S>) -> Self {
6055+
x.simd.cvt_u32_f32x16(x)
6056+
}
6057+
}
59986058
#[derive(Clone, Copy, Debug)]
59996059
#[repr(C, align(64))]
60006060
pub struct mask32x16<S: Simd> {

fearless_simd/src/traits.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,13 @@ impl SimdElement for u32 {
105105
impl SimdElement for i32 {
106106
type Mask = i32;
107107
}
108+
109+
/// Construction of integer vectors from floats by truncation
110+
pub trait SimdCvtTruncate<T> {
111+
fn truncate_from(x: T) -> Self;
112+
}
113+
114+
/// Construction of floating point vectors from integers
115+
pub trait SimdCvtFloat<T> {
116+
fn float_from(x: T) -> Self;
117+
}

fearless_simd_gen/src/mk_fallback.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ fn mk_simd_impl() -> TokenStream {
359359
methods.push(method);
360360
}
361361
}
362+
362363
// Note: the `vectorize` implementation is pretty boilerplate and should probably
363364
// be factored out for DRY.
364365
quote! {

fearless_simd_gen/src/mk_neon.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ fn mk_simd_impl(level: Level) -> TokenStream {
364364
methods.push(method);
365365
}
366366
}
367+
367368
// Note: the `vectorize` implementation is pretty boilerplate and should probably
368369
// be factored out for DRY.
369370
quote! {

fearless_simd_gen/src/mk_simd_trait.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,18 @@ pub fn mk_simd_trait() -> TokenStream {
2626
}
2727
}
2828
let mut code = quote! {
29-
use crate::{seal::Seal, Level, SimdElement, SimdInto};
29+
use crate::{seal::Seal, Level, SimdElement, SimdInto, SimdCvtTruncate, SimdCvtFloat};
3030
#imports
3131
/// TODO: docstring
3232
// TODO: Seal
3333
pub trait Simd: Sized + Clone + Copy + Send + Sync + Seal + 'static {
34-
type f32s: SimdFloat<f32, Self, Block = f32x4<Self>>;
34+
type f32s: SimdFloat<f32, Self, Block = f32x4<Self>> + SimdCvtFloat<Self::u32s> + SimdCvtFloat<Self::i32s>;
3535
type u8s: SimdInt<u8, Self, Block = u8x16<Self>>;
3636
type i8s: SimdInt<i8, Self, Block = i8x16<Self>>;
3737
type u16s: SimdInt<u16, Self, Block = u16x8<Self>>;
3838
type i16s: SimdInt<i16, Self, Block = i16x8<Self>>;
39-
type u32s: SimdInt<u32, Self, Block = u32x4<Self>>;
40-
type i32s: SimdInt<i32, Self, Block = i32x4<Self>>;
39+
type u32s: SimdInt<u32, Self, Block = u32x4<Self>> + SimdCvtTruncate<Self::f32s>;
40+
type i32s: SimdInt<i32, Self, Block = i32x4<Self>> + SimdCvtTruncate<Self::f32s>;
4141
type mask8s: SimdMask<i8, Self, Block = mask8x16<Self>>;
4242
type mask16s: SimdMask<i16, Self, Block = mask16x8<Self>>;
4343
type mask32s: SimdMask<i32, Self, Block = mask32x4<Self>>;
@@ -101,6 +101,9 @@ fn mk_simd_float() -> TokenStream {
101101
+ core::ops::Div<Output = Self>
102102
+ core::ops::Div<Element, Output = Self>
103103
{
104+
#[inline(always)]
105+
fn to_int<T: SimdCvtTruncate<Self>>(self) -> T { T::truncate_from(self) }
106+
104107
#( #methods )*
105108
}
106109
}
@@ -123,6 +126,9 @@ fn mk_simd_int() -> TokenStream {
123126
+ core::ops::BitXor<Output = Self>
124127
+ core::ops::BitXor<Element, Output = Self>
125128
{
129+
#[inline(always)]
130+
fn to_float<T: SimdCvtFloat<Self>>(self) -> T { T::float_from(self) }
131+
126132
#( #methods )*
127133
}
128134
}

fearless_simd_gen/src/mk_simd_types.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

44
use proc_macro2::{Ident, Literal, Span, TokenStream};
5-
use quote::quote;
5+
use quote::{format_ident, quote};
66

77
use crate::{
88
ops::{CORE_OPS, OpSig, TyFlavor, ops_for_type},
@@ -11,7 +11,7 @@ use crate::{
1111

1212
pub fn mk_simd_types() -> TokenStream {
1313
let mut result = quote! {
14-
use crate::{Bytes, Select, Simd, SimdFrom, SimdInto};
14+
use crate::{Bytes, Select, Simd, SimdFrom, SimdInto, SimdCvtFloat, SimdCvtTruncate};
1515
};
1616
for ty in SIMD_TYPES {
1717
let name = ty.rust();
@@ -42,6 +42,50 @@ pub fn mk_simd_types() -> TokenStream {
4242
.map(|idx| quote! { val[#idx] })
4343
.collect::<Vec<_>>(),
4444
);
45+
let mut cvt_impls = Vec::new();
46+
match ty.scalar {
47+
ScalarType::Float => {
48+
for src_scalar in [ScalarType::Unsigned, ScalarType::Int] {
49+
let src_ty = VecType {
50+
scalar: src_scalar,
51+
..*ty
52+
};
53+
let method = format_ident!(
54+
"cvt_{}_{}",
55+
ty.scalar.rust_name(ty.scalar_bits),
56+
src_ty.rust_name()
57+
);
58+
let src_ty = src_ty.rust();
59+
cvt_impls.push(quote! {
60+
impl<S: Simd> SimdCvtFloat<#src_ty<S>> for #name<S> {
61+
fn float_from(x: #src_ty<S>) -> Self {
62+
x.simd.#method(x)
63+
}
64+
}
65+
});
66+
}
67+
}
68+
ScalarType::Int | ScalarType::Unsigned if ty.scalar_bits == 32 => {
69+
let src_ty = VecType {
70+
scalar: ScalarType::Float,
71+
..*ty
72+
};
73+
let method = format_ident!(
74+
"cvt_{}_{}",
75+
ty.scalar.rust_name(ty.scalar_bits),
76+
src_ty.rust_name()
77+
);
78+
let src_ty = src_ty.rust();
79+
cvt_impls.push(quote! {
80+
impl<S: Simd> SimdCvtTruncate<#src_ty<S>> for #name<S> {
81+
fn truncate_from(x: #src_ty<S>) -> Self {
82+
x.simd.#method(x)
83+
}
84+
}
85+
});
86+
}
87+
_ => {}
88+
}
4589
result.extend(quote! {
4690
#[derive(Clone, Copy, Debug)]
4791
#[repr(C, align(#align_lit))]
@@ -119,6 +163,8 @@ pub fn mk_simd_types() -> TokenStream {
119163
}
120164

121165
#impl_block
166+
167+
#( #cvt_impls )*
122168
});
123169
}
124170
result

fearless_simd_gen/src/types.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ impl ScalarType {
2828
}
2929
}
3030

31+
pub fn rust_name(&self, scalar_bits: usize) -> String {
32+
format!("{}{}", self.prefix(), scalar_bits)
33+
}
34+
3135
pub fn rust(&self, scalar_bits: usize) -> TokenStream {
32-
let name = format!("{}{}", self.prefix(), scalar_bits);
33-
let ident = Ident::new(&name, Span::call_site());
36+
let ident = Ident::new(&self.rust_name(scalar_bits), Span::call_site());
3437
quote! { #ident }
3538
}
3639
}

fearless_simd_tests/tests/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
// Copyright 2025 the Fearless_SIMD Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4+
use fearless_simd::{Simd, SimdFloat};
5+
46
#[cfg(target_arch = "wasm32")]
57
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
68

79
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
810
mod wasm;
11+
12+
// Ensure that we can cast between generic native-width vectors
13+
#[allow(dead_code)]
14+
fn generic_cast<S: Simd>(x: S::f32s) -> S::u32s {
15+
x.to_int()
16+
}

0 commit comments

Comments
 (0)