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 : #include "cfd/core/logging.h"
24 :
25 : /* ============================================================================
26 : * LOGGING
27 : * ============================================================================ */
28 :
29 : /**
30 : * Log when SIMD backend is unavailable.
31 : * Callers should handle the NULL return and fall back to scalar if needed.
32 : */
33 21 : static void log_no_simd_available(const char* solver_type) {
34 21 : CFD_LOG_DEBUG("simd", "SIMD %s solver not available (detected arch: %s). "
35 : "Returning NULL for fallback.", solver_type, cfd_get_simd_name());
36 21 : }
37 :
38 : /* ============================================================================
39 : * FORWARD DECLARATIONS FOR ARCHITECTURE-SPECIFIC IMPLEMENTATIONS
40 : *
41 : * These are defined in avx2/ and neon/ subdirectories.
42 : * If a backend is not compiled (e.g., NEON on x86), these functions return NULL.
43 : * ============================================================================ */
44 :
45 : /* AVX2 implementations (x86-64) */
46 : extern poisson_solver_t* create_jacobi_avx2_solver(void);
47 : extern poisson_solver_t* create_redblack_avx2_solver(void);
48 : extern poisson_solver_t* create_cg_avx2_solver(void);
49 : extern poisson_solver_t* create_bicgstab_avx2_solver(void);
50 : extern poisson_solver_t* create_sor_avx2_solver(void);
51 :
52 : /* NEON implementations (ARM64) */
53 : extern poisson_solver_t* create_jacobi_neon_solver(void);
54 : extern poisson_solver_t* create_redblack_neon_solver(void);
55 : extern poisson_solver_t* create_cg_neon_solver(void);
56 : extern poisson_solver_t* create_bicgstab_neon_solver(void);
57 : extern poisson_solver_t* create_sor_neon_solver(void);
58 :
59 : /* ============================================================================
60 : * SIMD BACKEND AVAILABILITY (Runtime + Compile-time detection)
61 : *
62 : * This checks BOTH:
63 : * 1. Compile-time: Was code compiled with SIMD support (-mavx2 or ARM64)?
64 : * 2. Compile-time: Was OpenMP enabled at build time?
65 : * 3. Runtime: Does CPU support the SIMD instructions?
66 : *
67 : * All must be true for SIMD to be available.
68 : * ============================================================================ */
69 :
70 : /* Check if AVX2 implementation was compiled
71 : * CFD_HAS_AVX2 is set by CMake when -DCFD_ENABLE_AVX2=ON.
72 : * This works consistently across all compilers (GCC, Clang, MSVC).
73 : */
74 : #if defined(CFD_HAS_AVX2) && defined(CFD_ENABLE_OPENMP)
75 : #define HAS_AVX2_IMPL 1
76 : #else
77 : #define HAS_AVX2_IMPL 0
78 : #endif
79 :
80 : /* Check if NEON implementation was compiled */
81 : #if (defined(__aarch64__) || defined(_M_ARM64) || defined(__ARM_NEON) || defined(__ARM_NEON__)) && defined(CFD_ENABLE_OPENMP)
82 : #define HAS_NEON_IMPL 1
83 : #else
84 : #define HAS_NEON_IMPL 0
85 : #endif
86 :
87 18 : bool poisson_solver_simd_backend_available(void) {
88 18 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
89 :
90 : /* Check AVX2: compiled with AVX2 AND runtime CPU supports AVX2 */
91 18 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
92 : return true;
93 : }
94 :
95 : /* Check NEON: compiled with NEON AND runtime CPU supports NEON */
96 18 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
97 : return true;
98 : }
99 :
100 18 : return false;
101 : }
102 :
103 1 : const char* poisson_solver_simd_get_arch_name(void) {
104 1 : return cfd_get_simd_name();
105 : }
106 :
107 : /* Public API wrappers */
108 0 : bool poisson_solver_simd_available(void) {
109 0 : return poisson_solver_simd_backend_available();
110 : }
111 :
112 1 : const char* poisson_solver_get_simd_arch_name(void) {
113 1 : return poisson_solver_simd_get_arch_name();
114 : }
115 :
116 : /* ============================================================================
117 : * JACOBI SIMD DISPATCHER
118 : * ============================================================================ */
119 :
120 1 : poisson_solver_t* create_jacobi_simd_solver(void) {
121 1 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
122 :
123 : /* Check compile-time AND runtime availability before calling factory */
124 1 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
125 : return create_jacobi_avx2_solver();
126 : }
127 :
128 1 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
129 : return create_jacobi_neon_solver();
130 : }
131 :
132 : /* No SIMD backend available - report error and return NULL (no fallback) */
133 1 : log_no_simd_available("Jacobi");
134 1 : return NULL;
135 : }
136 :
137 : /* ============================================================================
138 : * RED-BLACK SOR SIMD DISPATCHER
139 : * ============================================================================ */
140 :
141 1 : poisson_solver_t* create_redblack_simd_solver(void) {
142 1 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
143 :
144 : /* Check compile-time AND runtime availability before calling factory */
145 1 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
146 : return create_redblack_avx2_solver();
147 : }
148 :
149 1 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
150 : return create_redblack_neon_solver();
151 : }
152 :
153 : /* No SIMD backend available - report error and return NULL (no fallback) */
154 1 : log_no_simd_available("Red-Black");
155 1 : return NULL;
156 : }
157 :
158 : /* ============================================================================
159 : * CONJUGATE GRADIENT SIMD DISPATCHER
160 : * ============================================================================ */
161 :
162 17 : poisson_solver_t* create_cg_simd_solver(void) {
163 17 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
164 :
165 : /* Check compile-time AND runtime availability before calling factory */
166 17 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
167 : return create_cg_avx2_solver();
168 : }
169 :
170 17 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
171 : return create_cg_neon_solver();
172 : }
173 :
174 : /* No SIMD backend available - report error and return NULL (no fallback) */
175 17 : log_no_simd_available("CG");
176 17 : return NULL;
177 : }
178 :
179 : /* ============================================================================
180 : * BICGSTAB SIMD DISPATCHER
181 : * ============================================================================ */
182 :
183 1 : poisson_solver_t* create_bicgstab_simd_solver(void) {
184 1 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
185 :
186 : /* Check compile-time AND runtime availability before calling factory */
187 1 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
188 : return create_bicgstab_avx2_solver();
189 : }
190 :
191 1 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
192 : return create_bicgstab_neon_solver();
193 : }
194 :
195 : /* No SIMD backend available - report error and return NULL (no fallback) */
196 1 : log_no_simd_available("BiCGSTAB");
197 1 : return NULL;
198 : }
199 :
200 : /* ============================================================================
201 : * SOR SIMD DISPATCHER (Block SOR)
202 : * ============================================================================ */
203 :
204 1 : poisson_solver_t* create_sor_simd_solver(void) {
205 1 : cfd_simd_arch_t arch = cfd_detect_simd_arch();
206 :
207 : /* Check compile-time AND runtime availability before calling factory */
208 1 : if (HAS_AVX2_IMPL && arch == CFD_SIMD_AVX2) {
209 : return create_sor_avx2_solver();
210 : }
211 :
212 1 : if (HAS_NEON_IMPL && arch == CFD_SIMD_NEON) {
213 : return create_sor_neon_solver();
214 : }
215 :
216 : /* No SIMD backend available - report error and return NULL (no fallback) */
217 1 : log_no_simd_available("SOR");
218 1 : return NULL;
219 : }
|