Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/VecSim/spaces/L2/L2_SSE_FP32.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,29 @@ float FP32_L2SqrSIMD16_SSE(const void *pVect1v, const void *pVect2v, size_t dime
if constexpr (residual % 4) {
__m128 v1, v2, diff;
if constexpr (residual % 4 == 3) {
// Load 3 floats and set the last one to 0
v1 = _mm_loadr_ps(pVect1); // load 4 floats
v2 = _mm_loadr_ps(pVect2);
// sets the last float of v1 to the last of v2, so the diff is 0.
v1 = _mm_move_ss(v1, v2);
// Load 4 floats (unaligned). Vectors are not guaranteed to be 16-byte aligned here
// (block stride is dim * 4 and no alignment hint is set when dim % 4 != 0), so an
// aligned load (_mm_loadr_ps / movaps) would fault.
v1 = _mm_loadu_ps(pVect1);
v2 = _mm_loadu_ps(pVect2);
diff = _mm_sub_ps(v1, v2);
// Rotate the out-of-residual 4th element into the low lane and zero it - that
// element is processed by the next step.
diff = _mm_shuffle_ps(diff, diff, _MM_SHUFFLE(2, 1, 0, 3));
diff = _mm_move_ss(diff, _mm_setzero_ps());
} else if constexpr (residual % 4 == 2) {
// Load 2 floats and set the last two to 0
v1 = _mm_loadh_pi(_mm_setzero_ps(), (__m64 *)pVect1);
v2 = _mm_loadh_pi(_mm_setzero_ps(), (__m64 *)pVect2);
diff = _mm_sub_ps(v1, v2);
} else if constexpr (residual % 4 == 1) {
// Load 1 float and set the last three to 0
v1 = _mm_load_ss(pVect1);
v2 = _mm_load_ss(pVect2);
diff = _mm_sub_ps(v1, v2);
}
pVect1 += residual % 4;
pVect2 += residual % 4;
diff = _mm_sub_ps(v1, v2);
sum = _mm_mul_ps(diff, diff);
}

Expand Down
22 changes: 22 additions & 0 deletions tests/unit/test_spaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,28 @@ TEST_F(SpacesTest, GetDistFuncSQ8FP16Asymmetric) {
}

#ifdef CPU_FEATURES_ARCH_X86_64
#ifdef OPT_SSE
// Regression test for MOD-16730: the FP32 L2 SSE kernel's residual % 4 == 3 path used
// _mm_loadr_ps (movaps), which faults on non-16-byte-aligned addresses. Vectors are not
// guaranteed such alignment: VecSimAllocator::allocate() returns malloc + 8 (allocation
// header), and the dispatcher sets no alignment hint when dim % 4 != 0. This test feeds the
// kernel buffers at that exact placement (16-aligned base + 8).
TEST_F(SpacesTest, FP32_L2Sqr_SSE_misaligned_residual3) {
constexpr size_t dim = 19; // dim % 16 == 3 -> residual 3 path
alignas(16) static char raw1[16 + dim * sizeof(float)];
alignas(16) static char raw2[16 + dim * sizeof(float)];
float *v1 = reinterpret_cast<float *>(raw1 + 8); // address == 8 (mod 16)
float *v2 = reinterpret_cast<float *>(raw2 + 8);
for (size_t i = 0; i < dim; i++) {
v1[i] = float(i);
v2[i] = float(i) + 1.5f;
}
float baseline = FP32_L2Sqr(v1, v2, dim);
dist_func_t<float> arch_opt_func = spaces::Choose_FP32_L2_implementation_SSE(dim);
ASSERT_EQ(baseline, arch_opt_func(v1, v2, dim));
}
#endif // OPT_SSE

TEST_F(SpacesTest, smallDimChooser) {
// Verify that small dimensions gets the no optimization function.
for (size_t dim = 1; dim < 8; dim++) {
Expand Down
Loading