target/arm: Fix SVE SDOT/UDOT/USDOT (4-way, indexed)

Our implementation of the indexed version of SVE SDOT/UDOT/USDOT got
the calculation of the inner loop terminator wrong.  Although we
correctly account for the element size when we calculate the
terminator for the first iteration:
   intptr_t segend = MIN(16 / sizeof(TYPED), opr_sz_n);
we don't do that when we move it forward after the first inner loop
completes.  The intention is that we process the vector in 128-bit
segments, which for a 64-bit element size should mean (1, 2), (3, 4),
(5, 6), etc.  This bug meant that we would iterate (1, 2), (3, 4, 5,
6), (7, 8, 9, 10) etc and apply the wrong indexed element to some of
the operations, and also index off the end of the vector.

You don't see this bug if the vector length is small enough that we
don't need to iterate the outer loop, i.e.  if it is only 128 bits,
or if it is the 64-bit special case from AA32/AA64 AdvSIMD.  If the
vector length is 256 bits then we calculate the right results for the
elements in the vector but do index off the end of the vector. Vector
lengths greater than 256 bits see wrong answers. The instructions
that produce 32-bit results behave correctly.

Fix the recalculation of 'segend' for subsequent iterations, and
restore a version of the comment that was lost in the refactor of
commit 7020ffd656 that explains why we only need to clamp segend to
opr_sz_n for the first iteration, not the later ones.

Cc: qemu-stable@nongnu.org
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2595
Fixes: 7020ffd656 ("target/arm: Macroize helper_gvec_{s,u}dot_idx_{b,h}")
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20241101185544.2130972-1-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2024-11-05 10:09:58 +00:00
parent efbe180ad2
commit e6b2fa1b81

View File

@ -836,6 +836,13 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \
{ \ { \
intptr_t i = 0, opr_sz = simd_oprsz(desc); \ intptr_t i = 0, opr_sz = simd_oprsz(desc); \
intptr_t opr_sz_n = opr_sz / sizeof(TYPED); \ intptr_t opr_sz_n = opr_sz / sizeof(TYPED); \
/* \
* Special case: opr_sz == 8 from AA64/AA32 advsimd means the \
* first iteration might not be a full 16 byte segment. But \
* for vector lengths beyond that this must be SVE and we know \
* opr_sz is a multiple of 16, so we need not clamp segend \
* to opr_sz_n when we advance it at the end of the loop. \
*/ \
intptr_t segend = MIN(16 / sizeof(TYPED), opr_sz_n); \ intptr_t segend = MIN(16 / sizeof(TYPED), opr_sz_n); \
intptr_t index = simd_data(desc); \ intptr_t index = simd_data(desc); \
TYPED *d = vd, *a = va; \ TYPED *d = vd, *a = va; \
@ -853,7 +860,7 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \
n[i * 4 + 2] * m2 + \ n[i * 4 + 2] * m2 + \
n[i * 4 + 3] * m3); \ n[i * 4 + 3] * m3); \
} while (++i < segend); \ } while (++i < segend); \
segend = i + 4; \ segend = i + (16 / sizeof(TYPED)); \
} while (i < opr_sz_n); \ } while (i < opr_sz_n); \
clear_tail(d, opr_sz, simd_maxsz(desc)); \ clear_tail(d, opr_sz, simd_maxsz(desc)); \
} }