DD: give PQCodeDistanceScalar a SIMD level so the AVX2 path cannot run AVX512 code (#5719)

Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5719

`PQCodeDistanceScalar<PQDecoderT>` is defined in a header and instantiated in every per-SIMD translation unit (`avx2.cpp` with `-mavx2`, `avx512.cpp` with `-mavx512*`, the generic TU). All copies have the same symbol, so the linker keeps one. In a dynamic-dispatch x86 build it kept the `-mavx512*` copy: `PQCodeDistanceScalar<PQDecoderGeneric>::distance_four_codes` contained 93 `zmm` instructions and was called from `PQCodeDistance<PQDecoderGeneric, SIMDLevel::AVX2>`. On a CPU without AVX512 (AMD Milan, T1_MLN), IVFPQ search with a non-8-bit PQ (`PQDecoderGeneric`, also `PQDecoder16`) on the AVX2 path raises SIGILL.

Found by running the faiss C++ tests under the vendored `qemu-x86_64`, which has no AVX512: `IVFPQFastScan.*` (`test_fast_scan_distance_to_code`) died with SIGILL at `vmovdqa64 ...,%zmm0` in that function. Same class as S672695 (AVX512 code reached on a CPU without it); found before any non-AVX512 host was added to the Cogwheel suite.

Fix: template `PQCodeDistanceScalar` on `SIMDLevel` as well, as the DD migration guide prescribes for code compiled per level, so each TU's instantiation is a distinct symbol. `PQCodeDistance<D, SL>` passes its level; the NONE and ARM_NEON specializations in `pq_code_distance-generic.h` pass theirs. Applied to both definitions (`impl/pq_code_distance/pq_code_distance-inl.h` and `utils/pq_code_distance.h`). `SL` defaults to `SIMDLevel::NONE` so code outside faiss that names only the decoder keeps compiling: `unicorn/features/encoders/PQFSTable.h` uses `faiss::PQCodeDistanceScalar<PQDecoderT>`, and without the default 15 Unicorn targets failed to build on this diff.

Reviewed By: mnorris11

Differential Revision: D124399620

fbshipit-source-id: ece1baf0e1eba39712f881976dea33fe22b265c1
This commit is contained in:
Weixiang Hong
2026-10-10 01:36:08 -07:00
committed by meta-codesync[bot]
parent 07979b10e6
commit 5d635f354c
3 changed files with 22 additions and 14 deletions
@@ -20,8 +20,8 @@ inline float pq_code_distance_8bit_single_impl<SIMDLevel::NONE>(
size_t M, size_t M,
const float* sim_table, const float* sim_table,
const uint8_t* code) { const uint8_t* code) {
return PQCodeDistanceScalar<PQDecoder8>::distance_single_code( return PQCodeDistanceScalar<PQDecoder8, SIMDLevel::NONE>::
M, 8, sim_table, code); distance_single_code(M, 8, sim_table, code);
} }
// NOLINTNEXTLINE(facebook-hte-MisplacedTemplateSpecialization) // NOLINTNEXTLINE(facebook-hte-MisplacedTemplateSpecialization)
@@ -37,7 +37,7 @@ inline void pq_code_distance_8bit_four_impl<SIMDLevel::NONE>(
float& result1, float& result1,
float& result2, float& result2,
float& result3) { float& result3) {
PQCodeDistanceScalar<PQDecoder8>::distance_four_codes( PQCodeDistanceScalar<PQDecoder8, SIMDLevel::NONE>::distance_four_codes(
M, M,
8, 8,
sim_table, sim_table,
@@ -60,8 +60,8 @@ inline float pq_code_distance_8bit_single_impl<SIMDLevel::ARM_NEON>(
size_t M, size_t M,
const float* sim_table, const float* sim_table,
const uint8_t* code) { const uint8_t* code) {
return PQCodeDistanceScalar<PQDecoder8>::distance_single_code( return PQCodeDistanceScalar<PQDecoder8, SIMDLevel::ARM_NEON>::
M, 8, sim_table, code); distance_single_code(M, 8, sim_table, code);
} }
// NOLINTNEXTLINE(facebook-hte-MisplacedTemplateSpecialization) // NOLINTNEXTLINE(facebook-hte-MisplacedTemplateSpecialization)
@@ -77,7 +77,7 @@ inline void pq_code_distance_8bit_four_impl<SIMDLevel::ARM_NEON>(
float& result1, float& result1,
float& result2, float& result2,
float& result3) { float& result3) {
PQCodeDistanceScalar<PQDecoder8>::distance_four_codes( PQCodeDistanceScalar<PQDecoder8, SIMDLevel::ARM_NEON>::distance_four_codes(
M, M,
8, 8,
sim_table, sim_table,
@@ -62,10 +62,14 @@ namespace pq_code_distance {
*********************************************************************/ *********************************************************************/
/// Scalar PQ code distance implementation. /// Scalar PQ code distance implementation.
/// Templated only on decoder type, independent of SIMD level.
/// Used directly by non-PQDecoder8 decoders (PQDecoder16, /// Used directly by non-PQDecoder8 decoders (PQDecoder16,
/// PQDecoderGeneric) and as fallback for PQDecoder8 at NONE/NEON. /// PQDecoderGeneric) and as fallback for PQDecoder8 at NONE/NEON.
template <typename PQDecoderT> // SL keeps each SIMD-level TU's instantiation a distinct symbol. Without it
// the linker merges them and may keep one compiled with -mavx512*, which
// then runs on the AVX2 path and raises SIGILL on CPUs without AVX512.
// The default keeps callers outside faiss that name only the decoder (e.g.
// unicorn/features/encoders/PQFSTable.h) compiling; faiss passes SL itself.
template <typename PQDecoderT, SIMDLevel SL = SIMDLevel::NONE>
struct PQCodeDistanceScalar { struct PQCodeDistanceScalar {
using PQDecoder = PQDecoderT; using PQDecoder = PQDecoderT;
@@ -168,7 +172,7 @@ struct PQCodeDistance {
if constexpr (std::is_same_v<PQDecoderT, PQDecoder8>) { if constexpr (std::is_same_v<PQDecoderT, PQDecoder8>) {
return pq_code_distance_8bit_single_impl<SL>(M, sim_table, code); return pq_code_distance_8bit_single_impl<SL>(M, sim_table, code);
} else { } else {
return PQCodeDistanceScalar<PQDecoderT>::distance_single_code( return PQCodeDistanceScalar<PQDecoderT, SL>::distance_single_code(
M, nbits, sim_table, code); M, nbits, sim_table, code);
} }
} }
@@ -198,7 +202,7 @@ struct PQCodeDistance {
result2, result2,
result3); result3);
} else { } else {
PQCodeDistanceScalar<PQDecoderT>::distance_four_codes( PQCodeDistanceScalar<PQDecoderT, SL>::distance_four_codes(
M, M,
nbits, nbits,
sim_table, sim_table,
+8 -4
View File
@@ -53,10 +53,14 @@ namespace pq_code_distance {
*********************************************************************/ *********************************************************************/
/// Scalar PQ code distance implementation. /// Scalar PQ code distance implementation.
/// Templated only on decoder type, independent of SIMD level.
/// Used directly by non-PQDecoder8 decoders (PQDecoder16, /// Used directly by non-PQDecoder8 decoders (PQDecoder16,
/// PQDecoderGeneric) and as fallback for PQDecoder8 at NONE/NEON. /// PQDecoderGeneric) and as fallback for PQDecoder8 at NONE/NEON.
template <typename PQDecoderT> // SL keeps each SIMD-level TU's instantiation a distinct symbol. Without it
// the linker merges them and may keep one compiled with -mavx512*, which
// then runs on the AVX2 path and raises SIGILL on CPUs without AVX512.
// The default keeps callers outside faiss that name only the decoder (e.g.
// unicorn/features/encoders/PQFSTable.h) compiling; faiss passes SL itself.
template <typename PQDecoderT, SIMDLevel SL = SIMDLevel::NONE>
struct PQCodeDistanceScalar { struct PQCodeDistanceScalar {
using PQDecoder = PQDecoderT; using PQDecoder = PQDecoderT;
@@ -160,7 +164,7 @@ struct PQCodeDistance {
if constexpr (std::is_same_v<PQDecoderT, PQDecoder8>) { if constexpr (std::is_same_v<PQDecoderT, PQDecoder8>) {
return pq_code_distance_single_impl<SL>(M, nbits, sim_table, code); return pq_code_distance_single_impl<SL>(M, nbits, sim_table, code);
} else { } else {
return PQCodeDistanceScalar<PQDecoderT>::distance_single_code( return PQCodeDistanceScalar<PQDecoderT, SL>::distance_single_code(
M, nbits, sim_table, code); M, nbits, sim_table, code);
} }
} }
@@ -191,7 +195,7 @@ struct PQCodeDistance {
result2, result2,
result3); result3);
} else { } else {
PQCodeDistanceScalar<PQDecoderT>::distance_four_codes( PQCodeDistanceScalar<PQDecoderT, SL>::distance_four_codes(
M, M,
nbits, nbits,
sim_table, sim_table,