From d1b8cda5d3b231ac79953807cb99887bc009df7f Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
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 <noreply@anthropic.com>
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

