Skip to content

Commit b139cc9

Browse files
Fix: Probe mrs for avoid SIGILL on older Arm
Closes #279 Co-authored-by: Laurens Priem <[email protected]> Co-authored-by: Laurens Priem <[email protected]>
1 parent 013e199 commit b139cc9

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

include/simsimd/simsimd.h

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@
116116
#include <sys/sysctl.h> // `sysctlbyname`
117117
#endif
118118

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+
119130
#ifdef __cplusplus
120131
extern "C" {
121132
#endif
@@ -454,6 +465,15 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_x86(void) {
454465
#pragma GCC target("arch=armv8.5-a+sve")
455466
#pragma clang attribute push(__attribute__((target("arch=armv8.5-a+sve"))), apply_to = function)
456467

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+
457477
/**
458478
* @brief Function to flush denormalized numbers to zero on Arm CPUs.
459479
* @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) {
504524

505525
#elif defined(_SIMSIMD_DEFINED_LINUX)
506526

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+
507557
// Read CPUID registers directly
508558
unsigned long id_aa64isar0_el1 = 0, id_aa64isar1_el1 = 0, id_aa64pfr0_el1 = 0, id_aa64zfr0_el1 = 0;
509559

@@ -561,7 +611,7 @@ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) {
561611
(simsimd_cap_sve2_k * (supports_sve2)) | //
562612
(simsimd_cap_sve2p1_k * (supports_sve2p1)) | //
563613
(simsimd_cap_serial_k));
564-
#else // if !_SIMSIMD_DEFINED_LINUX
614+
#else // if !_SIMSIMD_DEFINED_LINUX
565615
return simsimd_cap_serial_k;
566616
#endif
567617
}

0 commit comments

Comments
 (0)