1use std::mem::MaybeUninit;
2
3use rten_simd::ops::{BitOps, Extend, NarrowSaturate};
4use rten_simd::{Isa, SimdOp, SliceWriter, f16};
5
6pub struct F16ToF32<'s, 'd> {
8 src: &'s [f16],
9 dest: &'d mut [MaybeUninit<f32>],
10}
11
12impl<'s, 'd> F16ToF32<'s, 'd> {
13 pub fn new(src: &'s [f16], dest: &'d mut [MaybeUninit<f32>]) -> Self {
18 assert_eq!(src.len(), dest.len());
19 F16ToF32 { src, dest }
20 }
21}
22
23impl<'d> SimdOp for F16ToF32<'_, 'd> {
24 type Output = &'d mut [f32];
25
26 #[inline(always)]
27 fn eval<I: Isa>(self, isa: I) -> Self::Output {
28 let f16_ops = isa.f16();
29 let f32_ops = isa.f32();
30 let f16_v_len = f16_ops.len();
31
32 let mut dest_writer = SliceWriter::new(self.dest);
33
34 let mut chunks = self.src.chunks_exact(f16_v_len * 2);
36 for chunk in chunks.by_ref() {
37 let xs = f16_ops.load_many::<2>(chunk);
38 let lo0 = f16_ops.extend_low(xs[0]);
39 let hi0 = f16_ops.extend_high(xs[0]);
40 let lo1 = f16_ops.extend_low(xs[1]);
41 let hi1 = f16_ops.extend_high(xs[1]);
42 dest_writer.write_vecs(f32_ops, [lo0, hi0, lo1, hi1]);
44 }
45
46 let mut chunks = chunks.remainder().chunks_exact(f16_v_len);
48 for chunk in chunks.by_ref() {
49 let x = f16_ops.load(chunk);
50 let low = f16_ops.extend_low(x);
51 let high = f16_ops.extend_high(x);
52 dest_writer.write_vec(f32_ops, low);
53 dest_writer.write_vec(f32_ops, high);
54 }
55
56 for &x in chunks.remainder() {
58 dest_writer.write_scalar(x.to_f32());
59 }
60
61 dest_writer.into_mut_slice()
62 }
63}
64
65pub struct F32ToF16<'s, 'd> {
70 src: &'s [f32],
71 dest: &'d mut [MaybeUninit<f16>],
72}
73
74impl<'s, 'd> F32ToF16<'s, 'd> {
75 pub fn new(src: &'s [f32], dest: &'d mut [MaybeUninit<f16>]) -> Self {
80 assert_eq!(src.len(), dest.len());
81 F32ToF16 { src, dest }
82 }
83}
84
85impl<'d> SimdOp for F32ToF16<'_, 'd> {
86 type Output = &'d mut [f16];
87
88 #[inline(always)]
89 fn eval<I: Isa>(self, isa: I) -> Self::Output {
90 let f32_ops = isa.f32();
91 let f16_ops = isa.f16();
92 let f32_v_len = f32_ops.len();
93
94 let mut src_chunks = self.src.chunks_exact(f32_v_len * 2);
95 let mut dest_writer = SliceWriter::new(self.dest);
96
97 for src_chunk in src_chunks.by_ref() {
98 let xs = f32_ops.load_many::<2>(src_chunk);
99 let half = f32_ops.narrow_saturate(xs[0], xs[1]);
100 dest_writer.write_vec(f16_ops, half);
101 }
102
103 for &x in src_chunks.remainder() {
104 dest_writer.write_scalar(f16::from_f32(x));
105 }
106
107 dest_writer.into_mut_slice()
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use rten_simd::ops::BitOps;
114 use rten_simd::{Isa, SimdOp, f16};
115
116 use super::{F16ToF32, F32ToF16};
117
118 fn f16_vec_len() -> usize {
120 struct F16VecLen {}
121 impl SimdOp for F16VecLen {
122 type Output = usize;
123 fn eval<I: Isa>(self, isa: I) -> usize {
124 isa.f16().len()
125 }
126 }
127 F16VecLen {}.dispatch()
128 }
129
130 #[test]
131 fn test_f16_to_f32() {
132 let len = f16_vec_len() * 3 + 1;
136 let src: Vec<f16> = (0..len)
137 .map(|i| f16::from_f32(i as f32 * 0.5 - 3.0))
138 .collect();
139 let expected: Vec<f32> = src.iter().map(|x| x.to_f32()).collect();
140
141 let mut buf = Vec::with_capacity(src.len());
142 let actual = F16ToF32::new(&src, buf.spare_capacity_mut()).dispatch();
143
144 assert_eq!(actual, expected);
145 }
146
147 #[test]
148 fn test_f16_to_f32_empty() {
149 let src: Vec<f16> = Vec::new();
150 let mut buf: Vec<f32> = Vec::new();
151 let actual = F16ToF32::new(&src, buf.spare_capacity_mut()).dispatch();
152 assert!(actual.is_empty());
153 }
154
155 #[test]
156 fn test_f32_to_f16() {
157 let len = f16_vec_len() + 1;
161 let src: Vec<f32> = (0..len).map(|i| i as f32 * 0.5 - 3.0).collect();
162 let expected: Vec<f16> = src.iter().map(|&x| f16::from_f32(x)).collect();
163
164 let mut buf = Vec::with_capacity(src.len());
165 let actual = F32ToF16::new(&src, buf.spare_capacity_mut()).dispatch();
166
167 assert_eq!(actual, expected);
168 }
169
170 #[test]
171 fn test_f32_to_f16_empty() {
172 let src: Vec<f32> = Vec::new();
173 let mut buf: Vec<f16> = Vec::new();
174 let actual = F32ToF16::new(&src, buf.spare_capacity_mut()).dispatch();
175 assert!(actual.is_empty());
176 }
177
178 #[test]
180 fn test_roundtrip() {
181 let len = f16_vec_len() * 2 + 3;
182 let src: Vec<f32> = (0..len).map(|i| i as f32 * 0.25 - 5.0).collect();
183
184 let mut half_buf = Vec::with_capacity(len);
185 let half = F32ToF16::new(&src, half_buf.spare_capacity_mut()).dispatch();
186 let half: Vec<f16> = half.to_vec();
187
188 let mut back_buf = Vec::with_capacity(len);
189 let back = F16ToF32::new(&half, back_buf.spare_capacity_mut()).dispatch();
190
191 let expected: Vec<f32> = src.iter().map(|&x| f16::from_f32(x).to_f32()).collect();
192 assert_eq!(back, expected);
193 }
194}