Skip to main content

rustfft/sse/
sse_common.rs

1use std::any::TypeId;
2
3use crate::fft_helper::{
4    fft_helper_immut, fft_helper_immut_unroll2x, fft_helper_inplace, fft_helper_inplace_unroll2x,
5    fft_helper_outofplace, fft_helper_outofplace_unroll2x,
6};
7
8// Helper function to assert we have the right float type
9pub fn assert_f32<T: 'static>() {
10    let id_f32 = TypeId::of::<f32>();
11    let id_t = TypeId::of::<T>();
12    assert!(id_t == id_f32, "Wrong float type, must be f32");
13}
14
15// Helper function to assert we have the right float type
16pub fn assert_f64<T: 'static>() {
17    let id_f64 = TypeId::of::<f64>();
18    let id_t = TypeId::of::<T>();
19    assert!(id_t == id_f64, "Wrong float type, must be f64");
20}
21
22// Shuffle elements to interleave two contiguous sets of f32, from an array of simd vectors to a new array of simd vectors
23macro_rules! interleave_complex_f32 {
24    ($input:ident, $offset:literal, { $($idx:literal),* }) => {
25        [
26        $(
27            extract_lo_lo_f32($input[$idx], $input[$idx+$offset]),
28            extract_hi_hi_f32($input[$idx], $input[$idx+$offset]),
29        )*
30        ]
31    }
32}
33
34// Shuffle elements to interleave two contiguous sets of f32, from an array of simd vectors to a new array of simd vectors
35// This statement:
36// ```
37// let values = separate_interleaved_complex_f32!(input, {0, 2, 4});
38// ```
39// is equivalent to:
40// ```
41// let values = [
42//    extract_lo_lo_f32(input[0], input[1]),
43//    extract_lo_lo_f32(input[2], input[3]),
44//    extract_lo_lo_f32(input[4], input[5]),
45//    extract_hi_hi_f32(input[0], input[1]),
46//    extract_hi_hi_f32(input[2], input[3]),
47//    extract_hi_hi_f32(input[4], input[5]),
48// ];
49macro_rules! separate_interleaved_complex_f32 {
50    ($input:ident, { $($idx:literal),* }) => {
51        [
52        $(
53            extract_lo_lo_f32($input[$idx], $input[$idx+1]),
54        )*
55        $(
56            extract_hi_hi_f32($input[$idx], $input[$idx+1]),
57        )*
58        ]
59    }
60}
61
62macro_rules! boilerplate_fft_sse_oop {
63    ($struct_name:ident, $len_fn:expr) => {
64        impl<S: SseNum, T: FftNum> Fft<T> for $struct_name<S, T> {
65            fn process_immutable_with_scratch(
66                &self,
67                input: &[Complex<T>],
68                output: &mut [Complex<T>],
69                _scratch: &mut [Complex<T>],
70            ) {
71                unsafe {
72                    let simd_input = crate::array_utils::workaround_transmute(input);
73                    let simd_output = crate::array_utils::workaround_transmute_mut(output);
74                    super::sse_common::sse_fft_helper_immut(
75                        simd_input,
76                        simd_output,
77                        &mut [],
78                        self.len(),
79                        0,
80                        |input, output, _| self.perform_fft_immut(input, output, &mut []),
81                    );
82                }
83            }
84            fn process_outofplace_with_scratch(
85                &self,
86                input: &mut [Complex<T>],
87                output: &mut [Complex<T>],
88                _scratch: &mut [Complex<T>],
89            ) {
90                unsafe {
91                    let simd_input = crate::array_utils::workaround_transmute_mut(input);
92                    let simd_output = crate::array_utils::workaround_transmute_mut(output);
93                    super::sse_common::sse_fft_helper_outofplace(
94                        simd_input,
95                        simd_output,
96                        &mut [],
97                        self.len(),
98                        0,
99                        |input, output, _| self.perform_fft_out_of_place(input, output, &mut []),
100                    );
101                }
102            }
103            fn process_with_scratch(&self, buffer: &mut [Complex<T>], scratch: &mut [Complex<T>]) {
104                unsafe {
105                    let simd_buffer = crate::array_utils::workaround_transmute_mut(buffer);
106                    let simd_scratch = crate::array_utils::workaround_transmute_mut(scratch);
107                    super::sse_common::sse_fft_helper_inplace(
108                        simd_buffer,
109                        simd_scratch,
110                        self.len(),
111                        self.get_inplace_scratch_len(),
112                        |chunk, scratch| {
113                            self.perform_fft_out_of_place(chunk, scratch, &mut []);
114                            chunk.copy_from_slice(scratch);
115                        },
116                    )
117                }
118            }
119            #[inline(always)]
120            fn get_inplace_scratch_len(&self) -> usize {
121                self.len()
122            }
123            #[inline(always)]
124            fn get_outofplace_scratch_len(&self) -> usize {
125                0
126            }
127            #[inline(always)]
128            fn get_immutable_scratch_len(&self) -> usize {
129                0
130            }
131        }
132        impl<S: SseNum, T> Length for $struct_name<S, T> {
133            #[inline(always)]
134            fn len(&self) -> usize {
135                $len_fn(self)
136            }
137        }
138        impl<S: SseNum, T> Direction for $struct_name<S, T> {
139            #[inline(always)]
140            fn fft_direction(&self) -> FftDirection {
141                self.direction
142            }
143        }
144    };
145}
146
147// A wrapper for the FFT helper functions that make sure the entire thing happens with the benefit of the SSE target feature,
148// so that things like loading twiddle factor registers etc can be lifted out of the loop
149#[target_feature(enable = "sse4.1")]
150pub unsafe fn sse_fft_helper_immut<T>(
151    input: &[T],
152    output: &mut [T],
153    scratch: &mut [T],
154    chunk_size: usize,
155    required_scratch: usize,
156    chunk_fn: impl FnMut(&[T], &mut [T], &mut [T]),
157) {
158    fft_helper_immut(
159        input,
160        output,
161        scratch,
162        chunk_size,
163        required_scratch,
164        chunk_fn,
165    )
166}
167
168// A wrapper for the FFT helper functions that make sure the entire thing happens with the benefit of the SSE target feature,
169// so that things like loading twiddle factor registers etc can be lifted out of the loop
170#[target_feature(enable = "sse4.1")]
171pub unsafe fn sse_fft_helper_outofplace<T>(
172    input: &mut [T],
173    output: &mut [T],
174    scratch: &mut [T],
175    chunk_size: usize,
176    required_scratch: usize,
177    chunk_fn: impl FnMut(&mut [T], &mut [T], &mut [T]),
178) {
179    fft_helper_outofplace(
180        input,
181        output,
182        scratch,
183        chunk_size,
184        required_scratch,
185        chunk_fn,
186    )
187}
188
189// A wrapper for the FFT helper functions that make sure the entire thing happens with the benefit of the SSE target feature,
190// so that things like loading twiddle factor registers etc can be lifted out of the loop
191#[target_feature(enable = "sse4.1")]
192pub unsafe fn sse_fft_helper_inplace<T>(
193    buffer: &mut [T],
194    scratch: &mut [T],
195    chunk_size: usize,
196    required_scratch: usize,
197    chunk_fn: impl FnMut(&mut [T], &mut [T]),
198) {
199    fft_helper_inplace(buffer, scratch, chunk_size, required_scratch, chunk_fn)
200}
201
202// A wrapper for the FFT helper functions that make sure the entire thing happens with the benefit of the SSE target feature,
203// so that things like loading twiddle factor registers etc can be lifted out of the loop
204#[target_feature(enable = "sse4.1")]
205pub unsafe fn sse_fft_helper_immut_unroll2x<T>(
206    input: &[T],
207    output: &mut [T],
208    chunk_size: usize,
209    chunk2x_fn: impl FnMut(&[T], &mut [T]),
210    chunk_fn: impl FnMut(&[T], &mut [T]),
211) {
212    fft_helper_immut_unroll2x(input, output, chunk_size, chunk2x_fn, chunk_fn)
213}
214
215// A wrapper for the FFT helper functions that make sure the entire thing happens with the benefit of the SSE target feature,
216// so that things like loading twiddle factor registers etc can be lifted out of the loop
217#[target_feature(enable = "sse4.1")]
218pub unsafe fn sse_fft_helper_outofplace_unroll2x<T>(
219    input: &mut [T],
220    output: &mut [T],
221    chunk_size: usize,
222    chunk2x_fn: impl FnMut(&mut [T], &mut [T]),
223    chunk_fn: impl FnMut(&mut [T], &mut [T]),
224) {
225    fft_helper_outofplace_unroll2x(input, output, chunk_size, chunk2x_fn, chunk_fn)
226}
227
228// A wrapper for the FFT helper functions that make sure the entire thing happens with the benefit of the SSE target feature,
229// so that things like loading twiddle factor registers etc can be lifted out of the loop
230#[target_feature(enable = "sse4.1")]
231pub unsafe fn sse_fft_helper_inplace_unroll2x<T>(
232    buffer: &mut [T],
233    chunk_size: usize,
234    chunk2x_fn: impl FnMut(&mut [T]),
235    chunk_fn: impl FnMut(&mut [T]),
236) {
237    fft_helper_inplace_unroll2x(buffer, chunk_size, chunk2x_fn, chunk_fn)
238}