From 401ba5a47a0d2a194ddd75ac0bb56900dffc43fd Mon Sep 17 00:00:00 2001 From: Glenn Hickey Date: Tue, 27 Oct 2026 20:24:52 -0400 Subject: [PATCH] Only use x86 intrinsics on x86, and flush denormals on ARM too cactus defines __AVX2__ on ARM so abPOA can use SIMDe, which made the fast pair HMM include immintrin.h there and fail to build. The intrinsics are now gated on the target as well as the feature macros. On aarch64, FPCR.FZ is set for the same span as the x86 FTZ/DAZ bits so results match x86. Co-Authored-By: Claude Opus 5.5 --- impl/pairwiseAligner.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/impl/pairwiseAligner.c b/impl/pairwiseAligner.c index e6093a3..2e834e3 100644 --- a/impl/pairwiseAligner.c +++ b/impl/pairwiseAligner.c @@ -771,12 +771,18 @@ static void diagonalCalculationExpectations(StateMachine *sM, int64_t xay, DpMat /////////////////////////////////// /////////////////////////////////// +// The intrinsics below are x86 only. Checking the target as well as the feature macros matters +// on ARM, where cactus defines __AVX2__ itself (for abPOA's SIMDe) and the real headers do not exist. +#if defined(__x86_64__) || defined(__i386__) #if defined(__SSE2__) +#define PAIR_HMM_X86_SSE2 #include #endif #if defined(__AVX2__) +#define PAIR_HMM_X86_AVX2 #include #endif +#endif #define PAIR_HMM_MAX_STATES 5 @@ -1221,7 +1227,7 @@ static void fastDiagonal_emissions(PairHmm *hmm, int64_t type, FastSymbols *symb // is off its end. cX[i] is X[x0 - 1 + lo + i] and cY[i] is Y[y0 - 1 - lo - i]. const uint8_t *restrict cX = type != PAIR_HMM_GAP_Y ? symbols->x + (x0 - 1 + lo) : NULL; const uint8_t *restrict cY = type != PAIR_HMM_GAP_X ? symbols->yReversed + (symbols->lY - y0 + lo) : NULL; -#if defined(__AVX2__) +#if defined(PAIR_HMM_X86_AVX2) // Table lookups the compiler will not vectorise on its own __m256d vScale = _mm256_set1_pd(scale); __m128i vSymbols = _mm_set1_epi32(SYMBOL_NUMBER); @@ -1422,10 +1428,15 @@ static bool getPosteriorProbsWithBandingFast(StateMachine *sM, stList *anchorPai } int64_t ns = hmm.stateNumber; -#if defined(__SSE2__) +#if defined(PAIR_HMM_X86_SSE2) // Cells far off the alignment decay towards zero; flush them rather than crawl through denormals unsigned int oldCsr = _mm_getcsr(); _mm_setcsr(oldCsr | 0x8040); // flush-to-zero and denormals-are-zero +#elif defined(__aarch64__) + // Same on ARM (FPCR.FZ covers inputs and outputs), which also keeps results the same as x86 + uint64_t oldFpcr; + __asm__ __volatile__("mrs %0, fpcr" : "=r"(oldFpcr)); + __asm__ __volatile__("msr fpcr, %0" : : "r"(oldFpcr | (UINT64_C(1) << 24))); #endif Band *band = p->dynamicAnchorExpansion ? band_constructDynamic(anchorPairs, sX.length, sY.length) : @@ -1536,8 +1547,10 @@ static bool getPosteriorProbsWithBandingFast(StateMachine *sM, stList *anchorPai free(backCells[i]); } band_destruct(band); -#if defined(__SSE2__) +#if defined(PAIR_HMM_X86_SSE2) _mm_setcsr(oldCsr); +#elif defined(__aarch64__) + __asm__ __volatile__("msr fpcr, %0" : : "r"(oldFpcr)); #endif return 1; }