GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/extensions/dsp_extensions.c
Date: 2025-10-27 19:12:55
Exec Total Coverage
Lines: 6 118 5.1%
Functions: 1 4 25.0%
Branches: 0 64 0.0%

Line Branch Exec Source
1 /*
2 Copyright 2025 Joel Svensson svenssonjoel@yahoo.se
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "extensions.h"
19 #include "lbm_utils.h"
20
21 #include <math.h>
22 #include <string.h>
23 #include <stdio.h>
24
25 #ifdef LBM_OPT_DSP_EXTENSIONS_SIZE
26 #pragma GCC optimize ("-Os")
27 #endif
28 #ifdef LBM_OPT_DSP_EXTENSIONS_SIZE_AGGRESSIVE
29 #pragma GCC optimize ("-Oz")
30 #endif
31
32 #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
33 #define LBM_SYSTEM_LITTLE_ENDIAN true
34 #else
35 #define LBM_SYSTEM_LITTLE_ENDIAN false
36 #endif
37
38
39 static lbm_uint sym_inverse = 0;
40 static lbm_uint sym_big_endian = 0;
41 static lbm_uint sym_little_endian = 0;
42
43 static inline uint32_t byte_order_swap(uint32_t w) {
44 uint32_t r = w;
45 uint8_t *bytes = (uint8_t*)&r;
46 uint8_t tmp = bytes[0];
47 bytes[0] = bytes[3];
48 bytes[3] = tmp;
49 tmp = bytes[1];
50 bytes[1] = bytes[2];
51 bytes[2] = tmp;
52
53 return r;
54 }
55
56
57 void lbm_fft(float *real, float *imag, int n, int inverse) {
58 int k = 0;
59 for (int i = 1; i < n; i++) {
60 int bit = n >> 1;
61 while (k & bit) {
62 k ^= bit;
63 bit >>= 1;
64 }
65 k ^= bit;
66
67 if (i < k) {
68 float temp = real[i];
69 real[i] = real[k];
70 real[k] = temp;
71
72 temp = imag[i];
73 imag[i] = imag[k];
74 imag[k] = temp;
75 }
76 }
77
78 for (int len = 2; len <= n; len <<= 1) {
79 float angle = (inverse ? 2.0f : -2.0f) * (float)M_PI / (float)len;
80 float wlen_r = cosf(angle);
81 float wlen_i = sinf(angle);
82
83 for (int i = 0; i < n; i += len) {
84 float w_r = 1.0f;
85 float w_i = 0.0f;
86
87 for (int j = 0; j < len / 2; j++) {
88 float u_r = real[i + j];
89 float u_i = imag[i + j];
90 float v_r = real[i + j + len / 2];
91 float v_i = imag[i + j + len / 2];
92
93 float t_r = w_r * v_r - w_i * v_i;
94 float t_i = w_r * v_i + w_i * v_r;
95
96 real[i + j] = u_r + t_r;
97 imag[i + j] = u_i + t_i;
98 real[i + j + len / 2] = u_r - t_r;
99 imag[i + j + len / 2] = u_i - t_i;
100
101 float next_w_r = w_r * wlen_r - w_i * wlen_i;
102 float next_w_i = w_r * wlen_i + w_i * wlen_r;
103 w_r = next_w_r;
104 w_i = next_w_i;
105 }
106 }
107 }
108
109 if (inverse) {
110 for (int i = 0; i < n; i++) {
111 real[i] /= (float)n;
112 imag[i] /= (float)n;
113 }
114 }
115 }
116
117 static lbm_value ext_fft_f32(lbm_value *args, lbm_uint argn) {
118 lbm_value r = ENC_SYM_TERROR;
119 if (argn >= 2 &&
120 lbm_is_array_r(args[0]) &&
121 lbm_is_array_r(args[1])) {
122
123 int inverse = 0;
124 bool be = true;
125
126 if (argn > 2 &&
127 lbm_is_symbol(args[2])) {
128 lbm_uint sym = lbm_dec_sym(args[2]);
129 if (sym == sym_inverse) {
130 inverse = 1;
131 } else if (sym == sym_little_endian) {
132 be = false;
133 }
134 }
135
136 if (argn > 3 &&
137 lbm_is_symbol(args[3])) {
138 if (lbm_dec_sym(args[3]) == sym_little_endian) {
139 be = false;
140 }
141 }
142
143 bool swap_byte_order =
144 (be && LBM_SYSTEM_LITTLE_ENDIAN) ||
145 (!be && !LBM_SYSTEM_LITTLE_ENDIAN);
146
147 lbm_array_header_t *real_arr = lbm_dec_array_r(args[0]);
148 lbm_array_header_t *imag_arr = lbm_dec_array_r(args[1]);
149
150 if (!real_arr || !imag_arr) {
151 return ENC_SYM_TERROR;
152 }
153
154 if (real_arr->size == imag_arr->size &&
155 real_arr->size > sizeof(float) && // need more than 4 bytes for a float
156 (real_arr->size % sizeof(float) == 0)) {
157 lbm_uint n = real_arr->size / sizeof(float);
158 lbm_uint padded_size = n;
159 if ((n & (n - 1)) != 0) { // not a power of two. needs padding.
160 padded_size = 1;
161 while (padded_size < n) padded_size <<= 1;
162 }
163
164 lbm_value real_copy;
165 lbm_value imag_copy;
166 if (lbm_heap_allocate_array(&real_copy, padded_size * sizeof(float)) &&
167 lbm_heap_allocate_array(&imag_copy, padded_size * sizeof(float))) {
168 // cppcheck-suppress invalidPointerCast
169 float *real_data = (float*)real_arr->data;
170 // cppcheck-suppress invalidPointerCast
171 float *imag_data = (float*)imag_arr->data;
172
173 lbm_array_header_t *real_copy_arr = lbm_dec_array_r(real_copy);
174 lbm_array_header_t *imag_copy_arr = lbm_dec_array_r(imag_copy);
175 // cppcheck-suppress invalidPointerCast
176 float *real_copy_data = (float*)real_copy_arr->data;
177 // cppcheck-suppress invalidPointerCast
178 float *imag_copy_data = (float*)imag_copy_arr->data;
179 memset(real_copy_data, 0, padded_size * sizeof(float));
180 memset(imag_copy_data, 0, padded_size * sizeof(float));
181
182 if (swap_byte_order) {
183 for (lbm_uint w = 0; w < n; w ++) {
184 // cppcheck-suppress invalidPointerCast
185 uint32_t swapped = byte_order_swap(((uint32_t*)real_data)[w]);
186 // cppcheck-suppress invalidPointerCast
187 ((uint32_t*)real_copy_data)[w] = swapped;
188 // cppcheck-suppress invalidPointerCast
189 swapped = byte_order_swap(((uint32_t*)imag_data)[w]);
190 // cppcheck-suppress invalidPointerCast
191 ((uint32_t*)imag_copy_data)[w] = swapped;
192 }
193 } else {
194 memcpy(real_copy_data, real_data, real_arr->size);
195 memcpy(imag_copy_data, imag_data, real_arr->size);
196 }
197
198 lbm_fft(real_copy_data, imag_copy_data, (int)padded_size, inverse);
199
200 if (swap_byte_order) {
201 for (lbm_uint w = 0; w < padded_size; w ++) {
202 // cppcheck-suppress invalidPointerCast
203 uint32_t swapped = byte_order_swap(((uint32_t*)real_copy_data)[w]);
204 // cppcheck-suppress invalidPointerCast
205 ((uint32_t*)real_copy_data)[w] = swapped;
206 // cppcheck-suppress invalidPointerCast
207 swapped = byte_order_swap(((uint32_t*)imag_copy_data)[w]);
208 // cppcheck-suppress invalidPointerCast
209 ((uint32_t*)imag_copy_data)[w] = swapped;
210 }
211 }
212 r = lbm_cons(real_copy, imag_copy);
213 } else {
214 r = ENC_SYM_MERROR;
215 }
216 } else {
217 r = ENC_SYM_NIL;
218 }
219 }
220 return r;
221 }
222
223 281 void lbm_dsp_extensions_init(void) {
224 281 lbm_add_symbol("inverse", &sym_inverse);
225 281 lbm_add_symbol("little-endian", &sym_little_endian);
226 281 lbm_add_symbol("big-endian", &sym_big_endian);
227
228 281 lbm_add_extension("fft", ext_fft_f32);
229 281 }
230