Skip to content

Commit 5baa52a

Browse files
committed
feat(ailego): enable NEON math kernels on MSVC ARM64
Addresses @feihongxu0824's review comment on #352. Previously the *_neon.cc files compiled into empty translation units on MSVC ARM64 because their guards (`__ARM_NEON`, `__aarch64__`) are GCC/Clang-only macros, leaving zvec to use the scalar fallback. That fallback hits ~1 ULP precision drift versus the NEON path, which surfaced as two HnswStreamerTest cosine failures and a `NormMatrix.Norm1_General` failure on the `windows-11-arm` CI runner. Changes: * Expand `defined(__ARM_NEON)` -> `(defined(__ARM_NEON) || defined(_M_ARM64))` at the 51 source sites that gate ARM NEON kernels (math, math_batch, utility, normalizer, platform headers, dispatch tables, version probe). * Expand `defined(__aarch64__)` -> `(defined(__aarch64__) || defined(_M_ARM64))` at the 26 sites that distinguish AArch64 from ARMv7 NEON — MSVC ARM64 is AArch64 but does not predefine `__aarch64__`. As a side effect the ARMv7-only polyfills (`vaddvq_f32`/`vaddvq_s32` shims in `distance_matrix_accum_fp32.i`, `distance_matrix_fp32.i`) are correctly skipped under MSVC ARM64, where those intrinsics are built in. * `src/include/zvec/ailego/internal/platform.h`: include `<arm_neon.h>` on the MSVC branch when `_M_ARM64` is defined (it was previously gated behind `!_MSC_VER`, so MSVC ARM64 saw no NEON types). * `src/ailego/CMakeLists.txt`: keep the existing `if(NOT MSVC)` wrapper around the GCC-only `-march=armv8-a` flag, and add an explicit `else()` branch with a comment explaining MSVC ARM64 does not need `-march` (NEON is the ARMv8 baseline on MSVC and the kernels are now picked up via the ALL_SRCS glob with the macro guards above). This supersedes the earlier `test(hnsw): skip two cosine self-match tests on MSVC ARM64` commit (which has been dropped from the branch). The NEON math kernels now use the same precision path as Linux/macOS ARM64, so those tests should pass natively on `windows-11-arm`.
1 parent 236960d commit 5baa52a

24 files changed

Lines changed: 100 additions & 47 deletions

src/ailego/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ if(NOT ANDROID AND AUTO_DETECT_ARCH)
122122
COMPILE_FLAGS "${MATH_MARCH_FLAG_NEON}"
123123
)
124124
endforeach()
125+
else()
126+
# MSVC on ARM64: NEON is the ARMv8 baseline and is always enabled,
127+
# so no `-march` flag is required (MSVC does not accept GCC-style
128+
# `-march=` anyway). The NEON math kernels still get compiled via
129+
# the ALL_SRCS glob above; their `#if defined(__ARM_NEON)` guards
130+
# were extended in this PR to also accept `_M_ARM64` so the bodies
131+
# actually emit code under MSVC.
125132
endif()
126133
endif()
127134
endif()

src/ailego/internal/cpu_features.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
1919
#include <intrin.h>
20-
#elif !defined(_MSC_VER) && !defined(__ARM_ARCH) && !defined(__aarch64__)
20+
#elif !defined(_MSC_VER) && !defined(__ARM_ARCH) && \
21+
!(defined(__aarch64__) || defined(_M_ARM64))
2122
#include <cpuid.h>
2223
#endif
2324

