FastLED 3.10.9
Loading...
Searching...
No Matches
AutoResearchSimd.h
Go to the documentation of this file.
1// AutoResearchSimd.h - Comprehensive SIMD operation autoresearch tests
2//
3// Full SIMD test suite covering every operation in fl::simd namespace.
4// Tests include normal cases, boundary/edge cases, and signed arithmetic.
5//
6// Uses scalar fallback on platforms without native SIMD support.
7
8#pragma once
9
10#include <FastLED.h>
11#include "fl/stl/chrono.h"
12#include "fl/math/simd.h"
13#include "fl/stl/sstream.h"
19
20namespace autoresearch {
21namespace simd_check {
22
23using namespace fl::simd;
24
25// ============================================================================
26// Helper Functions
27// ============================================================================
28
29inline bool compare_u8(const uint8_t* a, const uint8_t* b, size_t n) {
30 for (size_t i = 0; i < n; i++) {
31 if (a[i] != b[i]) return false;
32 }
33 return true;
34}
35
36inline bool compare_u32(const uint32_t* a, const uint32_t* b, size_t n) {
37 for (size_t i = 0; i < n; i++) {
38 if (a[i] != b[i]) return false;
39 }
40 return true;
41}
42
43inline bool compare_f32(const float* a, const float* b, size_t n, float eps = 0.001f) {
44 for (size_t i = 0; i < n; i++) {
45 float diff = a[i] - b[i];
46 if (diff < -eps || diff > eps) return false;
47 }
48 return true;
49}
50
51// Reinterpret u32 as i32 for readability
52inline int32_t as_i32(uint32_t v) { return static_cast<int32_t>(v); }
53inline uint32_t as_u32(int32_t v) { return static_cast<uint32_t>(v); }
54
55// ============================================================================
56// u8x16 Load/Store Tests
57// ============================================================================
58
59inline bool test_load_store_u8_16() {
60 uint8_t input[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
61 uint8_t output[16] = {0};
62 simd_u8x16 v = load_u8_16(input);
63 store_u8_16(output, v);
64 return compare_u8(input, output, 16);
65}
66
68 uint8_t input[16] = {0,255,0,255, 0,255,0,255, 0,255,0,255, 0,255,0,255};
69 uint8_t output[16] = {0};
70 simd_u8x16 v = load_u8_16(input);
71 store_u8_16(output, v);
72 return compare_u8(input, output, 16);
73}
74
75// ============================================================================
76// u32x4 Load/Store Tests
77// ============================================================================
78
79inline bool test_load_store_u32_4() {
80 uint32_t input[4] = {0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210};
81 uint32_t output[4] = {0};
82 simd_u32x4 v = load_u32_4(input);
83 store_u32_4(output, v);
84 return compare_u32(input, output, 4);
85}
86
88 uint32_t input[4] = {0, 0xFFFFFFFF, 1, 0x80000000};
89 uint32_t output[4] = {0};
90 simd_u32x4 v = load_u32_4(input);
91 store_u32_4(output, v);
92 return compare_u32(input, output, 4);
93}
94
96 FL_ALIGNAS(16) uint32_t input[4] = {0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD};
97 FL_ALIGNAS(16) uint32_t output[4] = {0};
98 simd_u32x4 v = load_u32_4_aligned(input);
99 store_u32_4_aligned(output, v);
100 return compare_u32(input, output, 4);
101}
102
103// ============================================================================
104// f32x4 Load/Store Tests
105// ============================================================================
106
108 float input[4] = {1.5f, 2.5f, 3.5f, 4.5f};
109 float output[4] = {0.0f};
110 simd_f32x4 v = load_f32_4(input);
111 store_f32_4(output, v);
112 return compare_f32(input, output, 4);
113}
114
116 float input[4] = {0.0f, -0.0f, 1e30f, -1e30f};
117 float output[4] = {0.0f};
118 simd_f32x4 v = load_f32_4(input);
119 store_f32_4(output, v);
120 // Compare bitwise for zero sign preservation
121 for (int i = 0; i < 4; i++) {
122 float diff = input[i] - output[i];
123 if (diff < -0.001f || diff > 0.001f) return false;
124 }
125 return true;
126}
127
128// ============================================================================
129// u8x16 Saturating Arithmetic Tests
130// ============================================================================
131
132inline bool test_add_sat_u8_16() {
133 uint8_t a[16] = {100,150,200,250, 100,150,200,250, 100,150,200,250, 100,150,200,250};
134 uint8_t b[16] = {50,100,50,100, 50,100,50,100, 50,100,50,100, 50,100,50,100};
135 uint8_t expected[16] = {150,250,250,255, 150,250,250,255, 150,250,250,255, 150,250,250,255};
136 uint8_t output[16] = {0};
137 simd_u8x16 va = load_u8_16(a);
138 simd_u8x16 vb = load_u8_16(b);
139 store_u8_16(output, add_sat_u8_16(va, vb));
140 return compare_u8(expected, output, 16);
141}
142
144 // 255 + 255 = 255 (saturated), 0 + 0 = 0
145 uint8_t a[16] = {255,255,0,0, 128,1,254,255, 255,255,0,0, 128,1,254,255};
146 uint8_t b[16] = {255,1,0,0, 128,255,2,0, 255,1,0,0, 128,255,2,0};
147 uint8_t expected[16] = {255,255,0,0, 255,255,255,255, 255,255,0,0, 255,255,255,255};
148 uint8_t output[16] = {0};
149 store_u8_16(output, add_sat_u8_16(load_u8_16(a), load_u8_16(b)));
150 return compare_u8(expected, output, 16);
151}
152
153inline bool test_sub_sat_u8_16() {
154 uint8_t a[16] = {100,50,200,10, 100,50,200,10, 100,50,200,10, 100,50,200,10};
155 uint8_t b[16] = {50,100,50,100, 50,100,50,100, 50,100,50,100, 50,100,50,100};
156 uint8_t expected[16] = {50,0,150,0, 50,0,150,0, 50,0,150,0, 50,0,150,0};
157 uint8_t output[16] = {0};
158 store_u8_16(output, sub_sat_u8_16(load_u8_16(a), load_u8_16(b)));
159 return compare_u8(expected, output, 16);
160}
161
163 // 0 - 255 = 0 (clamped), 255 - 0 = 255, equal values = 0
164 uint8_t a[16] = {0,255,100,0, 1,0,0,0, 0,255,100,0, 1,0,0,0};
165 uint8_t b[16] = {255,0,100,0, 0,1,255,128, 255,0,100,0, 0,1,255,128};
166 uint8_t expected[16] = {0,255,0,0, 1,0,0,0, 0,255,0,0, 1,0,0,0};
167 uint8_t output[16] = {0};
168 store_u8_16(output, sub_sat_u8_16(load_u8_16(a), load_u8_16(b)));
169 return compare_u8(expected, output, 16);
170}
171
172// ============================================================================
173// u8x16 Scale / Blend Tests
174// ============================================================================
175
176inline bool test_scale_u8_16() {
177 uint8_t a[16] = {255,128,64,32, 255,128,64,32, 255,128,64,32, 255,128,64,32};
178 uint8_t output[16] = {0};
179 store_u8_16(output, scale_u8_16(load_u8_16(a), 128));
180 for (int i = 0; i < 16; i++) {
181 int expected = (a[i] * 128) / 256;
182 int diff = (int)output[i] - expected;
183 if (diff < -1 || diff > 1) return false;
184 }
185 return true;
186}
187
189 uint8_t a[16] = {255,128,64,32, 255,128,64,32, 255,128,64,32, 255,128,64,32};
190 uint8_t expected[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
191 uint8_t output[16] = {0};
192 store_u8_16(output, scale_u8_16(load_u8_16(a), 0));
193 return compare_u8(expected, output, 16);
194}
195
197 // scale by 255 should give approximately the original value
198 uint8_t a[16] = {255,128,64,32, 10,200,100,50, 255,128,64,32, 10,200,100,50};
199 uint8_t output[16] = {0};
200 store_u8_16(output, scale_u8_16(load_u8_16(a), 255));
201 for (int i = 0; i < 16; i++) {
202 int expected = (a[i] * 255) / 256;
203 int diff = (int)output[i] - expected;
204 if (diff < -1 || diff > 1) return false;
205 }
206 return true;
207}
208
209inline bool test_blend_u8_16() {
210 uint8_t a[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
211 uint8_t b[16] = {255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255};
212 uint8_t output[16] = {0};
213 store_u8_16(output, blend_u8_16(load_u8_16(a), load_u8_16(b), 128));
214 for (int i = 0; i < 16; i++) {
215 int diff = (int)output[i] - 128;
216 if (diff < -2 || diff > 2) return false;
217 }
218 return true;
219}
220
222 // amount=0 -> all a, amount=255 -> ~all b
223 uint8_t a[16] = {100,100,100,100, 100,100,100,100, 100,100,100,100, 100,100,100,100};
224 uint8_t b[16] = {200,200,200,200, 200,200,200,200, 200,200,200,200, 200,200,200,200};
225 uint8_t output0[16] = {0};
226 uint8_t output255[16] = {0};
227 store_u8_16(output0, blend_u8_16(load_u8_16(a), load_u8_16(b), 0));
228 store_u8_16(output255, blend_u8_16(load_u8_16(a), load_u8_16(b), 255));
229 for (int i = 0; i < 16; i++) {
230 if (output0[i] != 100) return false; // amount=0 -> a
231 int diff = (int)output255[i] - 200;
232 if (diff < -2 || diff > 2) return false; // amount=255 -> ~b
233 }
234 return true;
235}
236
237// ============================================================================
238// u8x16 Comparison Tests
239// ============================================================================
240
241inline bool test_min_u8_16() {
242 uint8_t a[16] = {10,200,30,240, 0,255,1,254, 10,200,30,240, 0,255,1,254};
243 uint8_t b[16] = {20,100,40,120, 0,0,255,255, 20,100,40,120, 0,0,255,255};
244 uint8_t expected[16] = {10,100,30,120, 0,0,1,254, 10,100,30,120, 0,0,1,254};
245 uint8_t output[16] = {0};
246 store_u8_16(output, min_u8_16(load_u8_16(a), load_u8_16(b)));
247 return compare_u8(expected, output, 16);
248}
249
250inline bool test_max_u8_16() {
251 uint8_t a[16] = {10,200,30,240, 0,255,1,254, 10,200,30,240, 0,255,1,254};
252 uint8_t b[16] = {20,100,40,120, 0,0,255,255, 20,100,40,120, 0,0,255,255};
253 uint8_t expected[16] = {20,200,40,240, 0,255,255,255, 20,200,40,240, 0,255,255,255};
254 uint8_t output[16] = {0};
255 store_u8_16(output, max_u8_16(load_u8_16(a), load_u8_16(b)));
256 return compare_u8(expected, output, 16);
257}
258
259inline bool test_avg_u8_16() {
260 uint8_t a[16] = {100,200,50,0, 255,0,1,254, 100,200,50,0, 255,0,1,254};
261 uint8_t b[16] = {200,100,150,0, 255,0,1,254, 200,100,150,0, 255,0,1,254};
262 uint8_t output[16] = {0};
263 store_u8_16(output, avg_u8_16(load_u8_16(a), load_u8_16(b)));
264 for (int i = 0; i < 16; i++) {
265 int expected = (a[i] + b[i]) / 2;
266 int diff = (int)output[i] - expected;
267 if (diff < -1 || diff > 1) return false;
268 }
269 return true;
270}
271
272inline bool test_avg_round_u8_16() {
273 uint8_t a[16] = {101,201,51,1, 255,0,3,253, 101,201,51,1, 255,0,3,253};
274 uint8_t b[16] = {200,100,150,0, 254,1,2,252, 200,100,150,0, 254,1,2,252};
275 uint8_t output[16] = {0};
276 store_u8_16(output, avg_round_u8_16(load_u8_16(a), load_u8_16(b)));
277 for (int i = 0; i < 16; i++) {
278 int expected = (a[i] + b[i] + 1) / 2;
279 int diff = (int)output[i] - expected;
280 if (diff < -1 || diff > 1) return false;
281 }
282 return true;
283}
284
285// ============================================================================
286// u8x16 Bitwise Tests
287// ============================================================================
288
289inline bool test_and_u8_16() {
290 uint8_t a[16] = {0xFF,0x0F,0xF0,0xAA, 0xFF,0x0F,0xF0,0xAA, 0xFF,0x0F,0xF0,0xAA, 0xFF,0x0F,0xF0,0xAA};
291 uint8_t b[16] = {0x0F,0xFF,0xFF,0x55, 0x0F,0xFF,0xFF,0x55, 0x0F,0xFF,0xFF,0x55, 0x0F,0xFF,0xFF,0x55};
292 uint8_t expected[16] = {0x0F,0x0F,0xF0,0x00, 0x0F,0x0F,0xF0,0x00, 0x0F,0x0F,0xF0,0x00, 0x0F,0x0F,0xF0,0x00};
293 uint8_t output[16] = {0};
294 store_u8_16(output, and_u8_16(load_u8_16(a), load_u8_16(b)));
295 return compare_u8(expected, output, 16);
296}
297
298inline bool test_or_u8_16() {
299 uint8_t a[16] = {0xFF,0x0F,0xF0,0xAA, 0x00,0x00,0xFF,0x80, 0xFF,0x0F,0xF0,0xAA, 0x00,0x00,0xFF,0x80};
300 uint8_t b[16] = {0x0F,0xFF,0xFF,0x55, 0x00,0xFF,0x00,0x01, 0x0F,0xFF,0xFF,0x55, 0x00,0xFF,0x00,0x01};
301 uint8_t expected[16] = {0xFF,0xFF,0xFF,0xFF, 0x00,0xFF,0xFF,0x81, 0xFF,0xFF,0xFF,0xFF, 0x00,0xFF,0xFF,0x81};
302 uint8_t output[16] = {0};
303 store_u8_16(output, or_u8_16(load_u8_16(a), load_u8_16(b)));
304 return compare_u8(expected, output, 16);
305}
306
307inline bool test_xor_u8_16() {
308 uint8_t a[16] = {0xFF,0x0F,0xF0,0xAA, 0xFF,0x00,0xAB,0x01, 0xFF,0x0F,0xF0,0xAA, 0xFF,0x00,0xAB,0x01};
309 uint8_t b[16] = {0x0F,0xFF,0xFF,0x55, 0xFF,0x00,0xAB,0x01, 0x0F,0xFF,0xFF,0x55, 0xFF,0x00,0xAB,0x01};
310 uint8_t expected[16] = {0xF0,0xF0,0x0F,0xFF, 0x00,0x00,0x00,0x00, 0xF0,0xF0,0x0F,0xFF, 0x00,0x00,0x00,0x00};
311 uint8_t output[16] = {0};
312 store_u8_16(output, xor_u8_16(load_u8_16(a), load_u8_16(b)));
313 return compare_u8(expected, output, 16);
314}
315
316inline bool test_andnot_u8_16() {
317 // andnot(a, b) = ~a & b
318 uint8_t a[16] = {0xFF,0x0F,0xF0,0xAA, 0x00,0xFF,0x55,0x80, 0xFF,0x0F,0xF0,0xAA, 0x00,0xFF,0x55,0x80};
319 uint8_t b[16] = {0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF};
320 uint8_t expected[16] = {0x00,0xF0,0x0F,0x55, 0xFF,0x00,0xAA,0x7F, 0x00,0xF0,0x0F,0x55, 0xFF,0x00,0xAA,0x7F};
321 uint8_t output[16] = {0};
322 store_u8_16(output, andnot_u8_16(load_u8_16(a), load_u8_16(b)));
323 return compare_u8(expected, output, 16);
324}
325
326// ============================================================================
327// u32x4 Broadcast / Construct Tests
328// ============================================================================
329
330inline bool test_set1_u32_4() {
331 uint32_t output[4] = {0};
332 store_u32_4(output, set1_u32_4(0xDEADBEEF));
333 uint32_t expected[4] = {0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF, 0xDEADBEEF};
334 return compare_u32(expected, output, 4);
335}
336
337inline bool test_set1_u32_4_zero() {
338 uint32_t output[4] = {1,1,1,1};
339 store_u32_4(output, set1_u32_4(0));
340 uint32_t expected[4] = {0, 0, 0, 0};
341 return compare_u32(expected, output, 4);
342}
343
344inline bool test_set_u32_4() {
345 uint32_t output[4] = {0};
346 store_u32_4(output, set_u32_4(0x11111111, 0x22222222, 0x33333333, 0x44444444));
347 uint32_t expected[4] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
348 return compare_u32(expected, output, 4);
349}
350
351inline bool test_set1_f32_4() {
352 float output[4] = {0.0f};
353 store_f32_4(output, set1_f32_4(3.14f));
354 float expected[4] = {3.14f, 3.14f, 3.14f, 3.14f};
355 return compare_f32(expected, output, 4);
356}
357
358// ============================================================================
359// u32x4 Extract Test
360// ============================================================================
361
362inline bool test_extract_u32_4() {
363 simd_u32x4 v = set_u32_4(10, 20, 30, 40);
364 if (extract_u32_4(v, 0) != 10) return false;
365 if (extract_u32_4(v, 1) != 20) return false;
366 if (extract_u32_4(v, 2) != 30) return false;
367 if (extract_u32_4(v, 3) != 40) return false;
368 return true;
369}
370
371// ============================================================================
372// u32x4 Bitwise Tests
373// ============================================================================
374
375inline bool test_xor_u32_4() {
376 uint32_t a[4] = {0xFFFFFFFF, 0x0F0F0F0F, 0xAAAAAAAA, 0x12345678};
377 uint32_t b[4] = {0x0F0F0F0F, 0xFFFFFFFF, 0x55555555, 0x12345678};
378 uint32_t expected[4] = {0xF0F0F0F0, 0xF0F0F0F0, 0xFFFFFFFF, 0x00000000};
379 uint32_t output[4] = {0};
380 store_u32_4(output, xor_u32_4(load_u32_4(a), load_u32_4(b)));
381 return compare_u32(expected, output, 4);
382}
383
384inline bool test_and_u32_4() {
385 uint32_t a[4] = {0xFFFF0000, 0x0F0F0F0F, 0xAAAAAAAA, 0x00000000};
386 uint32_t b[4] = {0x0000FFFF, 0xF0F0F0F0, 0xFFFFFFFF, 0xFFFFFFFF};
387 uint32_t expected[4] = {0x00000000, 0x00000000, 0xAAAAAAAA, 0x00000000};
388 uint32_t output[4] = {0};
389 store_u32_4(output, and_u32_4(load_u32_4(a), load_u32_4(b)));
390 return compare_u32(expected, output, 4);
391}
392
393inline bool test_or_u32_4() {
394 uint32_t a[4] = {0xFFFF0000, 0x0F0F0F0F, 0xAAAAAAAA, 0x00000000};
395 uint32_t b[4] = {0x0000FFFF, 0xF0F0F0F0, 0x55555555, 0x00000000};
396 uint32_t expected[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000};
397 uint32_t output[4] = {0};
398 store_u32_4(output, or_u32_4(load_u32_4(a), load_u32_4(b)));
399 return compare_u32(expected, output, 4);
400}
401
402// ============================================================================
403// i32x4 Arithmetic Tests
404// ============================================================================
405
406inline bool test_add_i32_4() {
407 // Use signed values stored as u32
408 uint32_t a[4] = {as_u32(100), as_u32(-100), as_u32(0x7FFFFFFF), as_u32(0)};
409 uint32_t b[4] = {as_u32(200), as_u32(-200), as_u32(1), as_u32(0)};
410 uint32_t output[4] = {0};
411 store_u32_4(output, add_i32_4(load_u32_4(a), load_u32_4(b)));
412 if (as_i32(output[0]) != 300) return false;
413 if (as_i32(output[1]) != -300) return false;
414 // 0x7FFFFFFF + 1 wraps (expected behavior, no saturation)
415 if (output[2] != 0x80000000) return false;
416 if (as_i32(output[3]) != 0) return false;
417 return true;
418}
419
420inline bool test_sub_i32_4() {
421 uint32_t a[4] = {as_u32(300), as_u32(-100), as_u32(0), as_u32(1000)};
422 uint32_t b[4] = {as_u32(200), as_u32(200), as_u32(0), as_u32(1000)};
423 uint32_t output[4] = {0};
424 store_u32_4(output, sub_i32_4(load_u32_4(a), load_u32_4(b)));
425 if (as_i32(output[0]) != 100) return false;
426 if (as_i32(output[1]) != -300) return false;
427 if (as_i32(output[2]) != 0) return false;
428 if (as_i32(output[3]) != 0) return false;
429 return true;
430}
431
432// ============================================================================
433// i32x4 Shift Tests
434// ============================================================================
435
436inline bool test_srl_u32_4() {
437 uint32_t input[4] = {0x80000000, 0xFFFFFFFF, 0x00000010, 0x12345678};
438 uint32_t output[4] = {0};
439 store_u32_4(output, srl_u32_4(load_u32_4(input), 4));
440 uint32_t expected[4] = {0x08000000, 0x0FFFFFFF, 0x00000001, 0x01234567};
441 return compare_u32(expected, output, 4);
442}
443
444inline bool test_sll_u32_4() {
445 uint32_t input[4] = {0x00000001, 0x0FFFFFFF, 0x80000000, 0x12345678};
446 uint32_t output[4] = {0};
447 store_u32_4(output, sll_u32_4(load_u32_4(input), 4));
448 uint32_t expected[4] = {0x00000010, 0xFFFFFFF0, 0x00000000, 0x23456780};
449 return compare_u32(expected, output, 4);
450}
451
452inline bool test_sra_i32_4() {
453 // Arithmetic shift preserves sign bit
454 uint32_t input[4] = {as_u32(-16), as_u32(16), 0x80000000, 0x7FFFFFFF};
455 uint32_t output[4] = {0};
456 store_u32_4(output, sra_i32_4(load_u32_4(input), 2));
457 if (as_i32(output[0]) != -4) return false; // -16 >> 2 = -4 (sign-extended)
458 if (as_i32(output[1]) != 4) return false; // 16 >> 2 = 4
459 if (as_i32(output[2]) != as_i32(0xE0000000)) return false; // 0x80000000 >> 2 sign-extended
460 if (as_i32(output[3]) != as_i32(0x1FFFFFFF)) return false; // 0x7FFFFFFF >> 2
461 return true;
462}
463
464// ============================================================================
465// i32x4 Min/Max Tests (signed)
466// ============================================================================
467
468inline bool test_min_i32_4() {
469 uint32_t a[4] = {as_u32(10), as_u32(-10), as_u32(0x7FFFFFFF), as_u32(-1)};
470 uint32_t b[4] = {as_u32(20), as_u32(10), as_u32((int32_t)0x80000000), as_u32(0)};
471 uint32_t output[4] = {0};
472 store_u32_4(output, min_i32_4(load_u32_4(a), load_u32_4(b)));
473 if (as_i32(output[0]) != 10) return false;
474 if (as_i32(output[1]) != -10) return false;
475 if (as_i32(output[2]) != (int32_t)0x80000000) return false; // INT32_MIN < INT32_MAX
476 if (as_i32(output[3]) != -1) return false; // -1 < 0
477 return true;
478}
479
480inline bool test_max_i32_4() {
481 uint32_t a[4] = {as_u32(10), as_u32(-10), as_u32(0x7FFFFFFF), as_u32(-1)};
482 uint32_t b[4] = {as_u32(20), as_u32(10), as_u32((int32_t)0x80000000), as_u32(0)};
483 uint32_t output[4] = {0};
484 store_u32_4(output, max_i32_4(load_u32_4(a), load_u32_4(b)));
485 if (as_i32(output[0]) != 20) return false;
486 if (as_i32(output[1]) != 10) return false;
487 if (as_i32(output[2]) != 0x7FFFFFFF) return false;
488 if (as_i32(output[3]) != 0) return false;
489 return true;
490}
491
492// ============================================================================
493// Fixed-Point Multiply Tests (Q16.16)
494// ============================================================================
495
496inline bool test_mulhi_i32_4() {
497 // mulhi_i32_4: ((i64)a * (i64)b) >> 16
498 // Test: 0x00010000 * 0x00020000 >> 16 = 0x00020000 (1.0 * 2.0 = 2.0 in Q16.16)
499 uint32_t a[4] = {0x00010000, 0x00020000, as_u32(-0x00010000), 0x00008000};
500 uint32_t b[4] = {0x00020000, 0x00010000, 0x00020000, 0x00008000};
501 uint32_t output[4] = {0};
502 store_u32_4(output, mulhi_i32_4(load_u32_4(a), load_u32_4(b)));
503 if (output[0] != 0x00020000) return false; // 1.0 * 2.0 = 2.0
504 if (output[1] != 0x00020000) return false; // 2.0 * 1.0 = 2.0
505 if (as_i32(output[2]) != -0x00020000) return false; // -1.0 * 2.0 = -2.0
506 if (output[3] != 0x00004000) return false; // 0.5 * 0.5 = 0.25
507 return true;
508}
509
510inline bool test_mulhi_u32_4() {
511 // mulhi_u32_4: ((u64)a * (u64)b) >> 16 (unsigned)
512 uint32_t a[4] = {0x00010000, 0x00020000, 0x00030000, 0x00008000};
513 uint32_t b[4] = {0x00020000, 0x00030000, 0x00010000, 0x00008000};
514 uint32_t output[4] = {0};
515 store_u32_4(output, mulhi_u32_4(load_u32_4(a), load_u32_4(b)));
516 if (output[0] != 0x00020000) return false; // 1.0 * 2.0
517 if (output[1] != 0x00060000) return false; // 2.0 * 3.0
518 if (output[2] != 0x00030000) return false; // 3.0 * 1.0
519 if (output[3] != 0x00004000) return false; // 0.5 * 0.5
520 return true;
521}
522
523inline bool test_mulhi_su32_4() {
524 // mulhi_su32_4: signed a * unsigned b >> 16
525 // When b >= 0, should match mulhi_i32_4
526 uint32_t a[4] = {as_u32(-0x00010000), 0x00020000, as_u32(-0x00020000), 0x00010000};
527 uint32_t b[4] = {0x00020000, 0x00010000, 0x00010000, 0x00030000};
528 uint32_t output[4] = {0};
529 store_u32_4(output, mulhi_su32_4(load_u32_4(a), load_u32_4(b)));
530 if (as_i32(output[0]) != -0x00020000) return false; // -1.0 * 2.0 = -2.0
531 if (output[1] != 0x00020000) return false; // 2.0 * 1.0 = 2.0
532 if (as_i32(output[2]) != -0x00020000) return false; // -2.0 * 1.0 = -2.0
533 if (output[3] != 0x00030000) return false; // 1.0 * 3.0 = 3.0
534 return true;
535}
536
537inline bool test_mulhi32_i32_4() {
538 // mulhi32_i32_4: ((i64)a * (i64)b) >> 32
539 // Test: 0x40000000 * 0x40000000 >> 32 = 0x10000000
540 uint32_t a[4] = {0x40000000, as_u32(-0x40000000), 0x7FFFFFFF, 0x00000001};
541 uint32_t b[4] = {0x40000000, 0x40000000, 0x00000002, 0x7FFFFFFF};
542 uint32_t output[4] = {0};
543 store_u32_4(output, mulhi32_i32_4(load_u32_4(a), load_u32_4(b)));
544 if (output[0] != 0x10000000) return false; // 0.5 * 0.5 = 0.25 (Q31)
545 if (as_i32(output[1]) != as_i32(0xF0000000)) return false; // -0.5 * 0.5 = -0.25
546 if (output[2] != 0x00000000) return false; // small * small -> 0
547 if (output[3] != 0x00000000) return false; // 1 * MAX -> 0 (high bits only)
548 return true;
549}
550
551// ============================================================================
552// u32x4 Interleave / Unpack Tests
553// ============================================================================
554
555inline bool test_unpacklo_u32_4() {
556 // {a0, b0, a1, b1}
557 simd_u32x4 a = set_u32_4(1, 2, 3, 4);
558 simd_u32x4 b = set_u32_4(10, 20, 30, 40);
559 uint32_t output[4] = {0};
560 store_u32_4(output, unpacklo_u32_4(a, b));
561 uint32_t expected[4] = {1, 10, 2, 20};
562 return compare_u32(expected, output, 4);
563}
564
565inline bool test_unpackhi_u32_4() {
566 // {a2, b2, a3, b3}
567 simd_u32x4 a = set_u32_4(1, 2, 3, 4);
568 simd_u32x4 b = set_u32_4(10, 20, 30, 40);
569 uint32_t output[4] = {0};
570 store_u32_4(output, unpackhi_u32_4(a, b));
571 uint32_t expected[4] = {3, 30, 4, 40};
572 return compare_u32(expected, output, 4);
573}
574
576 // {a0, a1, b0, b1}
577 simd_u32x4 a = set_u32_4(1, 2, 3, 4);
578 simd_u32x4 b = set_u32_4(10, 20, 30, 40);
579 uint32_t output[4] = {0};
580 store_u32_4(output, unpacklo_u64_as_u32_4(a, b));
581 uint32_t expected[4] = {1, 2, 10, 20};
582 return compare_u32(expected, output, 4);
583}
584
586 // {a2, a3, b2, b3}
587 simd_u32x4 a = set_u32_4(1, 2, 3, 4);
588 simd_u32x4 b = set_u32_4(10, 20, 30, 40);
589 uint32_t output[4] = {0};
590 store_u32_4(output, unpackhi_u64_as_u32_4(a, b));
591 uint32_t expected[4] = {3, 4, 30, 40};
592 return compare_u32(expected, output, 4);
593}
594
595// ============================================================================
596// f32x4 Arithmetic Tests
597// ============================================================================
598
599inline bool test_add_f32_4() {
600 float a[4] = {1.0f, 2.0f, 3.0f, 4.0f};
601 float b[4] = {5.0f, 6.0f, 7.0f, 8.0f};
602 float expected[4] = {6.0f, 8.0f, 10.0f, 12.0f};
603 float output[4] = {0.0f};
604 store_f32_4(output, add_f32_4(load_f32_4(a), load_f32_4(b)));
605 return compare_f32(expected, output, 4);
606}
607
609 float a[4] = {-1.0f, 2.0f, -3.0f, 0.0f};
610 float b[4] = {1.0f, -2.0f, -3.0f, 0.0f};
611 float expected[4] = {0.0f, 0.0f, -6.0f, 0.0f};
612 float output[4] = {0.0f};
613 store_f32_4(output, add_f32_4(load_f32_4(a), load_f32_4(b)));
614 return compare_f32(expected, output, 4);
615}
616
617inline bool test_sub_f32_4() {
618 float a[4] = {10.0f, 20.0f, 30.0f, 40.0f};
619 float b[4] = {1.0f, 2.0f, 3.0f, 4.0f};
620 float expected[4] = {9.0f, 18.0f, 27.0f, 36.0f};
621 float output[4] = {0.0f};
622 store_f32_4(output, sub_f32_4(load_f32_4(a), load_f32_4(b)));
623 return compare_f32(expected, output, 4);
624}
625
626inline bool test_mul_f32_4() {
627 float a[4] = {2.0f, 3.0f, 4.0f, 5.0f};
628 float b[4] = {3.0f, 4.0f, 5.0f, 6.0f};
629 float expected[4] = {6.0f, 12.0f, 20.0f, 30.0f};
630 float output[4] = {0.0f};
631 store_f32_4(output, mul_f32_4(load_f32_4(a), load_f32_4(b)));
632 return compare_f32(expected, output, 4);
633}
634
636 float a[4] = {-2.0f, 3.0f, -4.0f, 0.0f};
637 float b[4] = {3.0f, -4.0f, -5.0f, 100.0f};
638 float expected[4] = {-6.0f, -12.0f, 20.0f, 0.0f};
639 float output[4] = {0.0f};
640 store_f32_4(output, mul_f32_4(load_f32_4(a), load_f32_4(b)));
641 return compare_f32(expected, output, 4);
642}
643
644inline bool test_div_f32_4() {
645 float a[4] = {10.0f, 20.0f, 30.0f, 40.0f};
646 float b[4] = {2.0f, 4.0f, 5.0f, 8.0f};
647 float expected[4] = {5.0f, 5.0f, 6.0f, 5.0f};
648 float output[4] = {0.0f};
649 store_f32_4(output, div_f32_4(load_f32_4(a), load_f32_4(b)));
650 return compare_f32(expected, output, 4);
651}
652
653inline bool test_sqrt_f32_4() {
654 float a[4] = {4.0f, 9.0f, 16.0f, 25.0f};
655 float expected[4] = {2.0f, 3.0f, 4.0f, 5.0f};
656 float output[4] = {0.0f};
657 store_f32_4(output, sqrt_f32_4(load_f32_4(a)));
658 return compare_f32(expected, output, 4);
659}
660
661inline bool test_sqrt_f32_4_zero() {
662 float a[4] = {0.0f, 1.0f, 100.0f, 0.25f};
663 float expected[4] = {0.0f, 1.0f, 10.0f, 0.5f};
664 float output[4] = {0.0f};
665 store_f32_4(output, sqrt_f32_4(load_f32_4(a)));
666 return compare_f32(expected, output, 4);
667}
668
669inline bool test_min_f32_4() {
670 float a[4] = {1.0f, 5.0f, -3.0f, 7.0f};
671 float b[4] = {4.0f, 2.0f, 6.0f, -1.0f};
672 float expected[4] = {1.0f, 2.0f, -3.0f, -1.0f};
673 float output[4] = {0.0f};
674 store_f32_4(output, min_f32_4(load_f32_4(a), load_f32_4(b)));
675 return compare_f32(expected, output, 4);
676}
677
678inline bool test_max_f32_4() {
679 float a[4] = {1.0f, 5.0f, -3.0f, 7.0f};
680 float b[4] = {4.0f, 2.0f, 6.0f, -1.0f};
681 float expected[4] = {4.0f, 5.0f, 6.0f, 7.0f};
682 float output[4] = {0.0f};
683 store_f32_4(output, max_f32_4(load_f32_4(a), load_f32_4(b)));
684 return compare_f32(expected, output, 4);
685}
686
687// ============================================================================
688// Cross-AutoResearch Tests: SIMD vs Scalar Reference
689// These tests compute expected results using explicit scalar math, then compare
690// against the SIMD function output. Catches PIE assembly bugs by never trusting
691// the SIMD implementation for expected values.
692// ============================================================================
693
694// Scalar reference: bitwise AND of two u8[16] arrays
695inline void ref_and_u8_16(const uint8_t* a, const uint8_t* b, uint8_t* out) {
696 for (int i = 0; i < 16; i++) out[i] = a[i] & b[i];
697}
698inline void ref_or_u8_16(const uint8_t* a, const uint8_t* b, uint8_t* out) {
699 for (int i = 0; i < 16; i++) out[i] = a[i] | b[i];
700}
701inline void ref_xor_u8_16(const uint8_t* a, const uint8_t* b, uint8_t* out) {
702 for (int i = 0; i < 16; i++) out[i] = a[i] ^ b[i];
703}
704inline void ref_andnot_u8_16(const uint8_t* a, const uint8_t* b, uint8_t* out) {
705 for (int i = 0; i < 16; i++) out[i] = (~a[i]) & b[i];
706}
707inline void ref_xor_u32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
708 for (int i = 0; i < 4; i++) out[i] = a[i] ^ b[i];
709}
710inline void ref_and_u32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
711 for (int i = 0; i < 4; i++) out[i] = a[i] & b[i];
712}
713inline void ref_or_u32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
714 for (int i = 0; i < 4; i++) out[i] = a[i] | b[i];
715}
716
717// Adversarial cross-autoresearch: alternating bit patterns
719 uint8_t a[16] = {0xAA,0x55,0xFF,0x00, 0x0F,0xF0,0x81,0x7E, 0x01,0xFE,0xCC,0x33, 0xDB,0x24,0xA5,0x5A};
720 uint8_t b[16] = {0x55,0xAA,0x00,0xFF, 0xF0,0x0F,0x7E,0x81, 0xFE,0x01,0x33,0xCC, 0x24,0xDB,0x5A,0xA5};
721 uint8_t expected[16], actual[16];
722 ref_and_u8_16(a, b, expected);
723 store_u8_16(actual, and_u8_16(load_u8_16(a), load_u8_16(b)));
724 return compare_u8(expected, actual, 16);
725}
726
728 uint8_t a[16] = {0xAA,0x55,0xFF,0x00, 0x0F,0xF0,0x81,0x7E, 0x01,0xFE,0xCC,0x33, 0xDB,0x24,0xA5,0x5A};
729 uint8_t b[16] = {0x55,0xAA,0x00,0xFF, 0xF0,0x0F,0x7E,0x81, 0xFE,0x01,0x33,0xCC, 0x24,0xDB,0x5A,0xA5};
730 uint8_t expected[16], actual[16];
731 ref_or_u8_16(a, b, expected);
732 store_u8_16(actual, or_u8_16(load_u8_16(a), load_u8_16(b)));
733 return compare_u8(expected, actual, 16);
734}
735
737 uint8_t a[16] = {0xAA,0x55,0xFF,0x00, 0x0F,0xF0,0x81,0x7E, 0x01,0xFE,0xCC,0x33, 0xDB,0x24,0xA5,0x5A};
738 uint8_t b[16] = {0x55,0xAA,0x00,0xFF, 0xF0,0x0F,0x7E,0x81, 0xFE,0x01,0x33,0xCC, 0x24,0xDB,0x5A,0xA5};
739 uint8_t expected[16], actual[16];
740 ref_xor_u8_16(a, b, expected);
741 store_u8_16(actual, xor_u8_16(load_u8_16(a), load_u8_16(b)));
742 return compare_u8(expected, actual, 16);
743}
744
746 uint8_t a[16] = {0xAA,0x55,0xFF,0x00, 0x0F,0xF0,0x81,0x7E, 0x01,0xFE,0xCC,0x33, 0xDB,0x24,0xA5,0x5A};
747 uint8_t b[16] = {0x55,0xAA,0x00,0xFF, 0xF0,0x0F,0x7E,0x81, 0xFE,0x01,0x33,0xCC, 0x24,0xDB,0x5A,0xA5};
748 uint8_t expected[16], actual[16];
750 store_u8_16(actual, andnot_u8_16(load_u8_16(a), load_u8_16(b)));
751 return compare_u8(expected, actual, 16);
752}
753
754// Adversarial u32 cross-autoresearch: powers of 2, all-ones, sign bit
756 uint32_t a[4] = {0x80000001, 0x7FFFFFFE, 0xDEADBEEF, 0x00000000};
757 uint32_t b[4] = {0x80000001, 0x80000001, 0xCAFEBABE, 0xFFFFFFFF};
758 uint32_t expected[4], actual[4];
759 ref_xor_u32_4(a, b, expected);
760 store_u32_4(actual, xor_u32_4(load_u32_4(a), load_u32_4(b)));
761 return compare_u32(expected, actual, 4);
762}
763
765 uint32_t a[4] = {0x80000001, 0x7FFFFFFE, 0xDEADBEEF, 0x00000000};
766 uint32_t b[4] = {0x80000001, 0x80000001, 0xCAFEBABE, 0xFFFFFFFF};
767 uint32_t expected[4], actual[4];
768 ref_and_u32_4(a, b, expected);
769 store_u32_4(actual, and_u32_4(load_u32_4(a), load_u32_4(b)));
770 return compare_u32(expected, actual, 4);
771}
772
774 uint32_t a[4] = {0x80000001, 0x7FFFFFFE, 0xDEADBEEF, 0x00000000};
775 uint32_t b[4] = {0x80000001, 0x80000001, 0xCAFEBABE, 0xFFFFFFFF};
776 uint32_t expected[4], actual[4];
777 ref_or_u32_4(a, b, expected);
778 store_u32_4(actual, or_u32_4(load_u32_4(a), load_u32_4(b)));
779 return compare_u32(expected, actual, 4);
780}
781
782// Scalar reference: scale
783inline void ref_scale_u8_16(const uint8_t* v, uint8_t scale, uint8_t* out) {
784 for (int i = 0; i < 16; i++) {
785 out[i] = (uint8_t)(((uint16_t)v[i] * scale) >> 8);
786 }
787}
788
790 // Adversarial: mix of 0, 1, 127, 128, 254, 255 with various scale factors
791 uint8_t v[16] = {0,1,127,128, 254,255,0x55,0xAA, 0x0F,0xF0,0x80,0x7F, 0x01,0xFE,0xFF,0x00};
792 uint8_t scale_vals[] = {0, 1, 127, 128, 254, 255};
793 for (int s = 0; s < 6; s++) {
794 uint8_t expected[16], actual[16];
795 ref_scale_u8_16(v, scale_vals[s], expected);
796 store_u8_16(actual, scale_u8_16(load_u8_16(v), scale_vals[s]));
797 if (!compare_u8(expected, actual, 16)) return false;
798 }
799 return true;
800}
801
802// Scalar reference: unsigned saturating add/sub
803inline void ref_add_sat_u8_16(const uint8_t* a, const uint8_t* b, uint8_t* out) {
804 for (int i = 0; i < 16; i++) {
805 uint16_t sum = (uint16_t)a[i] + (uint16_t)b[i];
806 out[i] = (sum > 255) ? 255 : (uint8_t)sum;
807 }
808}
809inline void ref_sub_sat_u8_16(const uint8_t* a, const uint8_t* b, uint8_t* out) {
810 for (int i = 0; i < 16; i++) {
811 out[i] = (a[i] > b[i]) ? (a[i] - b[i]) : 0;
812 }
813}
814
815// Adversarial cross-autoresearch: sat add with every combination
817 // Test adversarial: all-255+all-255, alternating, near-overflow
818 uint8_t a[16] = {255,254,128,127, 0,1,200,50, 0xFF,0x80,0x7F,0x01, 100,200,150,250};
819 uint8_t b[16] = {255,2,128,129, 0,255,56,206, 0x01,0x80,0x81,0xFF, 156,56,106,6};
820 uint8_t expected[16], actual[16];
822 store_u8_16(actual, add_sat_u8_16(load_u8_16(a), load_u8_16(b)));
823 return compare_u8(expected, actual, 16);
824}
825
827 uint8_t a[16] = {255,0,128,127, 0,1,200,50, 0xFF,0x80,0x7F,0x01, 100,200,150,250};
828 uint8_t b[16] = {255,255,128,129, 0,255,56,206, 0x01,0x80,0x81,0xFF, 156,56,106,6};
829 uint8_t expected[16], actual[16];
831 store_u8_16(actual, sub_sat_u8_16(load_u8_16(a), load_u8_16(b)));
832 return compare_u8(expected, actual, 16);
833}
834
835// Scalar reference: i32 wrapping add/sub
836inline void ref_add_i32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
837 for (int i = 0; i < 4; i++) out[i] = a[i] + b[i]; // wrapping
838}
839inline void ref_sub_i32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
840 for (int i = 0; i < 4; i++) out[i] = a[i] - b[i]; // wrapping
841}
842
843// Adversarial wrapping add: overflow, underflow, sign flip
845 uint32_t a[4] = {0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, 0x00000001};
846 uint32_t b[4] = {0x00000001, 0x80000000, 0x00000001, 0xFFFFFFFF};
847 uint32_t expected[4], actual[4];
848 ref_add_i32_4(a, b, expected);
849 store_u32_4(actual, add_i32_4(load_u32_4(a), load_u32_4(b)));
850 return compare_u32(expected, actual, 4);
851}
852
854 uint32_t a[4] = {0x00000000, 0x80000000, 0x7FFFFFFF, 0x00000001};
855 uint32_t b[4] = {0x00000001, 0x7FFFFFFF, 0x80000000, 0x00000002};
856 uint32_t expected[4], actual[4];
857 ref_sub_i32_4(a, b, expected);
858 store_u32_4(actual, sub_i32_4(load_u32_4(a), load_u32_4(b)));
859 return compare_u32(expected, actual, 4);
860}
861
862// Scalar reference: shifts
863inline void ref_srl_u32_4(const uint32_t* v, int shift, uint32_t* out) {
864 for (int i = 0; i < 4; i++) out[i] = v[i] >> shift;
865}
866inline void ref_sll_u32_4(const uint32_t* v, int shift, uint32_t* out) {
867 for (int i = 0; i < 4; i++) out[i] = v[i] << shift;
868}
869inline void ref_sra_i32_4(const uint32_t* v, int shift, uint32_t* out) {
870 for (int i = 0; i < 4; i++) out[i] = (uint32_t)((int32_t)v[i] >> shift);
871}
872
874 uint32_t v[4] = {0x80000000, 0xFFFFFFFF, 0x00000001, 0xDEADBEEF};
875 for (int shift = 0; shift <= 31; shift += 7) {
876 uint32_t expected[4], actual[4];
877 ref_srl_u32_4(v, shift, expected);
878 store_u32_4(actual, srl_u32_4(load_u32_4(v), shift));
879 if (!compare_u32(expected, actual, 4)) return false;
880 }
881 return true;
882}
883
885 uint32_t v[4] = {0x80000000, 0xFFFFFFFF, 0x00000001, 0xDEADBEEF};
886 for (int shift = 0; shift <= 31; shift += 7) {
887 uint32_t expected[4], actual[4];
888 ref_sll_u32_4(v, shift, expected);
889 store_u32_4(actual, sll_u32_4(load_u32_4(v), shift));
890 if (!compare_u32(expected, actual, 4)) return false;
891 }
892 return true;
893}
894
896 uint32_t v[4] = {0x80000000, 0xFFFFFFFF, 0x7FFFFFFF, 0xDEADBEEF};
897 for (int shift = 0; shift <= 31; shift += 7) {
898 uint32_t expected[4], actual[4];
899 ref_sra_i32_4(v, shift, expected);
900 store_u32_4(actual, sra_i32_4(load_u32_4(v), shift));
901 if (!compare_u32(expected, actual, 4)) return false;
902 }
903 return true;
904}
905
906// Scalar reference: min/max i32
907inline void ref_min_i32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
908 for (int i = 0; i < 4; i++) {
909 int32_t ai = (int32_t)a[i], bi = (int32_t)b[i];
910 out[i] = (uint32_t)(ai < bi ? ai : bi);
911 }
912}
913inline void ref_max_i32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
914 for (int i = 0; i < 4; i++) {
915 int32_t ai = (int32_t)a[i], bi = (int32_t)b[i];
916 out[i] = (uint32_t)(ai > bi ? ai : bi);
917 }
918}
919
921 uint32_t a[4] = {0x80000000, 0x7FFFFFFF, 0xFFFFFFFF, 0x00000000};
922 uint32_t b[4] = {0x7FFFFFFF, 0x80000000, 0x00000000, 0xFFFFFFFF};
923 uint32_t expected[4], actual[4];
924 ref_min_i32_4(a, b, expected);
925 store_u32_4(actual, min_i32_4(load_u32_4(a), load_u32_4(b)));
926 return compare_u32(expected, actual, 4);
927}
928
930 uint32_t a[4] = {0x80000000, 0x7FFFFFFF, 0xFFFFFFFF, 0x00000000};
931 uint32_t b[4] = {0x7FFFFFFF, 0x80000000, 0x00000000, 0xFFFFFFFF};
932 uint32_t expected[4], actual[4];
933 ref_max_i32_4(a, b, expected);
934 store_u32_4(actual, max_i32_4(load_u32_4(a), load_u32_4(b)));
935 return compare_u32(expected, actual, 4);
936}
937
938// Scalar reference: multiply variants
939inline void ref_mulhi_i32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
940 for (int i = 0; i < 4; i++) {
941 int64_t prod = (int64_t)(int32_t)a[i] * (int64_t)(int32_t)b[i];
942 out[i] = (uint32_t)(int32_t)(prod >> 16);
943 }
944}
945inline void ref_mulhi_u32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
946 for (int i = 0; i < 4; i++) {
947 uint64_t prod = (uint64_t)a[i] * (uint64_t)b[i];
948 out[i] = (uint32_t)(prod >> 16);
949 }
950}
951inline void ref_mulhi32_i32_4(const uint32_t* a, const uint32_t* b, uint32_t* out) {
952 for (int i = 0; i < 4; i++) {
953 int64_t prod = (int64_t)(int32_t)a[i] * (int64_t)(int32_t)b[i];
954 out[i] = (uint32_t)(int32_t)(prod >> 32);
955 }
956}
957
959 uint32_t a[4] = {0x7FFFFFFF, 0x80000000, 0x00010000, 0xFFFF0000};
960 uint32_t b[4] = {0x00020000, 0x00020000, 0xFFFF0000, 0xFFFF0000};
961 uint32_t expected[4], actual[4];
963 store_u32_4(actual, mulhi_i32_4(load_u32_4(a), load_u32_4(b)));
964 return compare_u32(expected, actual, 4);
965}
966
968 uint32_t a[4] = {0xFFFFFFFF, 0x80000000, 0x00010000, 0x00000001};
969 uint32_t b[4] = {0x00000002, 0x00000002, 0x00010000, 0xFFFFFFFF};
970 uint32_t expected[4], actual[4];
972 store_u32_4(actual, mulhi_u32_4(load_u32_4(a), load_u32_4(b)));
973 return compare_u32(expected, actual, 4);
974}
975
977 uint32_t a[4] = {0x7FFFFFFF, 0x80000000, 0x40000000, 0xC0000000};
978 uint32_t b[4] = {0x7FFFFFFF, 0x80000000, 0x40000000, 0x40000000};
979 uint32_t expected[4], actual[4];
981 store_u32_4(actual, mulhi32_i32_4(load_u32_4(a), load_u32_4(b)));
982 return compare_u32(expected, actual, 4);
983}
984
985// Scalar reference: min/max u8 and float ops
986inline void ref_min_u8_16(const uint8_t* a, const uint8_t* b, uint8_t* out) {
987 for (int i = 0; i < 16; i++) out[i] = a[i] < b[i] ? a[i] : b[i];
988}
989inline void ref_max_u8_16(const uint8_t* a, const uint8_t* b, uint8_t* out) {
990 for (int i = 0; i < 16; i++) out[i] = a[i] > b[i] ? a[i] : b[i];
991}
992
994 uint8_t a[16] = {0,255,128,127, 1,254,0x55,0xAA, 0x0F,0xF0,0x80,0x7F, 0x01,0xFE,0xFF,0x00};
995 uint8_t b[16] = {255,0,127,128, 254,1,0xAA,0x55, 0xF0,0x0F,0x7F,0x80, 0xFE,0x01,0x00,0xFF};
996 uint8_t expected[16], actual[16];
997 ref_min_u8_16(a, b, expected);
998 store_u8_16(actual, min_u8_16(load_u8_16(a), load_u8_16(b)));
999 return compare_u8(expected, actual, 16);
1000}
1001
1003 uint8_t a[16] = {0,255,128,127, 1,254,0x55,0xAA, 0x0F,0xF0,0x80,0x7F, 0x01,0xFE,0xFF,0x00};
1004 uint8_t b[16] = {255,0,127,128, 254,1,0xAA,0x55, 0xF0,0x0F,0x7F,0x80, 0xFE,0x01,0x00,0xFF};
1005 uint8_t expected[16], actual[16];
1006 ref_max_u8_16(a, b, expected);
1007 store_u8_16(actual, max_u8_16(load_u8_16(a), load_u8_16(b)));
1008 return compare_u8(expected, actual, 16);
1009}
1010
1011// Scalar reference: float ops
1013 float a[4] = {-1.5f, 0.0f, 3.14159f, 1e10f};
1014 float b[4] = {2.5f, -0.0f, -2.71828f, 1e-10f};
1015 float out[4];
1016 // Test add
1017 store_f32_4(out, add_f32_4(load_f32_4(a), load_f32_4(b)));
1018 for (int i = 0; i < 4; i++) {
1019 float diff = out[i] - (a[i] + b[i]);
1020 if (diff < -0.001f || diff > 0.001f) return false;
1021 }
1022 // Test sub
1023 store_f32_4(out, sub_f32_4(load_f32_4(a), load_f32_4(b)));
1024 for (int i = 0; i < 4; i++) {
1025 float diff = out[i] - (a[i] - b[i]);
1026 if (diff < -0.001f || diff > 0.001f) return false;
1027 }
1028 // Test mul
1029 store_f32_4(out, mul_f32_4(load_f32_4(a), load_f32_4(b)));
1030 for (int i = 0; i < 4; i++) {
1031 float diff = out[i] - (a[i] * b[i]);
1032 if (diff < -1.0f && diff > 1.0f) return false; // large values need loose tolerance
1033 }
1034 return true;
1035}
1036
1037// Cross-autoresearch: aligned load/store with adversarial patterns
1039 FL_ALIGNAS(16) uint32_t src[4] = {0x80000000, 0x7FFFFFFF, 0xDEADBEEF, 0x00000000};
1040 FL_ALIGNAS(16) uint32_t dst[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
1041 simd_u32x4 v = load_u32_4_aligned(src);
1042 store_u32_4_aligned(dst, v);
1043 return compare_u32(src, dst, 4);
1044}
1045
1046// Cross-autoresearch: broadcast + adversarial values
1048 // Test adversarial values: sign bit, all ones, zero, alternating bits
1049 uint32_t test_values[] = {0x80000000, 0xFFFFFFFF, 0x00000000, 0xAAAAAAAA, 0x55555555, 0x01010101};
1050 for (int t = 0; t < 6; t++) {
1051 uint32_t val = test_values[t];
1052 uint32_t output[4];
1053 store_u32_4(output, set1_u32_4(val));
1054 for (int i = 0; i < 4; i++) {
1055 if (output[i] != val) return false;
1056 }
1057 }
1058 return true;
1059}
1060
1061// ============================================================================
1062// Chained Operation Tests (pipeline correctness)
1063// ============================================================================
1064
1066 // Simulate a brightness pipeline: scale by 128 then add bias, verify saturation
1067 uint8_t input[16] = {200,200,200,200, 200,200,200,200, 200,200,200,200, 200,200,200,200};
1068 uint8_t bias[16] = {200,200,200,200, 200,200,200,200, 200,200,200,200, 200,200,200,200};
1069 uint8_t output[16] = {0};
1070 simd_u8x16 v = load_u8_16(input);
1071 v = scale_u8_16(v, 128); // ~100
1072 v = add_sat_u8_16(v, load_u8_16(bias)); // ~300 -> saturated to 255
1073 store_u8_16(output, v);
1074 for (int i = 0; i < 16; i++) {
1075 if (output[i] < 250) return false; // Should be 255 (saturated)
1076 }
1077 return true;
1078}
1079
1081 // Simulate fixed-point angle decomposition: shift, add, mask
1082 uint32_t angles[4] = {0x10000000, 0x20000000, 0x30000000, 0x40000000};
1083 simd_u32x4 v = load_u32_4(angles);
1084 v = srl_u32_4(v, 16); // Extract high 16 bits
1085 v = add_i32_4(v, set1_u32_4(0x100)); // Add offset
1086 v = and_u32_4(v, set1_u32_4(0xFFFF)); // Mask to 16 bits
1087 uint32_t output[4] = {0};
1088 store_u32_4(output, v);
1089 uint32_t expected[4] = {0x1100, 0x2100, 0x3100, 0x4100};
1090 return compare_u32(expected, output, 4);
1091}
1092
1094 // Simulate color: multiply by gain, add bias, clamp to [0, 1]
1095 float input[4] = {0.3f, 0.6f, 0.9f, 1.2f};
1096 float gain[4] = {2.0f, 2.0f, 2.0f, 2.0f};
1097 float output[4] = {0.0f};
1098 simd_f32x4 v = load_f32_4(input);
1099 v = mul_f32_4(v, load_f32_4(gain)); // 0.6, 1.2, 1.8, 2.4
1100 v = min_f32_4(v, set1_f32_4(1.0f)); // clamp to 1.0
1101 v = max_f32_4(v, set1_f32_4(0.0f)); // clamp to 0.0
1102 store_f32_4(output, v);
1103 float expected[4] = {0.6f, 1.0f, 1.0f, 1.0f};
1104 return compare_f32(expected, output, 4);
1105}
1106
1107// ============================================================================
1108// Arithmetic Benchmark: add / sub / mul / div across float, s16x16, s16x16x4
1109// ============================================================================
1110//
1111// Each test runs 4-wide unrolled loops with feedback to prevent elimination.
1112// s16x16x4 has no division operator, so that cell is skipped.
1113
1115 volatile uint32_t sink;
1116
1118};
1119
1123
1125 int64_t iterations;
1126 // [op][type]: op={add,sub,mul,div}, type={float,s8x8,s16x16,u16x16,simd}
1131};
1132
1133// Helper: time 4-wide scalar float with a binary op
1134template <typename Op>
1135inline int64_t benchFloat4(int iters, Op op) {
1136 float a0 = 1.5f, a1 = 2.3f, a2 = 0.7f, a3 = 3.1f;
1137 float b0 = 0.5f, b1 = 1.2f, b2 = 2.0f, b3 = 0.9f;
1138 uint32_t t0 = fl::micros();
1139 for (int i = 0; i < iters; i++) {
1140 a0 = op(a0, b0); a1 = op(a1, b1);
1141 a2 = op(a2, b2); a3 = op(a3, b3);
1142 b0 = a0 + 0.001f; b1 = a1 + 0.001f;
1143 b2 = a2 + 0.001f; b3 = a3 + 0.001f;
1144 }
1145 uint32_t t1 = fl::micros();
1146 uint32_t tmp; fl::memcpy(&tmp, &a0, sizeof(tmp));
1147 simdBenchmarkState().sink = tmp;
1148 return static_cast<int64_t>(t1 - t0);
1149}
1150
1151// Helper: time 4-wide scalar s16x16 with a binary op
1152template <typename Op>
1153inline int64_t benchS16x16_4(int iters, Op op) {
1154 fl::s16x16 a0(1.5f), a1(2.3f), a2(0.7f), a3(3.1f);
1155 fl::s16x16 b0(0.5f), b1(1.2f), b2(2.0f), b3(0.9f);
1157 uint32_t t0 = fl::micros();
1158 for (int i = 0; i < iters; i++) {
1159 a0 = op(a0, b0); a1 = op(a1, b1);
1160 a2 = op(a2, b2); a3 = op(a3, b3);
1161 b0 = a0 + bump; b1 = a1 + bump;
1162 b2 = a2 + bump; b3 = a3 + bump;
1163 }
1164 uint32_t t1 = fl::micros();
1165 simdBenchmarkState().sink = static_cast<uint32_t>(a0.raw());
1166 return static_cast<int64_t>(t1 - t0);
1167}
1168
1169// Helper: time s16x16x4 SIMD with a binary op
1170template <typename Op>
1171inline int64_t benchSimd4(int iters, Op op) {
1173 set_u32_4(as_u32(fl::s16x16(1.5f).raw()), as_u32(fl::s16x16(2.3f).raw()),
1174 as_u32(fl::s16x16(0.7f).raw()), as_u32(fl::s16x16(3.1f).raw())));
1176 set_u32_4(as_u32(fl::s16x16(0.5f).raw()), as_u32(fl::s16x16(1.2f).raw()),
1177 as_u32(fl::s16x16(2.0f).raw()), as_u32(fl::s16x16(0.9f).raw())));
1179 uint32_t t0 = fl::micros();
1180 for (int i = 0; i < iters; i++) {
1181 a = op(a, b);
1182 b = a + bump;
1183 }
1184 uint32_t t1 = fl::micros();
1185 simdBenchmarkState().sink = extract_u32_4(a.raw, 0);
1186 return static_cast<int64_t>(t1 - t0);
1187}
1188
1189struct OpAdd {
1190 template<typename T> T operator()(T a, T b) const { return a + b; }
1191};
1192struct OpSub {
1193 template<typename T> T operator()(T a, T b) const { return a - b; }
1194};
1195struct OpMul {
1196 template<typename T> T operator()(T a, T b) const { return a * b; }
1197};
1198// Helper: time 4-wide scalar s8x8 with a binary op
1199template <typename Op>
1200inline int64_t benchS8x8_4(int iters, Op op) {
1201 fl::s8x8 a0(1.5f), a1(2.3f), a2(0.7f), a3(3.1f);
1202 fl::s8x8 b0(0.5f), b1(1.2f), b2(2.0f), b3(0.9f);
1203 fl::s8x8 bump = fl::s8x8::from_raw(1);
1204 uint32_t t0 = fl::micros();
1205 for (int i = 0; i < iters; i++) {
1206 a0 = op(a0, b0); a1 = op(a1, b1);
1207 a2 = op(a2, b2); a3 = op(a3, b3);
1208 b0 = a0 + bump; b1 = a1 + bump;
1209 b2 = a2 + bump; b3 = a3 + bump;
1210 }
1211 uint32_t t1 = fl::micros();
1212 simdBenchmarkState().sink = static_cast<uint32_t>(a0.raw());
1213 return static_cast<int64_t>(t1 - t0);
1214}
1215
1216// Helper: time 4-wide scalar u16x16 with a binary op
1217template <typename Op>
1218inline int64_t benchU16x16_4(int iters, Op op) {
1219 fl::u16x16 a0(1.5f), a1(2.3f), a2(0.7f), a3(3.1f);
1220 fl::u16x16 b0(0.5f), b1(1.2f), b2(2.0f), b3(0.9f);
1222 uint32_t t0 = fl::micros();
1223 for (int i = 0; i < iters; i++) {
1224 a0 = op(a0, b0); a1 = op(a1, b1);
1225 a2 = op(a2, b2); a3 = op(a3, b3);
1226 b0 = a0 + bump; b1 = a1 + bump;
1227 b2 = a2 + bump; b3 = a3 + bump;
1228 }
1229 uint32_t t1 = fl::micros();
1230 simdBenchmarkState().sink = static_cast<uint32_t>(a0.raw());
1231 return static_cast<int64_t>(t1 - t0);
1232}
1233
1235 float operator()(float a, float b) const { return a / b; }
1236};
1238 fl::s8x8 operator()(fl::s8x8 a, fl::s8x8 b) const { return a / b; }
1239};
1241 fl::s16x16 operator()(fl::s16x16 a, fl::s16x16 b) const { return a / b; }
1242};
1244 fl::u16x16 operator()(fl::u16x16 a, fl::u16x16 b) const { return a / b; }
1245};
1246
1247inline BenchmarkResult runMultiplyBenchmark(int iters = 10000) {
1249 r.iterations = iters;
1250
1251 // Add
1252 r.add_float_us = benchFloat4(iters, OpAdd());
1253 r.add_s8x8_us = benchS8x8_4(iters, OpAdd());
1254 r.add_s16x16_us = benchS16x16_4(iters, OpAdd());
1255 r.add_u16x16_us = benchU16x16_4(iters, OpAdd());
1256 r.add_simd_us = benchSimd4(iters, OpAdd());
1257
1258 // Sub
1259 r.sub_float_us = benchFloat4(iters, OpSub());
1260 r.sub_s8x8_us = benchS8x8_4(iters, OpSub());
1261 r.sub_s16x16_us = benchS16x16_4(iters, OpSub());
1262 r.sub_u16x16_us = benchU16x16_4(iters, OpSub());
1263 r.sub_simd_us = benchSimd4(iters, OpSub());
1264
1265 // Mul
1266 r.mul_float_us = benchFloat4(iters, OpMul());
1267 r.mul_s8x8_us = benchS8x8_4(iters, OpMul());
1268 r.mul_s16x16_us = benchS16x16_4(iters, OpMul());
1269 r.mul_u16x16_us = benchU16x16_4(iters, OpMul());
1270 r.mul_simd_us = benchSimd4(iters, OpMul());
1271
1272 // Div (no SIMD div for s16x16x4)
1273 r.div_float_us = benchFloat4(iters, OpDivFloat());
1274 r.div_s8x8_us = benchS8x8_4(iters, OpDivS8x8());
1277
1278 return r;
1279}
1280
1281// ============================================================================
1282// Test Runner
1283// ============================================================================
1284
1286 const char* name;
1287 bool (*func)();
1288};
1289
1291inline void getTests(const SimdTestEntry** out_tests, int* out_count) {
1292 static const SimdTestEntry tests[] = {
1293 // Load/Store u8x16
1294 {"load/store u8x16", test_load_store_u8_16},
1295 {"load/store u8x16 boundary", test_load_store_u8_16_boundary},
1296 // Load/Store u32x4
1297 {"load/store u32x4", test_load_store_u32_4},
1298 {"load/store u32x4 boundary", test_load_store_u32_4_boundary},
1299 {"load/store u32x4 aligned", test_load_store_u32_4_aligned},
1300 // Load/Store f32x4
1301 {"load/store f32x4", test_load_store_f32_4},
1302 {"load/store f32x4 special", test_load_store_f32_4_special},
1303 // Saturating Arithmetic u8x16
1304 {"add_sat u8x16", test_add_sat_u8_16},
1305 {"add_sat u8x16 full saturate", test_add_sat_u8_16_full_saturate},
1306 {"sub_sat u8x16", test_sub_sat_u8_16},
1307 {"sub_sat u8x16 full clamp", test_sub_sat_u8_16_full_clamp},
1308 // Scale / Blend u8x16
1309 {"scale u8x16", test_scale_u8_16},
1310 {"scale u8x16 zero", test_scale_u8_16_zero},
1311 {"scale u8x16 full", test_scale_u8_16_full},
1312 {"blend u8x16", test_blend_u8_16},
1313 {"blend u8x16 endpoints", test_blend_u8_16_endpoints},
1314 // Comparison u8x16
1315 {"min u8x16", test_min_u8_16},
1316 {"max u8x16", test_max_u8_16},
1317 {"avg u8x16", test_avg_u8_16},
1318 {"avg_round u8x16", test_avg_round_u8_16},
1319 // Bitwise u8x16
1320 {"and u8x16", test_and_u8_16},
1321 {"or u8x16", test_or_u8_16},
1322 {"xor u8x16", test_xor_u8_16},
1323 {"andnot u8x16", test_andnot_u8_16},
1324 // Broadcast / Construct u32x4
1325 {"set1 u32x4", test_set1_u32_4},
1326 {"set1 u32x4 zero", test_set1_u32_4_zero},
1327 {"set u32x4", test_set_u32_4},
1328 {"set1 f32x4", test_set1_f32_4},
1329 // Extract u32x4
1330 {"extract u32x4", test_extract_u32_4},
1331 // Bitwise u32x4
1332 {"xor u32x4", test_xor_u32_4},
1333 {"and u32x4", test_and_u32_4},
1334 {"or u32x4", test_or_u32_4},
1335 // Arithmetic i32x4
1336 {"add i32x4", test_add_i32_4},
1337 {"sub i32x4", test_sub_i32_4},
1338 // Shift u32x4 / i32x4
1339 {"srl u32x4", test_srl_u32_4},
1340 {"sll u32x4", test_sll_u32_4},
1341 {"sra i32x4", test_sra_i32_4},
1342 // Min/Max i32x4 (signed)
1343 {"min i32x4", test_min_i32_4},
1344 {"max i32x4", test_max_i32_4},
1345 // Fixed-Point Multiply
1346 {"mulhi i32x4 (Q16.16)", test_mulhi_i32_4},
1347 {"mulhi u32x4 (Q16.16)", test_mulhi_u32_4},
1348 {"mulhi su32x4 (Q16.16)", test_mulhi_su32_4},
1349 {"mulhi32 i32x4 (>>32)", test_mulhi32_i32_4},
1350 // Interleave / Unpack
1351 {"unpacklo u32x4", test_unpacklo_u32_4},
1352 {"unpackhi u32x4", test_unpackhi_u32_4},
1353 {"unpacklo u64 as u32x4", test_unpacklo_u64_as_u32_4},
1354 {"unpackhi u64 as u32x4", test_unpackhi_u64_as_u32_4},
1355 // Float Arithmetic
1356 {"add f32x4", test_add_f32_4},
1357 {"add f32x4 negative", test_add_f32_4_negative},
1358 {"sub f32x4", test_sub_f32_4},
1359 {"mul f32x4", test_mul_f32_4},
1360 {"mul f32x4 negative", test_mul_f32_4_negative},
1361 {"div f32x4", test_div_f32_4},
1362 {"sqrt f32x4", test_sqrt_f32_4},
1363 {"sqrt f32x4 zero", test_sqrt_f32_4_zero},
1364 {"min f32x4", test_min_f32_4},
1365 {"max f32x4", test_max_f32_4},
1366 // Cross-AutoResearch: SIMD vs Scalar Reference (adversarial patterns)
1367 {"crossval scale u8x16", test_crossval_scale_u8_16},
1368 {"crossval add_sat u8x16", test_crossval_add_sat_u8_16},
1369 {"crossval sub_sat u8x16", test_crossval_sub_sat_u8_16},
1370 {"crossval aligned load/store", test_crossval_aligned_load_store},
1371 {"crossval AND u8x16", test_crossval_and_u8_16},
1372 {"crossval OR u8x16", test_crossval_or_u8_16},
1373 {"crossval XOR u8x16", test_crossval_xor_u8_16},
1374 {"crossval ANDNOT u8x16", test_crossval_andnot_u8_16},
1375 {"crossval XOR u32x4", test_crossval_xor_u32_4},
1376 {"crossval AND u32x4", test_crossval_and_u32_4},
1377 {"crossval OR u32x4", test_crossval_or_u32_4},
1378 {"crossval broadcast u32x4", test_crossval_set1_u32_4},
1379 // Cross-AutoResearch: i32 arithmetic, shifts, min/max, multiply, u8 min/max, float
1380 {"crossval add i32x4", test_crossval_add_i32_4},
1381 {"crossval sub i32x4", test_crossval_sub_i32_4},
1382 {"crossval srl u32x4", test_crossval_srl_u32_4},
1383 {"crossval sll u32x4", test_crossval_sll_u32_4},
1384 {"crossval sra i32x4", test_crossval_sra_i32_4},
1385 {"crossval min i32x4", test_crossval_min_i32_4},
1386 {"crossval max i32x4", test_crossval_max_i32_4},
1387 {"crossval mulhi i32x4", test_crossval_mulhi_i32_4},
1388 {"crossval mulhi u32x4", test_crossval_mulhi_u32_4},
1389 {"crossval mulhi32 i32x4", test_crossval_mulhi32_i32_4},
1390 {"crossval min u8x16", test_crossval_min_u8_16},
1391 {"crossval max u8x16", test_crossval_max_u8_16},
1392 {"crossval float ops", test_crossval_float_ops},
1393 // Pipeline / Chained Operation Tests
1394 {"pipeline u8 scale+add+clamp", test_u8_pipeline_scale_add_clamp},
1395 {"pipeline i32 shift+add+mask", test_i32_pipeline_shift_add_mask},
1396 {"pipeline f32 mul+add+clamp", test_f32_pipeline_mul_add_clamp},
1397 };
1398 *out_tests = tests;
1399 *out_count = sizeof(tests) / sizeof(tests[0]);
1400}
1401
1403inline int runSimdTests() {
1404 const SimdTestEntry* tests = nullptr;
1405 int num_tests = 0;
1406 getTests(&tests, &num_tests);
1407
1408 int passed = 0;
1409 int failed = 0;
1410
1411 FL_PRINT("\n[SIMD AUTORESEARCH]");
1412 FL_PRINT("────────────────────────────────────────────────────────────────");
1413
1414#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
1415 FL_PRINT(" SIMD Backend: x86 SSE2");
1416#elif defined(__XTENSA__) && FL_XTENSA_HAS_PIE
1417 FL_PRINT(" SIMD Backend: Xtensa PIE (ESP32-S3)");
1418#elif defined(__XTENSA__)
1419 FL_PRINT(" SIMD Backend: Xtensa scalar");
1420#elif defined(__riscv)
1421 FL_PRINT(" SIMD Backend: RISC-V scalar");
1422#else
1423 FL_PRINT(" SIMD Backend: Scalar fallback");
1424#endif
1425
1426 fl::sstream ss;
1427 ss << " Running " << num_tests << " SIMD tests...\n";
1428 FL_PRINT(ss.str());
1429
1430 for (int i = 0; i < num_tests; i++) {
1431 bool ok = tests[i].func();
1432 if (ok) {
1433 passed++;
1434 ss.clear();
1435 ss << " [PASS] " << tests[i].name;
1436 FL_PRINT(ss.str());
1437 } else {
1438 failed++;
1439 ss.clear();
1440 ss << " [FAIL] " << tests[i].name;
1441 FL_ERROR(ss.str());
1442 }
1443 }
1444
1445 ss.clear();
1446 ss << "\n[SIMD RESULTS] " << passed << "/" << num_tests << " passed";
1447 if (failed > 0) {
1448 ss << ", " << failed << " FAILED";
1449 FL_ERROR(ss.str());
1450 } else {
1451 ss << " - ALL PASSED";
1452 FL_PRINT(ss.str());
1453 }
1454
1455 return failed;
1456}
1457
1458} // namespace simd_check
1459} // namespace autoresearch
fl::UISlider scale("Scale", 4,.1, 4,.1)
FastLED chrono implementation - duration types for time measurements.
static T & instance() FL_NO_EXCEPT
Definition singleton.h:81
expected type for operations that can fail (C++23-style)
Definition expected.h:79
static constexpr FASTLED_FORCE_INLINE s16x16 from_raw(i32 raw) FL_NO_EXCEPT
Definition s16x16.h:56
static constexpr FASTLED_FORCE_INLINE s8x8 from_raw(i16 raw) FL_NO_EXCEPT
Definition s8x8.h:53
void clear() FL_NO_EXCEPT
Definition strstream.h:366
string str() const FL_NO_EXCEPT
Definition strstream.h:43
static constexpr FASTLED_FORCE_INLINE u16x16 from_raw(u32 raw) FL_NO_EXCEPT
Definition u16x16.h:54
static uint32_t t
Definition Luminova.h:55
#define FL_ERROR(...)
Definition log.h:373
#define FL_PRINT(...)
Definition log.h:629
uint32_t as_u32(int32_t v)
void ref_srl_u32_4(const uint32_t *v, int shift, uint32_t *out)
void ref_and_u32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
void ref_add_sat_u8_16(const uint8_t *a, const uint8_t *b, uint8_t *out)
int64_t benchSimd4(int iters, Op op)
bool compare_u32(const uint32_t *a, const uint32_t *b, size_t n)
int64_t benchS16x16_4(int iters, Op op)
SimdBenchmarkState & simdBenchmarkState()
void ref_max_u8_16(const uint8_t *a, const uint8_t *b, uint8_t *out)
void ref_max_i32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
int64_t benchFloat4(int iters, Op op)
void ref_scale_u8_16(const uint8_t *v, uint8_t scale, uint8_t *out)
int runSimdTests()
Run the full SIMD test suite. Returns the number of failures.
void ref_xor_u8_16(const uint8_t *a, const uint8_t *b, uint8_t *out)
int64_t benchS8x8_4(int iters, Op op)
void ref_andnot_u8_16(const uint8_t *a, const uint8_t *b, uint8_t *out)
void ref_mulhi_i32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
void ref_sll_u32_4(const uint32_t *v, int shift, uint32_t *out)
int32_t as_i32(uint32_t v)
void ref_and_u8_16(const uint8_t *a, const uint8_t *b, uint8_t *out)
void getTests(const SimdTestEntry **out_tests, int *out_count)
Get the static test table. Used by both runSimdTests() and the RPC handler.
void ref_or_u32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
void ref_min_i32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
void ref_add_i32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
void ref_or_u8_16(const uint8_t *a, const uint8_t *b, uint8_t *out)
void ref_sub_i32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
bool compare_u8(const uint8_t *a, const uint8_t *b, size_t n)
void ref_xor_u32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
void ref_sra_i32_4(const uint32_t *v, int shift, uint32_t *out)
void ref_min_u8_16(const uint8_t *a, const uint8_t *b, uint8_t *out)
int64_t benchU16x16_4(int iters, Op op)
bool compare_f32(const float *a, const float *b, size_t n, float eps=0.001f)
BenchmarkResult runMultiplyBenchmark(int iters=10000)
void ref_mulhi_u32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
void ref_mulhi32_i32_4(const uint32_t *a, const uint32_t *b, uint32_t *out)
void ref_sub_sat_u8_16(const uint8_t *a, const uint8_t *b, uint8_t *out)
platforms::simd_u32x4 simd_u32x4
Definition types.h:26
platforms::simd_u8x16 simd_u8x16
Definition types.h:24
platforms::simd_f32x4 simd_f32x4
Definition types.h:27
fl::u32 uint32_t
Definition s16x16x4.h:219
fl::u16 uint16_t
Definition s16x16x4.h:214
fl::u32 micros() FL_NO_EXCEPT
Universal microsecond timer - returns microseconds since system startup.
FASTLED_FORCE_INLINE void * memcpy(void *dest, const void *src, size_t n) FL_NO_EXCEPT
Definition cstring.h:136
fl::i32 int32_t
Definition s16x16x4.h:220
unsigned char uint8_t
Definition s16x16x4.h:209
SIMD 4-wide s0x32 fixed-point vector type.
#define FL_ALIGNAS(N)
SIMD 4-wide s16x16 fixed-point vector type.
Umbrella header for SIMD subsystem.
float operator()(float a, float b) const
fl::s16x16 operator()(fl::s16x16 a, fl::s16x16 b) const
fl::s8x8 operator()(fl::s8x8 a, fl::s8x8 b) const
fl::u16x16 operator()(fl::u16x16 a, fl::u16x16 b) const
static FASTLED_FORCE_INLINE s16x16x4 from_raw(simd::simd_u32x4 r)
Definition s16x16x4.h:28
simd::simd_u32x4 raw
Definition s16x16x4.h:24
static FASTLED_FORCE_INLINE s16x16x4 set1(s16x16 value)
Definition s16x16x4.h:45
4-wide s16x16 vector (general fixed-point) Backed by 128-bit SIMD register (4× i32 in Q16 format)
Definition s16x16x4.h:23