|
116 | 116 | #include <sys/sysctl.h> // `sysctlbyname` |
117 | 117 | #endif |
118 | 118 |
|
| 119 | +// Detect POSIX extensions availability for signal handling. |
| 120 | +// POSIX extensions provide `sigaction`, `sigjmp_buf`, and `sigsetjmp` for safe signal handling. |
| 121 | +// These are needed on Linux ARM for safely testing `mrs` instruction availability. |
| 122 | +#if defined(_SIMSIMD_DEFINED_LINUX) && defined(_POSIX_VERSION) |
| 123 | +#include <setjmp.h> // `sigjmp_buf`, `sigsetjmp`, `siglongjmp` |
| 124 | +#include <signal.h> // `sigaction`, `SIGILL` |
| 125 | +#define _SIMSIMD_HAS_POSIX_EXTENSIONS 1 |
| 126 | +#else |
| 127 | +#define _SIMSIMD_HAS_POSIX_EXTENSIONS 0 |
| 128 | +#endif |
| 129 | + |
119 | 130 | #ifdef __cplusplus |
120 | 131 | extern "C" { |
121 | 132 | #endif |
@@ -454,6 +465,15 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_x86(void) { |
454 | 465 | #pragma GCC target("arch=armv8.5-a+sve") |
455 | 466 | #pragma clang attribute push(__attribute__((target("arch=armv8.5-a+sve"))), apply_to = function) |
456 | 467 |
|
| 468 | +#if _SIMSIMD_HAS_POSIX_EXTENSIONS |
| 469 | +/** @brief SIGILL handler for `mrs` instruction testing on Linux ARM */ |
| 470 | +static sigjmp_buf _simsimd_mrs_test_jump_buffer; |
| 471 | +static void _simsimd_mrs_test_sigill_handler(int sig) { |
| 472 | + (void)sig; // Unused parameter |
| 473 | + siglongjmp(_simsimd_mrs_test_jump_buffer, 1); |
| 474 | +} |
| 475 | +#endif |
| 476 | + |
457 | 477 | /** |
458 | 478 | * @brief Function to flush denormalized numbers to zero on Arm CPUs. |
459 | 479 | * @note This should be called on each thread before any SIMD operations to avoid performance penalties. |
@@ -504,6 +524,36 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) { |
504 | 524 |
|
505 | 525 | #elif defined(_SIMSIMD_DEFINED_LINUX) |
506 | 526 |
|
| 527 | + // Depending on the environment, reading system registers may cause SIGILL. |
| 528 | + // One option to avoid the crash is to use `getauxval(AT_HWCAP)` and `getauxval(AT_HWCAP2)`, |
| 529 | + // Linux APIs, but those aren't as informative as reading the registers directly. |
| 530 | + // So before reading the ID registers, we set up a signal handler to catch SIGILL |
| 531 | + // and probe one of the registers, reverting back to the old signal handler afterwards. |
| 532 | + // |
| 533 | + // This issue was originally observed in: https://github.com/ashvardanian/SimSIMD/issues/279 |
| 534 | +#if _SIMSIMD_HAS_POSIX_EXTENSIONS |
| 535 | + struct sigaction action_new, action_old; |
| 536 | + action_new.sa_handler = _simsimd_mrs_test_sigill_handler; |
| 537 | + sigemptyset(&action_new.sa_mask); |
| 538 | + action_new.sa_flags = 0; |
| 539 | + |
| 540 | + int mrs_works = 0; |
| 541 | + if (sigaction(SIGILL, &action_new, &action_old) == 0) { |
| 542 | + if (sigsetjmp(_simsimd_mrs_test_jump_buffer, 1) == 0) { |
| 543 | + unsigned long midr_value; |
| 544 | + __asm__ __volatile__("mrs %0, MIDR_EL1" : "=r"(midr_value)); |
| 545 | + mrs_works = 1; |
| 546 | + } |
| 547 | + sigaction(SIGILL, &action_old, NULL); |
| 548 | + } |
| 549 | + |
| 550 | + // Early exit if `mrs` doesn't work - return conservative NEON-only capabilities |
| 551 | + if (!mrs_works) return (simsimd_capability_t)(simsimd_cap_neon_k | simsimd_cap_serial_k); |
| 552 | +#else // _SIMSIMD_HAS_POSIX_EXTENSIONS |
| 553 | + // Without POSIX signal handlers, fall back to conservative NEON capabilities. |
| 554 | + return (simsimd_capability_t)(simsimd_cap_neon_k | simsimd_cap_serial_k); |
| 555 | +#endif // _SIMSIMD_HAS_POSIX_EXTENSIONS |
| 556 | + |
507 | 557 | // Read CPUID registers directly |
508 | 558 | unsigned long id_aa64isar0_el1 = 0, id_aa64isar1_el1 = 0, id_aa64pfr0_el1 = 0, id_aa64zfr0_el1 = 0; |
509 | 559 |
|
@@ -561,7 +611,7 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) { |
561 | 611 | (simsimd_cap_sve2_k * (supports_sve2)) | // |
562 | 612 | (simsimd_cap_sve2p1_k * (supports_sve2p1)) | // |
563 | 613 | (simsimd_cap_serial_k)); |
564 | | -#else // if !_SIMSIMD_DEFINED_LINUX |
| 614 | +#else // if !_SIMSIMD_DEFINED_LINUX |
565 | 615 | return simsimd_cap_serial_k; |
566 | 616 | #endif |
567 | 617 | } |
|
0 commit comments