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: 77.1 % 35 27
Test Date: 2026-03-04 10:22:18 Functions: 75.0 % 8 6

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

Generated by: LCOV version 2.0-1