From d1b8cda5d3b231ac79953807cb99887bc009df7f Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Tue, 28 Jul 2026 11:13:32 +0000 Subject: [PATCH 21/38] avcodec/ppc: add AltiVec 8-wide h264qpel MC (O7) Extend the AltiVec h264qpel port to the 8-wide functions (put/avg_h264_qpel_pixels_tab[1], all 16 MC modes, 8-bit). Besides h264 itself this also serves rv40 (which borrows mc20/mc02 = the plain h/v lowpass entries), rv30 (mc00) and snow (8-wide blocks). An earlier attempt at this via the existing 16-wide template structure was reverted: those kernels precompute their vec_lvsl alignment permutes once from the initial src pointer, which is only valid when every row keeps the same address mod 16, i.e. stride % 16 == 0. That holds for the 16-wide callers but not for 8-wide ones, where the H264_MC half buffers (and checkasm) legitimately use stride 8. The 8-wide kernels therefore get their own structure instead of a template parametrization: - every load derives its permute from the actual row address (qpel8_load() reads only the aligned words holding the needed bytes, so it also never touches memory past them); - two rows are stacked into one vector (row i in lanes 0..7, row i+1 in lanes 8..15), so the 16-lane arithmetic of the proven 16-wide kernels is reused at full utilization -- half the ALU work of a one-row-at-a-time port; the hv horizontal pass handles its odd 13th row as a final singleton; - stores are byte-precise on [dst, dst+8) via a vec_lvsr rotation and a pair of vec_ste word stores (with a byte-wise vec_ste fallback for non-4-aligned dst, which no known caller produces but checkasm cannot rule out). No read-modify-write: the H264_MC half buffers have zero slack after the last row, and RMW splicing would also be unsafe against concurrent neighbours. mc00 reuses the existing hpeldsp ff_put/avg_pixels8_altivec, which keep their long-standing contract (dst % 8 == 0, stride % 8 == 0); all tab[1][0] callers satisfy it. The 4-wide table (tab[2]) stays on C: 4-byte rows leave too little vector utilization to win on G4. Validated under qemu -cpu 7400 (AltiVec): - checkasm h264qpel: all 64 functions (was 32) dispatch to a named ALTIVEC block and pass across 3 seeds; full suite 569/569 (was 537, +32 = exactly the new functions); - a standalone C-vs-AltiVec sweep of all 32 new entry points across strides {8,16,24,61}, all 16 src offsets mod 16 and all 16 dst offsets mod 16 (mc00 restricted to its documented contract), with whole-arena comparison so any write outside the 8x8 block or any misplaced byte is caught: bit-exact everywhere. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01BaTf7hduqDkr3EhmqH6Ltz --- libavcodec/ppc/h264qpel.c | 64 +++++ libavcodec/ppc/h264qpel_template.c | 386 +++++++++++++++++++++++++++++ 2 files changed, 450 insertions(+) diff --git a/libavcodec/ppc/h264qpel.c b/libavcodec/ppc/h264qpel.c index fe896c8..67ed36b 100644 --- a/libavcodec/ppc/h264qpel.c +++ b/libavcodec/ppc/h264qpel.c @@ -43,6 +43,9 @@ #define PREFIX_h264_qpel16_v_lowpass_num altivec_put_h264_qpel16_v_lowpass_num #define PREFIX_h264_qpel16_hv_lowpass_altivec put_h264_qpel16_hv_lowpass_altivec #define PREFIX_h264_qpel16_hv_lowpass_num altivec_put_h264_qpel16_hv_lowpass_num +#define PREFIX_h264_qpel8_h_lowpass_altivec put_h264_qpel8_h_lowpass_altivec +#define PREFIX_h264_qpel8_v_lowpass_altivec put_h264_qpel8_v_lowpass_altivec +#define PREFIX_h264_qpel8_hv_lowpass_altivec put_h264_qpel8_hv_lowpass_altivec #include "h264qpel_template.c" #undef OP_U8_ALTIVEC #undef PREFIX_h264_qpel16_h_lowpass_altivec @@ -51,6 +54,9 @@ #undef PREFIX_h264_qpel16_v_lowpass_num #undef PREFIX_h264_qpel16_hv_lowpass_altivec #undef PREFIX_h264_qpel16_hv_lowpass_num +#undef PREFIX_h264_qpel8_h_lowpass_altivec +#undef PREFIX_h264_qpel8_v_lowpass_altivec +#undef PREFIX_h264_qpel8_hv_lowpass_altivec #define OP_U8_ALTIVEC AVG_OP_U8_ALTIVEC #define PREFIX_h264_qpel16_h_lowpass_altivec avg_h264_qpel16_h_lowpass_altivec @@ -59,6 +65,9 @@ #define PREFIX_h264_qpel16_v_lowpass_num altivec_avg_h264_qpel16_v_lowpass_num #define PREFIX_h264_qpel16_hv_lowpass_altivec avg_h264_qpel16_hv_lowpass_altivec #define PREFIX_h264_qpel16_hv_lowpass_num altivec_avg_h264_qpel16_hv_lowpass_num +#define PREFIX_h264_qpel8_h_lowpass_altivec avg_h264_qpel8_h_lowpass_altivec +#define PREFIX_h264_qpel8_v_lowpass_altivec avg_h264_qpel8_v_lowpass_altivec +#define PREFIX_h264_qpel8_hv_lowpass_altivec avg_h264_qpel8_hv_lowpass_altivec #include "h264qpel_template.c" #undef OP_U8_ALTIVEC #undef PREFIX_h264_qpel16_h_lowpass_altivec @@ -67,6 +76,9 @@ #undef PREFIX_h264_qpel16_v_lowpass_num #undef PREFIX_h264_qpel16_hv_lowpass_altivec #undef PREFIX_h264_qpel16_hv_lowpass_num +#undef PREFIX_h264_qpel8_h_lowpass_altivec +#undef PREFIX_h264_qpel8_v_lowpass_altivec +#undef PREFIX_h264_qpel8_hv_lowpass_altivec #define H264_MC(OPNAME, SIZE, CODETYPE) \ static void OPNAME ## h264_qpel ## SIZE ## _mc00_ ## CODETYPE (uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ @@ -278,6 +290,54 @@ static inline void avg_pixels16_l2_altivec( uint8_t * dst, const uint8_t * src1, H264_MC(put_, 16, altivec) H264_MC(avg_, 16, altivec) + +#if HAVE_BIGENDIAN +/* src2 rows are packed at stride 8 (the H264_MC half buffers), so a pair of + * them is one contiguous 16-byte unaligned load */ +static inline void put_pixels8_l2_altivec(uint8_t *dst, const uint8_t *src1, + const uint8_t *src2, int dst_stride, + int src_stride1, int h) +{ + int i; + + for (i = 0; i < h; i += 2) { + const vec_u8 a = qpel8_load2(src1, src1 + src_stride1); + const vec_u8 b = unaligned_load(0, src2); + const vec_u8 d = vec_avg(a, b); + + qpel8_store(d, dst); + qpel8_store(vec_sld(d, d, 8), dst + dst_stride); + + src1 += 2 * src_stride1; + src2 += 16; + dst += 2 * dst_stride; + } +} + +static inline void avg_pixels8_l2_altivec(uint8_t *dst, const uint8_t *src1, + const uint8_t *src2, int dst_stride, + int src_stride1, int h) +{ + int i; + + for (i = 0; i < h; i += 2) { + const vec_u8 a = qpel8_load2(src1, src1 + src_stride1); + const vec_u8 b = unaligned_load(0, src2); + const vec_u8 d = vec_avg(vec_avg(a, b), + qpel8_load2(dst, dst + dst_stride)); + + qpel8_store(d, dst); + qpel8_store(vec_sld(d, d, 8), dst + dst_stride); + + src1 += 2 * src_stride1; + src2 += 16; + dst += 2 * dst_stride; + } +} + +H264_MC(put_, 8, altivec) +H264_MC(avg_, 8, altivec) +#endif /* HAVE_BIGENDIAN */ #endif /* HAVE_ALTIVEC */ av_cold void ff_h264qpel_init_ppc(H264QpelContext *c, int bit_depth) @@ -309,6 +369,10 @@ av_cold void ff_h264qpel_init_ppc(H264QpelContext *c, int bit_depth) dspfunc(put_h264_qpel, 0, 16); dspfunc(avg_h264_qpel, 0, 16); +#if HAVE_BIGENDIAN + dspfunc(put_h264_qpel, 1, 8); + dspfunc(avg_h264_qpel, 1, 8); +#endif #undef dspfunc } #endif /* HAVE_ALTIVEC */ diff --git a/libavcodec/ppc/h264qpel_template.c b/libavcodec/ppc/h264qpel_template.c index 304604c..2ca916a 100644 --- a/libavcodec/ppc/h264qpel_template.c +++ b/libavcodec/ppc/h264qpel_template.c @@ -481,3 +481,389 @@ static void PREFIX_h264_qpel16_hv_lowpass_altivec(uint8_t *dst, int16_t *tmp, } } #endif /* PREFIX_h264_qpel16_hv_lowpass_altivec */ + +/* The 8-wide variants process two rows per iteration by stacking them into a + * single vector (first row in lanes 0..7, second row in lanes 8..15), which + * reuses the 16-lane arithmetic of the 16-wide kernels at full utilization. + * Unlike the 16-wide kernels above they make no stride % 16 assumption: every + * load derives its permute from the actual row address, and stores touch + * exactly [dst, dst+8) of each row (the H264_MC half buffers have no slack). */ +#if HAVE_BIGENDIAN + +#ifndef QPEL8_ALTIVEC_HELPERS +#define QPEL8_ALTIVEC_HELPERS +/* lanes [0, last] = s[0..last]; reads only the aligned words holding them */ +static inline vec_u8 qpel8_load(const uint8_t *s, int last) +{ + return vec_perm(vec_ld(0, s), vec_ld(last, s), vec_lvsl(0, s)); +} + +/* selects a[k..k+7] into lanes 0..7 and b[k..k+7] into lanes 8..15 */ +#define qpel8_stackperm(k) ((const vec_u8) \ + { 0 + k, 1 + k, 2 + k, 3 + k, 4 + k, 5 + k, 6 + k, 7 + k, \ + 16 + k, 17 + k, 18 + k, 19 + k, 20 + k, 21 + k, 22 + k, 23 + k }) + +/* two 8-byte rows stacked into one vector */ +static inline vec_u8 qpel8_load2(const uint8_t *a, const uint8_t *b) +{ + return vec_perm(qpel8_load(a, 7), qpel8_load(b, 7), qpel8_stackperm(0)); +} + +/* store lanes 0..7 to dst[0..7] without touching any other byte */ +static inline void qpel8_store(vec_u8 v, uint8_t *dst) +{ + const vec_u8 vs = vec_perm(v, v, vec_lvsr(0, dst)); + + if (!((uintptr_t)dst & 3)) { + vec_ste((vec_u32)vs, 0, (uint32_t *)dst); + vec_ste((vec_u32)vs, 4, (uint32_t *)dst); + } else { + int j; + for (j = 0; j < 8; j++) + vec_ste(vs, j, dst); + } +} +#endif /* QPEL8_ALTIVEC_HELPERS */ + +#ifdef PREFIX_h264_qpel8_h_lowpass_altivec +static void PREFIX_h264_qpel8_h_lowpass_altivec(uint8_t *dst, + const uint8_t *src, + int dstStride, int srcStride) +{ + register int i; + + LOAD_ZERO; + const vec_s16 v5ss = vec_splat_s16(5); + const vec_u16 v5us = vec_splat_u16(5); + const vec_s16 v20ss = vec_sl(vec_splat_s16(5),vec_splat_u16(2)); + const vec_s16 v16ss = vec_sl(vec_splat_s16(1),vec_splat_u16(4)); + + vec_s16 srcP0A, srcP0B, srcP1A, srcP1B, + srcP2A, srcP2B, srcP3A, srcP3B, + srcM1A, srcM1B, srcM2A, srcM2B, + sum1A, sum1B, sum2A, sum2B, sum3A, sum3B, + pp1A, pp1B, pp2A, pp2B, pp3A, pp3B, + psumA, psumB, sumA, sumB; + + vec_u8 sum, fsum; + + for (i = 0 ; i < 8 ; i += 2) { + /* rows i and i+1; lanes 0..12 of each hold src[-2..10] */ + const vec_u8 row0 = qpel8_load(src - 2, 12); + const vec_u8 row1 = qpel8_load(src + srcStride - 2, 12); + + const vec_u8 srcM2 = vec_perm(row0, row1, qpel8_stackperm(0)); + const vec_u8 srcM1 = vec_perm(row0, row1, qpel8_stackperm(1)); + const vec_u8 srcP0 = vec_perm(row0, row1, qpel8_stackperm(2)); + const vec_u8 srcP1 = vec_perm(row0, row1, qpel8_stackperm(3)); + const vec_u8 srcP2 = vec_perm(row0, row1, qpel8_stackperm(4)); + const vec_u8 srcP3 = vec_perm(row0, row1, qpel8_stackperm(5)); + + srcP0A = (vec_s16) VEC_MERGEH(zero_u8v, srcP0); + srcP0B = (vec_s16) VEC_MERGEL(zero_u8v, srcP0); + srcP1A = (vec_s16) VEC_MERGEH(zero_u8v, srcP1); + srcP1B = (vec_s16) VEC_MERGEL(zero_u8v, srcP1); + + srcP2A = (vec_s16) VEC_MERGEH(zero_u8v, srcP2); + srcP2B = (vec_s16) VEC_MERGEL(zero_u8v, srcP2); + srcP3A = (vec_s16) VEC_MERGEH(zero_u8v, srcP3); + srcP3B = (vec_s16) VEC_MERGEL(zero_u8v, srcP3); + + srcM1A = (vec_s16) VEC_MERGEH(zero_u8v, srcM1); + srcM1B = (vec_s16) VEC_MERGEL(zero_u8v, srcM1); + srcM2A = (vec_s16) VEC_MERGEH(zero_u8v, srcM2); + srcM2B = (vec_s16) VEC_MERGEL(zero_u8v, srcM2); + + sum1A = vec_adds(srcP0A, srcP1A); + sum1B = vec_adds(srcP0B, srcP1B); + sum2A = vec_adds(srcM1A, srcP2A); + sum2B = vec_adds(srcM1B, srcP2B); + sum3A = vec_adds(srcM2A, srcP3A); + sum3B = vec_adds(srcM2B, srcP3B); + + pp1A = vec_mladd(sum1A, v20ss, v16ss); + pp1B = vec_mladd(sum1B, v20ss, v16ss); + + pp2A = vec_mladd(sum2A, v5ss, zero_s16v); + pp2B = vec_mladd(sum2B, v5ss, zero_s16v); + + pp3A = vec_add(sum3A, pp1A); + pp3B = vec_add(sum3B, pp1B); + + psumA = vec_sub(pp3A, pp2A); + psumB = vec_sub(pp3B, pp2B); + + sumA = vec_sra(psumA, v5us); + sumB = vec_sra(psumB, v5us); + + sum = vec_packsu(sumA, sumB); + + OP_U8_ALTIVEC(fsum, sum, qpel8_load2(dst, dst + dstStride)); + + qpel8_store(fsum, dst); + qpel8_store(vec_sld(fsum, fsum, 8), dst + dstStride); + + src += 2 * srcStride; + dst += 2 * dstStride; + } +} +#endif /* PREFIX_h264_qpel8_h_lowpass_altivec */ + +#ifdef PREFIX_h264_qpel8_v_lowpass_altivec +static void PREFIX_h264_qpel8_v_lowpass_altivec(uint8_t *dst, + const uint8_t *src, + int dstStride, int srcStride) +{ + register int i; + + LOAD_ZERO; + const vec_s16 v20ss = vec_sl(vec_splat_s16(5),vec_splat_u16(2)); + const vec_u16 v5us = vec_splat_u16(5); + const vec_s16 v5ss = vec_splat_s16(5); + const vec_s16 v16ss = vec_sl(vec_splat_s16(1),vec_splat_u16(4)); + + const uint8_t *srcbis = src - (srcStride * 2); + + vec_u8 r0 = qpel8_load(srcbis, 7); + vec_u8 r1 = qpel8_load(srcbis += srcStride, 7); + vec_u8 r2 = qpel8_load(srcbis += srcStride, 7); + vec_u8 r3 = qpel8_load(srcbis += srcStride, 7); + vec_u8 r4 = qpel8_load(srcbis += srcStride, 7); + + /* row pairs (i-2,i-1) .. (i+1,i+2) for the first output pair i=0 */ + const vec_u8 srcM2 = vec_perm(r0, r1, qpel8_stackperm(0)); + const vec_u8 srcM1 = vec_perm(r1, r2, qpel8_stackperm(0)); + const vec_u8 srcP0 = vec_perm(r2, r3, qpel8_stackperm(0)); + const vec_u8 srcP1 = vec_perm(r3, r4, qpel8_stackperm(0)); + + vec_s16 srcM2ssA = (vec_s16) VEC_MERGEH(zero_u8v, srcM2); + vec_s16 srcM2ssB = (vec_s16) VEC_MERGEL(zero_u8v, srcM2); + vec_s16 srcM1ssA = (vec_s16) VEC_MERGEH(zero_u8v, srcM1); + vec_s16 srcM1ssB = (vec_s16) VEC_MERGEL(zero_u8v, srcM1); + vec_s16 srcP0ssA = (vec_s16) VEC_MERGEH(zero_u8v, srcP0); + vec_s16 srcP0ssB = (vec_s16) VEC_MERGEL(zero_u8v, srcP0); + vec_s16 srcP1ssA = (vec_s16) VEC_MERGEH(zero_u8v, srcP1); + vec_s16 srcP1ssB = (vec_s16) VEC_MERGEL(zero_u8v, srcP1); + + vec_s16 pp1A, pp1B, pp2A, pp2B, pp3A, pp3B, + psumA, psumB, sumA, sumB, + sum1A, sum1B, sum2A, sum2B, sum3A, sum3B; + + vec_u8 sum, fsum; + + for (i = 0 ; i < 8 ; i += 2) { + const vec_u8 r5 = qpel8_load(srcbis += srcStride, 7); + const vec_u8 r6 = qpel8_load(srcbis += srcStride, 7); + + const vec_u8 srcP2 = vec_perm(r4, r5, qpel8_stackperm(0)); + const vec_u8 srcP3 = vec_perm(r5, r6, qpel8_stackperm(0)); + + const vec_s16 srcP2ssA = (vec_s16) VEC_MERGEH(zero_u8v, srcP2); + const vec_s16 srcP2ssB = (vec_s16) VEC_MERGEL(zero_u8v, srcP2); + const vec_s16 srcP3ssA = (vec_s16) VEC_MERGEH(zero_u8v, srcP3); + const vec_s16 srcP3ssB = (vec_s16) VEC_MERGEL(zero_u8v, srcP3); + + r4 = r6; + + sum1A = vec_adds(srcP0ssA, srcP1ssA); + sum1B = vec_adds(srcP0ssB, srcP1ssB); + sum2A = vec_adds(srcM1ssA, srcP2ssA); + sum2B = vec_adds(srcM1ssB, srcP2ssB); + sum3A = vec_adds(srcM2ssA, srcP3ssA); + sum3B = vec_adds(srcM2ssB, srcP3ssB); + + /* slide the tap window two rows down */ + srcM2ssA = srcP0ssA; + srcM2ssB = srcP0ssB; + srcM1ssA = srcP1ssA; + srcM1ssB = srcP1ssB; + srcP0ssA = srcP2ssA; + srcP0ssB = srcP2ssB; + srcP1ssA = srcP3ssA; + srcP1ssB = srcP3ssB; + + pp1A = vec_mladd(sum1A, v20ss, v16ss); + pp1B = vec_mladd(sum1B, v20ss, v16ss); + + pp2A = vec_mladd(sum2A, v5ss, zero_s16v); + pp2B = vec_mladd(sum2B, v5ss, zero_s16v); + + pp3A = vec_add(sum3A, pp1A); + pp3B = vec_add(sum3B, pp1B); + + psumA = vec_sub(pp3A, pp2A); + psumB = vec_sub(pp3B, pp2B); + + sumA = vec_sra(psumA, v5us); + sumB = vec_sra(psumB, v5us); + + sum = vec_packsu(sumA, sumB); + + OP_U8_ALTIVEC(fsum, sum, qpel8_load2(dst, dst + dstStride)); + + qpel8_store(fsum, dst); + qpel8_store(vec_sld(fsum, fsum, 8), dst + dstStride); + + dst += 2 * dstStride; + } +} +#endif /* PREFIX_h264_qpel8_v_lowpass_altivec */ + +/* tmp must be 16-byte aligned with tmpStride * 2 % 16 == 0; the H264_MC + * callers pass a DECLARE_ALIGNED(16) buffer with tmpStride == 8 */ +#ifdef PREFIX_h264_qpel8_hv_lowpass_altivec +static void PREFIX_h264_qpel8_hv_lowpass_altivec(uint8_t *dst, int16_t *tmp, + const uint8_t *src, + int dstStride, int tmpStride, + int srcStride) +{ + register int i; + + LOAD_ZERO; + const vec_s16 v20ss = vec_sl(vec_splat_s16(5),vec_splat_u16(2)); + const vec_u32 v10ui = vec_splat_u32(10); + const vec_s16 v5ss = vec_splat_s16(5); + const vec_s16 v1ss = vec_splat_s16(1); + const vec_s32 v512si = vec_sl(vec_splat_s32(1),vec_splat_u32(9)); + const vec_u32 v16ui = vec_sl(vec_splat_u32(1),vec_splat_u32(4)); + + const vec_u8 mperm = (const vec_u8) + {0x00, 0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, + 0x00, 0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07}; + int16_t *tmpbis = tmp; + + vec_s16 srcP0A, srcP0B, srcP1A, srcP1B, + srcP2A, srcP2B, srcP3A, srcP3B, + srcM1A, srcM1B, srcM2A, srcM2B, + sum1A, sum1B, sum2A, sum2B, sum3A, sum3B, + pp1A, pp1B, pp2A, pp2B, psumA, psumB; + + vec_s16 tmpM1ss, tmpM2ss, tmpP0ss, tmpP1ss, tmpP2ss; + + vec_s32 pp1e, pp1o, pp2e, pp2o, pp3e, pp3o, + pp1ce, pp1co, pp32e, pp32o, sume, sumo, ssume, ssumo; + vec_u8 fsum, sumv, sum; + vec_s16 ssum; + + src -= (2 * srcStride); + /* horizontal pass over the 13 source rows, two per iteration; the last + * iteration handles the odd final row alone */ + for (i = 0 ; i < 13 ; i += 2) { + vec_u8 row1; + const vec_u8 row0 = qpel8_load(src - 2, 12); + if (i < 12) + row1 = qpel8_load(src + srcStride - 2, 12); + else + row1 = row0; + + { + const vec_u8 srcM2 = vec_perm(row0, row1, qpel8_stackperm(0)); + const vec_u8 srcM1 = vec_perm(row0, row1, qpel8_stackperm(1)); + const vec_u8 srcP0 = vec_perm(row0, row1, qpel8_stackperm(2)); + const vec_u8 srcP1 = vec_perm(row0, row1, qpel8_stackperm(3)); + const vec_u8 srcP2 = vec_perm(row0, row1, qpel8_stackperm(4)); + const vec_u8 srcP3 = vec_perm(row0, row1, qpel8_stackperm(5)); + + srcP0A = (vec_s16) VEC_MERGEH(zero_u8v, srcP0); + srcP0B = (vec_s16) VEC_MERGEL(zero_u8v, srcP0); + srcP1A = (vec_s16) VEC_MERGEH(zero_u8v, srcP1); + srcP1B = (vec_s16) VEC_MERGEL(zero_u8v, srcP1); + + srcP2A = (vec_s16) VEC_MERGEH(zero_u8v, srcP2); + srcP2B = (vec_s16) VEC_MERGEL(zero_u8v, srcP2); + srcP3A = (vec_s16) VEC_MERGEH(zero_u8v, srcP3); + srcP3B = (vec_s16) VEC_MERGEL(zero_u8v, srcP3); + + srcM1A = (vec_s16) VEC_MERGEH(zero_u8v, srcM1); + srcM1B = (vec_s16) VEC_MERGEL(zero_u8v, srcM1); + srcM2A = (vec_s16) VEC_MERGEH(zero_u8v, srcM2); + srcM2B = (vec_s16) VEC_MERGEL(zero_u8v, srcM2); + + sum1A = vec_adds(srcP0A, srcP1A); + sum1B = vec_adds(srcP0B, srcP1B); + sum2A = vec_adds(srcM1A, srcP2A); + sum2B = vec_adds(srcM1B, srcP2B); + sum3A = vec_adds(srcM2A, srcP3A); + sum3B = vec_adds(srcM2B, srcP3B); + + pp1A = vec_mladd(sum1A, v20ss, sum3A); + pp1B = vec_mladd(sum1B, v20ss, sum3B); + + pp2A = vec_mladd(sum2A, v5ss, zero_s16v); + pp2B = vec_mladd(sum2B, v5ss, zero_s16v); + + psumA = vec_sub(pp1A, pp2A); + psumB = vec_sub(pp1B, pp2B); + + vec_st(psumA, 0, tmp); + tmp += tmpStride; /* int16_t*, and tmpStride is 8, so it's OK here */ + if (i < 12) { + vec_st(psumB, 0, tmp); + tmp += tmpStride; + } + } + + src += 2 * srcStride; + } + + tmpM2ss = vec_ld(0, tmpbis); + tmpbis += tmpStride; + tmpM1ss = vec_ld(0, tmpbis); + tmpbis += tmpStride; + tmpP0ss = vec_ld(0, tmpbis); + tmpbis += tmpStride; + tmpP1ss = vec_ld(0, tmpbis); + tmpbis += tmpStride; + tmpP2ss = vec_ld(0, tmpbis); + tmpbis += tmpStride; + + for (i = 0 ; i < 8 ; i++) { + const vec_s16 tmpP3ss = vec_ld(0, tmpbis); + + const vec_s16 sum1 = vec_adds(tmpP0ss, tmpP1ss); + const vec_s16 sum2 = vec_adds(tmpM1ss, tmpP2ss); + const vec_s16 sum3 = vec_adds(tmpM2ss, tmpP3ss); + + tmpbis += tmpStride; + + tmpM2ss = tmpM1ss; + tmpM1ss = tmpP0ss; + tmpP0ss = tmpP1ss; + tmpP1ss = tmpP2ss; + tmpP2ss = tmpP3ss; + + pp1e = vec_mule(sum1, v20ss); + pp1o = vec_mulo(sum1, v20ss); + + pp2e = vec_mule(sum2, v5ss); + pp2o = vec_mulo(sum2, v5ss); + + pp3o = vec_mulo(sum3, v1ss); + pp3e = vec_sra((vec_s32)sum3, v16ui); + + pp1ce = vec_add(pp1e, v512si); + pp1co = vec_add(pp1o, v512si); + + pp32e = vec_sub(pp3e, pp2e); + pp32o = vec_sub(pp3o, pp2o); + + sume = vec_add(pp1ce, pp32e); + sumo = vec_add(pp1co, pp32o); + + ssume = vec_sra(sume, v10ui); + ssumo = vec_sra(sumo, v10ui); + + ssum = vec_packs(ssume, ssumo); + sumv = vec_packsu(ssum, ssum); + sum = vec_perm(sumv, sumv, mperm); + + OP_U8_ALTIVEC(fsum, sum, qpel8_load(dst, 7)); + + qpel8_store(fsum, dst); + + dst += dstStride; + } +} +#endif /* PREFIX_h264_qpel8_hv_lowpass_altivec */ + +#endif /* HAVE_BIGENDIAN */ -- 2.43.0