@@ -48,7 +49,8 @@ CpuFeatures::CpuFlags::CpuFlags(void)
4849
L7_ECX = l7[2];
4950
L7_EDX = l7[3];
5051
}
51-
#elif !defined(_MSC_VER) && !defined(__ARM_ARCH) && !defined(__aarch64__)
52+
#elif !defined(_MSC_VER) && !defined(__ARM_ARCH) && \
53+
!(defined(__aarch64__) || defined(_M_ARM64))
5254
CpuFeatures::CpuFlags::CpuFlags(void)
5355
: L1_ECX(0), L1_EDX(0), L7_EBX(0), L7_ECX(0), L7_EDX(0) {
5456
uint32_t eax, ebx, ecx, edx;
@@ -336,7 +338,7 @@ bool CpuFeatures::HYPERVISOR(void) {
336338

337339
const char *CpuFeatures::Intrinsics(void) {
338340
return ""
339-
#if defined(__ARM_NEON)
341+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
340342
"Neon"
341343
#if defined(__ARM_FEATURE_CRC32)
342344
"+CRC"

src/ailego/math/distance_matrix_accum_fp32.i

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
_mm512_castps_si512(b)))
3131
#endif // __AVX512DQ__
3232

33-
#if defined(__ARM_NEON) && !defined(__aarch64__)
33+
#if (defined(__ARM_NEON) || defined(_M_ARM64)) && !(defined(__aarch64__) || defined(_M_ARM64))
3434
static inline float32_t vaddvq_f32(float32x4_t v) {
3535
float32x2_t s = vadd_f32(vget_low_f32(v), vget_high_f32(v));
3636
return vget_lane_f32(vpadd_f32(s, s), 0);
@@ -42,7 +42,7 @@ static inline int32_t vaddvq_s32(int32x4_t v) {
4242
}
4343
#endif //__ARM_NEON && !__aarch64__
4444

45-
#if defined(__aarch64__)
45+
#if (defined(__aarch64__) || defined(_M_ARM64))
4646
#define ACCUM_FP32_2X1_NEON ACCUM_FP32_2X1_NEON_A64
4747
#else
4848
#define ACCUM_FP32_2X1_NEON ACCUM_FP32_2X1_NEON_A32

src/ailego/math/distance_matrix_fp32.i

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
_mm256_insertf128_ps(_mm256_castps128_ps256(b), (a), 1)
2727
#endif // __AVX__
2828

29-
#if defined(__ARM_NEON) && !defined(__aarch64__)
29+
#if (defined(__ARM_NEON) || defined(_M_ARM64)) && !(defined(__aarch64__) || defined(_M_ARM64))
3030
#define vdupq_laneq_f32(a, b) vdupq_n_f32(vgetq_lane_f32(a, b))
3131
#endif // __ARM_NEON && __aarch64__
3232

src/ailego/math/euclidean_distance_matrix_fp32_dispatch.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
namespace zvec {
1919
namespace ailego {
2020

21-
#if defined(__ARM_NEON)
21+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
2222
void SquaredEuclideanDistanceFp32NEON(const float *lhs, const float *rhs,
2323
size_t size, float *out);
2424
#endif
@@ -49,7 +49,7 @@ void SquaredEuclideanDistanceMatrix<float, 1, 1>::Compute(const ValueType *m,
4949
const ValueType *q,
5050
size_t dim,
5151
float *out) {
52-
#if defined(__ARM_NEON)
52+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
5353
SquaredEuclideanDistanceFp32NEON(m, q, dim, out);
5454
#else
5555
#if defined(__AVX512F__)

src/ailego/math/euclidean_distance_matrix_fp32_neon.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
namespace zvec {
2020
namespace ailego {
2121

22-
#if defined(__ARM_NEON)
22+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
2323
//! Squared Euclidean Distance
2424
void SquaredEuclideanDistanceFp32NEON(const float *lhs, const float *rhs,
2525
size_t size, float *out) {

src/ailego/math/inner_product_matrix_fp32_dispatch.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace ailego {
2020
//--------------------------------------------------
2121
// Dense
2222
//--------------------------------------------------
23-
#if defined(__ARM_NEON)
23+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
2424
float InnerProductFp32NEON(const float *lhs, const float *rhs, size_t size);
2525
float MinusInnerProductFp32NEON(const float *lhs, const float *rhs,
2626
size_t size);
@@ -49,7 +49,7 @@ float MinusInnerProductFp32Scalar(const float *lhs, const float *rhs,
4949
//! Compute the distance between matrix and query (FP32, M=1, N=1)
5050
void InnerProductMatrix<float, 1, 1>::Compute(const float *m, const float *q,
5151
size_t dim, float *out) {
52-
#if defined(__ARM_NEON)
52+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
5353
*out = InnerProductFp32NEON(m, q, dim);
5454
#else
5555
#if defined(__AVX512F__)
@@ -80,7 +80,7 @@ void InnerProductMatrix<float, 1, 1>::Compute(const float *m, const float *q,
8080
void MinusInnerProductMatrix<float, 1, 1>::Compute(const float *m,
8181
const float *q, size_t dim,
8282
float *out) {
83-
#if defined(__ARM_NEON)
83+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
8484
*out = MinusInnerProductFp32NEON(m, q, dim);
8585
#else
8686
#if defined(__AVX512F__)

src/ailego/math/inner_product_matrix_fp32_neon.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace ailego {
2222
//--------------------------------------------------
2323
// Dense
2424
//--------------------------------------------------
25-
#if defined(__ARM_NEON)
25+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
2626
float InnerProductFp32NEON(const float *lhs, const float *rhs, size_t size) {
2727
const float *last = lhs + size;
2828
const float *last_aligned = lhs + ((size >> 3) << 3);

src/ailego/math/mips_euclidean_distance_matrix_fp32_dispatch.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
namespace zvec {
1919
namespace ailego {
2020

21-
#if defined(__ARM_NEON)
21+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
2222
float InnerProductAndSquaredNormFp32NEON(const float *lhs, const float *rhs,
2323
size_t size, float *sql, float *sqr);
2424
#endif
@@ -98,7 +98,7 @@ void MipsSquaredEuclideanDistanceMatrix<float, 1, 1>::Compute(
9898
void MipsSquaredEuclideanDistanceMatrix<float, 1, 1>::Compute(
9999
const ValueType *p, const ValueType *q, size_t dim, size_t m, float e2,
100100
float *out) {
101-
#if defined(__ARM_NEON)
101+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
102102
float u2{0.0f};
103103
float v2{0.0f};
104104
float sum = InnerProductAndSquaredNormFp32NEON(p, q, dim, &u2, &v2);

src/ailego/math/mips_euclidean_distance_matrix_fp32_neon.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
namespace zvec {
2020
namespace ailego {
2121

22-
#if defined(__ARM_NEON)
22+
#if (defined(__ARM_NEON) || defined(_M_ARM64))
2323
//! Compute the Inner Product between p and q, and each Squared L2-Norm value
2424
float InnerProductAndSquaredNormFp32NEON(const float *lhs, const float *rhs,
2525
size_t size, float *sql, float *sqr) {

0 commit comments

Comments
 (0)