LCOV - code coverage report
Current view: top level - solvers/linear/simd - linear_solver_simd_dispatch.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 95.5 % 44 42
Test Date: 2026-06-23 13:41:07 Functions: 90.0 % 10 9

            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              : }
        

Generated by: LCOV version 2.0-1