Line data Source code
1 : /**
2 : * @file linear_solver_simd_dispatch.c
3 : * @brief SIMD Poisson solver dispatcher with runtime CPU detection
4 : *
5 : * This file provides unified SIMD solvers that select the appropriate
6 : * architecture-specific implementation at RUNTIME:
7 : * - AVX2 on x86-64 (detected via CPUID)
8 : * - NEON on ARM64 (always available on ARM64)
9 : *
10 : * The actual implementations remain in separate files:
11 : * - avx2/linear_solver_jacobi_avx2.c
12 : * - avx2/linear_solver_redblack_avx2.c
13 : * - avx2/linear_solver_cg_avx2.c
14 : * - neon/linear_solver_jacobi_neon.c
15 : * - neon/linear_solver_redblack_neon.c
16 : * - neon/linear_solver_cg_neon.c
17 : *
18 : * This design is identical to the boundary conditions SIMD dispatcher.
19 : */
20 :
21 : #include "../linear_solver_internal.h"
22 : #include "cfd/core/cpu_features.h"
23 :
24 : #include <stdio.h>
25 :
26 : /* ============================================================================
27 : * LOGGING
28 : * ============================================================================ */
29 :
30 : /**
31 : * Log when SIMD backend is unavailable.
32 : * Callers should handle the NULL return and fall back to scalar if needed.
33 : */
34 : static void log_no_simd_available(const char* solver_type) {
35 : /* Silent in release builds - caller handles fallback */
36 : #ifdef CFD_DEBUG
37 : fprintf(stderr,
38 : "DEBUG: SIMD %s solver not available "
39 : "(detected arch: %s). Returning NULL for fallback.\n",
40 : solver_type, cfd_get_simd_name());
41 : #else
42 : (void)solver_type; /* Suppress unused parameter warning */
43 : #endif
44 : }
45 :
46 : /* ============================================================================
47 : * FORWARD DECLARATIONS FOR ARCHITECTURE-SPECIFIC IMPLEMENTATIONS
48 : *
49 : * These are defined in avx2/ and neon/ subdirectories.
50 : * If a backend is not compiled (e.g., NEON on x86), these functions return NULL.
51 : * ============================================================================ */
52 :
53 : /* AVX2 implementations (x86-64) */
54 : extern poisson_solver_t* create_jacobi_avx2_solver(void);
55 : extern poisson_solver_t* create_redblack_avx2_solver(void);
56 : extern poisson_solver_t* create_cg_avx2_solver(void);
57 : extern poisson_solver_t* create_bicgstab_avx2_solver(void);
58 :
59 : /* NEON implementations (ARM64) */
60 : extern poisson_solver_t* create_jacobi_neon_solver(void);
61 : extern poisson_solver_t* create_redblack_neon_solver(void);
62 : extern poisson_solver_t* create_cg_neon_solver(void);
63 : extern poisson_solver_t* create_bicgstab_neon_solver(void);
64 :
65 : /* ============================================================================
66 : * SIMD BACKEND AVAILABILITY (Runtime + Compile-time detection)
67 : *
68 : * This checks BOTH:
69 : * 1. Compile-time: Was code compiled with SIMD support (-mavx2 or ARM64)?
70 : * 2. Compile-time: Was OpenMP enabled at build time?
71 : * 3. Runtime: Does CPU support the SIMD instructions?
72 : *
73 : * All must be true for SIMD to be available.
74 : * ============================================================================ */
75 :
76 : /* Check if AVX2 implementation was compiled
77 : * CFD_HAS_AVX2 is set by CMake when -DCFD_ENABLE_AVX2=ON.
78 : * This works consistently across all compilers (GCC, Clang, MSVC).
79 : */
80 : #if defined(CFD_HAS_AVX2) && defined(CFD_ENABLE_OPENMP)
81 : #define HAS_AVX2_IMPL 1
82 : #else
83 : #define HAS_AVX2_IMPL 0
84 : #endif
85 :
86 : /* Check if NEON implementation was compiled */
87 : #if (defined(__aarch64__) || defined(_M_ARM64) || defined(__ARM_NEON) || defined(__ARM_NEON__)) && defined(CFD_ENABLE_OPENMP)
88 : #define HAS_NEON_IMPL 1
89 : #else
90 : #define HAS_NEON_IMPL 0
91 : #endif
92 :
93 17 : bool poisson_solver_simd_backend_available(void) {
94 17 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
95 :
96 : /* Check AVX2: compiled with AVX2 AND runtime CPU supports AVX2 */
97 17 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
98 : return true;
99 : }
100 :
101 : /* Check NEON: compiled with NEON AND runtime CPU supports NEON */
102 17 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
103 : return true;
104 : }
105 :
106 17 : return false;
107 : }
108 :
109 1 : const char* poisson_solver_simd_get_arch_name(void) {
110 1 : return cfd_get_simd_name();
111 : }
112 :
113 : /* Public API wrappers */
114 0 : bool poisson_solver_simd_available(void) {
115 0 : return poisson_solver_simd_backend_available();
116 : }
117 :
118 1 : const char* poisson_solver_get_simd_arch_name(void) {
119 1 : return poisson_solver_simd_get_arch_name();
120 : }
121 :
122 : /* ============================================================================
123 : * JACOBI SIMD DISPATCHER
124 : * ============================================================================ */
125 :
126 1 : poisson_solver_t* create_jacobi_simd_solver(void) {
127 1 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
128 :
129 : /* Check compile-time AND runtime availability before calling factory */
130 1 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
131 : return create_jacobi_avx2_solver();
132 : }
133 :
134 1 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
135 : return create_jacobi_neon_solver();
136 : }
137 :
138 : /* No SIMD backend available - report error and return NULL (no fallback) */
139 1 : log_no_simd_available("Jacobi");
140 1 : return NULL;
141 : }
142 :
143 : /* ============================================================================
144 : * RED-BLACK SOR SIMD DISPATCHER
145 : * ============================================================================ */
146 :
147 1 : poisson_solver_t* create_redblack_simd_solver(void) {
148 1 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
149 :
150 : /* Check compile-time AND runtime availability before calling factory */
151 1 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
152 : return create_redblack_avx2_solver();
153 : }
154 :
155 1 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
156 : return create_redblack_neon_solver();
157 : }
158 :
159 : /* No SIMD backend available - report error and return NULL (no fallback) */
160 1 : log_no_simd_available("Red-Black");
161 1 : return NULL;
162 : }
163 :
164 : /* ============================================================================
165 : * CONJUGATE GRADIENT SIMD DISPATCHER
166 : * ============================================================================ */
167 :
168 16 : poisson_solver_t* create_cg_simd_solver(void) {
169 16 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
170 :
171 : /* Check compile-time AND runtime availability before calling factory */
172 16 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
173 : return create_cg_avx2_solver();
174 : }
175 :
176 16 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
177 : return create_cg_neon_solver();
178 : }
179 :
180 : /* No SIMD backend available - report error and return NULL (no fallback) */
181 16 : log_no_simd_available("CG");
182 16 : return NULL;
183 : }
184 :
185 : /* ============================================================================
186 : * BICGSTAB SIMD DISPATCHER
187 : * ============================================================================ */
188 :
189 0 : poisson_solver_t* create_bicgstab_simd_solver(void) {
190 0 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
191 :
192 : /* Check compile-time AND runtime availability before calling factory */
193 0 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
194 : return create_bicgstab_avx2_solver();
195 : }
196 :
197 0 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
198 : return create_bicgstab_neon_solver();
199 : }
200 :
201 : /* No SIMD backend available - report error and return NULL (no fallback) */
202 0 : log_no_simd_available("BiCGSTAB");
203 0 : return NULL;
204 : }
|