/*
 * Copyright (c) 2021, Alliance for Open Media. All rights reserved
 *
 * This source code is subject to the terms of the BSD 3-Clause Clear License
 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
 * License was not distributed with this source code in the LICENSE file, you
 * can obtain it at aomedia.org/license/software-license/bsd-3-c-c/.  If the
 * Alliance for Open Media Patent License 1.0 was not distributed with this
 * source code in the PATENTS file, you can obtain it at
 * aomedia.org/license/patent-license/.
 */

#include <immintrin.h>
#include <assert.h>

#include "config/avm_dsp_rtcd.h"
#include "config/av2_rtcd.h"

#include "av2/common/convolve.h"
#include "av2/common/restoration.h"
#include "avm_dsp/avm_dsp_common.h"
#include "avm_dsp/avm_filter.h"
#include "avm_dsp/x86/synonyms.h"
#include "avm_dsp/x86/synonyms_avx2.h"

// 128-bit xmmwords are written as [ ... ] with the MSB on the left.
// 256-bit ymmwords are written as two xmmwords, [ ... ][ ... ] with the MSB
// on the left.
// A row of, say, 16-bit pixels with values p0, p1, p2, ..., p14, p15 will be
// loaded and stored as [ p15 ... p9 p8 ][ p7 ... p1 p0 ].
void av2_highbd_wiener_convolve_add_src_avx2(
    const uint16_t *src, ptrdiff_t src_stride, uint16_t *dst,
    ptrdiff_t dst_stride, const int16_t *filter_x, int x_step_q4,
    const int16_t *filter_y, int y_step_q4, int w, int h,
    const WienerConvolveParams *conv_params, int bd) {
  assert(x_step_q4 == 16 && y_step_q4 == 16);
  assert(!(w & 7));
  assert(bd + FILTER_BITS - conv_params->round_0 + 2 <= 16);
  (void)x_step_q4;
  (void)y_step_q4;

  DECLARE_ALIGNED(32, uint16_t,
                  temp[(MAX_SB_SIZE + SUBPEL_TAPS - 1) * MAX_SB_SIZE]);
  int intermediate_height = h + SUBPEL_TAPS - 1;
  const int center_tap = ((SUBPEL_TAPS - 1) / 2);
  const uint16_t *const src_ptr = src - center_tap * src_stride - center_tap;

  const __m128i zero_128 = _mm_setzero_si128();
  const __m256i zero_256 = _mm256_setzero_si256();

  // Add an offset to account for the "add_src" part of the convolve function.
  const __m128i offset = _mm_insert_epi16(zero_128, 1 << FILTER_BITS, 3);

  const __m256i clamp_low = zero_256;

  /* Horizontal filter */
  {
    const __m256i clamp_high_ep =
        _mm256_set1_epi16(WIENER_CLAMP_LIMIT(conv_params->round_0, bd) - 1);

    // coeffs [ f7 f6 f5 f4 f3 f2 f1 f0 ]
    const __m128i coeffs_x = _mm_add_epi16(xx_loadu_128(filter_x), offset);

    // coeffs [ f3 f2 f3 f2 f1 f0 f1 f0 ]
    const __m128i coeffs_0123 = _mm_unpacklo_epi32(coeffs_x, coeffs_x);
    // coeffs [ f7 f6 f7 f6 f5 f4 f5 f4 ]
    const __m128i coeffs_4567 = _mm_unpackhi_epi32(coeffs_x, coeffs_x);

    // coeffs [ f1 f0 f1 f0 f1 f0 f1 f0 ]
    const __m128i coeffs_01_128 = _mm_unpacklo_epi64(coeffs_0123, coeffs_0123);
    // coeffs [ f3 f2 f3 f2 f3 f2 f3 f2 ]
    const __m128i coeffs_23_128 = _mm_unpackhi_epi64(coeffs_0123, coeffs_0123);
    // coeffs [ f5 f4 f5 f4 f5 f4 f5 f4 ]
    const __m128i coeffs_45_128 = _mm_unpacklo_epi64(coeffs_4567, coeffs_4567);
    // coeffs [ f7 f6 f7 f6 f7 f6 f7 f6 ]
    const __m128i coeffs_67_128 = _mm_unpackhi_epi64(coeffs_4567, coeffs_4567);

    // coeffs [ f1 f0 f1 f0 f1 f0 f1 f0 ][ f1 f0 f1 f0 f1 f0 f1 f0 ]
    const __m256i coeffs_01 = yy_set_m128i(coeffs_01_128, coeffs_01_128);
    // coeffs [ f3 f2 f3 f2 f3 f2 f3 f2 ][ f3 f2 f3 f2 f3 f2 f3 f2 ]
    const __m256i coeffs_23 = yy_set_m128i(coeffs_23_128, coeffs_23_128);
    // coeffs [ f5 f4 f5 f4 f5 f4 f5 f4 ][ f5 f4 f5 f4 f5 f4 f5 f4 ]
    const __m256i coeffs_45 = yy_set_m128i(coeffs_45_128, coeffs_45_128);
    // coeffs [ f7 f6 f7 f6 f7 f6 f7 f6 ][ f7 f6 f7 f6 f7 f6 f7 f6 ]
    const __m256i coeffs_67 = yy_set_m128i(coeffs_67_128, coeffs_67_128);

    const __m256i round_const = _mm256_set1_epi32(
        (1 << (conv_params->round_0 - 1)) + (1 << (bd + FILTER_BITS - 1)));

    for (int i = 0; i < intermediate_height; ++i) {
      for (int j = 0; j < w; j += 16) {
        const uint16_t *src_ij = src_ptr + i * src_stride + j;

        // Load 16-bit src data
        const __m256i src_0 = yy_loadu_256(src_ij + 0);
        const __m256i src_1 = yy_loadu_256(src_ij + 1);
        const __m256i src_2 = yy_loadu_256(src_ij + 2);
        const __m256i src_3 = yy_loadu_256(src_ij + 3);
        const __m256i src_4 = yy_loadu_256(src_ij + 4);
        const __m256i src_5 = yy_loadu_256(src_ij + 5);
        const __m256i src_6 = yy_loadu_256(src_ij + 6);
        const __m256i src_7 = yy_loadu_256(src_ij + 7);

        // Multiply src data by filter coeffs and sum pairs
        const __m256i res_0 = _mm256_madd_epi16(src_0, coeffs_01);
        const __m256i res_1 = _mm256_madd_epi16(src_1, coeffs_01);
        const __m256i res_2 = _mm256_madd_epi16(src_2, coeffs_23);
        const __m256i res_3 = _mm256_madd_epi16(src_3, coeffs_23);
        const __m256i res_4 = _mm256_madd_epi16(src_4, coeffs_45);
        const __m256i res_5 = _mm256_madd_epi16(src_5, coeffs_45);
        const __m256i res_6 = _mm256_madd_epi16(src_6, coeffs_67);
        const __m256i res_7 = _mm256_madd_epi16(src_7, coeffs_67);

        // Calculate scalar product for even- and odd-indices separately,
        // increasing to 32-bit precision
        const __m256i res_even_sum = _mm256_add_epi32(
            _mm256_add_epi32(res_0, res_4), _mm256_add_epi32(res_2, res_6));
        const __m256i res_even = _mm256_srai_epi32(
            _mm256_add_epi32(res_even_sum, round_const), conv_params->round_0);

        const __m256i res_odd_sum = _mm256_add_epi32(
            _mm256_add_epi32(res_1, res_5), _mm256_add_epi32(res_3, res_7));
        const __m256i res_odd = _mm256_srai_epi32(
            _mm256_add_epi32(res_odd_sum, round_const), conv_params->round_0);

        // Reduce to 16-bit precision and pack even- and odd-index results
        // back into one register. The _mm256_packs_epi32 intrinsic returns
        // a register with the pixels ordered as follows:
        // [ 15 13 11 9 14 12 10 8 ] [ 7 5 3 1 6 4 2 0 ]
        const __m256i res = _mm256_packs_epi32(res_even, res_odd);
        const __m256i res_clamped =
            _mm256_min_epi16(_mm256_max_epi16(res, clamp_low), clamp_high_ep);

        // Store in a temporary array
        yy_storeu_256(temp + i * MAX_SB_SIZE + j, res_clamped);
      }
    }
  }

  /* Vertical filter */
  {
    const __m256i clamp_high = _mm256_set1_epi16((1 << bd) - 1);

    // coeffs [ f7 f6 f5 f4 f3 f2 f1 f0 ]
    const __m128i coeffs_y = _mm_add_epi16(xx_loadu_128(filter_y), offset);

    // coeffs [ f3 f2 f3 f2 f1 f0 f1 f0 ]
    const __m128i coeffs_0123 = _mm_unpacklo_epi32(coeffs_y, coeffs_y);
    // coeffs [ f7 f6 f7 f6 f5 f4 f5 f4 ]
    const __m128i coeffs_4567 = _mm_unpackhi_epi32(coeffs_y, coeffs_y);

    // coeffs [ f1 f0 f1 f0 f1 f0 f1 f0 ]
    const __m128i coeffs_01_128 = _mm_unpacklo_epi64(coeffs_0123, coeffs_0123);
    // coeffs [ f3 f2 f3 f2 f3 f2 f3 f2 ]
    const __m128i coeffs_23_128 = _mm_unpackhi_epi64(coeffs_0123, coeffs_0123);
    // coeffs [ f5 f4 f5 f4 f5 f4 f5 f4 ]
    const __m128i coeffs_45_128 = _mm_unpacklo_epi64(coeffs_4567, coeffs_4567);
    // coeffs [ f7 f6 f7 f6 f7 f6 f7 f6 ]
    const __m128i coeffs_67_128 = _mm_unpackhi_epi64(coeffs_4567, coeffs_4567);

    // coeffs [ f1 f0 f1 f0 f1 f0 f1 f0 ][ f1 f0 f1 f0 f1 f0 f1 f0 ]
    const __m256i coeffs_01 = yy_set_m128i(coeffs_01_128, coeffs_01_128);
    // coeffs [ f3 f2 f3 f2 f3 f2 f3 f2 ][ f3 f2 f3 f2 f3 f2 f3 f2 ]
    const __m256i coeffs_23 = yy_set_m128i(coeffs_23_128, coeffs_23_128);
    // coeffs [ f5 f4 f5 f4 f5 f4 f5 f4 ][ f5 f4 f5 f4 f5 f4 f5 f4 ]
    const __m256i coeffs_45 = yy_set_m128i(coeffs_45_128, coeffs_45_128);
    // coeffs [ f7 f6 f7 f6 f7 f6 f7 f6 ][ f7 f6 f7 f6 f7 f6 f7 f6 ]
    const __m256i coeffs_67 = yy_set_m128i(coeffs_67_128, coeffs_67_128);

    const __m256i round_const =
        _mm256_set1_epi32((1 << (conv_params->round_1 - 1)) -
                          (1 << (bd + conv_params->round_1 - 1)));

    for (int i = 0; i < h; ++i) {
      for (int j = 0; j < w; j += 16) {
        const uint16_t *temp_ij = temp + i * MAX_SB_SIZE + j;

        // Load 16-bit data from the output of the horizontal filter in
        // which the pixels are ordered as follows:
        // [ 15 13 11 9 14 12 10 8 ] [ 7 5 3 1 6 4 2 0 ]
        const __m256i data_0 = yy_loadu_256(temp_ij + 0 * MAX_SB_SIZE);
        const __m256i data_1 = yy_loadu_256(temp_ij + 1 * MAX_SB_SIZE);
        const __m256i data_2 = yy_loadu_256(temp_ij + 2 * MAX_SB_SIZE);
        const __m256i data_3 = yy_loadu_256(temp_ij + 3 * MAX_SB_SIZE);
        const __m256i data_4 = yy_loadu_256(temp_ij + 4 * MAX_SB_SIZE);
        const __m256i data_5 = yy_loadu_256(temp_ij + 5 * MAX_SB_SIZE);
        const __m256i data_6 = yy_loadu_256(temp_ij + 6 * MAX_SB_SIZE);
        const __m256i data_7 = yy_loadu_256(temp_ij + 7 * MAX_SB_SIZE);

        // Filter the even-indices, increasing to 32-bit precision
        const __m256i src_0 = _mm256_unpacklo_epi16(data_0, data_1);
        const __m256i src_2 = _mm256_unpacklo_epi16(data_2, data_3);
        const __m256i src_4 = _mm256_unpacklo_epi16(data_4, data_5);
        const __m256i src_6 = _mm256_unpacklo_epi16(data_6, data_7);

        const __m256i res_0 = _mm256_madd_epi16(src_0, coeffs_01);
        const __m256i res_2 = _mm256_madd_epi16(src_2, coeffs_23);
        const __m256i res_4 = _mm256_madd_epi16(src_4, coeffs_45);
        const __m256i res_6 = _mm256_madd_epi16(src_6, coeffs_67);

        const __m256i res_even = _mm256_add_epi32(
            _mm256_add_epi32(res_0, res_2), _mm256_add_epi32(res_4, res_6));

        // Filter the odd-indices, increasing to 32-bit precision
        const __m256i src_1 = _mm256_unpackhi_epi16(data_0, data_1);
        const __m256i src_3 = _mm256_unpackhi_epi16(data_2, data_3);
        const __m256i src_5 = _mm256_unpackhi_epi16(data_4, data_5);
        const __m256i src_7 = _mm256_unpackhi_epi16(data_6, data_7);

        const __m256i res_1 = _mm256_madd_epi16(src_1, coeffs_01);
        const __m256i res_3 = _mm256_madd_epi16(src_3, coeffs_23);
        const __m256i res_5 = _mm256_madd_epi16(src_5, coeffs_45);
        const __m256i res_7 = _mm256_madd_epi16(src_7, coeffs_67);

        const __m256i res_odd = _mm256_add_epi32(
            _mm256_add_epi32(res_1, res_3), _mm256_add_epi32(res_5, res_7));

        // Pixels are currently in the following order:
        // res_even order: [ 14 12 10 8 ] [ 6 4 2 0 ]
        // res_odd order:  [ 15 13 11 9 ] [ 7 5 3 1 ]
        //
        // Rearrange the pixels into the following order:
        // res_lo order: [ 11 10  9  8 ] [ 3 2 1 0 ]
        // res_hi order: [ 15 14 13 12 ] [ 7 6 5 4 ]
        const __m256i res_lo = _mm256_unpacklo_epi32(res_even, res_odd);
        const __m256i res_hi = _mm256_unpackhi_epi32(res_even, res_odd);

        const __m256i res_lo_round = _mm256_srai_epi32(
            _mm256_add_epi32(res_lo, round_const), conv_params->round_1);
        const __m256i res_hi_round = _mm256_srai_epi32(
            _mm256_add_epi32(res_hi, round_const), conv_params->round_1);

        // Reduce to 16-bit precision and pack into the correct order:
        // [ 15 14 13 12 11 10 9 8 ][ 7 6 5 4 3 2 1 0 ]
        const __m256i res_16bit =
            _mm256_packs_epi32(res_lo_round, res_hi_round);
        const __m256i res_16bit_clamped = _mm256_min_epi16(
            _mm256_max_epi16(res_16bit, clamp_low), clamp_high);

        // Store in the dst array
        yy_storeu_256(dst + i * dst_stride + j, res_16bit_clamped);
      }
    }
  }
}

// 256bit intrinsic implementation of ROUND_POWER_OF_TWO_SIGNED.
static INLINE __m256i round_power_of_two_signed_avx2(__m256i v_val_d,
                                                     int bits) {
  const __m256i v_bias_d = _mm256_set1_epi32((1 << bits) >> 1);
  const __m256i v_sign_d = _mm256_srai_epi32(v_val_d, 31);
  const __m256i v_tmp_d =
      _mm256_add_epi32(_mm256_add_epi32(v_val_d, v_bias_d), v_sign_d);
  return _mm256_srai_epi32(v_tmp_d, bits);
}

// Clip the pixel values to zero/max.
static __m256i highbd_clamp_epi16(__m256i u, __m256i zero, __m256i max) {
  return _mm256_max_epi16(_mm256_min_epi16(u, max), zero);
}

// Downcast 256bit register to 128bit by considering lower 128bit.
#define CAST_LOW(a) _mm256_castsi128_si256(a)

// Load source data required for chroma filtering into registers.
#define LOAD_SRC_DATA_FOR_CHROMA_FILTERING(src, stride)                        \
  const __m128i src_a0 = _mm_loadu_si128((__m128i const *)src);                \
  const __m128i src_b0 = _mm_loadu_si128((__m128i const *)(src + stride));     \
  const __m128i src_c0 = _mm_loadu_si128((__m128i const *)(src + 2 * stride)); \
  const __m128i src_d0 = _mm_loadu_si128((__m128i const *)(src + 3 * stride)); \
  const __m128i src_e0 = _mm_loadu_si128((__m128i const *)(src + 4 * stride)); \
  const __m128i src_f0 = _mm_loadu_si128((__m128i const *)(src + 5 * stride)); \
  const __m128i src_g0 = _mm_loadu_si128((__m128i const *)(src + 6 * stride)); \
  const __m128i src_h0 = _mm_loadu_si128((__m128i const *)(src + 7 * stride));

// Forms 256bit source registers from loaded 128bit registers and pack them
// accordingly for filtering process.
#define FORM_AND_PACK_REG_FOR_CHROMA_FILTERING()                               \
  /* Form 256bit source registers.*/                                           \
  /* a0 a1 a2 a3 a4 a5 a6 a7 | b0 b1 b2 b3 b4 b5 b6 b7*/                       \
  const __m256i src_ab = _mm256_inserti128_si256(CAST_LOW(src_a0), src_b0, 1); \
  /* c0 c1 c2 c3 c4 c5 c6 c7 | d0 d1 d2 d3 d4 d5 d6 d7*/                       \
  const __m256i src_cd = _mm256_inserti128_si256(CAST_LOW(src_c0), src_d0, 1); \
  /* e0 e1 e2 e3 e4 e5 e6 e7 | f0 f1 f2 f3 f4 f5 f6 f7*/                       \
  const __m256i src_ef = _mm256_inserti128_si256(CAST_LOW(src_e0), src_f0, 1); \
  /* g0 g1 g2 g3 g4 g5 g6 g7 | h0 h1 h2 h3 h4 h5 h6 h7*/                       \
  const __m256i src_gh = _mm256_inserti128_si256(CAST_LOW(src_g0), src_h0, 1); \
  /* b0 b1 b2 b3 b4 b5 b6 b7 | c0 c1 c2 c3 c4 c5 c6 c7*/                       \
  const __m256i src_bc = _mm256_inserti128_si256(CAST_LOW(src_b0), src_c0, 1); \
  /* d0 d1 d2 d3 d4 d5 d6 d7 | e0 e1 e2 e3 e4 e5 e6 e7*/                       \
  const __m256i src_de = _mm256_inserti128_si256(CAST_LOW(src_d0), src_e0, 1); \
  /* f0 f1 f2 f3 f4 f5 f6 f7 | g0 g1 g2 g3 g4 g5 g6 g7*/                       \
  const __m256i src_fg = _mm256_inserti128_si256(CAST_LOW(src_f0), src_g0, 1); \
  /* Packing the source rows.*/                                                \
  /* a0 e0 a1 e1 a2 e2 a3 e3 | b0 f0 b1 f1 b2 f2 b3 f3*/                       \
  const __m256i ru0 = _mm256_unpacklo_epi16(src_ab, src_ef);                   \
  /* a4 e4 a5 e5 a6 e6 a7 e7 | b4 f4 b5 f5 b6 f6 b7 f7*/                       \
  const __m256i ru1 = _mm256_unpackhi_epi16(src_ab, src_ef);                   \
  /* c0 g0 c1 g1 c2 g2 c3 g3 | d0 h0 d1 h1 d2 h2 d3 h3*/                       \
  const __m256i ru2 = _mm256_unpacklo_epi16(src_cd, src_gh);                   \
  /* c4 g4 c5 g5 c6 g6 c7 g7 | d4 h4 d5 h5 d6 h6 d7 h7*/                       \
  const __m256i ru3 = _mm256_unpackhi_epi16(src_cd, src_gh);                   \
  /* b0 d0 b1 d1 b2 d2 b3 d3 | c0 e0 c1 e1 c2 e2 c3 e3*/                       \
  const __m256i ru4 = _mm256_unpacklo_epi16(src_bc, src_de);                   \
  /* b4 d4 b5 d5 b6 d6 b7 d7 | c4 e4 c5 e5 c6 e6 c7 e7*/                       \
  const __m256i ru5 = _mm256_unpackhi_epi16(src_bc, src_de);                   \
  /* d0 f0 d1 f1 d2 f2 d3 f3 | e0 g0 e1 g1 e2 g2 e3 g3*/                       \
  const __m256i ru6 = _mm256_unpacklo_epi16(src_de, src_fg);                   \
  /* d4 f4 d5 f5 d6 f6 d7 f7 | e4 g4 e5 g5 e6 g6 e7 g7*/                       \
  const __m256i ru7 = _mm256_unpackhi_epi16(src_de, src_fg);

// Multiply the formed registers with filter and add the result to accumulate
// registers.
#define MADD_AND_ACCUM_FOR_CHROMA_FILTERING(f0, f1, f2)         \
  {                                                             \
    const __m256i res_0 = _mm256_madd_epi16(ru10, f0);          \
    const __m256i res_1 = _mm256_madd_epi16(ru11, f0);          \
    const __m256i res_2 = _mm256_madd_epi16(ru12, f1);          \
    const __m256i res_3 = _mm256_madd_epi16(ru13, f1);          \
    const __m256i res_4 = _mm256_madd_epi16(ru14, f2);          \
    const __m256i res_5 = _mm256_madd_epi16(ru15, f2);          \
    /* r00 r01 r02 r03 | r10 r11 r12 r13 */                     \
    const __m256i out_0 = _mm256_add_epi32(res_0, res_2);       \
    const __m256i out_1 = _mm256_add_epi32(res_4, out_0);       \
    *accum_out_r0r1 = _mm256_add_epi32(out_1, *accum_out_r0r1); \
    /* r20 r21 r22 r23 | r30 r31 r32 r33 */                     \
    const __m256i out_2 = _mm256_add_epi32(res_1, res_3);       \
    const __m256i out_3 = _mm256_add_epi32(res_5, out_2);       \
    *accum_out_r2r3 = _mm256_add_epi32(out_3, *accum_out_r2r3); \
  }

// Variant with only two sets of pixels and coefficients
#define MADD_AND_ACCUM_FOR_CHROMA_FILTERING_2(f0, f1)           \
  {                                                             \
    const __m256i res_0 = _mm256_madd_epi16(ru10, f0);          \
    const __m256i res_1 = _mm256_madd_epi16(ru11, f0);          \
    const __m256i res_2 = _mm256_madd_epi16(ru12, f1);          \
    const __m256i res_3 = _mm256_madd_epi16(ru13, f1);          \
    /* r00 r01 r02 r03 | r10 r11 r12 r13 */                     \
    const __m256i out_0 = _mm256_add_epi32(res_0, res_2);       \
    *accum_out_r0r1 = _mm256_add_epi32(out_0, *accum_out_r0r1); \
    /* r20 r21 r22 r23 | r30 r31 r32 r33 */                     \
    const __m256i out_1 = _mm256_add_epi32(res_1, res_3);       \
    *accum_out_r2r3 = _mm256_add_epi32(out_1, *accum_out_r2r3); \
  }

// Implementation of DIAMOND shaped 7-tap filtering for block size of 4x4.
// The output for a particular pixel in a 4x4 block is calculated by considering
// a 5x5 grid surrounded by that pixel. The registers accum_out_r0r1 and
// accum_out_r2r3 are used to store the output. The following describes the
// algorithm briefly.
// Filter Coefficients: fc0 fc1 fc2 fc3 fc4 fc5 fc6 x
// Load Source Data:
// src_ra0 = a0 a1 a2 a3 a4 a5 a6 a7
// src_rb0 = b0 b1 b2 b3 b4 b5 b6 b7
// src_rc0 = c0 c1 c2 c3 c4 c5 c6 c7
// src_rd0 = d0 d1 d2 d3 d4 d5 d6 d7
// src_re0 = e0 e1 e2 e3 e4 e5 e6 e7
// src_rf0 = f0 f1 f2 f3 f4 f5 f6 f7
// src_rg0 = g0 g1 g2 g3 g4 g5 g6 g7
// The output for a pixel located at c2 position is calculated as below.
// Filtered_c2 = (a2+e2)*fc4 + (b1+d3)*fc2 + (b2+d2)*fc0 + (b3+d1)*fc3 +
// (c0+c4)*fc5 + (c1+c3)*fc1 + c2*fc6
// The source registers are unpacked such that the output corresponding to 2
// rows will be produced in a single register (i.e., processing 2 rows
// simultaneously).
//
// Example:
// The output corresponding to fc4 of rows 0 and 1 is achieved like below.
// __m256i src_reg3 = a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
// __m256i filter_4 = fc4 fc4 fc4 fc4 fc4 fc4 fc4 fc4 | fc4 fc4 fc4 fc4 fc4 fc4
// fc4 fc4
//  __m256i out_f4_01 = _mm256_madd_epi16(src_reg3, filter_4);
//                   = (a2*fc4+e2*fc4) (a3*fc4+e3*fc4) .. | (b2*fc4+f2*fc4) . .
// Here, out_f4_01 contains partial output of rows 0 and 1 corresponding to fc4.
static INLINE void apply_7tap_filtering_with_subtract_center_off(
    const uint16_t *dgd, int stride, const __m128i filt_coeff,
    __m256i *accum_out_r0r1, __m256i *accum_out_r2r3, int block_row_begin,
    int block_col_begin) {
  // Load source data
  const int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 2 * stride - 2;

  // Load source data required for chroma filtering into registers.
  LOAD_SRC_DATA_FOR_CHROMA_FILTERING(src_ptr, stride)

  // Forms 256bit source registers from loaded 128bit registers and pack them
  // accordingly for filtering process.
  FORM_AND_PACK_REG_FOR_CHROMA_FILTERING()

  // Output corresponding to filter coefficient f4.
  // a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
  const __m256i ru8 = _mm256_alignr_epi8(ru1, ru0, 8);
  // c2 g2 c3 g3 c4 g4 c5 g5 | d2 h2 d3 h3 d4 h4 d5 h5
  const __m256i ru9 = _mm256_alignr_epi8(ru3, ru2, 8);

  // f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4
  const __m256i fc4 =
      _mm256_broadcastd_epi32(_mm_unpackhi_epi16(filt_coeff, filt_coeff));
  // r00 r01 r02 r03 | r10 r11 r12 r13
  const __m256i out_f4_r0r1 = _mm256_madd_epi16(ru8, fc4);
  *accum_out_r0r1 = _mm256_add_epi32(out_f4_r0r1, *accum_out_r0r1);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  const __m256i out_f4_r2r3 = _mm256_madd_epi16(ru9, fc4);
  *accum_out_r2r3 = _mm256_add_epi32(out_f4_r2r3, *accum_out_r2r3);

  // Output corresponding to filter coefficient 2, 0, 3.
  // b1 d1 b2 d2 b3 d3 b4 d4 | c1 e1 c2 e2 c3 e3 c4 e4
  __m256i ru10 = _mm256_alignr_epi8(ru5, ru4, 4);
  // d1 f1 d2 f2 d3 f3 d4 f4 | e1 g1 e2 g2 e3 g3 e4 g4
  __m256i ru11 = _mm256_alignr_epi8(ru7, ru6, 4);
  // b2 d2 b3 d3 b4 d4 b5 d5 | c2 e2 c3 e3 c4 e4 c5 e5
  __m256i ru12 = _mm256_alignr_epi8(ru5, ru4, 8);
  // d2 f2 d3 f3 d4 f4 d5 f5 | e2 g2 e3 g3 e4 g4 e5 g5
  __m256i ru13 = _mm256_alignr_epi8(ru7, ru6, 8);
  // b3 d3 b4 d4 b5 d5 b6 d6 | c3 e3 c4 e4 c5 e5 c6 e6
  __m256i ru14 = _mm256_alignr_epi8(ru5, ru4, 12);
  // d3 f3 d4 f4 d5 f5 d6 f6 | e3 g3 e4 g4 e5 g5 e6 g6
  __m256i ru15 = _mm256_alignr_epi8(ru7, ru6, 12);

  // f2 f3 f2 f3 - - - -
  const __m256i fc23 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff, 0x0E));
  // f0 f0 f0 f0 - - - -
  const __m256i fc00 = _mm256_broadcastw_epi16(filt_coeff);
  // f3 f2 f3 f2 - - - -
  const __m256i fc32 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff, 0x0B));

  // Multiply the formed registers with filter and add the result to accumulate
  // registers.
  MADD_AND_ACCUM_FOR_CHROMA_FILTERING(fc23, fc00, fc32)

  // Output corresponding to filter coefficient 5, 1, 6.
  const __m256i zero = _mm256_set1_epi16(0x0);
  // c0 c1 c1 c2 c2 c3 c3 c4 || d0 d1 d1 d2 d2 d3 d3 d4
  ru10 = _mm256_unpacklo_epi16(src_cd, _mm256_bsrli_epi128(src_cd, 2));
  // e0 e1 e1 e2 e2 e3 e3 e4 || f0 f1 f1 f2 f2 f3 f3 f4
  ru11 = _mm256_unpacklo_epi16(src_ef, _mm256_bsrli_epi128(src_ef, 2));
  // c2 c3 c3 c4 c4 c5 c5 c6 || d2 d3 d3 d4 d4 d5 d5 d6
  ru12 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_cd, 4),
                               _mm256_bsrli_epi128(src_cd, 6));
  // e2 e3 e3 e4 e4 e5 e5 e6 || f2 f3 f3 f4 f4 f5 f5 f6
  ru13 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_ef, 4),
                               _mm256_bsrli_epi128(src_ef, 6));
  // c4 0 c5 0 c6 0 c7 0 || d4 0 d5 0 d6 0 d7 0
  ru14 = _mm256_unpackhi_epi16(src_cd, zero);
  // e4 0 e5 0 e6 0 e7 0 || f4 0 f5 0 f6 0 f7 0
  ru15 = _mm256_unpackhi_epi16(src_ef, zero);

  // f5 f1 f5 f1 - - - -
  const __m128i filt51 =
      _mm_blend_epi16(filt_coeff, _mm_bsrli_si128(filt_coeff, 4), 0x08);
  const __m256i fc51 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt51, 0x07));
  // f6 f1 f6 f1 - - - -
  const __m128i filt61 =
      _mm_blend_epi16(filt_coeff, _mm_bsrli_si128(filt_coeff, 6), 0x08);
  const __m256i fc61 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt61, 0x07));
  // f5 0 f5 0 f5 0 f5 0 - -
  const __m256i fc5z = _mm256_blend_epi16(fc51, zero, 0xAA);

  // Multiply the formed registers with filter and add the result to accumulate
  // registers.
  MADD_AND_ACCUM_FOR_CHROMA_FILTERING(fc51, fc61, fc5z)
}

// Similar to apply_7tap_filtering_with_subtract_center_off, but allow a generic
// asymmetric filter with 13 independent taps. The only difference is in how
// the filter taps are matched up with source pixels.
static INLINE void apply_asym_13tap_filtering_with_subtract_center_off(
    const uint16_t *dgd, int stride, const __m128i filt_coeff_lo,
    const __m128i filt_coeff_hi, __m256i *accum_out_r0r1,
    __m256i *accum_out_r2r3, int block_row_begin, int block_col_begin) {
  // Load source data
  const int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 2 * stride - 2;

  // Load source data required for chroma filtering into registers.
  LOAD_SRC_DATA_FOR_CHROMA_FILTERING(src_ptr, stride)

  // Forms 256bit source registers from loaded 128bit registers and pack them
  // accordingly for filtering process.
  FORM_AND_PACK_REG_FOR_CHROMA_FILTERING()

  // Output corresponding to filter coefficient f8,f9.
  // a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
  const __m256i ru8 = _mm256_alignr_epi8(ru1, ru0, 8);
  // c2 g2 c3 g3 c4 g4 c5 g5 | d2 h2 d3 h3 d4 h4 d5 h5
  const __m256i ru9 = _mm256_alignr_epi8(ru3, ru2, 8);

  // f9 f8 f9 f8 f9 f8 f9 f8 | f9 f8 f9 f8 f9 f8 f9 f8
  const __m256i fc98 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_hi, 0x01));
  // r00 r01 r02 r03 | r10 r11 r12 r13
  const __m256i out_f98_r0r1 = _mm256_madd_epi16(ru8, fc98);
  *accum_out_r0r1 = _mm256_add_epi32(out_f98_r0r1, *accum_out_r0r1);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  const __m256i out_f98_r2r3 = _mm256_madd_epi16(ru9, fc98);
  *accum_out_r2r3 = _mm256_add_epi32(out_f98_r2r3, *accum_out_r2r3);

  // Output corresponding to filter coefficient 5, 7, 0, 1, 6, 4
  // b1 d1 b2 d2 b3 d3 b4 d4 | c1 e1 c2 e2 c3 e3 c4 e4
  __m256i ru10 = _mm256_alignr_epi8(ru5, ru4, 4);
  // d1 f1 d2 f2 d3 f3 d4 f4 | e1 g1 e2 g2 e3 g3 e4 g4
  __m256i ru11 = _mm256_alignr_epi8(ru7, ru6, 4);
  // b2 d2 b3 d3 b4 d4 b5 d5 | c2 e2 c3 e3 c4 e4 c5 e5
  __m256i ru12 = _mm256_alignr_epi8(ru5, ru4, 8);
  // d2 f2 d3 f3 d4 f4 d5 f5 | e2 g2 e3 g3 e4 g4 e5 g5
  __m256i ru13 = _mm256_alignr_epi8(ru7, ru6, 8);
  // b3 d3 b4 d4 b5 d5 b6 d6 | c3 e3 c4 e4 c5 e5 c6 e6
  __m256i ru14 = _mm256_alignr_epi8(ru5, ru4, 12);
  // d3 f3 d4 f4 d5 f5 d6 f6 | e3 g3 e4 g4 e5 g5 e6 g6
  __m256i ru15 = _mm256_alignr_epi8(ru7, ru6, 12);

  // f5 f7 f5 f7 - - - -
  const __m256i fc57 = _mm256_broadcastd_epi32(
      _mm_shufflelo_epi16(_mm_bsrli_si128(filt_coeff_lo, 8), 0x0D));
  // f1 f0 f1 f0 - - - -
  const __m256i fc10 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_lo, 0x01));
  // f6 f4 f6 f4 - - - -
  const __m256i fc64 = _mm256_broadcastd_epi32(
      _mm_shufflelo_epi16(_mm_bsrli_si128(filt_coeff_lo, 8), 0x02));

  // Multiply the formed registers with filter and add the result to accumulate
  // registers.
  MADD_AND_ACCUM_FOR_CHROMA_FILTERING(fc57, fc10, fc64)

  // Output corresponding to filter coefficient 5, 1, 6.
  const __m256i zero = _mm256_set1_epi16(0x0);
  // c0 c1 c1 c2 c2 c3 c3 c4 || d0 d1 d1 d2 d2 d3 d3 d4
  ru10 = _mm256_unpacklo_epi16(src_cd, _mm256_bsrli_epi128(src_cd, 2));
  // e0 e1 e1 e2 e2 e3 e3 e4 || f0 f1 f1 f2 f2 f3 f3 f4
  ru11 = _mm256_unpacklo_epi16(src_ef, _mm256_bsrli_epi128(src_ef, 2));
  // c2  0 c3  0 c4  0 c5  0 || d2  0 d3  0 d4  0 d5  0
  ru12 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_cd, 4), zero);
  // e2  0 e3  0 e4  0 e5  0 || f2  0 f3  0 f4  0 f5  0
  ru13 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_ef, 4), zero);
  // c3 c4 c4 c5 c5 c6 c6 c7 || d3 d4 d4 d5 d5 d6 d6 d7
  ru14 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_cd, 6),
                               _mm256_bsrli_epi128(src_cd, 8));
  // e3 e4 e4 e5 e5 e6 e6 e7 || f3 f4 f4 f5 f5 f6 f6 f7
  ru15 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_ef, 6),
                               _mm256_bsrli_epi128(src_ef, 8));

  // f11  f3 f11  f3 - - - -
  const __m256i fc113 = _mm256_broadcastd_epi32(_mm_unpacklo_epi16(
      _mm_bsrli_si128(filt_coeff_hi, 6), _mm_bsrli_si128(filt_coeff_lo, 6)));

  // f12 f12 f12 f12 - - - -
  // Every second coefficient is multiplied by 0, so is ignored
  const __m256i fc12z =
      _mm256_broadcastw_epi16(_mm_bsrli_si128(filt_coeff_hi, 8));

  //  f2 f10  f2 f10 - - - -
  const __m256i fc210 = _mm256_broadcastd_epi32(_mm_unpacklo_epi16(
      _mm_bsrli_si128(filt_coeff_lo, 4), _mm_bsrli_si128(filt_coeff_hi, 4)));

  // Multiply the formed registers with filter and add the result to accumulate
  // registers.
  MADD_AND_ACCUM_FOR_CHROMA_FILTERING(fc113, fc12z, fc210)
}

// The registers accum_out_r0r1 and accum_out_r2r3 holds the filtered output.
// This function performs round, clip operations on filtered output before
// storing it to the destination.
static INLINE void round_and_store_avx2(const uint16_t *dst, int dst_stride,
                                        const NonsepFilterConfig *filter_config,
                                        int bit_depth, __m256i accum_out_r0r1,
                                        __m256i accum_out_r2r3,
                                        int block_row_begin,
                                        int block_col_begin) {
  // Rounding and clipping.
  accum_out_r0r1 =
      round_power_of_two_signed_avx2(accum_out_r0r1, filter_config->prec_bits);
  accum_out_r2r3 =
      round_power_of_two_signed_avx2(accum_out_r2r3, filter_config->prec_bits);
  // r00 r01 r02 r03 | r20 r21 r22 r23 | r10 r11 r12 r13 | r30 r31 r32 r33
  __m256i out_r0r2r1r3 = _mm256_packs_epi32(accum_out_r0r1, accum_out_r2r3);
  const __m256i max = _mm256_set1_epi16((1 << bit_depth) - 1);
  out_r0r2r1r3 = highbd_clamp_epi16(out_r0r2r1r3, _mm256_setzero_si256(), max);

  // Store the output.
  const int dst_id = block_row_begin * dst_stride + block_col_begin;
  const __m128i out_r1r3 = _mm256_extractf128_si256(out_r0r2r1r3, 1);
  _mm_storel_epi64((__m128i *)(dst + dst_id),
                   _mm256_castsi256_si128(out_r0r2r1r3));
  _mm_storel_epi64((__m128i *)(dst + dst_id + (1 * dst_stride)), out_r1r3);

  _mm_storel_epi64((__m128i *)(dst + dst_id + (2 * dst_stride)),
                   _mm_bsrli_si128(_mm256_castsi256_si128(out_r0r2r1r3), 8));
  _mm_storel_epi64((__m128i *)(dst + dst_id + (3 * dst_stride)),
                   _mm_bsrli_si128(out_r1r3, 8));
}

// Implementation of DIAMOND shaped 7-tap (6 symmetric + 1) filtering for block
// size of 4x4. The output for a particular pixel in a 4x4 block is calculated
// by considering a 5x5 grid surrounded by that pixel. The filter taps are
// expected to be passed as symmetric taps followed by center tap. The exact
// order of filter taps needed is shown below.
// Filter Coefficients: fc0 fc1 fc2 fc3 fc4 fc5 fc
// Here, 'fc0-fc5' corresponds to symmetric taps and 'fc' is the center tap.
static AVM_INLINE void convolve_symmetric_highbd_7tap_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  // Derive singleton_tap.
  // TODO(rachelbarker): Set up the singleton tap fully in
  // adjust_filter_and_config, so that we don't have to modify it here
  int32_t singleton_tap = 1 << filter_config->prec_bits;
  if (filter_config->num_pixels % 2) {
    const int singleton_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    singleton_tap += filter[singleton_tap_index];
  }

  // Load filter tap values.
  // fc0 fc1 fc2 fc3 fc4 fc5 center_tap x
  const __m128i filt_coeff_0 = _mm_loadu_si128((__m128i const *)(filter));
  // Replace the center_tap with derived singleton_tap.
  const __m128i center_tap = _mm_set1_epi16(singleton_tap);
  const __m128i filt_coeff = _mm_blend_epi16(filt_coeff_0, center_tap, 0x40);

  // Initializing the output registers with zero
  __m256i accum_out_r0r1 = _mm256_setzero_si256();
  __m256i accum_out_r2r3 = _mm256_setzero_si256();

  // Perform 7-tap filtering on source buffer
  apply_7tap_filtering_with_subtract_center_off(
      dgd, stride, filt_coeff, &accum_out_r0r1, &accum_out_r2r3,
      block_row_begin, block_col_begin);

  // Store the output after rounding and clipping
  round_and_store_avx2(dst, dst_stride, filter_config, bit_depth,
                       accum_out_r0r1, accum_out_r2r3, block_row_begin,
                       block_col_begin);
}
/* Not used. Can be deprecated - END */

// AVX2 intrinsic to convolve a block of pixels with origin-symmetric,
// non-separable filters. The output for a particular pixel in a 4x4 block is
// calculated with DIAMOND shaped filter considering a 7x7 grid surrounded by
// that pixel. DIAMOND shape uses 13-tap filter for convolution. The filter taps
// are expected to be passed as symmetric taps followed by center tap. The exact
// order of filter taps needed is shown below.
// Filter Coefficients: fc0 fc1 fc2 fc3 fc4 fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc
// Here, 'fc0-fc11' corresponds to symmetric taps and 'fc' is the center tap.
// The following describes the design considered for intrinsic implementation.
// Load Source Data:
// src_ra0 = a0 a1 a2 a3 a4 a5 a6 a7
// src_rb0 = b0 b1 b2 b3 b4 b5 b6 b7
// src_rc0 = c0 c1 c2 c3 c4 c5 c6 c7
// src_rd0 = d0 d1 d2 d3 d4 d5 d6 d7
// src_re0 = e0 e1 e2 e3 e4 e5 e6 e7
// src_rf0 = f0 f1 f2 f3 f4 f5 f6 f7
// src_rg0 = g0 g1 g2 g3 g4 g5 g6 g7
// The output for a pixel located at d3 position is calculated as below.
// Filtered_d3 = (a3+g3)*fc10 + (b2+f4)*fc6 + (b3+f3)*fc2 + (b4+f5)*fc7 +
//        (c1+e5)*fc8 + (c2+e4)*fc4 + (c3+e3)*fc0 + (c4+e2)*fc5 + (c5+e1)*fc9 +
//        (d0+d6)*fc11 + (d1+d5)*fc3 + (d2+d4)*fc1 + d3*fc.
// The source registers are unpacked such that the output corresponding
// to 2 rows will be produced in a single register (i.e., processing 2 rows
// simultaneously).
//
// Example:
// The output corresponding to fc0 of rows 0 and 1 is achieved like below.
// __m256i src_rag3 = a3 g3 a4 g4 a5 g5 a6 g6 | b3 h3 b4 h4 b5 h5 b6 h6
// __m256i filter_0 = f0 f0 f0 f0 f0 f0 f0 f0 | f0 f0 f0 f0 f0 f0 f0 f0
//  __m256i out_f0_0 = _mm256_madd_epi16(src_rag3, filter_0);
//                   = (a3*f0+g3*f0) (a4*f0+g4*f0) .. | (b3*f0+h3*f0) . .
// Here, out_f0_0 contains partial output of rows 1 and 2 corresponding to fc0.
static AVM_INLINE void convolve_symmetric_highbd_13tap_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter_, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  /*
  Note the original SIMD code here was designed for the configuration below,
  but is being changed to the one in config_13tap_avx2 above to align
  pc_wiener and wienerns configurations. However a permute of the coefficients
  is now necessary before the rest of the code can be used.
  { -3, 0, 0 },  { 3, 0, 0 },  { -2, -1, 1 }, { 2, 1, 1 },   { -2, 0, 2 },
  { 2, 0, 2 },   { -2, 1, 3 }, { 2, -1, 3 },  { -1, -2, 4 }, { 1, 2, 4 },
  { -1, -1, 5 }, { 1, 1, 5 },  { -1, 0, 6 },  { 1, 0, 6 },   { -1, 1, 7 },
  { 1, -1, 7 },  { -1, 2, 8 }, { 1, -2, 8 },  { 0, -3, 9 },  { 0, 3, 9 },
  { 0, -2, 10 }, { 0, 2, 10 }, { 0, -1, 11 }, { 0, 1, 11 },  { 0, 0, 12 },
  */
  // Begin permute
  int16_t filter[WIENERNS_TAPS_MAX];
  filter[6] = filter_[0];
  filter[11] = filter_[1];
  filter[2] = filter_[2];
  filter[10] = filter_[3];
  filter[5] = filter_[4];
  filter[7] = filter_[5];
  filter[1] = filter_[6];
  filter[3] = filter_[7];
  filter[4] = filter_[8];
  filter[8] = filter_[9];
  filter[0] = filter_[10];
  filter[9] = filter_[11];
  filter[12] = filter_[12];
  // End permute

  // Derive singleton_tap.
  // TODO(rachelbarker): Set up the singleton tap fully in
  // adjust_filter_and_config, so that we don't have to modify it here
  int32_t singleton_tap = 1 << filter_config->prec_bits;
  if (filter_config->num_pixels % 2) {
    const int singleton_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    singleton_tap += filter[singleton_tap_index];
  }

  // Load source data.
  int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 3 * stride - 3;
  const __m128i src_a0 = _mm_loadu_si128((__m128i const *)src_ptr);
  const __m128i src_b0 = _mm_loadu_si128((__m128i const *)(src_ptr + stride));
  const __m128i src_c0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 2 * stride));
  const __m128i src_d0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 3 * stride));
  const __m128i src_e0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride));
  const __m128i src_f0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 5 * stride));
  const __m128i src_g0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 6 * stride));
  const __m128i src_h0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride));
  const __m128i src_i0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 8 * stride));
  const __m128i src_j0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 9 * stride));

  // Load filter tap values.
  // fc0 fc1 fc2 fc3 fc4 fc5 fc6 fc7
  const __m128i filt_coeff_0 = _mm_loadu_si128((__m128i const *)(filter));
  // fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc12
  __m128i temp = _mm_loadu_si128((__m128i const *)(filter + 5));
  // Replace the fc12 with derived singleton_tap.
  const __m128i center_tap = _mm_set1_epi16(singleton_tap);
  const __m128i filt_coeff_1 = _mm_blend_epi16(temp, center_tap, 0x80);

  // Form 256bit source registers.
  // a0 a1 a2 a3 a4 a5 a6 a7 | b0 b1 b2 b3 b4 b5 b6 b7
  const __m256i src_ab =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_a0), src_b0, 1);
  // c0 c1 c2 c3 c4 c5 c6 c7 | d0 d1 d2 d3 d4 d5 d6 d7
  const __m256i src_cd =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_c0), src_d0, 1);
  // e0 e1 e2 e3 e4 e5 e6 e7 | f0 f1 f2 f3 f4 f5 f6 f7
  const __m256i src_ef =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_e0), src_f0, 1);
  // g0 g1 g2 g3 g4 g5 g6 g7 | h0 h1 h2 h3 h4 h5 h6 h7
  const __m256i src_gh =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_g0), src_h0, 1);
  // i0 i1 i2 i3 i4 i5 i6 i7 | j0 j1 j2 j3 j4 j5 j6 j7
  const __m256i src_ij =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_i0), src_j0, 1);

  // Packing the source rows.
  // a0 g0 a1 g1 a2 g2 a3 g3 | b0 h0 b1 h1 b2 h2 b3 h3
  const __m256i ru0 = _mm256_unpacklo_epi16(src_ab, src_gh);
  // a4 g4 a5 g5 a6 g6 a7 g7 | b4 h4 b5 h5 b6 h6 b7 h7
  const __m256i ru1 = _mm256_unpackhi_epi16(src_ab, src_gh);
  // c0 e0 c1 e1 c2 e2 c3 e3 | d0 f0 d1 f1 d2 f2 d3 f3
  const __m256i ru2 = _mm256_unpacklo_epi16(src_cd, src_ef);
  // c4 e4 c5 e5 c6 e6 c7 e7 | d4 f4 d5 f5 d6 f6 d7 f7
  const __m256i ru3 = _mm256_unpackhi_epi16(src_cd, src_ef);
  // c0 i0 c1 i1 c2 i2 c3 i3 | d0 j0 d1 j1 d2 j2 d3 j3
  const __m256i ru4 = _mm256_unpacklo_epi16(src_cd, src_ij);
  // c4 i4 c5 i5 c6 i6 c7 i7 | d4 j4 d5 j5 d6 j6 d7 j7
  const __m256i ru5 = _mm256_unpackhi_epi16(src_cd, src_ij);
  // e0 g0 e1 g1 e2 g2 e3 g3 | f0 h0 f1 h1 f2 h2 f3 h3
  const __m256i ru6 = _mm256_unpacklo_epi16(src_ef, src_gh);
  // e4 g4 e5 g5 e6 g6 e7 g7 | f4 h4 f5 h5 f6 h6 f7 h7
  const __m256i ru7 = _mm256_unpackhi_epi16(src_ef, src_gh);

  // Output corresponding to filter coefficient 0.
  // a3 g3 a4 g4 a5 g5 a6 g6 | b3 h3 b4 h4 b5 h5 b6 h6
  const __m256i ru8 = _mm256_alignr_epi8(ru1, ru0, 12);
  // c3 i3 c4 i4 c5 i5 c6 i6 | d3 j3 d4 j4 d5 j5 d6 j6
  const __m256i ru9 = _mm256_alignr_epi8(ru5, ru4, 12);
  // f0 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0
  const __m256i fc0 = _mm256_broadcastw_epi16(filt_coeff_0);

  // r00 r01 r02 r03 | r10 r11 r12 r13
  __m256i accum_out_r0r1 = _mm256_madd_epi16(ru8, fc0);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  __m256i accum_out_r2r3 = _mm256_madd_epi16(ru9, fc0);

  // Output corresponding to filter coefficients 4,5,6,7.
  // c1 e1 c2 e2 c3 e3 c4 e4 | d1 f1 d2 f2 d3 f3 d4 f4
  const __m256i ru10 = _mm256_alignr_epi8(ru3, ru2, 4);
  // e1 g1 e2 g2 e3 g3 e4 g4 | f1 h1 f2 h2 f3 h3 f4 h4
  const __m256i ru11 = _mm256_alignr_epi8(ru7, ru6, 4);
  // c2 e2 c3 e3 c4 e4 c5 e5 | d2 f2 d3 f3 d4 f4 d5 f5
  const __m256i ru12 = _mm256_alignr_epi8(ru3, ru2, 8);
  // e2 g2 e3 g3 e4 g4 e5 g5 | f2 h2 f3 h3 f4 h4 f5 h5
  const __m256i ru13 = _mm256_alignr_epi8(ru7, ru6, 8);
  // c3 e3 c4 e4 c5 e5 c6 e6 | d3 f3 d4 f4 d5 f5 d6 f6
  const __m256i ru14 = _mm256_alignr_epi8(ru3, ru2, 12);
  // e3 g3 e4 g4 e5 g5 e6 g6 | f3 h3 f4 h4 f5 h5 f6 h6
  const __m256i ru15 = _mm256_alignr_epi8(ru7, ru6, 12);

  // 0 fc5 fc6 fc7 fc8 fc9 fc10 fc11
  temp = _mm_bslli_si128(filt_coeff_1, 2);
  // fc4 fc8 fc5 fc9 fc6 fc10 fc7 fc11
  temp = _mm_unpackhi_epi16(filt_coeff_0, temp);
  // fc4 fc8 fc4 fc8 fc4 fc8 fc4 fc8 | fc4 fc8 fc4 fc8 fc4 fc8 fc4 fc8
  const __m256i filt48 = _mm256_broadcastd_epi32(temp);
  // fc5 fc7 fc5 fc7 fc5 fc7 fc5 fc7 | fc5 fc7 fc5 fc7 fc5 fc7 fc5 fc7
  const __m256i filt57 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x08));
  // fc6 fc6 fc6 fc6 fc6 fc6 fc6 fc6 | fc6 fc6 fc6 fc6 fc6 fc6 fc6 fc6
  const __m256i filt66 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x55));
  // fc7 fc5 fc7 fc5 fc7 fc5 fc7 fc5 | fc7 fc5 fc7 fc5 fc7 fc5 fc7 fc5
  const __m256i filt75 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x22));

  const __m256i res_0 = _mm256_madd_epi16(ru10, filt48);
  const __m256i res_1 = _mm256_madd_epi16(ru11, filt48);
  const __m256i res_2 = _mm256_madd_epi16(ru12, filt57);
  const __m256i res_3 = _mm256_madd_epi16(ru13, filt57);
  const __m256i res_4 = _mm256_madd_epi16(ru14, filt66);
  const __m256i res_5 = _mm256_madd_epi16(ru15, filt66);
  const __m256i res_6 = _mm256_madd_epi16(ru3, filt75);
  const __m256i res_7 = _mm256_madd_epi16(ru7, filt75);

  // r00 r01 r02 r03 | r10 r11 r12 r13
  const __m256i out_0 = _mm256_add_epi32(res_0, res_2);
  const __m256i out_1 = _mm256_add_epi32(res_4, res_6);
  const __m256i out_2 = _mm256_add_epi32(out_0, out_1);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, out_2);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  const __m256i out_3 = _mm256_add_epi32(res_1, res_3);
  const __m256i out_4 = _mm256_add_epi32(res_5, res_7);
  const __m256i out_5 = _mm256_add_epi32(out_3, out_4);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, out_5);

  // Output corresponding to filter coefficients 9,10,11,12.
  // d2 d3 d4	d5 d6 d7 d8 d9
  const __m128i src_d2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 3 * stride + 2));
  // e2 e3 e4 e5 e6 e7 e8 e9
  const __m128i src_e2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride + 2));
  // f2 f3 f4 f5 f6 f7 f8 f9
  const __m128i src_f2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 5 * stride + 2));
  // g2 g3 g4	g5 g6 g7 g8 g9
  const __m128i src_g2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 6 * stride + 2));

  // d0 d1 d2 d3 d4 d5 d6 d7 | e0 e1 e2 e3 e4 e5 e6 e7
  const __m256i rm0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d0), src_e0, 1);
  // d2 d3 d4 d5 d6 d7 d8 d9 | e2 e3 e4 e5 e6 e7 e8 e9
  const __m256i rm2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d2), src_e2, 1);
  // f0 f1 f2 f3 f4 f5 f6 f7 | g0 g1 g2 g3 g4 g5 g6 g7
  const __m256i rm00 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f0), src_g0, 1);
  // f2 f3 f4 f5 f6 f7 f8 f9 | g2 g3 g4 g5 g6 g7 g8 g9
  const __m256i rm22 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f2), src_g2, 1);
  // d1 d2 d3 d4 d5 d6 d7 d8 | e1 e2 e3 e4 e5 e6 e7 e8
  const __m256i rm1 = _mm256_alignr_epi8(_mm256_bsrli_epi128(rm2, 12), rm0, 2);
  const __m256i rm11 =
      _mm256_alignr_epi8(_mm256_bsrli_epi128(rm22, 12), rm00, 2);
  // d3 d4 d5 d6 d7 d8 d9 0 | e3 e4 e5 e6 e7 e8 e9 0
  const __m256i rm3 = _mm256_bsrli_epi128(rm2, 2);
  const __m256i rm33 = _mm256_bsrli_epi128(rm22, 2);
  // d0 d1 d1 d2 d2 d3 d3 d4 | e0 e1 e1 e2 e2 e3 e3 e4
  __m256i rm4 = _mm256_unpacklo_epi16(rm0, rm1);
  // d2 d3 d3 d4 d4 d5 d5 d6 | e2 e3 e3 e4 e4 e5 e5 e6
  __m256i rm5 = _mm256_unpacklo_epi16(rm2, rm3);
  // d4 d5 d5 d6 d6 d7 d7 d8 | e4 e5 e5 e6 e6 e7 e7 e8
  __m256i rm6 = _mm256_unpackhi_epi16(rm0, rm1);
  // d6 0 d7 0 d8 0 d9 0 | e6 0 e7 0 e8 0 e9 0
  __m256i rm7 = _mm256_unpackhi_epi16(rm2, _mm256_set1_epi16(0));

  __m256i rm44 = _mm256_unpacklo_epi16(rm00, rm11);
  __m256i rm55 = _mm256_unpacklo_epi16(rm22, rm33);
  __m256i rm66 = _mm256_unpackhi_epi16(rm00, rm11);
  __m256i rm77 = _mm256_unpackhi_epi16(rm22, _mm256_set1_epi16(0));
  // fc9 fc10 - - - - - - | fc9 fc10 - - - - - -
  const __m256i fc910 =
      _mm256_broadcastd_epi32(_mm_bsrli_si128(filt_coeff_1, 8));
  // fc11 fc12 - - - - - - | fc11 fc12 - - - - - -
  const __m256i fc1112 =
      _mm256_broadcastd_epi32(_mm_bsrli_si128(filt_coeff_1, 12));
  // fc11 fc10  - - - - - - | fc11 fc10 - - - - - -
  const __m256i fc1110 = _mm256_broadcastd_epi32(
      _mm_bsrli_si128(_mm_shufflehi_epi16(filt_coeff_1, 0x06), 8));

  rm4 = _mm256_madd_epi16(rm4, fc910);
  rm5 = _mm256_madd_epi16(rm5, fc1112);
  rm6 = _mm256_madd_epi16(rm6, fc1110);
  rm7 = _mm256_madd_epi16(rm7, fc910);
  rm44 = _mm256_madd_epi16(rm44, fc910);
  rm55 = _mm256_madd_epi16(rm55, fc1112);
  rm66 = _mm256_madd_epi16(rm66, fc1110);
  rm77 = _mm256_madd_epi16(rm77, fc910);

  // r00 r01 r02 r03 | r10 r11 r12 r13
  rm4 = _mm256_add_epi32(rm4, rm5);
  rm6 = _mm256_add_epi32(rm6, rm7);
  rm4 = _mm256_add_epi32(rm4, rm6);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, rm4);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  rm44 = _mm256_add_epi32(rm44, rm55);
  rm66 = _mm256_add_epi32(rm66, rm77);
  rm44 = _mm256_add_epi32(rm44, rm66);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, rm44);

  // Output corresponding to filter coefficients 1,2,3,8.
  const __m128i src_b2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 1 * stride + 2));
  const __m128i src_c2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 2 * stride + 2));
  const __m256i rn0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_b2), src_c2, 1);
  // b2 b3 b3 b4 b4 b5 b5 b6 | c2 c3 c3 c4 c4 c5 c5 c6
  __m256i r0 = _mm256_unpacklo_epi16(rn0, _mm256_bsrli_epi128(rn0, 2));

  const __m256i rcd2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_c2), src_d2, 1);
  // b4 c5 b5 c6 b6 c7 b7 c8 | c4 d5 c5 d6 c6 d7 c7 d8
  __m256i r1 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rn0, 4),
                                     _mm256_bsrli_epi128(rcd2, 6));

  const __m256i rfg2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f2), src_g2, 1);
  // f2 f3 f3 f4 f4 f5 f5 f6 | g2 g3 g3 g4 g4 g5 g5 g6
  __m256i r2 = _mm256_unpacklo_epi16(rfg2, _mm256_bsrli_epi128(rfg2, 2));

  const __m256i ref2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_e2), src_f2, 1);
  // f4 e5 f5 e6 f6 e7 f7 e8 | g4 f5 g5 f6 g6 f7 g7 f8
  __m256i r3 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rfg2, 4),
                                     _mm256_bsrli_epi128(ref2, 6));

  const __m128i tempn =
      _mm_blend_epi16(_mm_bsrli_si128(filt_coeff_0, 2), filt_coeff_1, 0x08);
  __m128i tempn2 = _mm_bsrli_si128(filt_coeff_0, 2);
  tempn2 = _mm_shufflelo_epi16(tempn2, 0x0c);
  // fc1 fc2 - - - - - - | fc1 fc2 - - - - - -
  const __m256i fc12 = _mm256_broadcastd_epi32(tempn);
  // fc1 fc4 - - - - - - | fc1 fc4 - - - - - -
  const __m256i fc14 = _mm256_broadcastd_epi32(tempn2);
  tempn2 = _mm_shufflelo_epi16(tempn, 0x06);
  // fc3 fc8 - - - - - - | fc3 fc8 - - - - - -
  const __m256i fc38 = _mm256_broadcastd_epi32(_mm_bsrli_si128(tempn, 4));
  // fc3 fc2 - - - - - - | fc3 fc2 - - - - - -
  const __m256i fc32 = _mm256_broadcastd_epi32(tempn2);

  r0 = _mm256_madd_epi16(r0, fc12);
  r1 = _mm256_madd_epi16(r1, fc38);
  r2 = _mm256_madd_epi16(r2, fc32);
  r3 = _mm256_madd_epi16(r3, fc14);

  // r00 r01 r02 r03 | r10 r11 r12 r13
  r0 = _mm256_add_epi32(r0, r1);
  r2 = _mm256_add_epi32(r2, r3);
  r0 = _mm256_add_epi32(r0, r2);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, r0);

  const __m256i rn1 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d2), src_e2, 1);
  // d2 d3 d3 d4 d4 d5 d5 d6 | e2 e3 e3 e4 e4 e5 e5 e6
  __m256i r00 = _mm256_unpacklo_epi16(rn1, _mm256_bsrli_epi128(rn1, 2));
  // d4 e5 d5 e6 d6 e7 d7 e8 | e4 f5 e5 f6 e6 f7 e7 f8
  __m256i r11 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rn1, 4),
                                      _mm256_bsrli_epi128(ref2, 6));

  const __m128i src_h2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride + 2));
  const __m128i src_i2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 8 * stride + 2));
  // h2 h3 h4 h5 h6 h7 h8 h9 | i2 i3 i4 i5 i6 i7 i8 i9
  __m256i rhi2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_h2), src_i2, 1);
  // h2 h3 h3 h4 h4 h5 h5 h6 | i2 i3 i3 i4 i4 i5 i5 i6
  __m256i r22 = _mm256_unpacklo_epi16(rhi2, _mm256_bsrli_epi128(rhi2, 2));
  // g2 g3 g4 g5 g6 g7 g8 g9 | h2 h3 h4 h5 h6 h7 h8 h9
  __m256i rgh2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_g2), src_h2, 1);
  __m256i r33 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rhi2, 4),
                                      _mm256_bsrli_epi128(rgh2, 6));
  r00 = _mm256_madd_epi16(r00, fc12);
  r11 = _mm256_madd_epi16(r11, fc38);
  r22 = _mm256_madd_epi16(r22, fc32);
  r33 = _mm256_madd_epi16(r33, fc14);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  r00 = _mm256_add_epi32(r00, r11);
  r22 = _mm256_add_epi32(r22, r33);
  r00 = _mm256_add_epi32(r00, r22);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, r00);

  // Rounding and clipping.
  accum_out_r0r1 =
      round_power_of_two_signed_avx2(accum_out_r0r1, filter_config->prec_bits);
  accum_out_r2r3 =
      round_power_of_two_signed_avx2(accum_out_r2r3, filter_config->prec_bits);
  // r00 r01 r02 r03 | r20 r21 r22 r23 | r10 r11 r12 r13 | r30 r31 r32 r33
  __m256i out_r0r2r1r3 = _mm256_packs_epi32(accum_out_r0r1, accum_out_r2r3);
  const __m256i max = _mm256_set1_epi16((1 << bit_depth) - 1);
  out_r0r2r1r3 = highbd_clamp_epi16(out_r0r2r1r3, _mm256_setzero_si256(), max);

  // Store the output.
  const int dst_id = block_row_begin * dst_stride + block_col_begin;
  const __m128i out_r1r3 = _mm256_extractf128_si256(out_r0r2r1r3, 1);
  _mm_storel_epi64((__m128i *)(dst + dst_id),
                   _mm256_castsi256_si128(out_r0r2r1r3));
  _mm_storel_epi64((__m128i *)(dst + dst_id + (1 * dst_stride)), out_r1r3);

  _mm_storel_epi64((__m128i *)(dst + dst_id + (2 * dst_stride)),
                   _mm_bsrli_si128(_mm256_castsi256_si128(out_r0r2r1r3), 8));
  _mm_storel_epi64((__m128i *)(dst + dst_id + (3 * dst_stride)),
                   _mm_bsrli_si128(out_r1r3, 8));
}

// AVX2 intrinsic function to convolve an 8x8 block with a origin-symmetric
// non-separable filter. The output for a particular pixel in an 8x8 block is
// calculated with DIAMOND shaped filter considering a 11x11 grid surrounded by
// that pixel. DIAMOND shape uses 13-tap filter for convolution. The filter taps
// are expected to be passed as symmetric taps followed by a center tap.
static AVM_INLINE void convolve_symmetric_highbd_13tap_blk8x8_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter_, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  /*
  Note the original SIMD code here was designed for the configuration below,
  but is being changed to the one in config_13tap_avx2 above to align
  pc_wiener and wienerns configurations. However a permute of the coefficients
  is now necessary before the rest of the code can be used.
  { -3, 0, 0 },  { 3, 0, 0 },  { -2, -1, 1 }, { 2, 1, 1 },   { -2, 0, 2 },
  { 2, 0, 2 },   { -2, 1, 3 }, { 2, -1, 3 },  { -1, -2, 4 }, { 1, 2, 4 },
  { -1, -1, 5 }, { 1, 1, 5 },  { -1, 0, 6 },  { 1, 0, 6 },   { -1, 1, 7 },
  { 1, -1, 7 },  { -1, 2, 8 }, { 1, -2, 8 },  { 0, -3, 9 },  { 0, 3, 9 },
  { 0, -2, 10 }, { 0, 2, 10 }, { 0, -1, 11 }, { 0, 1, 11 },  { 0, 0, 12 },
  */
  // Begin permute
  int16_t filter[WIENERNS_TAPS_MAX];
  filter[6] = filter_[0];
  filter[11] = filter_[1];
  filter[2] = filter_[2];
  filter[10] = filter_[3];
  filter[5] = filter_[4];
  filter[7] = filter_[5];
  filter[1] = filter_[6];
  filter[3] = filter_[7];
  filter[4] = filter_[8];
  filter[8] = filter_[9];
  filter[0] = filter_[10];
  filter[9] = filter_[11];
  filter[12] = filter_[12];
  // End permute
  const __m256i zero_256 = _mm256_setzero_si256();
  // Derive singleton_tap.
  // TODO(rachelbarker): Set up the singleton tap fully in
  // adjust_filter_and_config, so that we don't have to modify it here
  int32_t singleton_tap = 1 << filter_config->prec_bits;
  if (filter_config->num_pixels % 2) {
    const int singleton_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    singleton_tap += filter[singleton_tap_index];
  }

  // Load source data.
  int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 3 * stride - 3;
  const __m256i src_a0 = _mm256_loadu_si256((__m256i const *)(src_ptr));
  const __m256i src_b0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + stride));
  const __m256i src_c0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 2 * stride));
  const __m256i src_d0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 3 * stride));
  const __m256i src_e0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 4 * stride));
  const __m256i src_f0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 5 * stride));
  const __m256i src_g0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 6 * stride));
  const __m256i src_h0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 7 * stride));
  const __m256i src_i0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 8 * stride));
  const __m256i src_j0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 9 * stride));
  const __m256i src_k0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 10 * stride));
  const __m256i src_l0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 11 * stride));
  const __m256i src_m0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 12 * stride));
  const __m256i src_n0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 13 * stride));

  // Load filter tap values.
  // fc0 fc1 fc2 fc3 fc4 fc5 fc6 fc7
  const __m128i filt_coeff_0 = _mm_loadu_si128((__m128i const *)(filter));
  // fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc12
  __m128i temp = _mm_loadu_si128((__m128i const *)(filter + 5));
  // Replace the fc12 with derived singleton_tap.
  const __m128i center_tap = _mm_set1_epi16(singleton_tap);
  const __m128i filt_coeff_1 = _mm_blend_epi16(temp, center_tap, 0x80);

  // Form 256bit source registers.
  // c0 c1 c2 c3 c4 c5 c6 c7 | d0 d1 d2 d3 d4 d5 d6 d7
  const __m256i cd0 = _mm256_permute2x128_si256(src_c0, src_d0, 0x20);
  // c8 c9 c10 c11 c12 c13 c14 c15 | d8 d9 d10 d11 d12 d13 d14 d15
  const __m256i cd8 = _mm256_permute2x128_si256(src_c0, src_d0, 0x31);

  // e0 e1 e2 e3 e4 e5 e6 e7 | f0 f1 f2 f3 f4 f5 f6 f7
  const __m256i ef0 = _mm256_permute2x128_si256(src_e0, src_f0, 0x20);
  // e8 e9 e10 e11 e12 e13 e14 e15 | f8 f9 f10 f11 f12 f13 f14 f15
  const __m256i ef8 = _mm256_permute2x128_si256(src_e0, src_f0, 0x31);

  // g0 g1 g2 g3 g4 g5 g6 g7 | h0 h1 h2 h3 h4 h5 h6 h7
  const __m256i gh0 = _mm256_permute2x128_si256(src_g0, src_h0, 0x20);
  // g8 g9 g10 g11 g12 g13 g14 g15 | h8 h9 h10 h11 h12 h13 h14 h15
  const __m256i gh8 = _mm256_permute2x128_si256(src_g0, src_h0, 0x31);

  // i0 i1 i2 i3 i4 i5 i6 i7 | j0 j1 j2 j3 j4 j5 j6 j7
  const __m256i ij0 = _mm256_permute2x128_si256(src_i0, src_j0, 0x20);
  // i8 i9 i10 i11 i12 i13 i14 i15 | j8 j9 j10 j11 j12 j13 j14 j15
  const __m256i ij8 = _mm256_permute2x128_si256(src_i0, src_j0, 0x31);

  // k0 k1 k2 k3 k4 k5 k6 k7 | l0 l1 l2 l3 l4 l5 l6 l7
  const __m256i kl0 = _mm256_permute2x128_si256(src_k0, src_l0, 0x20);
  // k8 k9 k10 k11 k12 k13 k14 k15 | l8 l9 l10 l11 l12 l13 l14 l15
  const __m256i kl8 = _mm256_permute2x128_si256(src_k0, src_l0, 0x31);

  // m0 m1 m2 m3 m4 m5 m6 m7 | n0 n1 n2 n3 n4 n5 n6 n7
  const __m256i mn0 = _mm256_permute2x128_si256(src_m0, src_n0, 0x20);
  // m8 m9 m10 m11 m12 m13 m14 m15 | n8 n9 n10 n11 n12 n13 n14 n15
  const __m256i mn8 = _mm256_permute2x128_si256(src_m0, src_n0, 0x31);

  // c0 e0 c1 e1 c2 e2 c3 e3 | d0 f0 d1 f1 d2 f2 d3 f3
  const __m256i c0e0 = _mm256_unpacklo_epi16(cd0, ef0);
  // c4 e4 c5 e5 c6 e6 c7 e7 | d4 f4 d5 f5 d6 f6 d7 f7
  const __m256i c4e4 = _mm256_unpackhi_epi16(cd0, ef0);
  // c8 e8 c9 e9 c10 e10 c11 e11 | d8 f8 d9 f9 d10 f10 d11 f11
  const __m256i c8e8 = _mm256_unpacklo_epi16(cd8, ef8);
  // c12 e12 c13 e13 c14 e14 c15 e15 | d12 f12 d13 f13 d14 f14 d15 f15
  const __m256i c12e12 = _mm256_unpackhi_epi16(cd8, ef8);

  // e0 g0 e1 g1 e2 g2 e3 g3 | f0 h0 f1 h1 f2 h2 f3 h3
  const __m256i e0g0 = _mm256_unpacklo_epi16(ef0, gh0);
  // e4 g4 e5 g5 e6 g6 e7 g7 | f4 h4 f5 h5 f6 h6 f7 h7
  const __m256i e4g4 = _mm256_unpackhi_epi16(ef0, gh0);
  // e8 g8 e9 g9 e10 g10 e11 g11 | f8 h8 f9 h9 f10 h10 f11 h11
  const __m256i e8g8 = _mm256_unpacklo_epi16(ef8, gh8);
  // e12 g12 e13 g13 e14 g14 e15 g15 | f12 h12 f13 h13 f14 h14 f15 h15
  const __m256i e12g12 = _mm256_unpackhi_epi16(ef8, gh8);

  // g0 i0 g1 i1 g2 i2 g3 i3 | h0 j0 h1 j1 h2 j2 h3 j3
  const __m256i g0i0 = _mm256_unpacklo_epi16(gh0, ij0);
  // g4 i4 g5 i5 g6 i6 g7 i7 | h4 j4 h5 j5 h6 j6 h7 j7
  const __m256i g4i4 = _mm256_unpackhi_epi16(gh0, ij0);
  // g8 i8 g9 i9 g10 i10 g11 i11 | h8 j8 h9 j9 h10 j10 h11 j11
  const __m256i g8i8 = _mm256_unpacklo_epi16(gh8, ij8);
  // g12 i12 g13 i13 g14 i14 g15 i15 | h12 j12 h13 j13 h14 j14 h15 j15
  const __m256i g12i12 = _mm256_unpackhi_epi16(gh8, ij8);

  // i0 k0 i1 k1 i2 k2 i3 k3 | j0 l0 j1 l1 j2 l2 j3 l3
  const __m256i i0k0 = _mm256_unpacklo_epi16(ij0, kl0);
  // i4 k4 i5 k5 i6 k6 i7 k7 | j4 l4 j5 l5 j6 l6 j7 l7
  const __m256i i4k4 = _mm256_unpackhi_epi16(ij0, kl0);
  // i8 k8 i9 k9 i10 k10 i11 k11 | j8 l8 j9 l9 j10 l10 j11 l11
  const __m256i i8k8 = _mm256_unpacklo_epi16(ij8, kl8);
  // i12 k12 i13 k13 i14 k14 i15 k15 | j12 l12 j13 l13 j14 l14 j15 l15
  const __m256i i12k12 = _mm256_unpackhi_epi16(ij8, kl8);

  // Pixel values for filter coeff 4 and 8
  // c1 e1 c2 e2 c3 e3 c4 e4 | d1 f1 d2 f2 d3 f3 d4 f4
  const __m256i c1e1_c4e4 = _mm256_alignr_epi8(c4e4, c0e0, 4);
  // e1 g1 e2 g2 e3 g3 e4 g4 | f1 h1 f2 h2 f3 h3 f4 h4
  const __m256i e1g1_e4g4 = _mm256_alignr_epi8(e4g4, e0g0, 4);
  // g1 i1 g2 i2 g3 i3 g4 i4 | h1 j1 h2 j2 h3 j3 h4 j4
  const __m256i g1i1_g4i4 = _mm256_alignr_epi8(g4i4, g0i0, 4);
  // i1 k1 i2 k2 i3 k3 i4 k4 | j1 l1 j2 l2 j3 l3 j4 l4
  const __m256i i1k1_i4k4 = _mm256_alignr_epi8(i4k4, i0k0, 4);

  // Pixel values for filter coeff 5 and 7
  // c2 e2 c3 e3 c4 e4 c5 e5 | d2 f2 d3 f3 d4 f4 d5 f5
  const __m256i c2e2_c5e5 = _mm256_alignr_epi8(c4e4, c0e0, 8);
  // c6 e6 c7 e7 c8 e8 c9 e9 | d6 f6 d7 f7 d8 f8 d9 f9
  const __m256i c6e6_c9e9 = _mm256_alignr_epi8(c8e8, c4e4, 8);
  // e2 g2 e3 g3 e4 g4 e5 g5 | f2 h2 f3 h3 f4 h4 f5 h5
  const __m256i e2g2_e5g5 = _mm256_alignr_epi8(e4g4, e0g0, 8);
  // e6 g6 e7 g7 e8 g8 e9 g9 | f6 h6 f7 h7 f8 h8 f9 h9
  const __m256i e6g6_e9g9 = _mm256_alignr_epi8(e8g8, e4g4, 8);
  // g2 i2 g3 i3 g4 i4 g5 i5 | h2 j2 h3 j3 h4 j4 h5 j5
  const __m256i g2i2_g5i5 = _mm256_alignr_epi8(g4i4, g0i0, 8);
  // g6 i6 g7 i7 g8 i8 g9 i9 | h6 j6 h7 j7 h8 j8 h9 j9
  const __m256i g6i6_g9i9 = _mm256_alignr_epi8(g8i8, g4i4, 8);
  // i2 k2 i3 k3 i4 k4 i5 k5 | j2 l2 j3 l3 j4 l4 j5 l5
  const __m256i i2k2_i5k5 = _mm256_alignr_epi8(i4k4, i0k0, 8);
  // i6 k6 i7 k7 i8 k8 i9 k9 | j6 l6 j7 l7 j8 l8 j9 l9
  const __m256i i6k6_i9k9 = _mm256_alignr_epi8(i8k8, i4k4, 8);

  // Pixel values for filter coeff 6
  // c3 e3 c4 e4 c5 e5 c6 e6 | d3 f3 d4 f4 d5 f5 d6 f6
  const __m256i c3e3_c6e6 = _mm256_alignr_epi8(c4e4, c0e0, 12);
  // c7 e7 c8 e8 c9 e9 c10 e10 | d7 f7 d8 f8 d9 f9 d10 f10
  const __m256i c7e7_c10e10 = _mm256_alignr_epi8(c8e8, c4e4, 12);
  // e3 g3 e4 g4 e5 g5 e6 g6 | f3 h3 f4 h4 f5 h5 f6 h6
  const __m256i e3g3_e6g6 = _mm256_alignr_epi8(e4g4, e0g0, 12);
  // e7 g7 e8 g8 e9 g9 e10 g10 | f7 h7 f8 h8 f9 h9 f10 h10
  const __m256i e7g7_e10g10 = _mm256_alignr_epi8(e8g8, e4g4, 12);
  // g3 i3 g4 i4 g5 i5 g6 i6 | h3 j3 h4 j4 h5 j5 h6 j6
  const __m256i g3i3_g6i6 = _mm256_alignr_epi8(g4i4, g0i0, 12);
  // g7 i7 g8 i8 g9 i9 g10 i10 | h7 j7 h8 j8 h9 j9 h10 j10
  const __m256i g7i7_g10i10 = _mm256_alignr_epi8(g8i8, g4i4, 12);
  // i3 k3 i4 k4 i5 k5 i6 k6 | j3 l3 j4 l4 j5 l5 j6 l6
  const __m256i i3k3_i6k6 = _mm256_alignr_epi8(i4k4, i0k0, 12);
  // i7 k7 i8 k8 i9 k9 i10 k10 | j7 l7 j8 l8 j9 l9 j10 l10
  const __m256i i7k7_i10k10 = _mm256_alignr_epi8(i8k8, i4k4, 12);

  // Pixel values for filter coeff 4 and 8
  // c5 e5 c6 e6 c7 e7 c8 e8 | d5 f5 d6 f6 d7 f7 d8 f8
  const __m256i c5e5_c8e8 = _mm256_alignr_epi8(c8e8, c4e4, 4);
  // c9 e9 c10 e10 c11 e11 c12 e12 | d9 f9 d10 f10 d11 f11 d12 f12
  const __m256i c9e9_c12e12 = _mm256_alignr_epi8(c12e12, c8e8, 4);
  // e5 g5 e6 g6 e7 g7 e8 g8 | f5 h5 f6 h6 f7 h7 f8 h8
  const __m256i e5g5_e8g8 = _mm256_alignr_epi8(e8g8, e4g4, 4);
  // e9 g9 e10 g10 e11 g11 e12 g12 | f9 h9 f10 h10 f11 h11 f12 h12
  const __m256i e9g9_e12g12 = _mm256_alignr_epi8(e12g12, e8g8, 4);
  // g5 i5 g6 i6 g7 i7 g8 i8 | h5 j5 h6 j6 h7 j7 h8 j8
  const __m256i g5i5_g8i8 = _mm256_alignr_epi8(g8i8, g4i4, 4);
  // g9 i9 g10 i10 g11 i11 g12 i12 | h9 j9 h10 j10 h11 j11 h12 j12
  const __m256i g9i9_g12i12 = _mm256_alignr_epi8(g12i12, g8i8, 4);
  // i5 k5 i6 k6 i7 k7 i8 k8 | j5 l5 j6 l6 j7 l7 j8 l8
  const __m256i i5k5_i8k8 = _mm256_alignr_epi8(i8k8, i4k4, 4);
  // i9 k9 i10 k10 i11 k11 i12 k12 | j9 l9 j10 l10 j11 l11 j12 l12
  const __m256i i9k9_i12k12 = _mm256_alignr_epi8(i12k12, i8k8, 4);

  // 0 fc5 fc6 fc7 fc8 fc9 fc10 fc11
  __m128i temp1 = _mm_bslli_si128(filt_coeff_1, 2);
  // fc4 fc8 fc5 fc9 fc6 fc10 fc7 fc11
  temp = _mm_unpackhi_epi16(filt_coeff_0, temp1);
  // fc4 fc8 fc4 fc8 fc4 fc8 fc4 fc8 | fc4 fc8 fc4 fc8 fc4 fc8 fc4 fc8
  const __m256i filt48 = _mm256_broadcastd_epi32(temp);
  // fc5 fc7 fc5 fc7 fc5 fc7 fc5 fc7 | fc5 fc7 fc5 fc7 fc5 fc7 fc5 fc7
  const __m256i filt57 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x08));
  // fc6 fc6 fc6 fc6 fc6 fc6 fc6 fc6 | fc6 fc6 fc6 fc6 fc6 fc6 fc6 fc6
  const __m256i filt66 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x55));
  // fc7 fc5 fc7 fc5 fc7 fc5 fc7 fc5 | fc7 fc5 fc7 fc5 fc7 fc5 fc7 fc5
  const __m256i filt75 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x22));
  // fc8 fc4 fc9 fc5 fc10 fc6 fc11 fc7
  __m128i temp2 = _mm_unpackhi_epi16(temp1, filt_coeff_0);
  // fc8 fc4 fc8 fc4 fc8 fc4 fc8 fc4 | fc8 fc4 fc8 fc4 fc8 fc4 fc8 fc4
  const __m256i filt84 = _mm256_broadcastd_epi32(temp2);

  __m256i accum_out_r0_r1_1 = zero_256;
  __m256i accum_out_r0_r1_2 = zero_256;
  __m256i accum_out_r2_r3_1 = zero_256;
  __m256i accum_out_r2_r3_2 = zero_256;
  __m256i accum_out_r4_r5_1 = zero_256;
  __m256i accum_out_r4_r5_2 = zero_256;
  __m256i accum_out_r6_r7_1 = zero_256;
  __m256i accum_out_r6_r7_2 = zero_256;

  if (filter[4] != 0 || filter[8] != 0) {
    __m256i res_0 = _mm256_madd_epi16(c1e1_c4e4, filt48);
    __m256i res_1 = _mm256_madd_epi16(c5e5_c8e8, filt48);
    __m256i res_2 = _mm256_madd_epi16(e1g1_e4g4, filt48);
    __m256i res_3 = _mm256_madd_epi16(e5g5_e8g8, filt48);
    __m256i res_4 = _mm256_madd_epi16(g1i1_g4i4, filt48);
    __m256i res_5 = _mm256_madd_epi16(g5i5_g8i8, filt48);
    __m256i res_6 = _mm256_madd_epi16(i1k1_i4k4, filt48);
    __m256i res_7 = _mm256_madd_epi16(i5k5_i8k8, filt48);

    __m256i res_32 = _mm256_madd_epi16(c5e5_c8e8, filt84);
    __m256i res_33 = _mm256_madd_epi16(c9e9_c12e12, filt84);
    __m256i res_34 = _mm256_madd_epi16(e5g5_e8g8, filt84);
    __m256i res_35 = _mm256_madd_epi16(e9g9_e12g12, filt84);
    __m256i res_36 = _mm256_madd_epi16(g5i5_g8i8, filt84);
    __m256i res_37 = _mm256_madd_epi16(g9i9_g12i12, filt84);
    __m256i res_38 = _mm256_madd_epi16(i5k5_i8k8, filt84);
    __m256i res_39 = _mm256_madd_epi16(i9k9_i12k12, filt84);
    accum_out_r0_r1_1 = _mm256_add_epi32(res_0, res_32);
    accum_out_r0_r1_2 = _mm256_add_epi32(res_1, res_33);
    accum_out_r2_r3_1 = _mm256_add_epi32(res_2, res_34);
    accum_out_r2_r3_2 = _mm256_add_epi32(res_3, res_35);
    accum_out_r4_r5_1 = _mm256_add_epi32(res_4, res_36);
    accum_out_r4_r5_2 = _mm256_add_epi32(res_5, res_37);
    accum_out_r6_r7_1 = _mm256_add_epi32(res_6, res_38);
    accum_out_r6_r7_2 = _mm256_add_epi32(res_7, res_39);
  }

  if (filter[5] != 0 || filter[7] != 0) {
    __m256i res_8 = _mm256_madd_epi16(c2e2_c5e5, filt57);
    __m256i res_9 = _mm256_madd_epi16(c6e6_c9e9, filt57);
    __m256i res_10 = _mm256_madd_epi16(e2g2_e5g5, filt57);
    __m256i res_11 = _mm256_madd_epi16(e6g6_e9g9, filt57);
    __m256i res_12 = _mm256_madd_epi16(g2i2_g5i5, filt57);
    __m256i res_13 = _mm256_madd_epi16(g6i6_g9i9, filt57);
    __m256i res_14 = _mm256_madd_epi16(i2k2_i5k5, filt57);
    __m256i res_15 = _mm256_madd_epi16(i6k6_i9k9, filt57);

    __m256i res_24 = _mm256_madd_epi16(c4e4, filt75);
    __m256i res_25 = _mm256_madd_epi16(c8e8, filt75);
    __m256i res_26 = _mm256_madd_epi16(e4g4, filt75);
    __m256i res_27 = _mm256_madd_epi16(e8g8, filt75);
    __m256i res_28 = _mm256_madd_epi16(g4i4, filt75);
    __m256i res_29 = _mm256_madd_epi16(g8i8, filt75);
    __m256i res_30 = _mm256_madd_epi16(i4k4, filt75);
    __m256i res_31 = _mm256_madd_epi16(i8k8, filt75);
    accum_out_r0_r1_1 =
        _mm256_add_epi32(accum_out_r0_r1_1, _mm256_add_epi32(res_8, res_24));
    accum_out_r0_r1_2 =
        _mm256_add_epi32(accum_out_r0_r1_2, _mm256_add_epi32(res_9, res_25));
    accum_out_r2_r3_1 =
        _mm256_add_epi32(accum_out_r2_r3_1, _mm256_add_epi32(res_10, res_26));
    accum_out_r2_r3_2 =
        _mm256_add_epi32(accum_out_r2_r3_2, _mm256_add_epi32(res_11, res_27));
    accum_out_r4_r5_1 =
        _mm256_add_epi32(accum_out_r4_r5_1, _mm256_add_epi32(res_12, res_28));
    accum_out_r4_r5_2 =
        _mm256_add_epi32(accum_out_r4_r5_2, _mm256_add_epi32(res_13, res_29));
    accum_out_r6_r7_1 =
        _mm256_add_epi32(accum_out_r6_r7_1, _mm256_add_epi32(res_14, res_30));
    accum_out_r6_r7_2 =
        _mm256_add_epi32(accum_out_r6_r7_2, _mm256_add_epi32(res_15, res_31));
  }

  if (filter[6] != 0) {
    __m256i res_16 = _mm256_madd_epi16(c3e3_c6e6, filt66);
    __m256i res_17 = _mm256_madd_epi16(c7e7_c10e10, filt66);
    __m256i res_18 = _mm256_madd_epi16(e3g3_e6g6, filt66);
    __m256i res_19 = _mm256_madd_epi16(e7g7_e10g10, filt66);
    __m256i res_20 = _mm256_madd_epi16(g3i3_g6i6, filt66);
    __m256i res_21 = _mm256_madd_epi16(g7i7_g10i10, filt66);
    __m256i res_22 = _mm256_madd_epi16(i3k3_i6k6, filt66);
    __m256i res_23 = _mm256_madd_epi16(i7k7_i10k10, filt66);
    accum_out_r0_r1_1 = _mm256_add_epi32(accum_out_r0_r1_1, res_16);
    accum_out_r0_r1_2 = _mm256_add_epi32(accum_out_r0_r1_2, res_17);
    accum_out_r2_r3_1 = _mm256_add_epi32(accum_out_r2_r3_1, res_18);
    accum_out_r2_r3_2 = _mm256_add_epi32(accum_out_r2_r3_2, res_19);
    accum_out_r4_r5_1 = _mm256_add_epi32(accum_out_r4_r5_1, res_20);
    accum_out_r4_r5_2 = _mm256_add_epi32(accum_out_r4_r5_2, res_21);
    accum_out_r6_r7_1 = _mm256_add_epi32(accum_out_r6_r7_1, res_22);
    accum_out_r6_r7_2 = _mm256_add_epi32(accum_out_r6_r7_2, res_23);
  }

  // fc2 fc2 fc2 fc2 fc2 fc2 fc2 fc2 | fc2 fc2 fc2 fc2 fc2 fc2 fc2 fc2
  const __m256i filt22 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_0, 0xAA));
  // fc1 fc3 fc1 fc3 fc1 fc3 fc1 fc3 | fc1 fc3 fc1 fc3 fc1 fc3 fc1 fc3
  const __m256i filt13 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_0, 0x5D));
  // fc3 fc1 fc3 fc1 fc3 fc1 fc3 fc1 | fc3 fc1 fc3 fc1 fc3 fc1 fc3 fc1
  const __m256i filt31 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_0, 0xF7));

  // b0 b1 b2 b3 b4 b5 b6 b7 | c0 c1 c2 c3 c4 c5 c6 c7
  const __m256i bc0 = _mm256_permute2x128_si256(src_b0, src_c0, 0x20);
  // b8 b9 b10 b11 b12 b13 b14 b15 | c8 c9 c10 c11 c12 c13 c14 c15
  const __m256i bc8 = _mm256_permute2x128_si256(src_b0, src_c0, 0x31);
  // d0 d1 d2 d3 d4 d5 d6 d7 | e0 e1 e2 e3 e4 e5 e6 e7
  const __m256i de0 = _mm256_permute2x128_si256(src_d0, src_e0, 0x20);
  // d8 d9 d10 d11 d12 d13 d14 d15 | e0 e1 e2 e3 e4 e5 e6 e7
  const __m256i de8 = _mm256_permute2x128_si256(src_d0, src_e0, 0x31);
  // f0 f1 f2 f3 f4 f5 f6 f7 | g0 g1 g2 g3 g4 g5 g6 g7
  const __m256i fg0 = _mm256_permute2x128_si256(src_f0, src_g0, 0x20);
  // f8 f9 f10 f11 f12 f13 f14 f15 | g8 g9 g10 g11 g12 g13 g14 g15
  const __m256i fg8 = _mm256_permute2x128_si256(src_f0, src_g0, 0x31);
  // h0 h1 h2 h3 h4 h5 h6 h7 | i0 i1 i2 i3 i4 i5 i6 i7
  const __m256i hi0 = _mm256_permute2x128_si256(src_h0, src_i0, 0x20);
  // h8 h9 h10 h11 h12 h13 h14 h15 | i0 i1 i2 i3 i4 i5 i6 i7
  const __m256i hi8 = _mm256_permute2x128_si256(src_h0, src_i0, 0x31);
  // j0 j1 j2 j3 j4 j5 j6 j7 | k0 k1 k2 k3 k4 k5 k6 k7
  const __m256i jk0 = _mm256_permute2x128_si256(src_j0, src_k0, 0x20);
  // j8 j9 j10 j11 j12 j13 j14 j15 | k0 k1 k2 k3 k4 k5 k6 k7
  const __m256i jk8 = _mm256_permute2x128_si256(src_j0, src_k0, 0x31);
  // l0 l1 l2 l3 l4 l5 l6 l7 | m0 m1 m2 m3 m4 m5 m6 m7
  const __m256i lm0 = _mm256_permute2x128_si256(src_l0, src_m0, 0x20);
  // l8 l9 l10 l11 l12 l13 l14 l15 | m0 m1 m2 m3 m4 m5 m6 m7
  const __m256i lm8 = _mm256_permute2x128_si256(src_l0, src_m0, 0x31);

  // b0 f0 b1 f1 b2 f2 b3 f3 | c0 g0 c1 g1 c2 g2 c3 g3
  const __m256i b0f0 = _mm256_unpacklo_epi16(bc0, fg0);
  // b4 f4 b5 f5 b6 f6 b7 f7 | c4 g4 c5 g5 c6 g6 c7 g7
  const __m256i b4f4 = _mm256_unpackhi_epi16(bc0, fg0);
  // b8 f8 b9 f9 b10 f10 b11 f11 | c8 g8 c9 g9 c10 g10 c11 g11
  const __m256i b8f8 = _mm256_unpacklo_epi16(bc8, fg8);

  // d0 h0 d1 h1 d2 h2 d3 h3 | e0 i0 e1 i1 e2 i2 e3 i3
  const __m256i d0h0 = _mm256_unpacklo_epi16(de0, hi0);
  // d4 h4 d5 h5 d6 h6 d7 h7 | e4 i4 e5 i5 e6 i6 e7 i7
  const __m256i d4h4 = _mm256_unpackhi_epi16(de0, hi0);
  // d8 h8 d9 h9 d10 h10 d11 h11 | e8 i8 e9 i9 e10 i10 e11 i11
  const __m256i d8h8 = _mm256_unpacklo_epi16(de8, hi8);

  // f0 j0 f1 j1 f2 j2 f3 j3 | g0 k0 g1 k1 g2 k2 g3 k3
  const __m256i f0j0 = _mm256_unpacklo_epi16(fg0, jk0);
  // f4 j4 f5 j5 f6 j6 f7 j7 | g4 k4 g5 k5 g6 k6 g7 k7
  const __m256i f4j4 = _mm256_unpackhi_epi16(fg0, jk0);
  // f8 j8 f9 j9 f10 j10 f11 j11 | g8 k8 g9 k9 g10 k10 g11 k11
  const __m256i f8j8 = _mm256_unpacklo_epi16(fg8, jk8);

  // h0 l0 h1 l1 h2 l2 h3 l3 | i0 m0 i1 m1 i2 m2 i3 m3
  const __m256i h0l0 = _mm256_unpacklo_epi16(hi0, lm0);
  // h4 l4 h5 l5 h6 l6 h7 l7 | i4 m4 i5 m5 i6 m6 i7 m7
  const __m256i h4l4 = _mm256_unpackhi_epi16(hi0, lm0);
  // h8 l8 h9 l9 h10 l10 h11 l11 | i8 m8 i9 m9 i10 m10 i11 m11
  const __m256i h8l8 = _mm256_unpacklo_epi16(hi8, lm8);

  __m256i result_0 = zero_256;
  __m256i result_1 = zero_256;
  __m256i result_2 = zero_256;
  __m256i result_3 = zero_256;
  __m256i result_4 = zero_256;
  __m256i result_5 = zero_256;
  __m256i result_6 = zero_256;
  __m256i result_7 = zero_256;

  if (filter[1] != 0 || filter[3] != 0) {
    // Pixel values for filter coeff 1 and 3
    // b2 f2 b3 f3 b4 f4 b5 f5 | c2 g2 c3 g3 c4 g4 c5 g5
    const __m256i b2f2_b5f5 = _mm256_alignr_epi8(b4f4, b0f0, 8);
    // b6 f6 b7 f7 b8 f8 b9 f9 | c6 g6 c7 g7 c8 g8 c9 g9
    const __m256i b6f6_b9f9 = _mm256_alignr_epi8(b8f8, b4f4, 8);
    // d2 h2 d3 h3 d4 h4 d5 h5 | e2 i2 e3 i3 e4 i4 e5 i5
    const __m256i d2h2_d5h5 = _mm256_alignr_epi8(d4h4, d0h0, 8);
    // d6 h6 d7 h7 d8 h8 d9 h9 | e6 i6 e7 i7 e8 i8 e9 i9
    const __m256i d6h6_d9h9 = _mm256_alignr_epi8(d8h8, d4h4, 8);
    // f2 j2 f3 j3 f4 j4 f5 j5 | g2 k2 g3 k3 g4 k4 g5 k5
    const __m256i f2j2_f5j5 = _mm256_alignr_epi8(f4j4, f0j0, 8);
    // f6 j6 f7 j7 f8 j8 f9 j9 | g6 k6 g7 k7 g8 k8 g9 k9
    const __m256i f6j6_f9j9 = _mm256_alignr_epi8(f8j8, f4j4, 8);
    // h2 l2 h3 l3 h4 l4 h5 l5 | i2 m2 i3 m3 i4 m4 i5 m5
    const __m256i h2l2_h5l5 = _mm256_alignr_epi8(h4l4, h0l0, 8);
    // h6 l6 h7 l7 h8 l8 h9 l9 | i6 m6 i7 m7 i8 m8 i9 m9
    const __m256i h6l6_h9l9 = _mm256_alignr_epi8(h8l8, h4l4, 8);

    __m256i res_40 = _mm256_madd_epi16(b2f2_b5f5, filt13);
    __m256i res_41 = _mm256_madd_epi16(b6f6_b9f9, filt13);
    __m256i res_42 = _mm256_madd_epi16(d2h2_d5h5, filt13);
    __m256i res_43 = _mm256_madd_epi16(d6h6_d9h9, filt13);
    __m256i res_44 = _mm256_madd_epi16(f2j2_f5j5, filt13);
    __m256i res_45 = _mm256_madd_epi16(f6j6_f9j9, filt13);
    __m256i res_46 = _mm256_madd_epi16(h2l2_h5l5, filt13);
    __m256i res_47 = _mm256_madd_epi16(h6l6_h9l9, filt13);

    __m256i res_56 = _mm256_madd_epi16(b4f4, filt31);
    __m256i res_57 = _mm256_madd_epi16(b8f8, filt31);
    __m256i res_58 = _mm256_madd_epi16(d4h4, filt31);
    __m256i res_59 = _mm256_madd_epi16(d8h8, filt31);
    __m256i res_60 = _mm256_madd_epi16(f4j4, filt31);
    __m256i res_61 = _mm256_madd_epi16(f8j8, filt31);
    __m256i res_62 = _mm256_madd_epi16(h4l4, filt31);
    __m256i res_63 = _mm256_madd_epi16(h8l8, filt31);

    result_0 = _mm256_add_epi32(res_40, res_56);
    result_1 = _mm256_add_epi32(res_41, res_57);
    result_2 = _mm256_add_epi32(res_42, res_58);
    result_3 = _mm256_add_epi32(res_43, res_59);
    result_4 = _mm256_add_epi32(res_44, res_60);
    result_5 = _mm256_add_epi32(res_45, res_61);
    result_6 = _mm256_add_epi32(res_46, res_62);
    result_7 = _mm256_add_epi32(res_47, res_63);
  }

  if (filter[2] != 0) {
    // b3 f3 b4 f4 b5 f5 b6 f6 | c3 g3 c4 g4 c5 g5 c6 g6
    const __m256i b3f3_b6f6 = _mm256_alignr_epi8(b4f4, b0f0, 12);
    // b7 f7 b8 f8 b9 f9 b10 f10 | c7 g7 c8 g8 c9 g9 c10 g10
    const __m256i b7f7_b10f10 = _mm256_alignr_epi8(b8f8, b4f4, 12);
    // d3 h3 d4 h4 d5 h5 d6 h6 | e3 i3 e4 i4 e5 i5 e6 i6
    const __m256i d3h3_d6h6 = _mm256_alignr_epi8(d4h4, d0h0, 12);
    // d7 h7 d8 h8 d9 h9 d10 h10 | e7 i7 e8 i8 e9 i9 e10 i10
    const __m256i d7h7_d10h10 = _mm256_alignr_epi8(d8h8, d4h4, 12);
    // f3 j3 f4 j4 f5 j5 f6 j6 | g3 k3 g4 k4 g5 k5 g6 k6
    const __m256i f3j3_f6j6 = _mm256_alignr_epi8(f4j4, f0j0, 12);
    // f7 j7 f8 j8 f9 j9 f10 j10 | g7 k7 g8 k8 g9 k9 g10 k10
    const __m256i f7j7_f10j10 = _mm256_alignr_epi8(f8j8, f4j4, 12);
    // h3 l3 h4 l4 h5 l5 h6 l6 | i3 m3 i4 m4 i5 m5 i6 m6
    const __m256i h3l3_h6l6 = _mm256_alignr_epi8(h4l4, h0l0, 12);
    // h7 l7 h8 l8 h9 l9 h10 l10 | i7 m7 i8 m8 i9 m9 i10 m10
    const __m256i h7l7_h10l10 = _mm256_alignr_epi8(h8l8, h4l4, 12);

    __m256i res_48 = _mm256_madd_epi16(b3f3_b6f6, filt22);
    __m256i res_49 = _mm256_madd_epi16(b7f7_b10f10, filt22);
    __m256i res_50 = _mm256_madd_epi16(d3h3_d6h6, filt22);
    __m256i res_51 = _mm256_madd_epi16(d7h7_d10h10, filt22);
    __m256i res_52 = _mm256_madd_epi16(f3j3_f6j6, filt22);
    __m256i res_53 = _mm256_madd_epi16(f7j7_f10j10, filt22);
    __m256i res_54 = _mm256_madd_epi16(h3l3_h6l6, filt22);
    __m256i res_55 = _mm256_madd_epi16(h7l7_h10l10, filt22);

    result_0 = _mm256_add_epi32(result_0, res_48);
    result_1 = _mm256_add_epi32(result_1, res_49);
    result_2 = _mm256_add_epi32(result_2, res_50);
    result_3 = _mm256_add_epi32(result_3, res_51);
    result_4 = _mm256_add_epi32(result_4, res_52);
    result_5 = _mm256_add_epi32(result_5, res_53);
    result_6 = _mm256_add_epi32(result_6, res_54);
    result_7 = _mm256_add_epi32(result_7, res_55);
  }

  // f0 f0 f0 f0 f0 f0 f0 f0 | f0 f0 f0 f0 f0 f0 f0 f0
  const __m256i fc0 = _mm256_broadcastw_epi16(filt_coeff_0);

  // a0 a1 a2 a3 a4 a5 a6 a7 | b0 b1 b2 b3 b4 b5 b6 b7
  const __m256i ab0 = _mm256_permute2x128_si256(src_a0, src_b0, 0x20);
  // a8 a9 a10 a11 a12 a13 a14 a15 | b8 b9 b10 b11 b12 b13 b14 b15
  const __m256i ab8 = _mm256_permute2x128_si256(src_a0, src_b0, 0x31);

  // a0 g0 a1 g1 a2 g2 a3 g3 | b0 h0 b1 h1 b2 h2 b3 h3
  const __m256i a0g0 = _mm256_unpacklo_epi16(ab0, gh0);
  // a4 g4 a5 g5 a6 g6 a7 g7 | b4 h4 b5 h5 b6 h6 b7 h7
  const __m256i a4g4 = _mm256_unpackhi_epi16(ab0, gh0);
  // a8 g8 a9 g9 a10 g10 a11 g11 | b8 h8 b9 h9 b10 h10 b11 h11
  const __m256i a8g8 = _mm256_unpacklo_epi16(ab8, gh8);

  // c0 i0 c1 i1 c2 i2 c3 i3 | d0 j0 d1 j1 d2 j2 d3 j3
  const __m256i c0i0 = _mm256_unpacklo_epi16(cd0, ij0);
  // c4 i4 c5 i5 c6 i6 c7 i7 | d4 j4 d5 j5 d6 j6 d7 j7
  const __m256i c4i4 = _mm256_unpackhi_epi16(cd0, ij0);
  // c8 i8 c9 i9 c10 i10 c11 i11 | d8 j8 d9 j9 d10 j10 d11 j11
  const __m256i c8i8 = _mm256_unpacklo_epi16(cd8, ij8);

  // e0 k0 e1 k1 e2 k2 e3 k3 | f0 l0 f1 l1 f2 l2 f3 l3
  const __m256i e0k0 = _mm256_unpacklo_epi16(ef0, kl0);
  // e4 k4 e5 k5 e6 k6 e7 k7 | f4 l4 f5 l5 f6 l6 f7 l7
  const __m256i e4k4 = _mm256_unpackhi_epi16(ef0, kl0);
  // e8 k8 e9 k9 e10 k10 e11 k11 | f8 l8 f9 l9 f10 l10 f11 l11
  const __m256i e8k8 = _mm256_unpacklo_epi16(ef8, kl8);

  // g0 m0 g1 m1 g2 m2 g3 m3 | h0 n0 h1 n1 h2 n2 h3 n3
  const __m256i g0m0 = _mm256_unpacklo_epi16(gh0, mn0);
  // g4 m4 g5 m5 g6 m6 g7 m7 | h4 n4 h5 n5 h6 n6 h7 n7
  const __m256i g4m4 = _mm256_unpackhi_epi16(gh0, mn0);
  // g8 m8 g9 m9 g10 m10 g11 m11 | h8 n8 h9 n9 h10 n10 h11 n11
  const __m256i g8m8 = _mm256_unpacklo_epi16(gh8, mn8);

  if (filter[0] != 0) {
    // a3 g3 a4 g4 a5 g5 a6 g6 | b3 h3 b4 h4 b5 h5 b6 h6
    const __m256i a3g3_a6g6 = _mm256_alignr_epi8(a4g4, a0g0, 12);
    // a7 g7 a8 g8 a9 g9 a10 g10 | b7 h7 b8 h8 b9 h9 b10 h10
    const __m256i a7g7_a10g10 = _mm256_alignr_epi8(a8g8, a4g4, 12);
    // c3 i3 c4 i4 c5 i5 c6 i6 | d3 j3 d4 j4 d5 j5 d6 j6
    const __m256i c3i3_c6i6 = _mm256_alignr_epi8(c4i4, c0i0, 12);
    // c7 i7 c8 i8 c9 i9 c10 i10 | d7 j7 d8 j8 d9 j9 d10 j10
    const __m256i c7i7_c10i10 = _mm256_alignr_epi8(c8i8, c4i4, 12);

    // e3 k3 e4 k4 e5 k5 e6 k6 | f3 l3 f4 l4 f5 l5 f6 l6
    const __m256i e3k3_e6k6 = _mm256_alignr_epi8(e4k4, e0k0, 12);
    // e7 k7 e8 k8 e9 k9 e10 k10 | f7 l7 f8 l8 f9 l9 f10 l10
    const __m256i e7k7_e10k10 = _mm256_alignr_epi8(e8k8, e4k4, 12);
    // g3 m3 g4 m4 g5 m5 g6 m6 | h3 n3 h4 n4 h5 n5 h6 n6
    const __m256i g3m3_g6m6 = _mm256_alignr_epi8(g4m4, g0m0, 12);
    // g7 m7 g8 m8 g9 m9 g10 m10 | h7 n7 h8 n8 h9 n9 h10 n10
    const __m256i g7m7_g10m10 = _mm256_alignr_epi8(g8m8, g4m4, 12);

    __m256i res_64 = _mm256_madd_epi16(a3g3_a6g6, fc0);
    __m256i res_65 = _mm256_madd_epi16(a7g7_a10g10, fc0);
    __m256i res_66 = _mm256_madd_epi16(c3i3_c6i6, fc0);
    __m256i res_67 = _mm256_madd_epi16(c7i7_c10i10, fc0);
    __m256i res_68 = _mm256_madd_epi16(e3k3_e6k6, fc0);
    __m256i res_69 = _mm256_madd_epi16(e7k7_e10k10, fc0);
    __m256i res_70 = _mm256_madd_epi16(g3m3_g6m6, fc0);
    __m256i res_71 = _mm256_madd_epi16(g7m7_g10m10, fc0);

    result_0 = _mm256_add_epi32(result_0, res_64);
    result_1 = _mm256_add_epi32(result_1, res_65);
    result_2 = _mm256_add_epi32(result_2, res_66);
    result_3 = _mm256_add_epi32(result_3, res_67);
    result_4 = _mm256_add_epi32(result_4, res_68);
    result_5 = _mm256_add_epi32(result_5, res_69);
    result_6 = _mm256_add_epi32(result_6, res_70);
    result_7 = _mm256_add_epi32(result_7, res_71);
  }

  // fc9 fc10 - - - - - - | fc9 fc10 - - - - - -
  const __m256i fc910 =
      _mm256_broadcastd_epi32(_mm_bsrli_si128(filt_coeff_1, 8));
  // fc11 fc12 - - - - - - | fc11 fc12 - - - - - -
  const __m256i fc1112 =
      _mm256_broadcastd_epi32(_mm_bsrli_si128(filt_coeff_1, 12));
  // fc11 fc10  - - - - - - | fc11 fc10 - - - - - -
  const __m256i fc1110 = _mm256_broadcastd_epi32(
      _mm_bsrli_si128(_mm_shufflehi_epi16(filt_coeff_1, 0x06), 8));
  const __m128i zero = _mm_set1_epi16(0x0);
  const __m256i fc9_z = _mm256_broadcastd_epi32(
      _mm_unpacklo_epi16(_mm_bsrli_si128(filt_coeff_1, 8), zero));

  // d0 d0 d1 d1 d2 d2 d3 d3 | e0 e0 e1 e1 e2 e2 e3 e3
  const __m256i d0d0 = _mm256_unpacklo_epi16(de0, de0);
  // d4 d4 d5 d5 d6 d6 d7 d7 | e4 e4 e5 e5 e6 e6 e7 e7
  const __m256i d4d4 = _mm256_unpackhi_epi16(de0, de0);
  // d8 d8 d9 d9 d10 d10 d11 d11 | e8 e8 e9 e9 e10 e10 e11 e11
  const __m256i d8d8 = _mm256_unpacklo_epi16(de8, de8);
  // d12 d12 d13 d13 d14 d14 d15 d15 | e12 e12 e13 e13 e14 e14 e15 e15
  const __m256i d12d12 = _mm256_unpackhi_epi16(de8, de8);

  // f0 f0 f1 f1 f2 f2 f3 f3 | g0 g0 g1 g1 g2 g2 g3 g3
  const __m256i f0f0 = _mm256_unpacklo_epi16(fg0, fg0);
  // f4 f4 f5 f5 f6 f6 f7 f7 | g4 g4 g5 g5 g6 g6 g7 g7
  const __m256i f4f4 = _mm256_unpackhi_epi16(fg0, fg0);
  // f8 f8 f9 f9 f10 f10 f11 f11 | g8 g8 g9 g9 g10 g10 g11 g11
  const __m256i f8f8 = _mm256_unpacklo_epi16(fg8, fg8);
  // f12 f12 f13 f13 f14 f14 f15 f15 | g12 g12 g13 g13 g14 g14 g15 g15
  const __m256i f12f12 = _mm256_unpackhi_epi16(fg8, fg8);

  // h0 h0 h1 h1 h2 h2 h3 h3 | i0 i0 i1 i1 i2 i2 i3 i3
  const __m256i h0h0 = _mm256_unpacklo_epi16(hi0, hi0);
  // h4 h4 h5 h5 h6 h6 h7 h7 | i4 i4 i5 i5 i6 i6 i7 i7
  const __m256i h4h4 = _mm256_unpackhi_epi16(hi0, hi0);
  // h8 h8 h9 h9 h10 h10 h11 h11 | i8 i8 i9 i9 i10 i10 i11 i11
  const __m256i h8h8 = _mm256_unpacklo_epi16(hi8, hi8);
  // h12 h12 h13 h13 h14 h14 h15 h15 | i12 i12 i13 i13 i14 i14 i15 i15
  const __m256i h12h12 = _mm256_unpackhi_epi16(hi8, hi8);

  // j0 j0 j1 j1 j2 j2 j3 j3 | k0 k0 k1 k1 k2 k2 k3 k3
  const __m256i j0j0 = _mm256_unpacklo_epi16(jk0, jk0);
  // j4 j4 j5 j5 j6 j6 j7 j7 | k4 k4 k5 k5 k6 k6 k7 k7
  const __m256i j4j4 = _mm256_unpackhi_epi16(jk0, jk0);
  // j8 j8 j9 j9 j10 j10 j11 j11 | k8 k8 k9 k9 k10 k10 k11 k11
  const __m256i j8j8 = _mm256_unpacklo_epi16(jk8, jk8);
  // j12 j12 j13 j13 j14 j14 j15 j15 | k12 k12 k13 k13 k14 k14 k15 k15
  const __m256i j12j12 = _mm256_unpackhi_epi16(jk8, jk8);

  // d0 d1 d1 d2 d2 d3 d3 d4 | e0 e1 e1 e2 e2 e3 e3 e4
  __m256i d0d1 = _mm256_alignr_epi8(d4d4, d0d0, 2);
  // f0 f1 f1 f2 f2 f3 f3 f4 | g0 g1 g1 g2 g2 g3 g3 g4
  __m256i f0f1 = _mm256_alignr_epi8(f4f4, f0f0, 2);
  // h0 h1 h1 h2 h2 h3 h3 h4 | i0 i1 i1 i2 i2 i3 i3 i4
  __m256i h0h1 = _mm256_alignr_epi8(h4h4, h0h0, 2);
  // j0 j1 j1 j2 j2 j3 j3 j4 | k0 k1 k1 k2 k2 k3 k3 k4
  __m256i j0j1 = _mm256_alignr_epi8(j4j4, j0j0, 2);

  // d2 d3 d3 d4 d4 d5 d5 d6 | e2 e3 e3 e4 e4 e5 e5 e6
  __m256i d2d3 = _mm256_alignr_epi8(d4d4, d0d0, 10);
  // f2 f3 f3 f4 f4 f5 f5 f6 | g2 g3 g3 g4 g4 g5 g5 g6
  __m256i f2f3 = _mm256_alignr_epi8(f4f4, f0f0, 10);
  // h2 h3 h3 h4 h4 h5 h5 h6 | i2 i3 i3 i4 i4 i5 i5 i6
  __m256i h2h3 = _mm256_alignr_epi8(h4h4, h0h0, 10);
  // j2 j3 j3 j4 j4 j5 j5 j6 | k2 k3 k3 k4 k4 k5 k5 k6
  __m256i j2j3 = _mm256_alignr_epi8(j4j4, j0j0, 10);

  // d4 d5 d5 d6 d6 d7 d7 d8 | e4 e5 e5 e6 e6 e7 e7 e8
  __m256i d4d5 = _mm256_alignr_epi8(d8d8, d4d4, 2);
  // f4 f5 f5 f6 f6 f7 f7 f8 | g4 g5 g5 g6 g6 g7 g7 g8
  __m256i f4f5 = _mm256_alignr_epi8(f8f8, f4f4, 2);
  // h4 h5 h5 h6 h6 h7 h7 h8 | i4 i5 i5 i6 i6 i7 i7 i8
  __m256i h4h5 = _mm256_alignr_epi8(h8h8, h4h4, 2);
  // j4 j5 j5 j6 j6 j7 j7 j8 | k4 k5 k5 k6 k6 k7 k7 k8
  __m256i j4j5 = _mm256_alignr_epi8(j8j8, j4j4, 2);

  // d6 d7 d7 d8 d8 d9 d9 d10 | e6 e7 e7 e8 e8 e9 e9 e10
  __m256i d6d7 = _mm256_alignr_epi8(d8d8, d4d4, 10);
  // f6 f7 f7 f8 f8 f9 f9 f10 | g6 g7 g7 g8 g8 g9 g9 g10
  __m256i f6f7 = _mm256_alignr_epi8(f8f8, f4f4, 10);
  // h6 h7 h7 h8 h8 h9 h9 h10 | i6 i7 i7 i8 i8 i9 i9 i10
  __m256i h6h7 = _mm256_alignr_epi8(h8h8, h4h4, 10);
  // j6 j7 j7 j8 j8 j9 j9 j10 | k6 k7 k7 k8 k8 k9 k9 k10
  __m256i j6j7 = _mm256_alignr_epi8(j8j8, j4j4, 10);

  // d8 d9 d9 d10 d10 d11 d11 d12 | e8 e9 e9 e10 e10 e11 e11 e12
  __m256i d8d9 = _mm256_alignr_epi8(d12d12, d8d8, 2);
  // f8 f9 f9 f10 f10 f11 f11 f12 | g8 g9 g9 g10 g10 g11 g11 g12
  __m256i f8f9 = _mm256_alignr_epi8(f12f12, f8f8, 2);
  // h8 h9 h9 h10 h10 h11 h11 h12 | i8 i9 i9 i10 i10 i11 i11 i12
  __m256i h8h9 = _mm256_alignr_epi8(h12h12, h8h8, 2);
  // j8 j9 j9 j10 j10 j11 j11 j12 | k8 k9 k9 k10 k10 k11 k11 k12
  __m256i j8j9 = _mm256_alignr_epi8(j12j12, j8j8, 2);

  // d10 d11 d11 d12 d12 d13 d13 d14 | e10 e11 e11 e12 e12 e13 e13 e14
  __m256i d10d11 = _mm256_alignr_epi8(d12d12, d8d8, 10);
  // f10 f11 f11 f12 f12 f13 f13 f14 | g10 g11 g11 g12 g12 g13 g13 g14
  __m256i f10f11 = _mm256_alignr_epi8(f12f12, f8f8, 10);
  // h10 h11 h11 h12 h12 h13 h13 h14 | i10 i11 i11 i12 i12 i13 i13 i14
  __m256i h10h11 = _mm256_alignr_epi8(h12h12, h8h8, 10);
  // j10 j11 j11 j12 j12 j13 j13 j14 | k10 k11 k11 k12 k12 k13 k13 k14
  __m256i j10j11 = _mm256_alignr_epi8(j12j12, j8j8, 10);

  if (filter[9] != 0 || filter[10] != 0) {
    __m256i res_72 = _mm256_madd_epi16(d0d1, fc910);
    __m256i res_73 = _mm256_madd_epi16(d4d5, fc910);
    __m256i res_74 = _mm256_madd_epi16(f0f1, fc910);
    __m256i res_75 = _mm256_madd_epi16(f4f5, fc910);
    __m256i res_76 = _mm256_madd_epi16(h0h1, fc910);
    __m256i res_77 = _mm256_madd_epi16(h4h5, fc910);
    __m256i res_78 = _mm256_madd_epi16(j0j1, fc910);
    __m256i res_79 = _mm256_madd_epi16(j4j5, fc910);

    result_0 = _mm256_add_epi32(result_0, res_72);
    result_1 = _mm256_add_epi32(result_1, res_73);
    result_2 = _mm256_add_epi32(result_2, res_74);
    result_3 = _mm256_add_epi32(result_3, res_75);
    result_4 = _mm256_add_epi32(result_4, res_76);
    result_5 = _mm256_add_epi32(result_5, res_77);
    result_6 = _mm256_add_epi32(result_6, res_78);
    result_7 = _mm256_add_epi32(result_7, res_79);
  }

  __m256i res_80 = _mm256_madd_epi16(d2d3, fc1112);
  __m256i res_81 = _mm256_madd_epi16(d6d7, fc1112);
  __m256i res_82 = _mm256_madd_epi16(f2f3, fc1112);
  __m256i res_83 = _mm256_madd_epi16(f6f7, fc1112);
  __m256i res_84 = _mm256_madd_epi16(h2h3, fc1112);
  __m256i res_85 = _mm256_madd_epi16(h6h7, fc1112);
  __m256i res_86 = _mm256_madd_epi16(j2j3, fc1112);
  __m256i res_87 = _mm256_madd_epi16(j6j7, fc1112);

  __m256i res_88 = _mm256_madd_epi16(d4d5, fc1110);
  __m256i res_89 = _mm256_madd_epi16(d8d9, fc1110);
  __m256i res_90 = _mm256_madd_epi16(f4f5, fc1110);
  __m256i res_91 = _mm256_madd_epi16(f8f9, fc1110);
  __m256i res_92 = _mm256_madd_epi16(h4h5, fc1110);
  __m256i res_93 = _mm256_madd_epi16(h8h9, fc1110);
  __m256i res_94 = _mm256_madd_epi16(j4j5, fc1110);
  __m256i res_95 = _mm256_madd_epi16(j8j9, fc1110);

  if (filter[9] != 0) {
    __m256i res_96 = _mm256_madd_epi16(d6d7, fc9_z);
    __m256i res_97 = _mm256_madd_epi16(d10d11, fc9_z);
    __m256i res_98 = _mm256_madd_epi16(f6f7, fc9_z);
    __m256i res_99 = _mm256_madd_epi16(f10f11, fc9_z);
    __m256i res_100 = _mm256_madd_epi16(h6h7, fc9_z);
    __m256i res_101 = _mm256_madd_epi16(h10h11, fc9_z);
    __m256i res_102 = _mm256_madd_epi16(j6j7, fc9_z);
    __m256i res_103 = _mm256_madd_epi16(j10j11, fc9_z);

    result_0 = _mm256_add_epi32(result_0, res_96);
    result_1 = _mm256_add_epi32(result_1, res_97);
    result_2 = _mm256_add_epi32(result_2, res_98);
    result_3 = _mm256_add_epi32(result_3, res_99);
    result_4 = _mm256_add_epi32(result_4, res_100);
    result_5 = _mm256_add_epi32(result_5, res_101);
    result_6 = _mm256_add_epi32(result_6, res_102);
    result_7 = _mm256_add_epi32(result_7, res_103);
  }

  accum_out_r0_r1_1 = _mm256_add_epi32(
      accum_out_r0_r1_1,
      _mm256_add_epi32(result_0, _mm256_add_epi32(res_80, res_88)));
  accum_out_r0_r1_2 = _mm256_add_epi32(
      accum_out_r0_r1_2,
      _mm256_add_epi32(result_1, _mm256_add_epi32(res_81, res_89)));
  accum_out_r2_r3_1 = _mm256_add_epi32(
      accum_out_r2_r3_1,
      _mm256_add_epi32(result_2, _mm256_add_epi32(res_82, res_90)));
  accum_out_r2_r3_2 = _mm256_add_epi32(
      accum_out_r2_r3_2,
      _mm256_add_epi32(result_3, _mm256_add_epi32(res_83, res_91)));
  accum_out_r4_r5_1 = _mm256_add_epi32(
      accum_out_r4_r5_1,
      _mm256_add_epi32(result_4, _mm256_add_epi32(res_84, res_92)));
  accum_out_r4_r5_2 = _mm256_add_epi32(
      accum_out_r4_r5_2,
      _mm256_add_epi32(result_5, _mm256_add_epi32(res_85, res_93)));
  accum_out_r6_r7_1 = _mm256_add_epi32(
      accum_out_r6_r7_1,
      _mm256_add_epi32(result_6, _mm256_add_epi32(res_86, res_94)));
  accum_out_r6_r7_2 = _mm256_add_epi32(
      accum_out_r6_r7_2,
      _mm256_add_epi32(result_7, _mm256_add_epi32(res_87, res_95)));

  // Rounding and clipping.
  accum_out_r0_r1_1 = round_power_of_two_signed_avx2(accum_out_r0_r1_1,
                                                     filter_config->prec_bits);
  accum_out_r0_r1_2 = round_power_of_two_signed_avx2(accum_out_r0_r1_2,
                                                     filter_config->prec_bits);
  accum_out_r2_r3_1 = round_power_of_two_signed_avx2(accum_out_r2_r3_1,
                                                     filter_config->prec_bits);
  accum_out_r2_r3_2 = round_power_of_two_signed_avx2(accum_out_r2_r3_2,
                                                     filter_config->prec_bits);
  accum_out_r4_r5_1 = round_power_of_two_signed_avx2(accum_out_r4_r5_1,
                                                     filter_config->prec_bits);
  accum_out_r4_r5_2 = round_power_of_two_signed_avx2(accum_out_r4_r5_2,
                                                     filter_config->prec_bits);
  accum_out_r6_r7_1 = round_power_of_two_signed_avx2(accum_out_r6_r7_1,
                                                     filter_config->prec_bits);
  accum_out_r6_r7_2 = round_power_of_two_signed_avx2(accum_out_r6_r7_2,
                                                     filter_config->prec_bits);

  // ######
  const __m256i max = _mm256_set1_epi16((1 << bit_depth) - 1);

  __m256i accum_out_r0_r1 =
      _mm256_packs_epi32(accum_out_r0_r1_1, accum_out_r0_r1_2);
  __m256i accum_out_r2_r3 =
      _mm256_packs_epi32(accum_out_r2_r3_1, accum_out_r2_r3_2);
  __m256i accum_out_r4_r5 =
      _mm256_packs_epi32(accum_out_r4_r5_1, accum_out_r4_r5_2);
  __m256i accum_out_r6_r7 =
      _mm256_packs_epi32(accum_out_r6_r7_1, accum_out_r6_r7_2);

  __m256i out_r0_r1 = highbd_clamp_epi16(accum_out_r0_r1, zero_256, max);
  __m256i out_r2_r3 = highbd_clamp_epi16(accum_out_r2_r3, zero_256, max);
  __m256i out_r4_r5 = highbd_clamp_epi16(accum_out_r4_r5, zero_256, max);
  __m256i out_r6_r7 = highbd_clamp_epi16(accum_out_r6_r7, zero_256, max);

  // Store the output.
  const int dst_id = block_row_begin * dst_stride + block_col_begin;
  _mm256_storeu2_m128i((__m128i *)(dst + dst_id + (1 * dst_stride)),
                       (__m128i *)(dst + dst_id), out_r0_r1);
  _mm256_storeu2_m128i((__m128i *)(dst + dst_id + (3 * dst_stride)),
                       (__m128i *)(dst + dst_id + (2 * dst_stride)), out_r2_r3);
  _mm256_storeu2_m128i((__m128i *)(dst + dst_id + (5 * dst_stride)),
                       (__m128i *)(dst + dst_id + (4 * dst_stride)), out_r4_r5);
  _mm256_storeu2_m128i((__m128i *)(dst + dst_id + (7 * dst_stride)),
                       (__m128i *)(dst + dst_id + (6 * dst_stride)), out_r6_r7);
}

// AVX2 intrinsic function to convolve a 4x4 block with a origin-symmetric
// non-separable filter. The output for a particular pixel in a 4x4 block is
// calculated with DIAMOND shaped filter considering a 8x8 grid surrounded by
// that pixel. DIAMOND shape uses 16-tap filter for convolution. The filter taps
// are expected to be passed as symmetric taps followed by a center tap.
static AVM_INLINE void convolve_symmetric_highbd_16tap9x9_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter_, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  // Begin permute
  int16_t filter[WIENERNS_TAPS_MAX];
  filter[6] = filter_[0];
  filter[11] = filter_[1];
  filter[2] = filter_[2];
  filter[10] = filter_[3];
  filter[5] = filter_[4];
  filter[7] = filter_[5];
  filter[1] = filter_[6];
  filter[3] = filter_[7];
  filter[4] = filter_[8];
  filter[8] = filter_[9];
  filter[0] = filter_[10];
  filter[9] = filter_[11];
  filter[12] = filter_[12];
  filter[13] = filter_[13];
  filter[14] = filter_[14];
  filter[15] = filter_[15];
  filter[16] = filter_[16];
  // End permute

  // Derive singleton_tap.
  // TODO(rachelbarker): Set up the singleton tap fully in
  // adjust_filter_and_config, so that we don't have to modify it here
  int32_t singleton_tap = 1 << filter_config->prec_bits;
  if (filter_config->num_pixels % 2) {
    const int singleton_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    singleton_tap += filter[singleton_tap_index];
  }

  // Load source data.
  int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 4 * stride - 4;
  const __m128i src_a1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + stride + 1));
  const __m128i src_b1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 2 * stride + 1));
  const __m128i src_c1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 3 * stride + 1));
  const __m128i src_d1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride + 1));
  const __m128i src_e1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 5 * stride + 1));
  const __m128i src_f1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 6 * stride + 1));
  const __m128i src_g1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride + 1));
  const __m128i src_h1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 8 * stride + 1));
  const __m128i src_i1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 9 * stride + 1));
  const __m128i src_j1 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 10 * stride + 1));

  // Load filter tap values.
  // fc0 fc1 fc2 fc3 fc4 fc5 fc6 fc7
  const __m128i filt_coeff_0 = _mm_loadu_si128((__m128i const *)(filter));
  // fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc12
  __m128i temp = _mm_loadu_si128((__m128i const *)(filter + 5));
  // Replace the fc16 with derived singleton_tap.
  const __m128i center_tap = _mm_set1_epi16(singleton_tap);
  // fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc16
  const __m128i filt_coeff_1 = _mm_blend_epi16(temp, center_tap, 0x80);

  // fc12 fc13 fc14 fc15 fc16 x x x
  const __m128i filt_coeff_2 = _mm_loadu_si128((__m128i const *)(filter + 12));

  // Form 256bit source registers.
  // c1 c2 c3 c4 c5 c6 c7 c8 | d1 d2 d3 d4 d5 d6 d7 d8
  const __m256i src_cd1 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_c1), src_d1, 1);
  // e1 e2 e3 e4 e5 e6 e7 e8 | f1 f2 f3 f4 f5 f6 f7 f8
  const __m256i src_ef1 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_e1), src_f1, 1);
  // g1 g2 g3 g4 g5 g6 g7 g8 | h1 h2 h3 h4 h5 h6 h7 h8
  const __m256i src_gh1 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_g1), src_h1, 1);

  // Packing the source rows.
  // c1 e1 c2 e2 c3 e3 c4 e4 | d1 f1 d2 f2 d3 f3 d4 f4
  const __m256i ru2 = _mm256_unpacklo_epi16(src_cd1, src_ef1);
  // c5 e5 c6 e6 c7 e7 c8 e8 | d5 f5 d6 f6 d7 f7 d8 f8
  const __m256i ru3 = _mm256_unpackhi_epi16(src_cd1, src_ef1);
  // e1 g1 e2 g2 e3 g3 e4 g4 | f1 h1 f2 h2 f3 h3 f4 h4
  const __m256i ru6 = _mm256_unpacklo_epi16(src_ef1, src_gh1);
  // e5 g5 e6 g6 e7 g7 e8 g8 | f5 h5 f6 h6 f7 h7 f8 h8
  const __m256i ru7 = _mm256_unpackhi_epi16(src_ef1, src_gh1);

  // Output corresponding to filter coefficients 4,5,6,7.
  // c2 e2 c3 e3 c4 e4 c5 e5 | d2 f2 d3 f3 d4 f4 d5 f5
  const __m256i ru10 = _mm256_alignr_epi8(ru3, ru2, 4);
  // e2 g2 e3 g3 e4 g4 e5 g5 | f2 h2 f3 h3 f4 h4 f5 h5
  const __m256i ru11 = _mm256_alignr_epi8(ru7, ru6, 4);
  // c3 e3 c4 e4 c5 e5 c6 e6 | d3 f3 d4 f4 d5 f5 d6 f6
  const __m256i ru12 = _mm256_alignr_epi8(ru3, ru2, 8);
  // e3 g3 e4 g4 e5 g5 e6 g6 | f3 h3 f4 h4 f5 h5 f6 h6
  const __m256i ru13 = _mm256_alignr_epi8(ru7, ru6, 8);
  // c4 e4 c5 e5 c6 e6 c7 e7 | d4 f4 d5 f5 d6 f6 d7 f7
  const __m256i ru14 = _mm256_alignr_epi8(ru3, ru2, 12);
  // e4 g4 e5 g5 e6 g6 e7 g7 | f4 h4 f5 h5 f6 h6 f7 h7
  const __m256i ru15 = _mm256_alignr_epi8(ru7, ru6, 12);

  // 0 fc5 fc6 fc7 fc8 fc9 fc10 fc11
  temp = _mm_bslli_si128(filt_coeff_1, 2);
  // fc4 fc8 fc5 fc9 fc6 fc10 fc7 fc11
  temp = _mm_unpackhi_epi16(filt_coeff_0, temp);
  // fc4 fc8 fc4 fc8 fc4 fc8 fc4 fc8 | fc4 fc8 fc4 fc8 fc4 fc8 fc4 fc8
  const __m256i filt48 = _mm256_broadcastd_epi32(temp);
  // fc5 fc7 fc5 fc7 fc5 fc7 fc5 fc7 | fc5 fc7 fc5 fc7 fc5 fc7 fc5 fc7
  const __m256i filt57 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x08));
  // fc6 fc6 fc6 fc6 fc6 fc6 fc6 fc6 | fc6 fc6 fc6 fc6 fc6 fc6 fc6 fc6
  const __m256i filt66 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x55));
  // fc7 fc5 fc7 fc5 fc7 fc5 fc7 fc5 | fc7 fc5 fc7 fc5 fc7 fc5 fc7 fc5
  const __m256i filt75 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x22));

  const __m256i res_0 = _mm256_madd_epi16(ru10, filt48);
  const __m256i res_1 = _mm256_madd_epi16(ru11, filt48);
  const __m256i res_2 = _mm256_madd_epi16(ru12, filt57);
  const __m256i res_3 = _mm256_madd_epi16(ru13, filt57);
  const __m256i res_4 = _mm256_madd_epi16(ru14, filt66);
  const __m256i res_5 = _mm256_madd_epi16(ru15, filt66);
  const __m256i res_6 = _mm256_madd_epi16(ru3, filt75);
  const __m256i res_7 = _mm256_madd_epi16(ru7, filt75);

  // r00 r01 r02 r03 | r10 r11 r12 r13
  const __m256i out_0 = _mm256_add_epi32(res_0, res_2);
  const __m256i out_1 = _mm256_add_epi32(res_4, res_6);
  const __m256i out_2 = _mm256_add_epi32(out_0, out_1);
  __m256i accum_out_r0r1 = out_2;
  // r20 r21 r22 r23 | r30 r31 r32 r33
  const __m256i out_3 = _mm256_add_epi32(res_1, res_3);
  const __m256i out_4 = _mm256_add_epi32(res_5, res_7);
  const __m256i out_5 = _mm256_add_epi32(out_3, out_4);
  __m256i accum_out_r2r3 = out_5;

  // Output corresponding to filter coefficients 9,10,11,16.
  // d3 d4 d5 d6 d7 d8 d9 d10
  const __m128i src_d3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride + 3));
  // e3 e4 e5 e6 e7 e8 e9 e10
  const __m128i src_e3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 5 * stride + 3));
  // f3 f4 f5 f6 f7 f8 f9 f10
  const __m128i src_f3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 6 * stride + 3));
  // g3 g4 g5 g6 g7 g8 g9 g10
  const __m128i src_g3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride + 3));

  // d1 d2 d3 d4 d5 d6 d7 d8 | e1 e2 e3 e4 e5 e6 e7 e8
  const __m256i rm0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d1), src_e1, 1);
  // d3 d4 d5 d6 d7 d8 d9 d10 | e3 e4 e5 e6 e7 e8 e9 e10
  const __m256i rm2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d3), src_e3, 1);
  // f1 f2 f3 f4 f5 f6 f7 f8 | g1 g2 g3 g4 g5 g6 g7 g8
  const __m256i rm00 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f1), src_g1, 1);
  // f3 f4 f5 f6 f7 f8 f9 f10 | g3 g4 g5 g6 g7 g8 g9 g10
  const __m256i rm22 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f3), src_g3, 1);
  // d2 d3 d4 d5 d6 d7 d8 d9 | e2 e3 e4 e5 e6 e7 e8 e9
  const __m256i rm1 = _mm256_alignr_epi8(_mm256_bsrli_epi128(rm2, 12), rm0, 2);
  const __m256i rm11 =
      _mm256_alignr_epi8(_mm256_bsrli_epi128(rm22, 12), rm00, 2);
  // d4 d5 d6 d7 d8 d9 d10 0 | e4 e5 e6 e7 e8 e9 e10 0
  const __m256i rm3 = _mm256_bsrli_epi128(rm2, 2);
  const __m256i rm33 = _mm256_bsrli_epi128(rm22, 2);
  // d1 d2 d2 d3 d3 d4 d4 d5 | e1 e2 e2 e3 e3 e4 e4 e5
  __m256i rm4 = _mm256_unpacklo_epi16(rm0, rm1);
  // d3 d4 d4 d5 d5 d6 d6 d7 | e3 e4 e4 e5 e5 e6 e6 e7
  __m256i rm5 = _mm256_unpacklo_epi16(rm2, rm3);
  // d5 d6 d6 d7 d7 d8 d8 d9 | e5 e6 e6 e7 e7 e8 d8 d9
  __m256i rm6 = _mm256_unpackhi_epi16(rm0, rm1);
  // d7 0 d8 0 d9 0 d10 0 | e7 0 e8 0 e9 0 d10 0
  __m256i rm7 = _mm256_unpackhi_epi16(rm2, _mm256_set1_epi16(0));

  __m256i rm44 = _mm256_unpacklo_epi16(rm00, rm11);
  __m256i rm55 = _mm256_unpacklo_epi16(rm22, rm33);
  __m256i rm66 = _mm256_unpackhi_epi16(rm00, rm11);
  __m256i rm77 = _mm256_unpackhi_epi16(rm22, _mm256_set1_epi16(0));
  // fc9 fc10 - - - - - - | fc9 fc10 - - - - - -
  const __m256i fc910 =
      _mm256_broadcastd_epi32(_mm_bsrli_si128(filt_coeff_1, 8));
  // fc11 fc16 - - - - - - | fc11 fc16 - - - - - -
  const __m256i fc1116 =
      _mm256_broadcastd_epi32(_mm_bsrli_si128(filt_coeff_1, 12));
  // fc11 fc10  - - - - - - | fc11 fc10 - - - - - -
  const __m256i fc1110 = _mm256_broadcastd_epi32(
      _mm_bsrli_si128(_mm_shufflehi_epi16(filt_coeff_1, 0x06), 8));

  rm4 = _mm256_madd_epi16(rm4, fc910);
  rm5 = _mm256_madd_epi16(rm5, fc1116);
  rm6 = _mm256_madd_epi16(rm6, fc1110);
  rm7 = _mm256_madd_epi16(rm7, fc910);
  rm44 = _mm256_madd_epi16(rm44, fc910);
  rm55 = _mm256_madd_epi16(rm55, fc1116);
  rm66 = _mm256_madd_epi16(rm66, fc1110);
  rm77 = _mm256_madd_epi16(rm77, fc910);

  // r00 r01 r02 r03 | r10 r11 r12 r13
  rm4 = _mm256_add_epi32(rm4, rm5);
  rm6 = _mm256_add_epi32(rm6, rm7);
  rm4 = _mm256_add_epi32(rm4, rm6);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, rm4);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  rm44 = _mm256_add_epi32(rm44, rm55);
  rm66 = _mm256_add_epi32(rm66, rm77);
  rm44 = _mm256_add_epi32(rm44, rm66);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, rm44);

  // Output corresponding to filter coefficients 1,2,3,8.
  const __m128i src_b3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 2 * stride + 3));
  const __m128i src_c3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 3 * stride + 3));
  const __m256i rn0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_b3), src_c3, 1);
  // b3 b4 b4 b5 b5 b6 b6 b7 | c3 c4 c4 c5 c5 c6 c6 c7
  __m256i r0 = _mm256_unpacklo_epi16(rn0, _mm256_bsrli_epi128(rn0, 2));

  const __m256i rcd2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_c3), src_d3, 1);
  // b5 c6 b6 c7 b7 c8 b8 c9 | c5 d6 c6 d7 c7 d8 c8 d9
  __m256i r1 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rn0, 4),
                                     _mm256_bsrli_epi128(rcd2, 6));

  const __m256i rfg2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f3), src_g3, 1);
  // f3 f4 f4 f5 f5 f6 f6 f7 | g3 g4 g4 g5 g5 g6 g6 g7
  __m256i r2 = _mm256_unpacklo_epi16(rfg2, _mm256_bsrli_epi128(rfg2, 2));

  const __m256i ref2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_e3), src_f3, 1);
  // f5 e6 f6 e7 f7 e8 f8 e9 | g5 f6 g6 f7 g7 f8 g8 f9
  __m256i r3 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rfg2, 4),
                                     _mm256_bsrli_epi128(ref2, 6));
  const __m128i tempn =
      _mm_blend_epi16(_mm_bsrli_si128(filt_coeff_0, 2), filt_coeff_1, 0x08);
  __m128i tempn2 = _mm_bsrli_si128(filt_coeff_0, 2);
  tempn2 = _mm_shufflelo_epi16(tempn2, 0x0c);
  // fc1 fc2 - - - - - - | fc1 fc2 - - - - - -
  const __m256i fc12 = _mm256_broadcastd_epi32(tempn);
  // fc1 fc4 - - - - - - | fc1 fc4 - - - - - -
  const __m256i fc14 = _mm256_broadcastd_epi32(tempn2);
  tempn2 = _mm_shufflelo_epi16(tempn, 0x06);
  // fc3 fc8 - - - - - - | fc3 fc8 - - - - - -
  const __m256i fc38 = _mm256_broadcastd_epi32(_mm_bsrli_si128(tempn, 4));
  // fc3 fc2 - - - - - - | fc3 fc2 - - - - - -
  const __m256i fc32 = _mm256_broadcastd_epi32(tempn2);

  r0 = _mm256_madd_epi16(r0, fc12);
  r1 = _mm256_madd_epi16(r1, fc38);
  r2 = _mm256_madd_epi16(r2, fc32);
  r3 = _mm256_madd_epi16(r3, fc14);

  // r00 r01 r02 r03 | r10 r11 r12 r13
  r0 = _mm256_add_epi32(r0, r1);
  r2 = _mm256_add_epi32(r2, r3);
  r0 = _mm256_add_epi32(r0, r2);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, r0);

  const __m256i rn1 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d3), src_e3, 1);
  // d3 d4 d4 d5 d5 d6 d6 d7 | e3 e4 e4 e5 e5 e6 e6 e7
  __m256i r00 = _mm256_unpacklo_epi16(rn1, _mm256_bsrli_epi128(rn1, 2));
  // d5 e6 d6 e7 d7 e8 d8 e9 | e5 f6 e6 f7 e7 f8 e8 f9
  __m256i r11 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rn1, 4),
                                      _mm256_bsrli_epi128(ref2, 6));

  const __m128i src_h3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 8 * stride + 3));
  const __m128i src_i3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 9 * stride + 3));
  // h3 h4 h5 h6 h7 h8 h9 h10 | i3 i4 i5 i6 i7 i8 i9 i10
  __m256i rhi3 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_h3), src_i3, 1);
  // h3 h4 h4 h5 h5 h6 h6 h7 | i3 i4 i4 i5 i5 i6 i6 i7
  __m256i r22 = _mm256_unpacklo_epi16(rhi3, _mm256_bsrli_epi128(rhi3, 2));
  // g3 g4 g5 g6 g7 g8 g9 g10 | h3 h4 h5 h6 h7 h8 h9 h10
  __m256i rgh3 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_g3), src_h3, 1);
  __m256i r33 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rhi3, 4),
                                      _mm256_bsrli_epi128(rgh3, 6));
  r00 = _mm256_madd_epi16(r00, fc12);
  r11 = _mm256_madd_epi16(r11, fc38);
  r22 = _mm256_madd_epi16(r22, fc32);
  r33 = _mm256_madd_epi16(r33, fc14);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  r00 = _mm256_add_epi32(r00, r11);
  r22 = _mm256_add_epi32(r22, r33);
  r00 = _mm256_add_epi32(r00, r22);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, r00);

  // Output corresponding to filter coefficients 14, 15, 0.
  const __m128i src_a3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + stride + 3));
  const __m128i src_j3 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 10 * stride + 3));

  // a3 a4 a5 a6 a7 a8 a9 a10 | b3 b4 b5 b6 b7 b8 b9 b10
  const __m256i src_ab3 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_a3), src_b3, 1);
  // c3 c4 c5 c6 c7 c8 c9 c10 | d3 d4 d5 d6 d7 d8 d9 d10
  const __m256i src_cd3 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_c3), src_d3, 1);
  // g3 g4 g5 g6 g7 g8 g9 g10 | h3 h4 h5 h6 h7 h8 h9 h10
  const __m256i src_gh3 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_g3), src_h3, 1);
  // i3 i4 i5 i6 i7 i8 i9 i10 | j3 j4 j5 j6 j7 j8 j9 j10
  const __m256i src_ij3 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_i3), src_j3, 1);
  // a1 a2 a3 a4 a5 a6 a7 a8 | b1 b2 b3 b4 b5 b6 b7 b8
  const __m256i src_ab1 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_a1), src_b1, 1);
  // i1 i2 i3 i4 i5 i6 i7 h8 | j1 j2 j3 j4 j5 j6 j7 j8
  const __m256i src_ij1 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_i1), src_j1, 1);
  // a1 g1 a2 g2 a3 g3 a4 g4 | b1 h1 b2 h2 b3 h3 b4 h4
  const __m256i a1g1 = _mm256_unpacklo_epi16(src_ab1, src_gh1);
  // c1 i1 c2 i2 c3 i3 c4 i4 | d1 j1 d2 j2 d3 j3 d4 j4
  const __m256i c1i1 = _mm256_unpacklo_epi16(src_cd1, src_ij1);

  // a7 g7 a8 g8 a9 g9 a10 g10 | b7 h7 b8 h8 b9 h9 b10 h10
  const __m256i a7g7 = _mm256_unpackhi_epi16(src_ab3, src_gh3);
  // c7 i7 c8 i8 c9 i9 c10 i10 | c7 j7 c8 j8 c9 j9 c10 j10
  const __m256i c7i7 = _mm256_unpackhi_epi16(src_cd3, src_ij3);

  const __m128i src_a4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + stride + 4));
  const __m128i src_b4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 2 * stride + 4));
  const __m128i src_c4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 3 * stride + 4));
  const __m128i src_d4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride + 4));
  const __m128i src_g4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride + 4));
  const __m128i src_h4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 8 * stride + 4));
  const __m128i src_i4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 9 * stride + 4));
  const __m128i src_j4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 10 * stride + 4));

  // a4 a5 a6 a7 a8 a9 a10 a11 | b4 b5 b6 b7 b8 b9 b10 b11
  const __m256i src_ab4 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_a4), src_b4, 1);
  // c4 c5 c6 c7 c8 c9 c10 c11 | d4 d5 d6 d7 d8 d9 d10 d11
  const __m256i src_cd4 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_c4), src_d4, 1);
  // g4 g5 g6 g7 g8 g9 g10 g11 | h4 h5 h6 h7 h8 h9 h10 h11
  const __m256i src_gh4 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_g4), src_h4, 1);
  // i4 i5 i6 i7 i8 i9 i10 i11 | j4 j5 j6 j7 j8 j9 j10 j11
  const __m256i src_ij4 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_i4), src_j4, 1);

  // a4 g4 a5 g5 a6 g6 a7 g7 | b4 h4 b5 h5 b6 h6 b7 h7
  const __m256i a4g4 = _mm256_unpacklo_epi16(src_ab4, src_gh4);
  // c4 i4 c5 i5 c6 i6 c7 i7 | d4 j4 d5 j5 d6 j6 d7 j7
  const __m256i c4i4 = _mm256_unpacklo_epi16(src_cd4, src_ij4);

  const __m128i tmp_fc14_15 = _mm_bsrli_si128(filt_coeff_2, 4);
  // f14 f15 f14 f15 f14 f15 f14 f15 | f14 f15 f14 f15 f14 f15 f14 f15
  const __m256i fc14_15 = _mm256_broadcastd_epi32(tmp_fc14_15);
  // f15 f14 f15 f14 f15 f14 f15 f14 | f15 f14 f15 f14 f15 f14 f15 f14
  const __m256i fc15_14 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(tmp_fc14_15, 0x01));
  // f0 f0 f0 f0 f0 f0 f0 f0 | f0 f0 f0 f0 f0 f0 f0 f0
  const __m256i fc0 = _mm256_broadcastw_epi16(filt_coeff_0);

  const __m256i res_8 = _mm256_madd_epi16(a1g1, fc14_15);
  const __m256i res_9 = _mm256_madd_epi16(c1i1, fc14_15);
  const __m256i res_10 = _mm256_madd_epi16(a4g4, fc0);
  const __m256i res_11 = _mm256_madd_epi16(c4i4, fc0);
  const __m256i res_12 = _mm256_madd_epi16(a7g7, fc15_14);
  const __m256i res_13 = _mm256_madd_epi16(c7i7, fc15_14);

  accum_out_r0r1 = _mm256_add_epi32(
      accum_out_r0r1,
      _mm256_add_epi32(_mm256_add_epi32(res_8, res_10), res_12));
  accum_out_r2r3 = _mm256_add_epi32(
      accum_out_r2r3,
      _mm256_add_epi32(_mm256_add_epi32(res_9, res_11), res_13));

  // Output corresponding to filter coefficient 12.
  const __m128i src_z4 = _mm_loadu_si128((__m128i const *)(src_ptr + 4));
  const __m128i src_k4 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 11 * stride + 4));

  // z4 z5 z6 z7 z8 z9 z10 z11 | a4 a5 a6 a7 a8 a9 a10 a11
  const __m256i src_za4 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_z4), src_a4, 1);
  // b4 b5 b6 b7 b8 b9 b10 b11 | c4 c5 c6 c7 c8 c9 c10 c11
  const __m256i src_bc4 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_b4), src_c4, 1);
  // h4 h5 h6 h7 h8 h9 h10 h11 | i4 i5 i6 i7 i8 i9 i10 i11
  const __m256i src_hi4 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_h4), src_i4, 1);
  // j4 j5 j6 j7 j8 j9 j10 j11 | k4 k5 k6 k7 k8 k9 k10 k11
  const __m256i src_jk4 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_j4), src_k4, 1);

  // z4 h4 z5 h5 z6 h6 z7 h7 | a4 i4 a5 i5 a6 i6 a7 i7
  const __m256i z4h4 = _mm256_unpacklo_epi16(src_za4, src_hi4);
  // b4 j4 b5 j5 b6 j6 b7 j7 | c4 k4 c5 k5 c6 k6 c7 k7
  const __m256i b4j4 = _mm256_unpacklo_epi16(src_bc4, src_jk4);

  // f12 f12 f12 f12 f12 f12 f12 f12 | f12 f12 f12 f12 f12 f12 f12 f12
  const __m256i fc_12 = _mm256_broadcastw_epi16(filt_coeff_2);

  const __m256i res_30 = _mm256_madd_epi16(z4h4, fc_12);
  const __m256i res_31 = _mm256_madd_epi16(b4j4, fc_12);

  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, res_30);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, res_31);

  // Output corresponding to filter coefficient 13.
  const __m128i src_d0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride));
  const __m128i src_e0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 5 * stride));
  const __m128i src_f0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 6 * stride));
  const __m128i src_g0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride));
  const __m128i src_d8 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride + 8));
  const __m128i src_e8 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 5 * stride + 8));
  const __m128i src_f8 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 6 * stride + 8));
  const __m128i src_g8 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride + 8));

  // d0 d1 d2 d3 d4 d5 d6 d7 | e0 e1 e2 e3 e4 e5 e6 e7
  const __m256i src_d0e0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d0), src_e0, 1);
  // d8 d9 d10 d11 d12 d13 d14 d15 | e8 e9 e10 e11 e12 e13 e14 e15
  const __m256i src_d8e8 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d8), src_e8, 1);
  // f0 f1 f2 f3 f4 f5 f6 f7 | g0 g1 g2 g3 g4 g5 g6 g7
  const __m256i src_f0g0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f0), src_g0, 1);
  // f8 f9 f10 f11 f12 f13 f14 f15 | g8 g9 g10 g11 g12 g13 g14 g15
  const __m256i src_f8g8 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f8), src_g8, 1);

  // d0 d8 d1 d9 d2 d10 d3 d11 | e0 e8 e1 e9 e2 e10 e3 e11
  const __m256i d0d8 = _mm256_unpacklo_epi16(src_d0e0, src_d8e8);

  // f0 f8 f1 f9 f2 f10 f3 f11 | g0 g8 g1 g9 g2 g10 g3 g11
  const __m256i f0f8 = _mm256_unpacklo_epi16(src_f0g0, src_f8g8);

  // f13 f13 f13 f13 f13 f13 f13 f13 | f13 f13 f13 f13 f13 f13 f13 f13
  const __m256i fc13 =
      _mm256_broadcastw_epi16(_mm_bsrli_si128(filt_coeff_2, 2));

  const __m256i res_32 = _mm256_madd_epi16(d0d8, fc13);
  const __m256i res_33 = _mm256_madd_epi16(f0f8, fc13);

  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, res_32);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, res_33);

  // Rounding and clipping.
  accum_out_r0r1 =
      round_power_of_two_signed_avx2(accum_out_r0r1, filter_config->prec_bits);
  accum_out_r2r3 =
      round_power_of_two_signed_avx2(accum_out_r2r3, filter_config->prec_bits);
  // r00 r01 r02 r03 | r20 r21 r22 r23 | r10 r11 r12 r13 | r30 r31 r32 r33
  __m256i out_r0r2r1r3 = _mm256_packs_epi32(accum_out_r0r1, accum_out_r2r3);
  const __m256i max = _mm256_set1_epi16((1 << bit_depth) - 1);
  out_r0r2r1r3 = highbd_clamp_epi16(out_r0r2r1r3, _mm256_setzero_si256(), max);

  // Store the output.
  const int dst_id = block_row_begin * dst_stride + block_col_begin;
  const __m128i out_r1r3 = _mm256_extractf128_si256(out_r0r2r1r3, 1);
  _mm_storel_epi64((__m128i *)(dst + dst_id),
                   _mm256_castsi256_si128(out_r0r2r1r3));
  _mm_storel_epi64((__m128i *)(dst + dst_id + (1 * dst_stride)), out_r1r3);

  _mm_storel_epi64((__m128i *)(dst + dst_id + (2 * dst_stride)),
                   _mm_bsrli_si128(_mm256_castsi256_si128(out_r0r2r1r3), 8));
  _mm_storel_epi64((__m128i *)(dst + dst_id + (3 * dst_stride)),
                   _mm_bsrli_si128(out_r1r3, 8));
}

// AVX2 intrinsic function to convolve an 8x8 block with a origin-symmetric
// non-separable filter. The output for a particular pixel in an 8x8 block is
// calculated with DIAMOND shaped filter considering a 12x12 grid surrounded by
// that pixel. DIAMOND shape uses 16-tap filter for convolution. The filter taps
// are expected to be passed as symmetric taps followed by a center tap.
static AVM_INLINE void convolve_symmetric_highbd_16tap9x9_blk8x8_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter_, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  // Begin permute
  int16_t filter[WIENERNS_TAPS_MAX];
  filter[6] = filter_[0];
  filter[11] = filter_[1];
  filter[2] = filter_[2];
  filter[10] = filter_[3];
  filter[5] = filter_[4];
  filter[7] = filter_[5];
  filter[1] = filter_[6];
  filter[3] = filter_[7];
  filter[4] = filter_[8];
  filter[8] = filter_[9];
  filter[0] = filter_[10];
  filter[9] = filter_[11];
  filter[12] = filter_[12];
  filter[13] = filter_[13];
  filter[14] = filter_[14];
  filter[15] = filter_[15];
  filter[16] = filter_[16];
  // End permute

  // Derive singleton_tap.
  // TODO(rachelbarker): Set up the singleton tap fully in
  // adjust_filter_and_config, so that we don't have to modify it here
  int32_t singleton_tap = 1 << filter_config->prec_bits;
  if (filter_config->num_pixels % 2) {
    const int singleton_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    singleton_tap += filter[singleton_tap_index];
  }
  // filter[12] = singleton_tap;

  // Load source data.
  int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 4 * stride - 4;
  const __m256i src_a0 = _mm256_loadu_si256((__m256i const *)(src_ptr));
  const __m256i src_b0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + stride));
  const __m256i src_c0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 2 * stride));
  const __m256i src_d0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 3 * stride));
  const __m256i src_e0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 4 * stride));
  const __m256i src_f0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 5 * stride));
  const __m256i src_g0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 6 * stride));
  const __m256i src_h0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 7 * stride));
  const __m256i src_i0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 8 * stride));
  const __m256i src_j0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 9 * stride));
  const __m256i src_k0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 10 * stride));
  const __m256i src_l0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 11 * stride));
  const __m256i src_m0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 12 * stride));
  const __m256i src_n0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 13 * stride));
  const __m256i src_o0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 14 * stride));
  const __m256i src_p0 =
      _mm256_loadu_si256((__m256i const *)(src_ptr + 15 * stride));

  // Load filter tap values.
  // fc0 fc1 fc2 fc3 fc4 fc5 fc6 fc7
  const __m128i filt_coeff_0 = _mm_loadu_si128((__m128i const *)(filter));
  // fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc12
  __m128i temp = _mm_loadu_si128((__m128i const *)(filter + 5));
  // Replace the fc16 with derived singleton_tap.
  const __m128i center_tap = _mm_set1_epi16(singleton_tap);
  // fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc16
  const __m128i filt_coeff_1 = _mm_blend_epi16(temp, center_tap, 0x80);

  // fc12 fc13 fc14 fc15 fc16 x x x
  const __m128i filt_coeff_2 = _mm_loadu_si128((__m128i const *)(filter + 12));

  // d0 d1 d2 d3 d4 d5 d6 d7 | e0 e1 e2 e3 e4 e5 e6 e7
  const __m256i de0 = _mm256_permute2x128_si256(src_d0, src_e0, 0x20);
  // d8 d9 d10 d11 d12 d13 d14 d15 | e0 e1 e2 e3 e4 e5 e6 e7
  const __m256i de8 = _mm256_permute2x128_si256(src_d0, src_e0, 0x31);
  // f0 f1 f2 f3 f4 f5 f6 f7 | g0 g1 g2 g3 g4 g5 g6 g7
  const __m256i fg0 = _mm256_permute2x128_si256(src_f0, src_g0, 0x20);
  // f8 f9 f10 f11 f12 f13 f14 f15 | g8 g9 g10 g11 g12 g13 g14 g15
  const __m256i fg8 = _mm256_permute2x128_si256(src_f0, src_g0, 0x31);
  // h0 h1 h2 h3 h4 h5 h6 h7 | i0 i1 i2 i3 i4 i5 i6 i7
  const __m256i hi0 = _mm256_permute2x128_si256(src_h0, src_i0, 0x20);
  // h8 h9 h10 h11 h12 h13 h14 h15 | i0 i1 i2 i3 i4 i5 i6 i7
  const __m256i hi8 = _mm256_permute2x128_si256(src_h0, src_i0, 0x31);
  // j0 j1 j2 j3 j4 j5 j6 j7 | k0 k1 k2 k3 k4 k5 k6 k7
  const __m256i jk0 = _mm256_permute2x128_si256(src_j0, src_k0, 0x20);
  // j8 j9 j10 j11 j12 j13 j14 j15 | k0 k1 k2 k3 k4 k5 k6 k7
  const __m256i jk8 = _mm256_permute2x128_si256(src_j0, src_k0, 0x31);
  // l0 l1 l2 l3 l4 l5 l6 l7 | m0 m1 m2 m3 m4 m5 m6 m7
  const __m256i lm0 = _mm256_permute2x128_si256(src_l0, src_m0, 0x20);
  // l8 l9 l10 l11 l12 l13 l14 l15 | m0 m1 m2 m3 m4 m5 m6 m7
  const __m256i lm8 = _mm256_permute2x128_si256(src_l0, src_m0, 0x31);

  // Packing the source rows.
  // d0 f0 d1 f1 d2 f2 d3 f3 | e0 g0 e1 g1 e2 g2 e3 g3
  const __m256i d0f0 = _mm256_unpacklo_epi16(de0, fg0);
  // d4 f4 d5 f5 d6 f6 d7 f7 | e4 g4 e5 g5 e6 g6 e7 g7
  const __m256i d4f4 = _mm256_unpackhi_epi16(de0, fg0);
  // d8 f8 d9 f9 d10 f10 d11 f11 | e8 g8 e9 g9 e10 g10 e11 g11
  const __m256i d8f8 = _mm256_unpacklo_epi16(de8, fg8);
  // d12 f12 d13 f13 d14 f14 d15 f15 | e12 g12 e13 g13 e14 g14 e15 g15
  const __m256i d12f12 = _mm256_unpackhi_epi16(de8, fg8);

  // f0 h0 f1 h1 f2 h2 f3 h3 | g0 i0 g1 i1 g2 i2 g3 i3
  const __m256i f0h0 = _mm256_unpacklo_epi16(fg0, hi0);
  // f4 h4 f5 h5 f6 h6 f7 h7 | g4 i4 g5 i5 g6 i6 g7 i7
  const __m256i f4h4 = _mm256_unpackhi_epi16(fg0, hi0);
  // f8 h8 f9 h9 f10 h10 f11 h11 | g8 i8 g9 i9 g10 i10 g11 i11
  const __m256i f8h8 = _mm256_unpacklo_epi16(fg8, hi8);
  // f12 h12 f13 h13 f14 h14 f15 h15 | g12 i12 g13 i13 g14 i14 g15 i15
  const __m256i f12h12 = _mm256_unpackhi_epi16(fg8, hi8);

  // h0 j0 h1 j1 h2 j2 h3 j3 | i0 k0 i1 k1 i2 k2 i3 k3
  const __m256i h0j0 = _mm256_unpacklo_epi16(hi0, jk0);
  // h4 j4 h5 j5 h6 j6 h7 j7 | i4 k4 i5 k5 i6 k6 i7 k7
  const __m256i h4j4 = _mm256_unpackhi_epi16(hi0, jk0);
  // h8 j8 h9 j9 h10 j10 h11 j11 | i8 k8 i9 k9 i10 k10 i11 k11
  const __m256i h8j8 = _mm256_unpacklo_epi16(hi8, jk8);
  // h12 j12 h13 j13 h14 j14 h15 j15 | i12 k12 i13 k13 i14 k14 i15 k15
  const __m256i h12j12 = _mm256_unpackhi_epi16(hi8, jk8);

  // j0 l0 j1 l1 j2 l2 j3 l3 | k0 m0 k1 m1 k2 m2 k3 m3
  const __m256i j0l0 = _mm256_unpacklo_epi16(jk0, lm0);
  // j4 l4 j5 l5 j6 l6 j7 l7 | k4 m4 k5 m5 k6 m6 k7 m7
  const __m256i j4l4 = _mm256_unpackhi_epi16(jk0, lm0);
  // j8 l8 j9 l9 j10 l10 j11 l11 | k8 m8 k9 m9 k10 m10 k11 m11
  const __m256i j8l8 = _mm256_unpacklo_epi16(jk8, lm8);
  // j12 l12 j13 l13 j14 l14 j15 l15 | k12 m12 k13 m13 k14 m14 k15 m15
  const __m256i j12l12 = _mm256_unpackhi_epi16(jk8, lm8);

  // Pixel values for filter coeff 4 and 8
  // d2 f2 d3 f3 d4 f4 d5 f5 | e2 g2 e3 g3 e4 g4 e5 g5
  const __m256i d2f2_d5f5 = _mm256_alignr_epi8(d4f4, d0f0, 8);
  // d6 f6 d7 f7 d8 f8 d9 f9 | e6 g6 e7 g7 e8 g8 e9 g9
  const __m256i d6f6_d9f9 = _mm256_alignr_epi8(d8f8, d4f4, 8);
  // f2 h2 f3 h3 f4 h4 f5 h5 | g2 i2 g3 i3 g4 i4 g5 i5
  const __m256i f2h2_f5h5 = _mm256_alignr_epi8(f4h4, f0h0, 8);
  // f6 h6 f7 h7 f8 h8 f9 h9 | g6 i6 g7 i7 g8 i8 g9 i9
  const __m256i f6h6_f9h9 = _mm256_alignr_epi8(f8h8, f4h4, 8);
  // h2 j2 h3 j3 h4 j4 h5 j5 | i2 k2 i3 k3 i4 k4 i5 k5
  const __m256i h2j2_h5j5 = _mm256_alignr_epi8(h4j4, h0j0, 8);
  // h6 j6 h7 j7 h8 j8 h9 j9 | i6 k6 i7 k7 i8 k8 i9 k9
  const __m256i h6j6_h9j9 = _mm256_alignr_epi8(h8j8, h4j4, 8);
  // j2 l2 j3 l3 j4 l4 j5 l5 | k2 m2 k3 m3 k4 m4 k5 m5
  const __m256i j2l2_j5l5 = _mm256_alignr_epi8(j4l4, j0l0, 8);
  // j6 l6 j7 l7 j8 l8 j9 l9 | k6 m6 k7 m7 k8 m8 k9 m9
  const __m256i j6l6_j9l9 = _mm256_alignr_epi8(j8l8, j4l4, 8);

  // Pixel values for filter coeff 5 and 7
  // d3 f3 d4 f4 d5 f5 d6 f6 | e3 g3 e4 g4 e5 g5 e6 g6
  const __m256i d3f3_d6f6 = _mm256_alignr_epi8(d4f4, d0f0, 12);
  // d7 f7 d8 f8 d9 f9 d10 f10 | e7 g7 e8 g8 e9 g9 e10 g10
  const __m256i d7f7_d10f10 = _mm256_alignr_epi8(d8f8, d4f4, 12);
  // f3 h3 f4 h4 f5 h5 f6 g6 | g3 i3 g4 i4 g5 i5 g6 i6
  const __m256i f3h3_f6h6 = _mm256_alignr_epi8(f4h4, f0h0, 12);
  // f7 h7 f8 h8 f9 h9 f10 h10 | g7 i7 g8 i8 g9 i9 g10 i10
  const __m256i f7h7_f10h10 = _mm256_alignr_epi8(f8h8, f4h4, 12);
  // h3 j3 h4 j4 h5 j5 h6 j6 | i3 k3 i4 k4 i5 k5 i6 k6
  const __m256i h3j3_h6j6 = _mm256_alignr_epi8(h4j4, h0j0, 12);
  // h7 j7 h8 j8 h9 j9 h10 j10 |i7 k7 i8 k8 i9 k9 i10 k10
  const __m256i h7j7_h10j10 = _mm256_alignr_epi8(h8j8, h4j4, 12);
  // j3 l3 j4 l4 j5 l5 j6 l6 | k3 m3 k4 m4 k5 m5 k6 m6
  const __m256i j3l3_j6l6 = _mm256_alignr_epi8(j4l4, j0l0, 12);
  // j7 l7 j8 l8 j9 l9 j10 k10 | k7 m7 k8 m8 k9 m9 k10 m10
  const __m256i j7l7_j10l10 = _mm256_alignr_epi8(j8l8, j4l4, 12);

  // Pixel values for filter coeff 7 and 5
  // d5 f5 d6 f6  d7 f7 d8 f8 | e5 g5 e6 g6 e7 g7 e8 g8
  const __m256i d5f5_d8f8 = _mm256_alignr_epi8(d8f8, d4f4, 4);
  // d9 f9 d10 f10 d11 f11 d12 f12 | e9 g9 e10 g10 e11 g11 e12 g12
  const __m256i d9f9_d12f12 = _mm256_alignr_epi8(d12f12, d8f8, 4);
  // f5 h5 f6 h6  f7 h7 f8 h8 | g5 i5 g6 i6 g7 i7 g8 i8
  const __m256i f5h5_f8h8 = _mm256_alignr_epi8(f8h8, f4h4, 4);
  // f9 h9 f10 h10 f11 h11 f12 h12 | g9 i9 g10 i10 g11 i11 g12 i12
  const __m256i f9h9_f12h12 = _mm256_alignr_epi8(f12h12, f8h8, 4);
  // h5 j5 h6 j6  h7 j7 h8 j8 | i5 k5 i6 k6 i7 k7 i8 k8
  const __m256i h5j5_h8j8 = _mm256_alignr_epi8(h8j8, h4j4, 4);
  // h9 j9 h10 j10 h11 j11 h12 j12 | i9 k9 i10 k10 i11 k11 i12 k12
  const __m256i h9j9_h12j12 = _mm256_alignr_epi8(h12j12, h8j8, 4);
  // j5 l5 j6 l6  j7 l7 j8 l8 | k5 m5 k6 m6 k7 m7 k8 m8
  const __m256i j5l5_j8l8 = _mm256_alignr_epi8(j8l8, j4l4, 4);
  // j9 l9 j10 l10 j11 l11 j12 l12 | k9 m9 k10 m10 k11 m11 k12 m12
  const __m256i j9l9_j12l12 = _mm256_alignr_epi8(j12l12, j8l8, 4);

  // Pixel values for filter coeff 4 and 8
  // d10 f10 d11 f11 d12 f12 d13 f13 | e10 g10 e11 g11 e12 g12 e13 g13
  const __m256i d10f10_d13f13 = _mm256_alignr_epi8(d12f12, d8f8, 8);
  // f10 h10 f11 h11 f12 h12 f13 h13 | g10 i10 g11 i11 g12 i12 g13 i13
  const __m256i f10h10_f13h13 = _mm256_alignr_epi8(f12h12, f8h8, 8);
  // h10 j10 h11 j11 h12 j12 h13 j13 | i10 k10 i11 k11 i12 k12 i13 k13
  const __m256i h10j10_h13j13 = _mm256_alignr_epi8(h12j12, h8j8, 8);
  // j10 l10 j11 l11 j12 l12 j13 l13 | k10 m10 k11 m11 k12 m12 k13 m13
  const __m256i j10l10_j13l12 = _mm256_alignr_epi8(j12l12, j8l8, 8);

  // 0 fc5 fc6 fc7 fc8 fc9 fc10 fc11
  __m128i temp1 = _mm_bslli_si128(filt_coeff_1, 2);
  // fc4 fc8 fc5 fc9 fc6 fc10 fc7 fc11
  temp = _mm_unpackhi_epi16(filt_coeff_0, temp1);
  // fc4 fc8 fc4 fc8 fc4 fc8 fc4 fc8 | fc4 fc8 fc4 fc8 fc4 fc8 fc4 fc8
  const __m256i filt48 = _mm256_broadcastd_epi32(temp);
  // fc5 fc7 fc5 fc7 fc5 fc7 fc5 fc7 | fc5 fc7 fc5 fc7 fc5 fc7 fc5 fc7
  const __m256i filt57 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x08));
  // fc6 fc6 fc6 fc6 fc6 fc6 fc6 fc6 | fc6 fc6 fc6 fc6 fc6 fc6 fc6 fc6
  const __m256i filt66 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x55));
  // fc7 fc5 fc7 fc5 fc7 fc5 fc7 fc5 | fc7 fc5 fc7 fc5 fc7 fc5 fc7 fc5
  const __m256i filt75 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x22));
  // fc8 fc4 fc9 fc5 fc10 fc6 fc11 fc7
  __m128i temp2 = _mm_unpackhi_epi16(temp1, filt_coeff_0);
  // fc8 fc4 fc8 fc4 fc8 fc4 fc8 fc4 | fc8 fc4 fc8 fc4 fc8 fc4 fc8 fc4
  const __m256i filt84 = _mm256_broadcastd_epi32(temp2);

  __m256i res_0 = _mm256_madd_epi16(d2f2_d5f5, filt48);
  __m256i res_1 = _mm256_madd_epi16(d6f6_d9f9, filt48);
  __m256i res_2 = _mm256_madd_epi16(f2h2_f5h5, filt48);
  __m256i res_3 = _mm256_madd_epi16(f6h6_f9h9, filt48);
  __m256i res_4 = _mm256_madd_epi16(h2j2_h5j5, filt48);
  __m256i res_5 = _mm256_madd_epi16(h6j6_h9j9, filt48);
  __m256i res_6 = _mm256_madd_epi16(j2l2_j5l5, filt48);
  __m256i res_7 = _mm256_madd_epi16(j6l6_j9l9, filt48);

  __m256i res_8 = _mm256_madd_epi16(d3f3_d6f6, filt57);
  __m256i res_9 = _mm256_madd_epi16(d7f7_d10f10, filt57);
  __m256i res_10 = _mm256_madd_epi16(f3h3_f6h6, filt57);
  __m256i res_11 = _mm256_madd_epi16(f7h7_f10h10, filt57);
  __m256i res_12 = _mm256_madd_epi16(h3j3_h6j6, filt57);
  __m256i res_13 = _mm256_madd_epi16(h7j7_h10j10, filt57);
  __m256i res_14 = _mm256_madd_epi16(j3l3_j6l6, filt57);
  __m256i res_15 = _mm256_madd_epi16(j7l7_j10l10, filt57);

  __m256i res_16 = _mm256_madd_epi16(d4f4, filt66);
  __m256i res_17 = _mm256_madd_epi16(d8f8, filt66);
  __m256i res_18 = _mm256_madd_epi16(f4h4, filt66);
  __m256i res_19 = _mm256_madd_epi16(f8h8, filt66);
  __m256i res_20 = _mm256_madd_epi16(h4j4, filt66);
  __m256i res_21 = _mm256_madd_epi16(h8j8, filt66);
  __m256i res_22 = _mm256_madd_epi16(j4l4, filt66);
  __m256i res_23 = _mm256_madd_epi16(j8l8, filt66);

  __m256i res_24 = _mm256_madd_epi16(d5f5_d8f8, filt75);
  __m256i res_25 = _mm256_madd_epi16(d9f9_d12f12, filt75);
  __m256i res_26 = _mm256_madd_epi16(f5h5_f8h8, filt75);
  __m256i res_27 = _mm256_madd_epi16(f9h9_f12h12, filt75);
  __m256i res_28 = _mm256_madd_epi16(h5j5_h8j8, filt75);
  __m256i res_29 = _mm256_madd_epi16(h9j9_h12j12, filt75);
  __m256i res_30 = _mm256_madd_epi16(j5l5_j8l8, filt75);
  __m256i res_31 = _mm256_madd_epi16(j9l9_j12l12, filt75);

  __m256i res_65 = _mm256_madd_epi16(d6f6_d9f9, filt84);
  __m256i res_66 = _mm256_madd_epi16(d10f10_d13f13, filt84);
  __m256i res_67 = _mm256_madd_epi16(f6h6_f9h9, filt84);
  __m256i res_68 = _mm256_madd_epi16(f10h10_f13h13, filt84);
  __m256i res_69 = _mm256_madd_epi16(h6j6_h9j9, filt84);
  __m256i res_70 = _mm256_madd_epi16(h10j10_h13j13, filt84);
  __m256i res_71 = _mm256_madd_epi16(j6l6_j9l9, filt84);
  __m256i res_72 = _mm256_madd_epi16(j10l10_j13l12, filt84);

  __m256i accum_out_r0_r1_1 =
      _mm256_add_epi32(_mm256_add_epi32(_mm256_add_epi32(res_0, res_8),
                                        _mm256_add_epi32(res_16, res_24)),
                       res_65);

  __m256i accum_out_r0_r1_2 =
      _mm256_add_epi32(_mm256_add_epi32(_mm256_add_epi32(res_1, res_9),
                                        _mm256_add_epi32(res_17, res_25)),
                       res_66);

  __m256i accum_out_r2_r3_1 =
      _mm256_add_epi32(_mm256_add_epi32(_mm256_add_epi32(res_2, res_10),
                                        _mm256_add_epi32(res_18, res_26)),
                       res_67);

  __m256i accum_out_r2_r3_2 =
      _mm256_add_epi32(_mm256_add_epi32(_mm256_add_epi32(res_3, res_11),
                                        _mm256_add_epi32(res_19, res_27)),
                       res_68);

  __m256i accum_out_r4_r5_1 =
      _mm256_add_epi32(_mm256_add_epi32(_mm256_add_epi32(res_4, res_12),
                                        _mm256_add_epi32(res_20, res_28)),
                       res_69);

  __m256i accum_out_r4_r5_2 =
      _mm256_add_epi32(_mm256_add_epi32(_mm256_add_epi32(res_5, res_13),
                                        _mm256_add_epi32(res_21, res_29)),
                       res_70);

  __m256i accum_out_r6_r7_1 =
      _mm256_add_epi32(_mm256_add_epi32(_mm256_add_epi32(res_6, res_14),
                                        _mm256_add_epi32(res_22, res_30)),
                       res_71);

  __m256i accum_out_r6_r7_2 =
      _mm256_add_epi32(_mm256_add_epi32(_mm256_add_epi32(res_7, res_15),
                                        _mm256_add_epi32(res_23, res_31)),
                       res_72);

  // Filter coeff 14, 15, 0
  const __m128i tmp_fc14_15 = _mm_bsrli_si128(filt_coeff_2, 4);
  // f14 f15 f14 f15 f14 f15 f14 f15 | f14 f15 f14 f15 f14 f15 f14 f15
  const __m256i fc14_15 = _mm256_broadcastd_epi32(tmp_fc14_15);
  // f15 f14 f15 f14 f15 f14 f15 f14 | f15 f14 f15 f14 f15 f14 f15 f14
  const __m256i fc15_14 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(tmp_fc14_15, 0x01));
  // f0 f0 f0 f0 f0 f0 f0 f0 | f0 f0 f0 f0 f0 f0 f0 f0
  const __m256i fc0 = _mm256_broadcastw_epi16(filt_coeff_0);

  // b0 b1 b2 b3 b4 b5 b6 b7 | c0 c1 c2 c3 c4 c5 c6 c7
  const __m256i bc0 = _mm256_permute2x128_si256(src_b0, src_c0, 0x20);
  // b8 b9 b10 b11 b12 b13 b14 b15 | c8 c9 c10 c11 c12 c13 c14 c15
  const __m256i bc8 = _mm256_permute2x128_si256(src_b0, src_c0, 0x31);

  // n0 n1 n2 n3 n4 n5 n6 n7 | o0 o1 o2 o3 o4 o5 o6 o7
  const __m256i no0 = _mm256_permute2x128_si256(src_n0, src_o0, 0x20);
  // n8 n9 n10 n11 n12 n13 n14 n15 | o8 o9 o10 o11 o12 o13 o14 o15
  const __m256i no8 = _mm256_permute2x128_si256(src_n0, src_o0, 0x31);

  // Packing the source rows.
  // b0 h0 b1 h1 b2 h2 b3 h3 | c0 i0 c1 i1 c2 i2 c3 i3
  const __m256i b0h0 = _mm256_unpacklo_epi16(bc0, hi0);
  // b4 h4 b5 h5 b6 h6 b7 h7 | c4 i4 c5 i5 c6 i6 c7 i7
  const __m256i b4h4 = _mm256_unpackhi_epi16(bc0, hi0);
  // b8 h8 b9 h9 b10 h10 b11 h11 | c8 i8 c9 i9 c10 i10 c11 i11
  const __m256i b8h8 = _mm256_unpacklo_epi16(bc8, hi8);
  // b12 h12 b13 h13 b14 h14 b15 h15 | c12 i12 c13 i13 c14 i14 c15 i15
  const __m256i b12h12 = _mm256_unpackhi_epi16(bc8, hi8);

  // d0 j0 d1 j1 d2 j2 d3 j3 | e0 k0 e1 k1 e2 k2 e3 k3
  const __m256i d0j0 = _mm256_unpacklo_epi16(de0, jk0);
  // d4 j4 d5 j5 d6 j6 d7 j7 | e4 k4 e5 k5 e6 k6 e7 k7
  const __m256i d4j4 = _mm256_unpackhi_epi16(de0, jk0);
  // d8 j8 d9 j9 d10 j10 d11 j11 | e8 k8 e9 k9 e10 k10 e11 k11
  const __m256i d8j8 = _mm256_unpacklo_epi16(de8, jk8);
  // d12 j12 d13 j13 d14 j14 d15 j15 | e12 k12 e13 k13 e14 k14 e15 k15
  const __m256i d12j12 = _mm256_unpackhi_epi16(de8, jk8);

  // f0 l0 f1 l1 f2 l2 f3 l3 | g0 m0 g1 m1 g2 m2 g3 m3
  const __m256i f0l0 = _mm256_unpacklo_epi16(fg0, lm0);
  // f4 l4 f5 l5 f6 l6 f7 l7 | g4 m4 g5 m5 g6 m6 g7 m7
  const __m256i f4l4 = _mm256_unpackhi_epi16(fg0, lm0);
  // f8 l8 f9 l9 f10 l10 f11 l11 | g8 m8 g9 m9 g10 m10 g11 m11
  const __m256i f8l8 = _mm256_unpacklo_epi16(fg8, lm8);
  // f12 l12 f13 l13 f14 l14 f15 l15 | g12 m12 g13 m13 g14 m14 g15 m15
  const __m256i f12l12 = _mm256_unpackhi_epi16(fg8, lm8);

  // h0 n0 h1 n1 h2 n2 h3 n3 | i0 o0 i1 o1 i2 o2 i3 o3
  const __m256i h0n0 = _mm256_unpacklo_epi16(hi0, no0);
  // h4 n4 h5 n5 h6 n6 h7 n7 | i4 o4 i5 o5 i6 o6 i7 o7
  const __m256i h4n4 = _mm256_unpackhi_epi16(hi0, no0);
  // h8 n8 h9 n9 h10 n10 h11 n11 | i8 o8 i9 o9 i10 o10 i11 o11
  const __m256i h8n8 = _mm256_unpacklo_epi16(hi8, no8);
  // h12 n12 h13 n13 h14 n14 h15 n15 | i12 o12 i13 o13 i14 o14 i15 o15
  const __m256i h12n12 = _mm256_unpackhi_epi16(hi8, no8);

  //  b1 h1 b2 h2 b3 h3 b4 h4 |c1 i1 c2 i2 c3 i3 c4 i4
  const __m256i b1h1_b4h4 = _mm256_alignr_epi8(b4h4, b0h0, 4);
  //  b5 h5 b6 h6 b7 h7 b8 h8 | c5 i5 c6 i6 c7 i7 c8 i8
  const __m256i b5h5_b8h8 = _mm256_alignr_epi8(b8h8, b4h4, 4);
  //  d1 j1 d2 j2 d3 j3 d4 j4 | e1 k1 e2 k2 e3 k3 e4 k4
  const __m256i d1j1_d4j4 = _mm256_alignr_epi8(d4j4, d0j0, 4);
  //  d5 j5 d6 j6 d7 j7 d8 j8 | e5 k5 e6 k6 e7 k7 e8 k8
  const __m256i d5j5_d8j8 = _mm256_alignr_epi8(d8j8, d4j4, 4);
  //  f1 l1 f2 l2 f3 l3 f4 l4 | g1 m1 g2 m2 g3 m3 g4 m4
  const __m256i f1l1_f4l4 = _mm256_alignr_epi8(f4l4, f0l0, 4);
  //  f5 l5 f6 l6 f7 l7 f8 l8 | g5 m5 g6 m6 g7 m7 g8 m8
  const __m256i f5l5_f8l8 = _mm256_alignr_epi8(f8l8, f4l4, 4);
  //  h1 n1 h2 n2 h3 n3 h4 n4 |  i1 o1 i2 o2 i3 o3
  const __m256i h1n1_h4n4 = _mm256_alignr_epi8(h4n4, h0n0, 4);
  //  h5 n5 h6 n6 h7 n7 h8 n8| i5 o5 i6 o6 i7 o7 i8 o8
  const __m256i h5n5_h8n8 = _mm256_alignr_epi8(h8n8, h4n4, 4);

  // b7 h7 b8 h8 b9 h9 b10 h10 | c7 i7 c8 i8 c9 i9 c10 i10
  const __m256i b7h7_b10h10 = _mm256_alignr_epi8(b8h8, b4h4, 12);
  // b11 h11 b12 h12 b13 h13 b14 h14 | c11 i11 c12 i12 c13 i13 c14 i14
  const __m256i b11h11_b14h14 = _mm256_alignr_epi8(b12h12, b8h8, 12);
  // d7 j7 d8 j8 d9 j9 d10 j10 | e7 k7 e8 k8 e9 k9 e10 k10
  const __m256i d7j7_d10j10 = _mm256_alignr_epi8(d8j8, d4j4, 12);
  // d11 j11 d12 j12 d13 j13 d14 j14 | e11 k11 e12 k12 e13 k13 e14 k14
  const __m256i d11j11_d14j14 = _mm256_alignr_epi8(d12j12, d8j8, 12);
  // f7 l7 f8 l8 f9 l9 f10 l10 | g7 m7 g8 m8 g9 m9 g10 m10
  const __m256i f7l7_f10l10 = _mm256_alignr_epi8(f8l8, f4l4, 12);
  // f11 l11 f12 l12 f13 l13 f14 l14 | g11 m11 g12 m12 g13 m13 g14 m14
  const __m256i f11l11_f13l13 = _mm256_alignr_epi8(f12l12, f8l8, 12);
  // h7 n7 h8 n8 h9 n9 h10 n10 | i7 o7 i8 o8 i9 o9 i10 o10
  const __m256i h7n7_h10n10 = _mm256_alignr_epi8(h8n8, h4n4, 12);
  // h11 n11 h12 n12 h13 n13 h14 n14 | i11 o11 i12 o12 i13 o13 i14 o14
  const __m256i h11n11_h14n14 = _mm256_alignr_epi8(h12n12, h8n8, 12);

  const __m256i res_33 = _mm256_madd_epi16(b1h1_b4h4, fc14_15);
  const __m256i res_34 = _mm256_madd_epi16(b5h5_b8h8, fc14_15);
  const __m256i res_35 = _mm256_madd_epi16(d1j1_d4j4, fc14_15);
  const __m256i res_36 = _mm256_madd_epi16(d5j5_d8j8, fc14_15);
  const __m256i res_37 = _mm256_madd_epi16(f1l1_f4l4, fc14_15);
  const __m256i res_38 = _mm256_madd_epi16(f5l5_f8l8, fc14_15);
  const __m256i res_39 = _mm256_madd_epi16(h1n1_h4n4, fc14_15);
  const __m256i res_40 = _mm256_madd_epi16(h5n5_h8n8, fc14_15);

  const __m256i res_41 = _mm256_madd_epi16(b4h4, fc0);
  const __m256i res_42 = _mm256_madd_epi16(b8h8, fc0);
  const __m256i res_43 = _mm256_madd_epi16(d4j4, fc0);
  const __m256i res_44 = _mm256_madd_epi16(d8j8, fc0);
  const __m256i res_45 = _mm256_madd_epi16(f4l4, fc0);
  const __m256i res_46 = _mm256_madd_epi16(f8l8, fc0);
  const __m256i res_47 = _mm256_madd_epi16(h4n4, fc0);
  const __m256i res_48 = _mm256_madd_epi16(h8n8, fc0);

  const __m256i res_49 = _mm256_madd_epi16(b7h7_b10h10, fc15_14);
  const __m256i res_50 = _mm256_madd_epi16(b11h11_b14h14, fc15_14);
  const __m256i res_51 = _mm256_madd_epi16(d7j7_d10j10, fc15_14);
  const __m256i res_52 = _mm256_madd_epi16(d11j11_d14j14, fc15_14);
  const __m256i res_53 = _mm256_madd_epi16(f7l7_f10l10, fc15_14);
  const __m256i res_54 = _mm256_madd_epi16(f11l11_f13l13, fc15_14);
  const __m256i res_55 = _mm256_madd_epi16(h7n7_h10n10, fc15_14);
  const __m256i res_56 = _mm256_madd_epi16(h11n11_h14n14, fc15_14);

  accum_out_r0_r1_1 = _mm256_add_epi32(
      accum_out_r0_r1_1,
      _mm256_add_epi32(_mm256_add_epi32(res_33, res_41), res_49));
  accum_out_r0_r1_2 = _mm256_add_epi32(
      accum_out_r0_r1_2,
      _mm256_add_epi32(_mm256_add_epi32(res_34, res_42), res_50));
  accum_out_r2_r3_1 = _mm256_add_epi32(
      accum_out_r2_r3_1,
      _mm256_add_epi32(_mm256_add_epi32(res_35, res_43), res_51));
  accum_out_r2_r3_2 = _mm256_add_epi32(
      accum_out_r2_r3_2,
      _mm256_add_epi32(_mm256_add_epi32(res_36, res_44), res_52));
  accum_out_r4_r5_1 = _mm256_add_epi32(
      accum_out_r4_r5_1,
      _mm256_add_epi32(_mm256_add_epi32(res_37, res_45), res_53));
  accum_out_r4_r5_2 = _mm256_add_epi32(
      accum_out_r4_r5_2,
      _mm256_add_epi32(_mm256_add_epi32(res_38, res_46), res_54));
  accum_out_r6_r7_1 = _mm256_add_epi32(
      accum_out_r6_r7_1,
      _mm256_add_epi32(_mm256_add_epi32(res_39, res_47), res_55));
  accum_out_r6_r7_2 = _mm256_add_epi32(
      accum_out_r6_r7_2,
      _mm256_add_epi32(_mm256_add_epi32(res_40, res_48), res_56));

  // f12 f12 f12 f12 f12 f12 f12 f12 | f12 f12 f12 f12 f12 f12 f12 f12
  const __m256i fc_12 = _mm256_broadcastw_epi16(filt_coeff_2);

  // a0 a1 a2 a3 a4 a5 a6 a7 | b0 b1 b2 b3 b4 b5 b6 b7
  const __m256i ab0 = _mm256_permute2x128_si256(src_a0, src_b0, 0x20);
  // a8 a9 a10 a11 a12 a13 a14 a15 | b8 b9 b10 b11 b12 b13 b14 b15
  const __m256i ab8 = _mm256_permute2x128_si256(src_a0, src_b0, 0x31);

  // c0 c1 c2 c3 c4 c5 c6 c7 | d0 d1 d2 d3 d4 d5 d6 d7
  const __m256i cd0 = _mm256_permute2x128_si256(src_c0, src_d0, 0x20);
  // c8 c9 c10 c11 c12 c13 c14 c15 | d8 d9 d10 d11 d12 d13 d14 d15
  const __m256i cd8 = _mm256_permute2x128_si256(src_c0, src_d0, 0x31);

  // e0 e1 e2 e3 e4 e5 e6 e7 | f0 f1 f2 f3 f4 f5 f6 f7
  const __m256i ef0 = _mm256_permute2x128_si256(src_e0, src_f0, 0x20);
  // e8 e9 e10 e11 e12 e13 e14 e15 | f8 f9 f10 f11 f12 f13 f14 f15
  const __m256i ef8 = _mm256_permute2x128_si256(src_e0, src_f0, 0x31);

  // g0 g1 g2 g3 g4 g5 g6 g7 | h0 h1 h2 h3 h4 h5 h6 h7
  const __m256i gh0 = _mm256_permute2x128_si256(src_g0, src_h0, 0x20);
  // g8 g9 g10 g11 g12 g13 g14 g15 | h8 h9 h10 h11 h12 h13 h14 h15
  const __m256i gh8 = _mm256_permute2x128_si256(src_g0, src_h0, 0x31);

  // i0 i1 i2 i3 i4 i5 i6 i7 | j0 j1 j2 j3 j4 j5 j6 j7
  const __m256i ij0 = _mm256_permute2x128_si256(src_i0, src_j0, 0x20);
  // i8 i9 i10 i11 i12 i13 i14 i15 | j8 j9 j10 j11 j12 j13 j14 j15
  const __m256i ij8 = _mm256_permute2x128_si256(src_i0, src_j0, 0x31);

  // k0 k1 k2 k3 k4 k5 k6 k7 | l0 l1 l2 l3 l4 l5 l6 l7
  const __m256i kl0 = _mm256_permute2x128_si256(src_k0, src_l0, 0x20);
  // k8 k9 k10 k11 k12 k13 k14 k15 | l8 l9 l10 l11 l12 l13 l14 l15
  const __m256i kl8 = _mm256_permute2x128_si256(src_k0, src_l0, 0x31);

  // m0 m1 m2 m3 m4 m5 m6 m7 | n0 n1 n2 n3 n4 n5 n6 n7
  const __m256i mn0 = _mm256_permute2x128_si256(src_m0, src_n0, 0x20);
  // m8 m9 m10 m11 m12 m13 m14 m15 | n8 n9 n10 n11 n12 n13 n14 n15
  const __m256i mn8 = _mm256_permute2x128_si256(src_m0, src_n0, 0x31);

  // o0 o1 o2 o3 o4 o5 o6 o7 | p0 p1 p2 p3 p4 p5 p6 p7
  const __m256i op0 = _mm256_permute2x128_si256(src_o0, src_p0, 0x20);
  // o8 o9 o10 o11 o12 o13 o14 o15 | p8 p9 p10 p11 p12 p13 p14 p15
  const __m256i op8 = _mm256_permute2x128_si256(src_o0, src_p0, 0x31);

  // a4 i4 a5 i5 a6 i6 a7 i7 | b4 j4 b5 j5 b6 j6 b7 j7
  const __m256i a4i4 = _mm256_unpackhi_epi16(ab0, ij0);
  // a8 i8 a9 i9 a10 i10 a11 i11 | b8 j8 b9 j9 b10 j10 b11 j11
  const __m256i a8i8 = _mm256_unpacklo_epi16(ab8, ij8);
  // c4 k4 c5 k5 c6 k6 c7 k7 | d4 l4 d5 l5 d6 l6 d7 l7
  const __m256i c4k4 = _mm256_unpackhi_epi16(cd0, kl0);
  // c8 k8 c9 k9 c10 k10 c11 k11 | d8 l8 d9 l9 d10 l10 d11 l11
  const __m256i c8k8 = _mm256_unpacklo_epi16(cd8, kl8);
  // e4 m4 e5 m5 e6 m6 e7 m7 | f4 n4 f5 n5 f6 n6 f7 n7
  const __m256i e4m4 = _mm256_unpackhi_epi16(ef0, mn0);
  // e8 m8 e9 m9 e10 m10 e11 m11 | f8 n8 f9 n9 f10 n10 f11 n11
  const __m256i e8m8 = _mm256_unpacklo_epi16(ef8, mn8);
  // g4 o4 g5 o5 g6 o6 g7 o7 | h4 p4 h5 p5 h6 p6 h7 p7
  const __m256i g4o4 = _mm256_unpackhi_epi16(gh0, op0);
  // g8 o8 g9 o9 g10 o10 g11 o11 | h8 p8 h9 p9 h10 p10 h11 p11
  const __m256i g8o8 = _mm256_unpacklo_epi16(gh8, op8);

  const __m256i res_57 = _mm256_madd_epi16(a4i4, fc_12);
  const __m256i res_58 = _mm256_madd_epi16(a8i8, fc_12);
  const __m256i res_59 = _mm256_madd_epi16(c4k4, fc_12);
  const __m256i res_60 = _mm256_madd_epi16(c8k8, fc_12);
  const __m256i res_61 = _mm256_madd_epi16(e4m4, fc_12);
  const __m256i res_62 = _mm256_madd_epi16(e8m8, fc_12);
  const __m256i res_63 = _mm256_madd_epi16(g4o4, fc_12);
  const __m256i res_64 = _mm256_madd_epi16(g8o8, fc_12);

  accum_out_r0_r1_1 = _mm256_add_epi32(accum_out_r0_r1_1, res_57);
  accum_out_r0_r1_2 = _mm256_add_epi32(accum_out_r0_r1_2, res_58);
  accum_out_r2_r3_1 = _mm256_add_epi32(accum_out_r2_r3_1, res_59);
  accum_out_r2_r3_2 = _mm256_add_epi32(accum_out_r2_r3_2, res_60);
  accum_out_r4_r5_1 = _mm256_add_epi32(accum_out_r4_r5_1, res_61);
  accum_out_r4_r5_2 = _mm256_add_epi32(accum_out_r4_r5_2, res_62);
  accum_out_r6_r7_1 = _mm256_add_epi32(accum_out_r6_r7_1, res_63);
  accum_out_r6_r7_2 = _mm256_add_epi32(accum_out_r6_r7_2, res_64);

  // fc2 fc2 fc2 fc2 fc2 fc2 fc2 fc2 | fc2 fc2 fc2 fc2 fc2 fc2 fc2 fc2
  const __m256i filt22 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_0, 0xAA));
  // fc1 fc3 fc1 fc3 fc1 fc3 fc1 fc3 | fc1 fc3 fc1 fc3 fc1 fc3 fc1 fc3
  const __m256i filt13 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_0, 0x5D));
  // fc3 fc1 fc3 fc1 fc3 fc1 fc3 fc1 | fc3 fc1 fc3 fc1 fc3 fc1 fc3 fc1
  const __m256i filt31 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_0, 0xF7));

  // c0 g0 c1 g1 c2 g2 c3 g3 | d0 h0 d1 h1 d2 h2 d3 h3
  const __m256i c0g0 = _mm256_unpacklo_epi16(cd0, gh0);
  // c4 g4 c5 g5 c6 g6 c7 g7 | d4 h4 d5 h5 d6 h6 d7 h7
  const __m256i c4g4 = _mm256_unpackhi_epi16(cd0, gh0);
  // c8 g8 c9 g9 c10 g10 c11 g11 | d8 h8 d9 h9 d10 h10 d11 h11
  const __m256i c8g8 = _mm256_unpacklo_epi16(cd8, gh8);
  // c12 g12 c13 g13 c14 g14 c15 g15 | d12 h12 d13 h13 d14 h14 d15 h15
  const __m256i c12g12 = _mm256_unpackhi_epi16(cd8, gh8);

  // e0 i0 e1 i1 e2 i2 e3 i3 | f0 j0 f1 j1 f2 j2 f3 j3
  const __m256i e0i0 = _mm256_unpacklo_epi16(ef0, ij0);
  // e4 i4 e5 i5 e6 i6 e7 i7 | f4 j4 f5 j5 f6 j6 f7 j7
  const __m256i e4i4 = _mm256_unpackhi_epi16(ef0, ij0);
  // e8 i8 e9 i9 e10 i10 e11 i11 | f8 j8 f9 j9 f10 j10 f11 j11
  const __m256i e8i8 = _mm256_unpacklo_epi16(ef8, ij8);
  // e12 i12 e13 i13 e14 i14 e15 i15 | f12 j12 f13 j13 f14 j14 f15 j15
  const __m256i e12i12 = _mm256_unpackhi_epi16(ef8, ij8);

  // g0 k0 g1 k1 g2 k2 g3 k3 | h0 l0 h1 l1 h2 l2 h3 l3
  const __m256i g0k0 = _mm256_unpacklo_epi16(gh0, kl0);
  // g4 k4 g5 k5 g6 k6 g7 k7 | h4 l4 h5 l5 h6 l6 h7 l7
  const __m256i g4k4 = _mm256_unpackhi_epi16(gh0, kl0);
  // g8 k8 g9 k9 g10 k10 g11 k11 | h8 l8 h9 l9 h10 l10 h11 l11
  const __m256i g8k8 = _mm256_unpacklo_epi16(gh8, kl8);
  // g12 k12 g13 k13 g14 k14 g15 k15 | h12 l12 h13 l13 h14 l14 h15 l15
  const __m256i g12k12 = _mm256_unpackhi_epi16(gh8, kl8);

  // i0 m0 i1 m1 i2 m2 i3 m3 | j0 n0 j1 n1 j2 n2 j3 n3
  const __m256i i0m0 = _mm256_unpacklo_epi16(ij0, mn0);
  // i4 m4 i5 m5 i6 m6 i7 m7 | j4 n4 j5 n5 j6 n6 j7 n7
  const __m256i i4m4 = _mm256_unpackhi_epi16(ij0, mn0);
  // i8 m8 i9 m9 i10 m10 i11 m11 | j8 n8 j9 n9 j10 n10 j11 n11
  const __m256i i8m8 = _mm256_unpacklo_epi16(ij8, mn8);
  // i12 m12 i13 m13 i14 m14 i15 m15 | j12 n12 j13 n13 j14 n14 j15 n15
  const __m256i i12m12 = _mm256_unpackhi_epi16(ij8, mn8);

  // c3 g3 c4 g4 c5 g5 c6 g6 | d3 h3 d4 h4 d5 h5 d6 h6
  const __m256i c3g3_c6g6 = _mm256_alignr_epi8(c4g4, c0g0, 12);
  // c7 g7 c8 g8 c9 g9 c10 g10 | d7 h7 d8 h8 d9 h9 d10 h10
  const __m256i c7g7_c10g10 = _mm256_alignr_epi8(c8g8, c4g4, 12);
  // e3 i3 e4 i4 e5 i5 e6 i6 | f3 j3 f4 j4 f5 j5 f6 j6
  const __m256i e3i3_e6i6 = _mm256_alignr_epi8(e4i4, e0i0, 12);
  // e7 i7 e8 i8 e9 i9 e10 i10 | f7 j7 f8 j8 f9 j9 f10 j10
  const __m256i e7i7_e10i10 = _mm256_alignr_epi8(e8i8, e4i4, 12);
  // g3 k3 g4 k4 g5 k5 g6 k6 | h3 l3 h4 l4 h5 l5 h6 l6
  const __m256i g3k3_g6k6 = _mm256_alignr_epi8(g4k4, g0k0, 12);
  // g7 k7 g8 k8 g9 k9 g10 k10 | h7 l7 h8 l8 h9 l9 h10 l10
  const __m256i g7k7_g10k10 = _mm256_alignr_epi8(g8k8, g4k4, 12);
  // i3 m3 i4 m4 i5 m5 i6 m6 | j3 n3 j4 n4 j5 n5 j6 n6
  const __m256i i3m3_i6m6 = _mm256_alignr_epi8(i4m4, i0m0, 12);
  // i7 m7 i8 m8 i9 m9 i10 m10 | j7 n7 j8 n8 j9 n9 j10 n10
  const __m256i i7m7_i10m10 = _mm256_alignr_epi8(i8m8, i4m4, 12);

  // c5 g5 c6 g6 c7 g7 c8 g8 | d5 h5 d6 h6 d7 h7 d8 h8
  const __m256i c5g5_c8g8 = _mm256_alignr_epi8(c8g8, c4g4, 4);
  //  c9 g9 c10 g10 c11 g11 c12 g12 | d9 h9 d10 h10 d11 h11 d12 h12
  const __m256i c9g9_c12g12 = _mm256_alignr_epi8(c12g12, c8g8, 4);
  // e5 i5 e6 i6 e7 i7 e8 i8 | f5 j5 f6 j6 f7 j7 f8 j8
  const __m256i e5i5_e8i8 = _mm256_alignr_epi8(e8i8, e4i4, 4);
  // e9 i9 e10 i10 e11 i11 e12 i12 | f9 j9 f10 j10 f11 j11 f12 j12
  const __m256i e9i9_e12i12 = _mm256_alignr_epi8(e12i12, e8i8, 4);
  // g5 k5 g6 k6 g7 k7 g8 k8 | h5 l5 h6 l6 h7 l7 h8 l8
  const __m256i g5k5_g8k8 = _mm256_alignr_epi8(g8k8, g4k4, 4);
  // g9 k9 g10 k10 g11 k11 g12 k12 | h9 l9 h10 l10 h11 l11 h12 l12
  const __m256i g9k9_g12k12 = _mm256_alignr_epi8(g12k12, g8k8, 4);
  // i5 m5 i6 m6 i7 m7 i8 m8 | j5 n5 j6 n6 j7 n7 j8 n8
  const __m256i i5m5_i8m8 = _mm256_alignr_epi8(i8m8, i4m4, 4);
  // i9 m9 i10 m10 i11 m11 i12 m12 | j9 n9 j10 n10 j11 n11 j12 n12
  const __m256i i9m9_i12m12 = _mm256_alignr_epi8(i12m12, i8m8, 4);

  __m256i res_73 = _mm256_madd_epi16(c3g3_c6g6, filt13);
  __m256i res_74 = _mm256_madd_epi16(c7g7_c10g10, filt13);
  __m256i res_75 = _mm256_madd_epi16(e3i3_e6i6, filt13);
  __m256i res_76 = _mm256_madd_epi16(e7i7_e10i10, filt13);
  __m256i res_77 = _mm256_madd_epi16(g3k3_g6k6, filt13);
  __m256i res_78 = _mm256_madd_epi16(g7k7_g10k10, filt13);
  __m256i res_79 = _mm256_madd_epi16(i3m3_i6m6, filt13);
  __m256i res_80 = _mm256_madd_epi16(i7m7_i10m10, filt13);

  __m256i res_81 = _mm256_madd_epi16(c4g4, filt22);
  __m256i res_82 = _mm256_madd_epi16(c8g8, filt22);
  __m256i res_83 = _mm256_madd_epi16(e4i4, filt22);
  __m256i res_84 = _mm256_madd_epi16(e8i8, filt22);
  __m256i res_85 = _mm256_madd_epi16(g4k4, filt22);
  __m256i res_86 = _mm256_madd_epi16(g8k8, filt22);
  __m256i res_87 = _mm256_madd_epi16(i4m4, filt22);
  __m256i res_88 = _mm256_madd_epi16(i8m8, filt22);

  __m256i res_89 = _mm256_madd_epi16(c5g5_c8g8, filt31);
  __m256i res_90 = _mm256_madd_epi16(c9g9_c12g12, filt31);
  __m256i res_91 = _mm256_madd_epi16(e5i5_e8i8, filt31);
  __m256i res_92 = _mm256_madd_epi16(e9i9_e12i12, filt31);
  __m256i res_93 = _mm256_madd_epi16(g5k5_g8k8, filt31);
  __m256i res_94 = _mm256_madd_epi16(g9k9_g12k12, filt31);
  __m256i res_95 = _mm256_madd_epi16(i5m5_i8m8, filt31);
  __m256i res_96 = _mm256_madd_epi16(i9m9_i12m12, filt31);

  accum_out_r0_r1_1 = _mm256_add_epi32(
      accum_out_r0_r1_1,
      _mm256_add_epi32(_mm256_add_epi32(res_73, res_81), res_89));
  accum_out_r0_r1_2 = _mm256_add_epi32(
      accum_out_r0_r1_2,
      _mm256_add_epi32(_mm256_add_epi32(res_74, res_82), res_90));
  accum_out_r2_r3_1 = _mm256_add_epi32(
      accum_out_r2_r3_1,
      _mm256_add_epi32(_mm256_add_epi32(res_75, res_83), res_91));
  accum_out_r2_r3_2 = _mm256_add_epi32(
      accum_out_r2_r3_2,
      _mm256_add_epi32(_mm256_add_epi32(res_76, res_84), res_92));
  accum_out_r4_r5_1 = _mm256_add_epi32(
      accum_out_r4_r5_1,
      _mm256_add_epi32(_mm256_add_epi32(res_77, res_85), res_93));
  accum_out_r4_r5_2 = _mm256_add_epi32(
      accum_out_r4_r5_2,
      _mm256_add_epi32(_mm256_add_epi32(res_78, res_86), res_94));
  accum_out_r6_r7_1 = _mm256_add_epi32(
      accum_out_r6_r7_1,
      _mm256_add_epi32(_mm256_add_epi32(res_79, res_87), res_95));
  accum_out_r6_r7_2 = _mm256_add_epi32(
      accum_out_r6_r7_2,
      _mm256_add_epi32(_mm256_add_epi32(res_80, res_88), res_96));

  const __m256i fc13_9 = _mm256_broadcastd_epi32(_mm_unpacklo_epi16(
      _mm_bsrli_si128(filt_coeff_2, 2), _mm_bsrli_si128(filt_coeff_1, 8)));
  // fc10 fc11 - - - - - - | fc10 fc11 - - - - - -
  const __m256i fc10_11 =
      _mm256_broadcastd_epi32(_mm_bsrli_si128(filt_coeff_1, 10));
  // fc16 fc11 - - - - - - | fc16 fc11 - - - - - -
  const __m256i fc16_11 = _mm256_broadcastd_epi32(
      _mm_bsrli_si128(_mm_shufflehi_epi16(filt_coeff_1, 0x1B), 8));
  // fc10 fc9  - - - - - - | fc10 fc9 - - - - - -
  const __m256i fc10_9 = _mm256_broadcastd_epi32(
      _mm_bsrli_si128(_mm_shufflehi_epi16(filt_coeff_1, 0xE1), 8));

  const __m128i zero = _mm_set1_epi16(0x0);
  const __m256i fc13_z = _mm256_broadcastd_epi32(
      _mm_unpacklo_epi16(_mm_bsrli_si128(filt_coeff_2, 2), zero));

  // e0 e0 e1 e1 e2 e2 e3 e3 | f0 f0 f1 f1 f2 f2 f3 f3
  const __m256i e0e0 = _mm256_unpacklo_epi16(ef0, ef0);
  // e4 e4 e5 e5 e6 e6 e7 e7 | f4 f4 f5 f5 f6 f6 f7 f7
  const __m256i e4e4 = _mm256_unpackhi_epi16(ef0, ef0);
  // e8 e8 e9 e9 e10 e10 e11 e11 | f8 f8 f9 f9 f10 f10 f11 f11
  const __m256i e8e8 = _mm256_unpacklo_epi16(ef8, ef8);
  // e12 e12 e13 e13 e14 e14 e15 e15 | f12 f12 f13 f13 f14 f14 f15 f15
  const __m256i e12e12 = _mm256_unpackhi_epi16(ef8, ef8);

  // g0 g0 g1 g1 g2 g2 g3 g3 | h0 h0 h1 h1 h2 h2 h3 h3
  const __m256i g0g0 = _mm256_unpacklo_epi16(gh0, gh0);
  // g4 g4 g5 g5 g6 g6 g7 g7 | h4 h4 h5 h5 h6 h6 h7 h7
  const __m256i g4g4 = _mm256_unpackhi_epi16(gh0, gh0);
  // g8 g8 g9 g9 g10 g10 g11 g11 | h8 h8 h9 h9 h10 h10 h11 h11
  const __m256i g8g8 = _mm256_unpacklo_epi16(gh8, gh8);
  // g12 g12 g13 g13 g14 g14 g15 g15 | h12 h12 h13 h13 h14 h14 h15 h15
  const __m256i g12g12 = _mm256_unpackhi_epi16(gh8, gh8);

  // i0 i0 i1 i1 i2 i2 i3 i3 | j0 j0 j1 j1 j2 j2 j3 j3
  const __m256i i0i0 = _mm256_unpacklo_epi16(ij0, ij0);
  // i4 i4 i5 i5 i6 i6 i7 i7 | j4 j4 j5 j5 j6 j6 j7 j7
  const __m256i i4i4 = _mm256_unpackhi_epi16(ij0, ij0);
  // i8 i8 i9 i9 i10 i10 i11 i11 | j8 j8 j9 j9 j10 j10 j11 j11
  const __m256i i8i8 = _mm256_unpacklo_epi16(ij8, ij8);
  // i12 i12 i13 i13 i14 i14 i15 i15 | j12 j12 j13 j13 j14 j14 j15 j15
  const __m256i i12i12 = _mm256_unpackhi_epi16(ij8, ij8);

  // k0 k0 k1 k1 k2 k2 k3 k3 | l0 l0 l1 l1 l2 l2 l3 l3
  const __m256i k0k0 = _mm256_unpacklo_epi16(kl0, kl0);
  // k4 k4 k5 k5 k6 k6 k7 k7 | l4 l4 l5 l5 l6 l6 l7 l7
  const __m256i k4k4 = _mm256_unpackhi_epi16(kl0, kl0);
  // k8 k8 k9 k9 k10 k10 k11 k11 | l8 l8 l9 l9 l10 l10 l11 l11
  const __m256i k8k8 = _mm256_unpacklo_epi16(kl8, kl8);
  // k12 k12 k13 k13 k14 k14 k15 k15 | l12 l12 l13 l13 l14 l14 l15 l15
  const __m256i k12k12 = _mm256_unpackhi_epi16(kl8, kl8);

  // Pixel value corresponding filter coeff f13 and f9
  // e0 e1 e1 e2 e2 e3 e3 e4 | f0 f1 f1 f2 f2 f3 f3 f4
  __m256i e0e1 = _mm256_alignr_epi8(e4e4, e0e0, 2);
  // g0 g1 g1 g2 g2 g3 g3 g4 | h0 h1 h1 h2 h2 h3 h3 h4
  __m256i g0g1 = _mm256_alignr_epi8(g4g4, g0g0, 2);
  // i0 i1 i1 i2 i2 i3 i3 i4 | j0 j1 j1 j2 j2 j3 j3 j4
  __m256i i0i1 = _mm256_alignr_epi8(i4i4, i0i0, 2);
  // k0 k1 k1 k2 k2 k3 k3 k4 | l0 l1 l1 l2 l2 l3 l3 l4
  __m256i k0k1 = _mm256_alignr_epi8(k4k4, k0k0, 2);

  // Pixel value corresponding filter coeff f10 and f11
  // e2 e3 e3 e4 e4 e5 e5 e6 | f2 f3 f3 f4 f4 f5 f5 f6
  __m256i e2e3 = _mm256_alignr_epi8(e4e4, e0e0, 10);
  // g2 g3 g3 g4 g4 g5 g5 g6 | h2 h3 h3 h4 h4 h5 h5 h6
  __m256i g2g3 = _mm256_alignr_epi8(g4g4, g0g0, 10);
  // i2 i3 i3 i4 i4 i5 i5 i6 | j2 j3 j3 j4 j4 j5 j5 j6
  __m256i i2i3 = _mm256_alignr_epi8(i4i4, i0i0, 10);
  // k2 k3 k3 k4 k4 k5 k5 k6 | l2 l3 l3 l4 l4 l5 l5 l6
  __m256i k2k3 = _mm256_alignr_epi8(k4k4, k0k0, 10);

  // Pixel value corresponding filter coeff f16 and f11
  // e4 e5 e5 e6 e6 e7 e7 e8 | f4 f5 f5 f6 f6 f7 f7 f8
  __m256i e4e5 = _mm256_alignr_epi8(e8e8, e4e4, 2);
  // g4 g5 g5 g6 g6 g7 g7 g8 | h4 h5 h5 h6 h6 h7 h7 h8
  __m256i g4g5 = _mm256_alignr_epi8(g8g8, g4g4, 2);
  // i4 i5 i5 i6 i6 i7 i7 i8 | j4 j5 j5 j6 j6 j7 j7 j8
  __m256i i4i5 = _mm256_alignr_epi8(i8i8, i4i4, 2);
  // k4 k5 k5 k6 k6 k7 k7 k8 | l4 l5 l5 l6 l6 l7 l7 l8
  __m256i k4k5 = _mm256_alignr_epi8(k8k8, k4k4, 2);

  // Pixel value corresponding filter coeff f10 and f9
  // e6 e7 e7 e8 e8 e9 e9 e10 | f6 f7 f7 f8 f8 f9 f9 f10
  __m256i e6e7 = _mm256_alignr_epi8(e8e8, e4e4, 10);
  // g6 g7 g7 g8 g8 g9 g9 g10 | h6 h7 h7 h8 h8 h9 h9 h10
  __m256i g6g7 = _mm256_alignr_epi8(g8g8, g4g4, 10);
  // i6 i7 i7 i8 i8 i9 i9 i10 | j6 j7 j7 j8 j8 j9 j9 j10
  __m256i i6i7 = _mm256_alignr_epi8(i8i8, i4i4, 10);
  // k6 k7 k7 k8 k8 k9 k9 k10 | l6 l7 l7 l8 l8 l9 l9 l10
  __m256i k6k7 = _mm256_alignr_epi8(k8k8, k4k4, 10);

  // e8 e9 e9 e10 e10 e11 e11 e12 | f8 f9 f9 f10 f10 f11 f11 f12
  __m256i e8e9 = _mm256_alignr_epi8(e12e12, e8e8, 2);
  // g8 g9 g9 g10 g10 g11 g11 g12 | h8 h9 h9 h10 h10 h11 h11 h12
  __m256i g8g9 = _mm256_alignr_epi8(g12g12, g8g8, 2);
  // i8 i9 i9 i10 i10 i11 i11 i12 | j8 j9 j9 j10 j10 j11 j11 j12
  __m256i i8i9 = _mm256_alignr_epi8(i12i12, i8i8, 2);
  // k8 k9 k9 k10 k10 k11 k11 k12 | l8 l9 l9 l10 l10 l11 l11 l12
  __m256i k8k9 = _mm256_alignr_epi8(k12k12, k8k8, 2);

  // e10 e11 e11 e12 e12 e13 e13 e14 | f10 f11 f11 f12 f12 f13 f13 f14
  __m256i e10e11 = _mm256_alignr_epi8(e12e12, e8e8, 10);
  // g10 g11 g11 g12 g12 g13 g13 g14 | h10 h11 h11 h12 h12 h13 h13 h14
  __m256i g10g11 = _mm256_alignr_epi8(g12g12, g8g8, 10);
  // i10 i11 i11 i12 i12 i13 i13 i14 | j10 j11 j11 j12 j12 j13 j13 j14
  __m256i i10i11 = _mm256_alignr_epi8(i12i12, i8i8, 10);
  // k10 k11 k11 k12 k12 k13 k13 k14 | l10 l11 l11 l12 l12 l13 l13 l14
  __m256i k10k11 = _mm256_alignr_epi8(k12k12, k8k8, 10);

  __m256i res_97 = _mm256_madd_epi16(e0e1, fc13_9);
  __m256i res_98 = _mm256_madd_epi16(e4e5, fc13_9);
  __m256i res_99 = _mm256_madd_epi16(g0g1, fc13_9);
  __m256i res_100 = _mm256_madd_epi16(g4g5, fc13_9);
  __m256i res_101 = _mm256_madd_epi16(i0i1, fc13_9);
  __m256i res_102 = _mm256_madd_epi16(i4i5, fc13_9);
  __m256i res_103 = _mm256_madd_epi16(k0k1, fc13_9);
  __m256i res_104 = _mm256_madd_epi16(k4k5, fc13_9);

  __m256i res_105 = _mm256_madd_epi16(e2e3, fc10_11);
  __m256i res_106 = _mm256_madd_epi16(e6e7, fc10_11);
  __m256i res_107 = _mm256_madd_epi16(g2g3, fc10_11);
  __m256i res_108 = _mm256_madd_epi16(g6g7, fc10_11);
  __m256i res_109 = _mm256_madd_epi16(i2i3, fc10_11);
  __m256i res_110 = _mm256_madd_epi16(i6i7, fc10_11);
  __m256i res_111 = _mm256_madd_epi16(k2k3, fc10_11);
  __m256i res_112 = _mm256_madd_epi16(k6k7, fc10_11);

  __m256i res_113 = _mm256_madd_epi16(e4e5, fc16_11);
  __m256i res_114 = _mm256_madd_epi16(e8e9, fc16_11);
  __m256i res_115 = _mm256_madd_epi16(g4g5, fc16_11);
  __m256i res_116 = _mm256_madd_epi16(g8g9, fc16_11);
  __m256i res_117 = _mm256_madd_epi16(i4i5, fc16_11);
  __m256i res_118 = _mm256_madd_epi16(i8i9, fc16_11);
  __m256i res_119 = _mm256_madd_epi16(k4k5, fc16_11);
  __m256i res_120 = _mm256_madd_epi16(k8k9, fc16_11);

  __m256i res_121 = _mm256_madd_epi16(e6e7, fc10_9);
  __m256i res_122 = _mm256_madd_epi16(e10e11, fc10_9);
  __m256i res_123 = _mm256_madd_epi16(g6g7, fc10_9);
  __m256i res_124 = _mm256_madd_epi16(g10g11, fc10_9);
  __m256i res_125 = _mm256_madd_epi16(i6i7, fc10_9);
  __m256i res_126 = _mm256_madd_epi16(i10i11, fc10_9);
  __m256i res_127 = _mm256_madd_epi16(k6k7, fc10_9);
  __m256i res_128 = _mm256_madd_epi16(k10k11, fc10_9);

  __m256i res_129 = _mm256_madd_epi16(e8e8, fc13_z);
  __m256i res_130 = _mm256_madd_epi16(e12e12, fc13_z);
  __m256i res_131 = _mm256_madd_epi16(g8g8, fc13_z);
  __m256i res_132 = _mm256_madd_epi16(g12g12, fc13_z);
  __m256i res_133 = _mm256_madd_epi16(i8i8, fc13_z);
  __m256i res_134 = _mm256_madd_epi16(i12i12, fc13_z);
  __m256i res_135 = _mm256_madd_epi16(k8k8, fc13_z);
  __m256i res_136 = _mm256_madd_epi16(k12k12, fc13_z);

  accum_out_r0_r1_1 = _mm256_add_epi32(
      accum_out_r0_r1_1,
      _mm256_add_epi32(
          _mm256_add_epi32(
              _mm256_add_epi32(_mm256_add_epi32(res_97, res_105), res_113),
              res_121),
          res_129));
  accum_out_r0_r1_2 = _mm256_add_epi32(
      accum_out_r0_r1_2,
      _mm256_add_epi32(
          _mm256_add_epi32(
              _mm256_add_epi32(_mm256_add_epi32(res_98, res_106), res_114),
              res_122),
          res_130));
  accum_out_r2_r3_1 = _mm256_add_epi32(
      accum_out_r2_r3_1,
      _mm256_add_epi32(
          _mm256_add_epi32(
              _mm256_add_epi32(_mm256_add_epi32(res_99, res_107), res_115),
              res_123),
          res_131));
  accum_out_r2_r3_2 = _mm256_add_epi32(
      accum_out_r2_r3_2,
      _mm256_add_epi32(
          _mm256_add_epi32(
              _mm256_add_epi32(_mm256_add_epi32(res_100, res_108), res_116),
              res_124),
          res_132));
  accum_out_r4_r5_1 = _mm256_add_epi32(
      accum_out_r4_r5_1,
      _mm256_add_epi32(
          _mm256_add_epi32(
              _mm256_add_epi32(_mm256_add_epi32(res_101, res_109), res_117),
              res_125),
          res_133));
  accum_out_r4_r5_2 = _mm256_add_epi32(
      accum_out_r4_r5_2,
      _mm256_add_epi32(
          _mm256_add_epi32(
              _mm256_add_epi32(_mm256_add_epi32(res_102, res_110), res_118),
              res_126),
          res_134));
  accum_out_r6_r7_1 = _mm256_add_epi32(
      accum_out_r6_r7_1,
      _mm256_add_epi32(
          _mm256_add_epi32(
              _mm256_add_epi32(_mm256_add_epi32(res_103, res_111), res_119),
              res_127),
          res_135));
  accum_out_r6_r7_2 = _mm256_add_epi32(
      accum_out_r6_r7_2,
      _mm256_add_epi32(
          _mm256_add_epi32(
              _mm256_add_epi32(_mm256_add_epi32(res_104, res_112), res_120),
              res_128),
          res_136));

  // Rounding and clipping.
  accum_out_r0_r1_1 = round_power_of_two_signed_avx2(accum_out_r0_r1_1,
                                                     filter_config->prec_bits);
  accum_out_r0_r1_2 = round_power_of_two_signed_avx2(accum_out_r0_r1_2,
                                                     filter_config->prec_bits);
  accum_out_r2_r3_1 = round_power_of_two_signed_avx2(accum_out_r2_r3_1,
                                                     filter_config->prec_bits);
  accum_out_r2_r3_2 = round_power_of_two_signed_avx2(accum_out_r2_r3_2,
                                                     filter_config->prec_bits);
  accum_out_r4_r5_1 = round_power_of_two_signed_avx2(accum_out_r4_r5_1,
                                                     filter_config->prec_bits);
  accum_out_r4_r5_2 = round_power_of_two_signed_avx2(accum_out_r4_r5_2,
                                                     filter_config->prec_bits);
  accum_out_r6_r7_1 = round_power_of_two_signed_avx2(accum_out_r6_r7_1,
                                                     filter_config->prec_bits);
  accum_out_r6_r7_2 = round_power_of_two_signed_avx2(accum_out_r6_r7_2,
                                                     filter_config->prec_bits);

  const __m256i max = _mm256_set1_epi16((1 << bit_depth) - 1);
  const __m256i zero_256 = _mm256_setzero_si256();

  __m256i accum_out_r0_r1 =
      _mm256_packs_epi32(accum_out_r0_r1_1, accum_out_r0_r1_2);
  __m256i accum_out_r2_r3 =
      _mm256_packs_epi32(accum_out_r2_r3_1, accum_out_r2_r3_2);
  __m256i accum_out_r4_r5 =
      _mm256_packs_epi32(accum_out_r4_r5_1, accum_out_r4_r5_2);
  __m256i accum_out_r6_r7 =
      _mm256_packs_epi32(accum_out_r6_r7_1, accum_out_r6_r7_2);

  __m256i out_r0_r1 = highbd_clamp_epi16(accum_out_r0_r1, zero_256, max);
  __m256i out_r2_r3 = highbd_clamp_epi16(accum_out_r2_r3, zero_256, max);
  __m256i out_r4_r5 = highbd_clamp_epi16(accum_out_r4_r5, zero_256, max);
  __m256i out_r6_r7 = highbd_clamp_epi16(accum_out_r6_r7, zero_256, max);

  // Store the output.
  const int dst_id = block_row_begin * dst_stride + block_col_begin;
  _mm256_storeu2_m128i((__m128i *)(dst + dst_id + (1 * dst_stride)),
                       (__m128i *)(dst + dst_id), out_r0_r1);
  _mm256_storeu2_m128i((__m128i *)(dst + dst_id + (3 * dst_stride)),
                       (__m128i *)(dst + dst_id + (2 * dst_stride)), out_r2_r3);
  _mm256_storeu2_m128i((__m128i *)(dst + dst_id + (5 * dst_stride)),
                       (__m128i *)(dst + dst_id + (4 * dst_stride)), out_r4_r5);
  _mm256_storeu2_m128i((__m128i *)(dst + dst_id + (7 * dst_stride)),
                       (__m128i *)(dst + dst_id + (6 * dst_stride)), out_r6_r7);
}

// SIMD implementation to convolve a block of pixels with origin-symmetric, pc
// wiener filter corresponds to CONFIG_PC_WIENER loop restoration. DIAMOND shape
// with 16-tap (16 symmetric+1) or 13-tap (12 symmetric+1) or 7-tap (6 symmetric
// + 1) filter is used for convolution.
void av2_convolve_symmetric_highbd_avx2(const uint16_t *dgd, int stride,
                                        const NonsepFilterConfig *filter_config,
                                        const int16_t *filter, uint16_t *dst,
                                        int dst_stride, int bit_depth,
                                        int block_row_begin, int block_row_end,
                                        int block_col_begin,
                                        int block_col_end) {
  assert(!filter_config->subtract_center);

  const int num_rows = block_row_end - block_row_begin;
  const int num_cols = block_col_end - block_col_begin;

  const int num_sym_taps = filter_config->num_pixels / 2;

  // Dispatch to the appropriate SIMD function for the selected filter shape
  // and block size. If none are applicable, fall back to C
  // TODO(rachelbarker): Add plumbing logic to adjust_filter_and_config so that
  // the 6-tap branch here can actually be used for reduced-length filters
  if (num_rows == 4 && num_cols == 4 &&
      (filter_config->config == wienerns_simd_large_config_y ||
       !memcmp(wienerns_simd_large_config_y, filter_config->config,
               filter_config->num_pixels * 3 *
                   sizeof(filter_config->config[0][0])))) {
    if (filter[12] == 0 && filter[13] == 0 && filter[14] == 0 &&
        filter[15] == 0) {
      int16_t filter_[13];
      memcpy(filter_, filter, 13 * sizeof(filter[0]));
      filter_[12] = filter[16];
      const NonsepFilterConfig filter_config_ = { filter_config->prec_bits,
                                                  25,
                                                  0,
                                                  wienerns_simd_config_y,
                                                  NULL,
                                                  0,
                                                  0,
                                                  0,
                                                  0 };
      convolve_symmetric_highbd_13tap_avx2(dgd, stride, &filter_config_,
                                           filter_, dst, dst_stride, bit_depth,
                                           block_row_begin, block_col_begin);
    } else {
      convolve_symmetric_highbd_16tap9x9_avx2(
          dgd, stride, filter_config, filter, dst, dst_stride, bit_depth,
          block_row_begin, block_col_begin);
    }
    return;
  }
  if (num_rows == 4 && num_cols == 4 &&
      (filter_config->config == wienerns_simd_config_y ||
       !memcmp(wienerns_simd_config_y, filter_config->config,
               filter_config->num_pixels * 3 *
                   sizeof(filter_config->config[0][0])))) {
    convolve_symmetric_highbd_13tap_avx2(dgd, stride, filter_config, filter,
                                         dst, dst_stride, bit_depth,
                                         block_row_begin, block_col_begin);
  } else if (num_rows == 4 && num_cols == 4 && num_sym_taps == 6 &&
             (filter_config->config == wienerns_simd_config_uv_from_uvonly ||
              !memcmp(wienerns_simd_config_uv_from_uvonly,
                      filter_config->config,
                      filter_config->num_pixels * 3 *
                          sizeof(filter_config->config[0][0])))) {
    convolve_symmetric_highbd_7tap_avx2(dgd, stride, filter_config, filter, dst,
                                        dst_stride, bit_depth, block_row_begin,
                                        block_col_begin);
  } else {
    av2_convolve_symmetric_highbd_c(
        dgd, stride, filter_config, filter, dst, dst_stride, bit_depth,
        block_row_begin, block_row_end, block_col_begin, block_col_end);
  }
}

// AVX2 intrinsic function to convolve an 8x8 block with a origin-symmetric
// non-separable filter. For an 8x8 block, the convolution is
// performed with a DIAMOND-shaped filter, using either a 16-tap (16 symmetric +
// 1) or a 13-tap (12 symmetric + 1) filter.
void av2_convolve_symmetric_blk8x8_highbd_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_row_end, int block_col_begin,
    int block_col_end) {
  assert(!filter_config->subtract_center);

  const int num_rows = block_row_end - block_row_begin;
  const int num_cols = block_col_end - block_col_begin;

  if (num_rows == 8 && num_cols == 8 &&
      (filter_config->config == wienerns_simd_large_config_y ||
       !memcmp(wienerns_simd_large_config_y, filter_config->config,
               filter_config->num_pixels * 3 *
                   sizeof(filter_config->config[0][0])))) {
    if (filter[12] == 0 && filter[13] == 0 && filter[14] == 0 &&
        filter[15] == 0) {
      int16_t filter_[13];
      memcpy(filter_, filter, 13 * sizeof(filter[0]));
      filter_[12] = filter[16];
      const NonsepFilterConfig filter_config_ = { filter_config->prec_bits,
                                                  25,
                                                  0,
                                                  wienerns_simd_config_y,
                                                  NULL,
                                                  0,
                                                  0,
                                                  0,
                                                  0 };
      convolve_symmetric_highbd_13tap_blk8x8_avx2(dgd, stride, &filter_config_,
                                                  filter_, dst, dst_stride,
                                                  bit_depth, 0, 0);
    } else {
      convolve_symmetric_highbd_16tap9x9_blk8x8_avx2(
          dgd, stride, filter_config, filter, dst, dst_stride, bit_depth, 0, 0);
    }
    return;
  }
  if (num_rows == 8 && num_cols == 8 &&
      (filter_config->config == wienerns_simd_config_y ||
       !memcmp(wienerns_simd_config_y, filter_config->config,
               filter_config->num_pixels * 3 *
                   sizeof(filter_config->config[0][0])))) {
    convolve_symmetric_highbd_13tap_blk8x8_avx2(
        dgd, stride, filter_config, filter, dst, dst_stride, bit_depth,
        block_row_begin, block_col_begin);
  } else {
    av2_convolve_symmetric_highbd(
        dgd, stride, filter_config, filter, dst, dst_stride, bit_depth,
        block_row_begin, block_row_end, block_col_begin, block_col_end);
  }
}

void av2_convolve_mixedsymmetric_highbd_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_row_end, int block_col_begin,
    int block_col_end) {
  const int num_rows = block_row_end - block_row_begin;
  const int num_cols = block_col_end - block_col_begin;
  (void)num_rows;
  (void)num_cols;

  av2_convolve_mixedsymmetric_highbd_c(
      dgd, stride, filter_config, filter, dst, dst_stride, bit_depth,
      block_row_begin, block_row_end, block_col_begin, block_col_end);
}

// TODO(Arun Negi): Difference of source and center pixel needs to go through
// clip_base(). Implement clip_base() in intrinsic once the support is added.
//
// Implementation of DIAMOND shaped 6-tap filtering for block size of 4x4.
// The output for a particular pixel in a 4x4 block is calculated by considering
// a 5x5 grid surrounded by that pixel. The registers accum_out_r0r1 and
// accum_out_r2r3 are used to store the output. The following describes the
// algorithm briefly.
// Filter Coefficients: fc0 fc1 fc2 fc3 fc4 fc5 x x
// Load Source Data:
// src_ra0 = a0 a1 a2 a3 a4 a5 a6 a7
// src_rb0 = b0 b1 b2 b3 b4 b5 b6 b7
// src_rc0 = c0 c1 c2 c3 c4 c5 c6 c7
// src_rd0 = d0 d1 d2 d3 d4 d5 d6 d7
// src_re0 = e0 e1 e2 e3 e4 e5 e6 e7
// src_rf0 = f0 f1 f2 f3 f4 f5 f6 f7
// src_rg0 = g0 g1 g2 g3 g4 g5 g6 g7
// The output for a pixel located at c2 position is calculated as below.
// Filtered_c2 = ((a2-c2)+(e2-c2))*fc4 + ((b1-c2+d3-c2))*fc2 +
// (b2-c2+d2-c2)*fc0 + (b3-c2+d1-c2)*fc3 + (c0-c2+c4-c2)*fc5 +
// (c1-c2+c3-c2)*fc1 + c2*singleton_tap + dc_offset
// The source registers are unpacked such that the output corresponding to 2
// rows will be produced in a single register (i.e., processing 2 rows
// simultaneously).
//
// Example:
// The output corresponding to fc4 of rows 0 and 1 is achieved like below.
// __m256i centerpixel_row01 = c2 c2 c3 c3 c4 c4 c5 c5 | d2 d2 d3 d3 d4 d4 d5 d5
// __m256i src_reg3 = a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
// __m256i filter_4 = fc4 fc4 fc4 fc4 fc4 fc4 fc4 fc4 | fc4 fc4 fc4 fc4 fc4 fc4
// fc4 fc4
// __m256 src_reg3 = _mm256_sub_epi16(src_reg3, centerpixel_row01);
//  __m256i out_f4_01 = _mm256_madd_epi16(src_reg3, filter_4);
//                   = ((a2-c2)*fc4+(e2-c2)*fc4) (a3-c3)*fc4+(e3-c3)*fc4) .. |
//                   (b2-d2)*fc4 +(f2-d2)*fc4) . .
// Here, out_f4_01 contains partial output of rows 0 and 1 corresponding to fc4.
static INLINE void apply_6tap_filtering(const uint16_t *dgd, int stride,
                                        const __m128i filt_coeff,
                                        __m256i *accum_out_r0r1,
                                        __m256i *accum_out_r2r3,
                                        int block_row_begin,
                                        int block_col_begin) {
  // Load source data
  const int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 2 * stride - 2;

  // Load source data required for chroma filtering into registers.
  LOAD_SRC_DATA_FOR_CHROMA_FILTERING(src_ptr, stride)

  // Forms 256bit source registers from loaded 128bit registers and pack them
  // accordingly for filtering process.
  FORM_AND_PACK_REG_FOR_CHROMA_FILTERING()

  // Derive registers to hold center pixel
  // c2 c2 c3 c3 c4 c4 c5 c5 | d2 d2 d3 d3 d4 d4 d5 d5
  const __m256i cp0 = _mm256_bslli_epi128(src_cd, 4);
  const __m256i center_pixel_row01_0 = _mm256_unpackhi_epi16(cp0, cp0);
  // e2 e2 e3 e3 e4 e4 e5 e5 | f2 f2 f3 f3 f4 f4 f5 f5
  const __m256i cp1 = _mm256_bslli_epi128(src_ef, 4);
  const __m256i center_pixel_row23_0 = _mm256_unpackhi_epi16(cp1, cp1);
  const __m256i zero = _mm256_set1_epi16(0x0);
  // 0 c2 0 c3 0 c4 0 c5 | 0 d2 0 d3 0 d4 0 d5
  const __m256i center_pixel_row01_1 = _mm256_unpackhi_epi16(zero, cp0);
  // 0 e2 0 e3 0 e4 0 e5 | 0 f2 0 f3 0 f4 0 f5
  const __m256i center_pixel_row23_1 = _mm256_unpackhi_epi16(zero, cp1);

  // Output corresponding to filter coefficient f4.
  // a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
  const __m256i ru8_0 = _mm256_alignr_epi8(ru1, ru0, 8);
  const __m256i ru8 = _mm256_sub_epi16(ru8_0, center_pixel_row01_0);
  // c2 g2 c3 g3 c4 g4 c5 g5 | d2 h2 d3 h3 d4 h4 d5 h5
  const __m256i ru9_0 = _mm256_alignr_epi8(ru3, ru2, 8);
  const __m256i ru9 = _mm256_sub_epi16(ru9_0, center_pixel_row23_0);

  // f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4 f4
  const __m256i fc4 =
      _mm256_broadcastd_epi32(_mm_unpackhi_epi16(filt_coeff, filt_coeff));
  // r00 r01 r02 r03 | r10 r11 r12 r13
  const __m256i out_f4_r0r1 = _mm256_madd_epi16(ru8, fc4);
  *accum_out_r0r1 = _mm256_add_epi32(out_f4_r0r1, *accum_out_r0r1);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  const __m256i out_f4_r2r3 = _mm256_madd_epi16(ru9, fc4);
  *accum_out_r2r3 = _mm256_add_epi32(out_f4_r2r3, *accum_out_r2r3);

  // Output corresponding to filter coefficient 2, 0, 3.
  // b1 d1 b2 d2 b3 d3 b4 d4 | c1 e1 c2 e2 c3 e3 c4 e4
  const __m256i ru10_0 = _mm256_alignr_epi8(ru5, ru4, 4);
  __m256i ru10 = _mm256_sub_epi16(ru10_0, center_pixel_row01_0);
  // d1 f1 d2 f2 d3 f3 d4 f4 | e1 g1 e2 g2 e3 g3 e4 g4
  const __m256i ru11_0 = _mm256_alignr_epi8(ru7, ru6, 4);
  __m256i ru11 = _mm256_sub_epi16(ru11_0, center_pixel_row23_0);

  // b2 d2 b3 d3 b4 d4 b5 d5 | c2 e2 c3 e3 c4 e4 c5 e5
  const __m256i ru12_0 = _mm256_alignr_epi8(ru5, ru4, 8);
  __m256i ru12 = _mm256_sub_epi16(ru12_0, center_pixel_row01_0);
  // d2 f2 d3 f3 d4 f4 d5 f5 | e2 g2 e3 g3 e4 g4 e5 g5
  const __m256i ru13_0 = _mm256_alignr_epi8(ru7, ru6, 8);
  __m256i ru13 = _mm256_sub_epi16(ru13_0, center_pixel_row23_0);

  // b3 d3 b4 d4 b5 d5 b6 d6 | c3 e3 c4 e4 c5 e5 c6 e6
  const __m256i ru14_0 = _mm256_alignr_epi8(ru5, ru4, 12);
  __m256i ru14 = _mm256_sub_epi16(ru14_0, center_pixel_row01_0);
  // d3 f3 d4 f4 d5 f5 d6 f6 | e3 g3 e4 g4 e5 g5 e6 g6
  const __m256i ru15_0 = _mm256_alignr_epi8(ru7, ru6, 12);
  __m256i ru15 = _mm256_sub_epi16(ru15_0, center_pixel_row23_0);

  // f2 f3 f2 f3 - - - -
  const __m256i fc23 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff, 0x0E));
  // f0 f0 f0 f0 - - - -
  const __m256i fc00 = _mm256_broadcastw_epi16(filt_coeff);
  // f3 f2 f3 f2 - - - -
  const __m256i fc32 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff, 0x0B));

  // Multiply the formed registers with filter and add the result to accumulate
  // registers.
  MADD_AND_ACCUM_FOR_CHROMA_FILTERING(fc23, fc00, fc32)

  // Output corresponding to filter coefficient 5, 1, 6.
  // c0 c1 c1 c2 c2 c3 c3 c4 || d0 d1 d1 d2 d2 d3 d3 d4
  ru10 = _mm256_unpacklo_epi16(src_cd, _mm256_bsrli_epi128(src_cd, 2));
  ru10 = _mm256_sub_epi16(ru10, center_pixel_row01_0);
  // e0 e1 e1 e2 e2 e3 e3 e4 || f0 f1 f1 f2 f2 f3 f3 f4
  ru11 = _mm256_unpacklo_epi16(src_ef, _mm256_bsrli_epi128(src_ef, 2));
  ru11 = _mm256_sub_epi16(ru11, center_pixel_row23_0);
  // c2 c3 c3 c4 c4 c5 c5 c6 || d2 d3 d3 d4 d4 d5 d5 d6
  ru12 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_cd, 4),
                               _mm256_bsrli_epi128(src_cd, 6));
  ru12 = _mm256_sub_epi16(ru12, center_pixel_row01_1);
  // e2 e3 e3 e4 e4 e5 e5 e6 || f2 f3 f3 f4 f4 f5 f5 f6
  ru13 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_ef, 4),
                               _mm256_bsrli_epi128(src_ef, 6));
  ru13 = _mm256_sub_epi16(ru13, center_pixel_row23_1);
  // c4 0 c5 0 c6 0 c7 0 || d4 0 d5 0 d6 0 d7 0
  ru14 = _mm256_unpackhi_epi16(src_cd, zero);
  ru14 = _mm256_sub_epi16(ru14, center_pixel_row01_0);
  // e4 0 e5 0 e6 0 e7 0 || f4 0 f5 0 f6 0 f7 0
  ru15 = _mm256_unpackhi_epi16(src_ef, zero);
  ru15 = _mm256_sub_epi16(ru15, center_pixel_row23_0);

  // f5 f1 f5 f1 - - - -
  const __m128i filt51 =
      _mm_blend_epi16(filt_coeff, _mm_bsrli_si128(filt_coeff, 4), 0x08);
  const __m256i fc51 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt51, 0x07));
  // f6 f1 f6 f1 - - - -
  const __m128i filt61 =
      _mm_blend_epi16(filt_coeff, _mm_bsrli_si128(filt_coeff, 6), 0x08);
  const __m256i fc61 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt61, 0x07));
  // f5 0 f5 0 f5 0 f5 0 - -
  const __m256i fc5z = _mm256_blend_epi16(fc51, zero, 0xAA);

  // Multiply the formed registers with filter and add the result to accumulate
  // registers.
  MADD_AND_ACCUM_FOR_CHROMA_FILTERING(fc51, fc61, fc5z)
}

static INLINE void apply_asym_12tap_filtering(
    const uint16_t *dgd, int stride, const __m128i filt_coeff_lo,
    const __m128i filt_coeff_hi, __m256i *accum_out_r0r1,
    __m256i *accum_out_r2r3, int block_row_begin, int block_col_begin) {
  // Load source data
  const int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 2 * stride - 2;

  // Load source data required for chroma filtering into registers.
  LOAD_SRC_DATA_FOR_CHROMA_FILTERING(src_ptr, stride)

  // Forms 256bit source registers from loaded 128bit registers and pack them
  // accordingly for filtering process.
  FORM_AND_PACK_REG_FOR_CHROMA_FILTERING()

  // Derive registers to hold center pixel
  // c2 c2 c3 c3 c4 c4 c5 c5 | d2 d2 d3 d3 d4 d4 d5 d5
  const __m256i cp0 = _mm256_bslli_epi128(src_cd, 4);
  const __m256i center_pixel_row01_0 = _mm256_unpackhi_epi16(cp0, cp0);
  // e2 e2 e3 e3 e4 e4 e5 e5 | f2 f2 f3 f3 f4 f4 f5 f5
  const __m256i cp1 = _mm256_bslli_epi128(src_ef, 4);
  const __m256i center_pixel_row23_0 = _mm256_unpackhi_epi16(cp1, cp1);

  // Output corresponding to filter coefficient 8 and 9.
  // a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
  const __m256i ru8_0 = _mm256_alignr_epi8(ru1, ru0, 8);
  const __m256i ru8 = _mm256_sub_epi16(ru8_0, center_pixel_row01_0);
  // c2 g2 c3 g3 c4 g4 c5 g5 | d2 h2 d3 h3 d4 h4 d5 h5
  const __m256i ru9_0 = _mm256_alignr_epi8(ru3, ru2, 8);
  const __m256i ru9 = _mm256_sub_epi16(ru9_0, center_pixel_row23_0);

  // f9 f8 f9 f8 f9 f8 f9 f8 | f9 f8 f9 f8 f9 f8 f9 f8
  const __m256i fc98 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_hi, 0x01));
  // r00 r01 r02 r03 | r10 r11 r12 r13
  const __m256i out_f98_r0r1 = _mm256_madd_epi16(ru8, fc98);
  *accum_out_r0r1 = _mm256_add_epi32(out_f98_r0r1, *accum_out_r0r1);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  const __m256i out_f98_r2r3 = _mm256_madd_epi16(ru9, fc98);
  *accum_out_r2r3 = _mm256_add_epi32(out_f98_r2r3, *accum_out_r2r3);

  // Output corresponding to filter coefficient 2, 0, 3.
  // b1 d1 b2 d2 b3 d3 b4 d4 | c1 e1 c2 e2 c3 e3 c4 e4
  const __m256i ru10_0 = _mm256_alignr_epi8(ru5, ru4, 4);
  __m256i ru10 = _mm256_sub_epi16(ru10_0, center_pixel_row01_0);
  // d1 f1 d2 f2 d3 f3 d4 f4 | e1 g1 e2 g2 e3 g3 e4 g4
  const __m256i ru11_0 = _mm256_alignr_epi8(ru7, ru6, 4);
  __m256i ru11 = _mm256_sub_epi16(ru11_0, center_pixel_row23_0);

  // b2 d2 b3 d3 b4 d4 b5 d5 | c2 e2 c3 e3 c4 e4 c5 e5
  const __m256i ru12_0 = _mm256_alignr_epi8(ru5, ru4, 8);
  __m256i ru12 = _mm256_sub_epi16(ru12_0, center_pixel_row01_0);
  // d2 f2 d3 f3 d4 f4 d5 f5 | e2 g2 e3 g3 e4 g4 e5 g5
  const __m256i ru13_0 = _mm256_alignr_epi8(ru7, ru6, 8);
  __m256i ru13 = _mm256_sub_epi16(ru13_0, center_pixel_row23_0);

  // b3 d3 b4 d4 b5 d5 b6 d6 | c3 e3 c4 e4 c5 e5 c6 e6
  const __m256i ru14_0 = _mm256_alignr_epi8(ru5, ru4, 12);
  __m256i ru14 = _mm256_sub_epi16(ru14_0, center_pixel_row01_0);
  // d3 f3 d4 f4 d5 f5 d6 f6 | e3 g3 e4 g4 e5 g5 e6 g6
  const __m256i ru15_0 = _mm256_alignr_epi8(ru7, ru6, 12);
  __m256i ru15 = _mm256_sub_epi16(ru15_0, center_pixel_row23_0);

  // f5 f7 f5 f7 - - - -
  const __m256i fc57 = _mm256_broadcastd_epi32(
      _mm_shufflelo_epi16(_mm_bsrli_si128(filt_coeff_lo, 8), 0x0D));
  // f1 f0 f1 f0 - - - -
  const __m256i fc10 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_lo, 0x01));
  // f6 f4 f6 f4 - - - -
  const __m256i fc64 = _mm256_broadcastd_epi32(
      _mm_shufflelo_epi16(_mm_bsrli_si128(filt_coeff_lo, 8), 0x02));

  // Multiply the formed registers with filter and add the result to accumulate
  // registers.
  MADD_AND_ACCUM_FOR_CHROMA_FILTERING(fc57, fc10, fc64)

  // Output corresponding to filter coefficient 5, 1, 6.
  // c0 c1 c1 c2 c2 c3 c3 c4 || d0 d1 d1 d2 d2 d3 d3 d4
  ru10 = _mm256_unpacklo_epi16(src_cd, _mm256_bsrli_epi128(src_cd, 2));
  ru10 = _mm256_sub_epi16(ru10, center_pixel_row01_0);
  // e0 e1 e1 e2 e2 e3 e3 e4 || f0 f1 f1 f2 f2 f3 f3 f4
  ru11 = _mm256_unpacklo_epi16(src_ef, _mm256_bsrli_epi128(src_ef, 2));
  ru11 = _mm256_sub_epi16(ru11, center_pixel_row23_0);

  // c3 c4 c4 c5 c5 c6 c6 c7 || d3 d4 d4 d5 d5 d6 d6 d7
  ru12 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_cd, 6),
                               _mm256_bsrli_epi128(src_cd, 8));
  ru12 = _mm256_sub_epi16(ru12, center_pixel_row01_0);
  // e3 e4 e4 e5 e5 e6 e6 e7 || f3 f4 f4 f5 f5 f6 f6 f7
  ru13 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(src_ef, 6),
                               _mm256_bsrli_epi128(src_ef, 8));
  ru13 = _mm256_sub_epi16(ru13, center_pixel_row23_0);

  // f11  f3 f11  f3 - - - -
  const __m256i fc113 = _mm256_broadcastd_epi32(_mm_unpacklo_epi16(
      _mm_bsrli_si128(filt_coeff_hi, 6), _mm_bsrli_si128(filt_coeff_lo, 6)));

  //  f2 f10  f2 f10 - - - -
  const __m256i fc210 = _mm256_broadcastd_epi32(_mm_unpacklo_epi16(
      _mm_bsrli_si128(filt_coeff_lo, 4), _mm_bsrli_si128(filt_coeff_hi, 4)));

  // Multiply the formed registers with filter and add the result to accumulate
  // registers.
  MADD_AND_ACCUM_FOR_CHROMA_FILTERING_2(fc113, fc210)
}

// AVX2 intrinsic for convolve wiener non-separable loop restoration with 6-tap
// filtering. The output for a particular pixel in a 4x4 block is calculated
// with DIAMOND shaped filter considering a 5x5 grid surrounded by that pixel.
// Filter Coefficients: fc0 fc1 fc2 fc3 fc4 fc5 x x
// Load Source Data:
// src_ra0 = a0 a1 a2 a3 a4 a5 a6 a7
// src_rb0 = b0 b1 b2 b3 b4 b5 b6 b7
// src_rc0 = c0 c1 c2 c3 c4 c5 c6 c7
// src_rd0 = d0 d1 d2 d3 d4 d5 d6 d7
// src_re0 = e0 e1 e2 e3 e4 e5 e6 e7
// src_rf0 = f0 f1 f2 f3 f4 f5 f6 f7
// src_rg0 = g0 g1 g2 g3 g4 g5 g6 g7
// The output for a pixel located at c2 position is calculated as below.
// Filtered_c2 = ((a2-c2)+(e2-c2))*fc4 + ((b1-c2+d3-c2))*fc2 +
// (b2-c2+d2-c2)*fc0 + (b3-c2+d1-c2)*fc3 + (c0-c2+c4-c2)*fc5 +
// (c1-c2+c3-c2)*fc1 + c2*singleton_tap + dc_offset
// The source registers are unpacked such that the output corresponding to 2
// rows will be produced in a single register (i.e., processing 2 rows
// simultaneously).
//
// Example:
// The output corresponding to fc4 of rows 0 and 1 is achieved like below.
// __m256i centerpixel_row01 = c2 c2 c3 c3 c4 c4 c5 c5 | d2 d2 d3 d3 d4 d4 d5 d5
// __m256i src_reg3 = a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
// __m256i filter_4 = fc4 fc4 fc4 fc4 fc4 fc4 fc4 fc4 | fc4 fc4 fc4 fc4 fc4 fc4
// fc4 fc4
// __m256 src_reg3 = _mm256_sub_epi16(src_reg3, centerpixel_row01);
//  __m256i out_f4_01 = _mm256_madd_epi16(src_reg3, filter_4);
//                   = ((a2-c2)*fc4+(e2-c2)*fc4) (a3-c3)*fc4+(e3-c3)*fc4) .. |
//                   (b2-d2)*fc4 +(f2-d2)*fc4) . .
// Here, out_f4_01 contains partial output of rows 0 and 1 corresponding to fc4.
void av2_convolve_symmetric_subtract_center_highbd_6tap_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  // Derive singleton_tap and dc_offset.
  const int32_t singleton_tap = 1 << filter_config->prec_bits;
  int32_t dc_offset = 0;
  if (filter_config->num_pixels % 2) {
    const int dc_offset_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    dc_offset = filter[dc_offset_tap_index];
  }

  // Load filter tap values.
  // fc0 fc1 fc2 fc3 fc4 fc5 center_tap x
  const __m128i filt_coeff_0 = _mm_loadu_si128((__m128i const *)(filter));
  // Replace the center_tap with derived singleton_tap.
  const __m128i center_tap = _mm_set1_epi16(singleton_tap);
  const __m128i filt_coeff = _mm_blend_epi16(filt_coeff_0, center_tap, 0x40);

  // Initializing the output registers with zero
  __m256i accum_out_r0r1 = _mm256_setzero_si256();
  __m256i accum_out_r2r3 = _mm256_setzero_si256();

  // Perform 6-tap filtering on source buffer
  apply_6tap_filtering(dgd, stride, filt_coeff, &accum_out_r0r1,
                       &accum_out_r2r3, block_row_begin, block_col_begin);

  // Offset addition
  const __m128i offset_reg = _mm_set1_epi32(dc_offset);
  const __m256i ofs = _mm256_inserti128_si256(
      _mm256_castsi128_si256(offset_reg), offset_reg, 1);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, ofs);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, ofs);

  // Store the output after rounding and clipping
  round_and_store_avx2(dst, dst_stride, filter_config, bit_depth,
                       accum_out_r0r1, accum_out_r2r3, block_row_begin,
                       block_col_begin);
}

// TODO(Arun Negi): Difference of source and center pixel needs to go through
// clip_base(). Implement clip_base() in intrinsic once the support is added.
//
// AVX2 intrinsic for convolve wiener non-separable loop restoration with
// 12/13-tap filtering. The output for a particular pixel in a 4x4 block is
// calculated with DIAMOND shaped filter considering a 7x7 grid surrounded by
// that pixel.
// The following describes the design considered for intrinsic
// implementation.
// Filter Coefficients: fc0 fc1 fc2 fc3 fc4 fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc12
// Load Source Data:
// src_ra0 = a0 a1 a2 a3 a4 a5 a6 a7
// src_rb0 = b0 b1 b2 b3 b4 b5 b6 b7
// src_rc0 = c0 c1 c2 c3 c4 c5 c6 c7
// src_rd0 = d0 d1 d2 d3 d4 d5 d6 d7
// src_re0 = e0 e1 e2 e3 e4 e5 e6 e7
// src_rf0 = f0 f1 f2 f3 f4 f5 f6 f7
// src_rg0 = g0 g1 g2 g3 g4 g5 g6 g7
// The output for a pixel located at d3 position is calculated as below.
// Filtered_d3 = ((a3-d3)+(g3-d3))*fc10 +
// ((b2-d3+f4-d3))*fc6 + (b3-d3+f3-d3)*fc2 + (b4-d3+f5-d3)*fc7 +
// (c1-d3+e5-d3)*fc8 + (c2-d3+e4)*fc4 + (c3-d3+e3-d3)*fc0 + (c4-d3+e2-d3)*fc5 +
// (c4-d3+e2-d3)*fc5 + (c5-d3+e1-d3)*fc9 + (d0-d3+d6-d3)*fc11 +
// (d1-d3+d5-d3)*fc03 + (d2-d3+d4-d3)*fc01 + d3*f12 + dc_offset
// The source registers are unpacked such that the output corresponding to 2
// rows will be produced in a single register (i.e., processing 2 rows
// simultaneously).
//
// Example:
// The output corresponding to fc10 of rows 0 and 1 is achieved like below.
// __m256i centerpixel_row01 = d3 d3 d4 d4 d5 d5 d6 d6 | e3 e3 e4 e4 e5 e5 e6 e6
// __m256i src_reg3 = a3 g3 a4 g4 a5 g5 a6 g6 | b3 h3 b4 h4 b5 h5 b6 h6
// __m256i filter_10 = f10 f10 f10 f10 f10 f10 f10 f10 | f10 f10 f10 f10 f10 f10
// f10 f10
// __m256 src_reg3 = _mm256_sub_epi16(src_reg3, centerpixel_row01);
// __m256i out_f10_01 = _mm256_madd_epi16(src_reg3, filter_10);
//                   = ((a3-d3)*f10+(g3-d3)*f10) (a4-d4)*f10+(g4-d4)*f10) .. |
//                   (b3-e3)*f10+(h3-e3)*f10) . .
// Here, out_f10_01 contains partial output of rows 0 and 1 corresponding to
// fc10.
void av2_convolve_symmetric_subtract_center_highbd_12tap_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  // Derive singleton_tap and dc_offset.
  const int32_t singleton_tap = 1 << filter_config->prec_bits;
  int32_t dc_offset = 0;
  if (filter_config->num_pixels % 2) {
    const int dc_offset_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    dc_offset = filter[dc_offset_tap_index];
  }

  // Load source data.
  int src_index_start = block_row_begin * stride + block_col_begin;
  const uint16_t *src_ptr = dgd + src_index_start - 3 * stride - 3;
  const __m128i src_a0 = _mm_loadu_si128((__m128i const *)src_ptr);
  const __m128i src_b0 = _mm_loadu_si128((__m128i const *)(src_ptr + stride));
  const __m128i src_c0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 2 * stride));
  const __m128i src_d0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 3 * stride));
  const __m128i src_e0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride));
  const __m128i src_f0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 5 * stride));
  const __m128i src_g0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 6 * stride));
  const __m128i src_h0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride));
  const __m128i src_i0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 8 * stride));
  const __m128i src_j0 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 9 * stride));

  // Load filter tap values.
  // fc0 fc1 fc2 fc3 fc4 fc5 fc6 fc7
  const __m128i filt_coeff_0 = _mm_loadu_si128((__m128i const *)(filter));
  // fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc12
  const __m128i filta = _mm_loadu_si128((__m128i const *)(filter + 5));
  // Replace the fc12 with derived singleton_tap.
  const __m128i center_tap = _mm_set1_epi16(singleton_tap);
  const __m128i filt_coeff_1 = _mm_blend_epi16(filta, center_tap, 0x80);

  // Form 256bit source registers.
  // a0 a1 a2 a3 a4 a5 a6 a7 | b0 b1 b2 b3 b4 b5 b6 b7
  const __m256i src_ab =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_a0), src_b0, 1);
  // c0 c1 c2 c3 c4 c5 c6 c7 | d0 d1 d2 d3 d4 d5 d6 d7
  const __m256i src_cd =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_c0), src_d0, 1);
  // e0 e1 e2 e3 e4 e5 e6 e7 | f0 f1 f2 f3 f4 f5 f6 f7
  const __m256i src_ef =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_e0), src_f0, 1);
  // g0 g1 g2 g3 g4 g5 g6 g7 | h0 h1 h2 h3 h4 h5 h6 h7
  const __m256i src_gh =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_g0), src_h0, 1);
  // i0 i1 i2 i3 i4 i5 i6 i7 | j0 j1 j2 j3 j4 j5 j6 j7
  const __m256i src_ij =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_i0), src_j0, 1);

  // Derive registers to hold center pixel
  const __m128i center_pixel_d = _mm_bslli_si128(src_d0, 2);
  const __m128i cp_d_high = _mm_unpackhi_epi16(center_pixel_d, center_pixel_d);
  const __m128i center_pixel_e = _mm_bslli_si128(src_e0, 2);
  const __m128i cp_e_high = _mm_unpackhi_epi16(center_pixel_e, center_pixel_e);
  // d3 d3 d4 d4 d5 d5 d6 d6 | e3 e3 e4 e4 e5 e5 e6 e6
  const __m256i center_pixel_row01_0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(cp_d_high), cp_e_high, 1);
  const __m128i center_pixel_f = _mm_bslli_si128(src_f0, 2);
  const __m128i cp_f_high = _mm_unpackhi_epi16(center_pixel_f, center_pixel_f);
  const __m128i center_pixel_g = _mm_bslli_si128(src_g0, 2);
  const __m128i cp_g_high = _mm_unpackhi_epi16(center_pixel_g, center_pixel_g);
  // f3 f3 f4 f4 f5 f5 f6 f6 | g3 g3 g4 g4 g5 g5 g6 g6
  const __m256i center_pixel_row23_0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(cp_f_high), cp_g_high, 1);

  const __m128i zero = _mm_set1_epi16(0x0);
  const __m128i cp_d0_high = _mm_unpackhi_epi16(center_pixel_d, zero);
  const __m128i cp_e0_high = _mm_unpackhi_epi16(center_pixel_e, zero);
  // d3 0 d4 0 d5 0 d6 0 | e3 0 e4 0 e5 0 e6 0
  const __m256i center_pixel_row01_1 = _mm256_inserti128_si256(
      _mm256_castsi128_si256(cp_d0_high), cp_e0_high, 1);
  const __m128i cp_f0_high = _mm_unpackhi_epi16(center_pixel_f, zero);
  const __m128i cp_g0_high = _mm_unpackhi_epi16(center_pixel_g, zero);
  // f3 0 f4 0 f5 0 f6 0 | g3 0 g4 0 g5 0 g6 0
  const __m256i center_pixel_row23_1 = _mm256_inserti128_si256(
      _mm256_castsi128_si256(cp_f0_high), cp_g0_high, 1);

  // Packing the source rows.
  // a0 g0 a1 g1 a2 g2 a3 g3 | b0 h0 b1 h1 b2 h2 b3 h3
  const __m256i ru0 = _mm256_unpacklo_epi16(src_ab, src_gh);
  // a4 g4 a5 g5 a6 g6 a7 g7 | b4 h4 b5 h5 b6 h6 b7 h7
  const __m256i ru1 = _mm256_unpackhi_epi16(src_ab, src_gh);
  // c0 e0 c1 e1 c2 e2 c3 e3 | d0 f0 d1 f1 d2 f2 d3 f3
  const __m256i ru2 = _mm256_unpacklo_epi16(src_cd, src_ef);
  // c4 e4 c5 e5 c6 e6 c7 e7 | d4 f4 d5 f5 d6 f6 d7 f7
  __m256i ru3 = _mm256_unpackhi_epi16(src_cd, src_ef);
  // c0 i0 c1 i1 c2 i2 c3 i3 | d0 j0 d1 j1 d2 j2 d3 j3
  const __m256i ru4 = _mm256_unpacklo_epi16(src_cd, src_ij);
  // c4 i4 c5 i5 c6 i6 c7 i7 | d4 j4 d5 j5 d6 j6 d7 j7
  const __m256i ru5 = _mm256_unpackhi_epi16(src_cd, src_ij);
  // e0 g0 e1 g1 e2 g2 e3 g3 | f0 h0 f1 h1 f2 h2 f3 h3
  const __m256i ru6 = _mm256_unpacklo_epi16(src_ef, src_gh);
  // e4 g4 e5 g5 e6 g6 e7 g7 | f4 h4 f5 h5 f6 h6 f7 h7
  __m256i ru7 = _mm256_unpackhi_epi16(src_ef, src_gh);

  // Output corresponding to filter coefficient 10.
  // a3 g3 a4 g4 a5 g5 a6 g6 | b3 h3 b4 h4 b5 h5 b6 h6
  const __m256i ru8_0 = _mm256_alignr_epi8(ru1, ru0, 12);
  const __m256i ru8 = _mm256_sub_epi16(ru8_0, center_pixel_row01_0);
  // c3 i3 c4 i4 c5 i5 c6 i6 | d3 j3 d4 j4 d5 j5 d6 j6
  const __m256i ru9_0 = _mm256_alignr_epi8(ru5, ru4, 12);
  const __m256i ru9 = _mm256_sub_epi16(ru9_0, center_pixel_row23_0);
  // f10 f10 f10 f10 f10 f10 f10 f10 f10 f10 f10 f10 f10 f10 f10 f10
  const __m128i fil10 = _mm_bsrli_si128(filt_coeff_1, 4);
  const __m256i fc10 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(fil10, 0x0F));
  // r00 r01 r02 r03 | r10 r11 r12 r13
  __m256i accum_out_r0r1 = _mm256_madd_epi16(ru8, fc10);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  __m256i accum_out_r2r3 = _mm256_madd_epi16(ru9, fc10);

  // Output corresponding to filter coefficients 8,4,0,5.
  // c1 e1 c2 e2 c3 e3 c4 e4 | d1 f1 d2 f2 d3 f3 d4 f4
  const __m256i ru10_0 = _mm256_alignr_epi8(ru3, ru2, 4);
  const __m256i ru10 = _mm256_sub_epi16(ru10_0, center_pixel_row01_0);

  // e1 g1 e2 g2 e3 g3 e4 g4 | f1 h1 f2 h2 f3 h3 f4 h4
  const __m256i ru11_0 = _mm256_alignr_epi8(ru7, ru6, 4);
  const __m256i ru11 = _mm256_sub_epi16(ru11_0, center_pixel_row23_0);

  // c2 e2 c3 e3 c4 e4 c5 e5 | d2 f2 d3 f3 d4 f4 d5 f5
  const __m256i ru12_0 = _mm256_alignr_epi8(ru3, ru2, 8);
  const __m256i ru12 = _mm256_sub_epi16(ru12_0, center_pixel_row01_0);

  // e2 g2 e3 g3 e4 g4 e5 g5 | f2 h2 f3 h3 f4 h4 f5 h5
  const __m256i ru13_0 = _mm256_alignr_epi8(ru7, ru6, 8);
  const __m256i ru13 = _mm256_sub_epi16(ru13_0, center_pixel_row23_0);

  // c3 e3 c4 e4 c5 e5 c6 e6 | d3 f3 d4 f4 d5 f5 d6 f6
  const __m256i ru14_0 = _mm256_alignr_epi8(ru3, ru2, 12);
  const __m256i ru14 = _mm256_sub_epi16(ru14_0, center_pixel_row01_0);

  // e3 g3 e4 g4 e5 g5 e6 g6 | f3 h3 f4 h4 f5 h5 f6 h6
  const __m256i ru15_0 = _mm256_alignr_epi8(ru7, ru6, 12);
  const __m256i ru15 = _mm256_sub_epi16(ru15_0, center_pixel_row23_0);

  // fc6 fc7 fc8 fc9 fc10 fc11 fc12 0
  const __m128i filt89 = _mm_bsrli_si128(filt_coeff_1, 6);
  const __m256i fc89 = _mm256_broadcastd_epi32(filt89);
  const __m128i filt45 = _mm_bsrli_si128(filt_coeff_0, 8);
  const __m256i fc45 = _mm256_broadcastd_epi32(filt45);
  const __m256i fc00 = _mm256_broadcastw_epi16(filt_coeff_0);
  const __m128i filt54 = _mm_bsrli_si128(filt_coeff_0, 4);
  const __m256i fc54 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt54, 0x0B));

  ru3 = _mm256_sub_epi16(ru3, center_pixel_row01_0);
  ru7 = _mm256_sub_epi16(ru7, center_pixel_row23_0);
  const __m256i res_0 = _mm256_madd_epi16(ru10, fc89);
  const __m256i res_1 = _mm256_madd_epi16(ru11, fc89);
  const __m256i res_2 = _mm256_madd_epi16(ru12, fc45);
  const __m256i res_3 = _mm256_madd_epi16(ru13, fc45);
  const __m256i res_4 = _mm256_madd_epi16(ru14, fc00);
  const __m256i res_5 = _mm256_madd_epi16(ru15, fc00);
  const __m256i res_6 = _mm256_madd_epi16(ru3, fc54);
  const __m256i res_7 = _mm256_madd_epi16(ru7, fc54);
  // r00 r01 r02 r03 | r10 r11 r12 r13
  const __m256i out_0 = _mm256_add_epi32(res_0, res_2);
  const __m256i out_1 = _mm256_add_epi32(res_4, res_6);
  const __m256i out_2 = _mm256_add_epi32(out_0, out_1);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, out_2);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  const __m256i out_3 = _mm256_add_epi32(res_1, res_3);
  const __m256i out_4 = _mm256_add_epi32(res_5, res_7);
  const __m256i out_5 = _mm256_add_epi32(out_3, out_4);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, out_5);

  // Output corresponding to filter coefficients 11,3,1,12.
  // d2 d3 d4	d5 d6 d7 d8 d9
  const __m128i src_d2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 3 * stride + 2));
  // e2 e3 e4 e5 e6 e7 e8 e9
  const __m128i src_e2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 4 * stride + 2));
  // f2 f3 f4 f5 f6 f7 f8 f9
  const __m128i src_f2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 5 * stride + 2));
  // g2 g3 g4	g5 g6 g7 g8 g9
  const __m128i src_g2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 6 * stride + 2));

  // d0 d1 d2 d3 d4 d5 d6 d7 | e0 e1 e2 e3 e4 e5 e6 e7
  const __m256i rm0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d0), src_e0, 1);
  // d2 d3 d4 d5 d6 d7 d8 d9 | e2 e3 e4 e5 e6 e7 e8 e9
  const __m256i rm2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d2), src_e2, 1);
  // f0 f1 f2 f3 f4 f5 f6 f7 | g0 g1 g2 g3 g4 g5 g6 g7
  const __m256i rm00 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f0), src_g0, 1);
  // f2 f3 f4 f5 f6 f7 f8 f9 | g2 g3 g4 g5 g6 g7 g8 g9
  const __m256i rm22 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f2), src_g2, 1);
  // d1 d2 d3 d4 d5 d6 d7 d8 | e1 e2 e3 e4 e5 e6 e7 e8
  const __m256i rm1 = _mm256_alignr_epi8(_mm256_bsrli_epi128(rm2, 12), rm0, 2);
  const __m256i rm11 =
      _mm256_alignr_epi8(_mm256_bsrli_epi128(rm22, 12), rm00, 2);
  // d3 d4 d5 d6 d7 d8 d9 0 | e3 e4 e5 e6 e7 e8 e9 0
  const __m256i rm3 = _mm256_bsrli_epi128(rm2, 2);
  const __m256i rm33 = _mm256_bsrli_epi128(rm22, 2);
  // d0 d1 d1 d2 d2 d3 d3 d4 | e0 e1 e1 e2 e2 e3 e3 e4
  __m256i rm4 = _mm256_unpacklo_epi16(rm0, rm1);
  rm4 = _mm256_sub_epi16(rm4, center_pixel_row01_0);
  // d2 d3 d3 d4 d4 d5 d5 d6 | e2 e3 e3 e4 e4 e5 e5 e6
  __m256i rm5 = _mm256_unpacklo_epi16(rm2, rm3);
  rm5 = _mm256_sub_epi16(rm5, center_pixel_row01_1);
  // d4 d5 d5 d6 d6 d7 d7 d8 | e4 e5 e5 e6 e6 e7 e7 e8
  __m256i rm6 = _mm256_unpackhi_epi16(rm0, rm1);
  rm6 = _mm256_sub_epi16(rm6, center_pixel_row01_0);
  // d6 0 d7 0 d8 0 d9 0 | e6 0 e7 0 e8 0 e9 0
  __m256i rm7 = _mm256_unpackhi_epi16(rm2, _mm256_set1_epi16(0));
  rm7 = _mm256_sub_epi16(rm7, center_pixel_row01_0);

  __m256i rm44 = _mm256_unpacklo_epi16(rm00, rm11);
  rm44 = _mm256_sub_epi16(rm44, center_pixel_row23_0);
  __m256i rm55 = _mm256_unpacklo_epi16(rm22, rm33);
  rm55 = _mm256_sub_epi16(rm55, center_pixel_row23_1);
  __m256i rm66 = _mm256_unpackhi_epi16(rm00, rm11);
  rm66 = _mm256_sub_epi16(rm66, center_pixel_row23_0);
  __m256i rm77 = _mm256_unpackhi_epi16(rm22, _mm256_set1_epi16(0));
  rm77 = _mm256_sub_epi16(rm77, center_pixel_row23_0);

  // fc11 fc03 fc11 fc03 - -
  const __m128i fc11_fc03 =
      _mm_blend_epi16(_mm_bsrli_si128(filt_coeff_1, 8), filt_coeff_0, 0x08);
  const __m256i filt1103 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(fc11_fc03, 0x0E));

  // fc01 fc12 fc01 fc12 - -
  const __m128i fc01_fc12 =
      _mm_blend_epi16(_mm_bsrli_si128(filt_coeff_1, 8), filt_coeff_0, 0x02);
  const __m256i filt0112 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(fc01_fc12, 0x0D));

  // fc01 fc03 fc01 fc03 - -
  const __m256i fc0103 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_0, 0x0D));

  // f11 0 f11 0 f11 0 f11 0 - -
  const __m256i fc11z = _mm256_broadcastd_epi32(_mm_shufflelo_epi16(
      _mm_bsrli_si128(_mm_unpackhi_epi16(filt_coeff_1, zero), 4), 0x0E));

  rm4 = _mm256_madd_epi16(rm4, filt1103);
  rm5 = _mm256_madd_epi16(rm5, filt0112);
  rm6 = _mm256_madd_epi16(rm6, fc0103);
  rm7 = _mm256_madd_epi16(rm7, fc11z);
  rm44 = _mm256_madd_epi16(rm44, filt1103);
  rm55 = _mm256_madd_epi16(rm55, filt0112);
  rm66 = _mm256_madd_epi16(rm66, fc0103);
  rm77 = _mm256_madd_epi16(rm77, fc11z);

  // r00 r01 r02 r03 | r10 r11 r12 r13
  rm4 = _mm256_add_epi32(rm4, rm5);
  rm6 = _mm256_add_epi32(rm6, rm7);
  rm4 = _mm256_add_epi32(rm4, rm6);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, rm4);

  // r20 r21 r22 r23 | r30 r31 r32 r33
  rm44 = _mm256_add_epi32(rm44, rm55);
  rm66 = _mm256_add_epi32(rm66, rm77);
  rm44 = _mm256_add_epi32(rm44, rm66);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, rm44);

  // Output corresponding to filter coefficients 6,2,7,9.
  const __m128i src_b2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 1 * stride + 2));
  const __m128i src_c2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 2 * stride + 2));
  const __m256i rn0 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_b2), src_c2, 1);
  // b2 b3 b3 b4 b4 b5 b5 b6 | c2 c3 c3 c4 c4 c5 c5 c6
  __m256i r0 = _mm256_unpacklo_epi16(rn0, _mm256_bsrli_epi128(rn0, 2));
  r0 = _mm256_sub_epi16(r0, center_pixel_row01_0);

  const __m256i rcd2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_c2), src_d2, 1);
  // b4 c5 b5 c6 b6 c7 b7 c8 | c4 d5 c5 d6 c6 d7 c7 d8
  __m256i r1 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rn0, 4),
                                     _mm256_bsrli_epi128(rcd2, 6));
  r1 = _mm256_sub_epi16(r1, center_pixel_row01_0);

  const __m256i rfg2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_f2), src_g2, 1);
  // f2 f3 f3 f4 f4 f5 f5 f6 | g2 g3 g3 g4 g4 g5 g5 g6
  __m256i r2 = _mm256_unpacklo_epi16(rfg2, _mm256_bsrli_epi128(rfg2, 2));
  r2 = _mm256_sub_epi16(r2, center_pixel_row01_0);

  const __m256i ref2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_e2), src_f2, 1);
  // f4 e5 f5 e6 f6 e7 f7 e8 | g4 f5 g5 f6 g6 f7 g7 f8
  __m256i r3 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rfg2, 4),
                                     _mm256_bsrli_epi128(ref2, 6));
  r3 = _mm256_sub_epi16(r3, center_pixel_row01_0);

  const __m128i filt62 = _mm_blend_epi16(filt_coeff_1, filt_coeff_0, 0x04);
  // fc6 fc2 - - - - - - | fc6 fc2 - - - - - -
  const __m256i fc62 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt62, 0x09));
  // fc7 fc9 - - - - - - | fc7 fc9 - - - - - -
  const __m256i fc79 = _mm256_broadcastd_epi32(
      _mm_shufflelo_epi16(_mm_bsrli_si128(filt_coeff_1, 2), 0x0D));
  // fc7 fc2 - - - - - - | fc7 fc2 - - - - - -
  const __m128i filt72 =
      _mm_blend_epi16(_mm_bsrli_si128(filt_coeff_1, 2), filt_coeff_0, 0x04);
  const __m256i fc72 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt72, 0x09));
  // fc6 fc8 - - - - - - | fc6 fc8 - - - - - -
  const __m256i fc68 =
      _mm256_broadcastd_epi32(_mm_shufflelo_epi16(filt_coeff_1, 0x0D));

  r0 = _mm256_madd_epi16(r0, fc62);
  r1 = _mm256_madd_epi16(r1, fc79);
  r2 = _mm256_madd_epi16(r2, fc72);
  r3 = _mm256_madd_epi16(r3, fc68);
  // r00 r01 r02 r03 | r10 r11 r12 r13
  r0 = _mm256_add_epi32(r0, r1);
  r2 = _mm256_add_epi32(r2, r3);
  r0 = _mm256_add_epi32(r0, r2);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, r0);

  const __m256i rn1 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_d2), src_e2, 1);
  // d2 d3 d3 d4 d4 d5 d5 d6 | e2 e3 e3 e4 e4 e5 e5 e6
  __m256i r00 = _mm256_unpacklo_epi16(rn1, _mm256_bsrli_epi128(rn1, 2));
  r00 = _mm256_sub_epi16(r00, center_pixel_row23_0);
  // d4 e5 d5 e6 d6 e7 d7 e8 | e4 f5 e5 f6 e6 f7 e7 f8
  __m256i r11 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rn1, 4),
                                      _mm256_bsrli_epi128(ref2, 6));
  r11 = _mm256_sub_epi16(r11, center_pixel_row23_0);

  const __m128i src_h2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 7 * stride + 2));
  const __m128i src_i2 =
      _mm_loadu_si128((__m128i const *)(src_ptr + 8 * stride + 2));
  // h2 h3 h4 h5 h6 h7 h8 h9 | i2 i3 i4 i5 i6 i7 i8 i9
  __m256i rhi2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_h2), src_i2, 1);
  // h2 h3 h3 h4 h4 h5 h5 h6 | i2 i3 i3 i4 i4 i5 i5 i6
  __m256i r22 = _mm256_unpacklo_epi16(rhi2, _mm256_bsrli_epi128(rhi2, 2));
  r22 = _mm256_sub_epi16(r22, center_pixel_row23_0);
  // g2 g3 g4 g5 g6 g7 g8 g9 | h2 h3 h4 h5 h6 h7 h8 h9
  __m256i rgh2 =
      _mm256_inserti128_si256(_mm256_castsi128_si256(src_g2), src_h2, 1);
  __m256i r33 = _mm256_unpacklo_epi16(_mm256_bsrli_epi128(rhi2, 4),
                                      _mm256_bsrli_epi128(rgh2, 6));
  r33 = _mm256_sub_epi16(r33, center_pixel_row23_0);

  r00 = _mm256_madd_epi16(r00, fc62);
  r11 = _mm256_madd_epi16(r11, fc79);
  r22 = _mm256_madd_epi16(r22, fc72);
  r33 = _mm256_madd_epi16(r33, fc68);
  // r20 r21 r22 r23 | r30 r31 r32 r33
  r00 = _mm256_add_epi32(r00, r11);
  r22 = _mm256_add_epi32(r22, r33);
  r00 = _mm256_add_epi32(r00, r22);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, r00);

  // Offset addition
  const __m128i offset_reg = _mm_set1_epi32(dc_offset);
  const __m256i ofs = _mm256_inserti128_si256(
      _mm256_castsi128_si256(offset_reg), offset_reg, 1);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, ofs);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, ofs);

  // Rounding and clipping.
  accum_out_r0r1 =
      round_power_of_two_signed_avx2(accum_out_r0r1, filter_config->prec_bits);
  accum_out_r2r3 =
      round_power_of_two_signed_avx2(accum_out_r2r3, filter_config->prec_bits);
  // r00 r01 r02 r03 | r20 r21 r22 r23 | r10 r11 r12 r13 | r30 r31 r32 r33
  __m256i out_r0r2r1r3 = _mm256_packs_epi32(accum_out_r0r1, accum_out_r2r3);
  const __m256i max = _mm256_set1_epi16((1 << bit_depth) - 1);
  out_r0r2r1r3 = highbd_clamp_epi16(out_r0r2r1r3, _mm256_setzero_si256(), max);

  // Store the output.
  const int dst_id = block_row_begin * dst_stride + block_col_begin;
  const __m128i out_r1r3 = _mm256_extractf128_si256(out_r0r2r1r3, 1);
  _mm_storel_epi64((__m128i *)(dst + dst_id),
                   _mm256_castsi256_si128(out_r0r2r1r3));
  _mm_storel_epi64((__m128i *)(dst + dst_id + (1 * dst_stride)), out_r1r3);

  _mm_storel_epi64((__m128i *)(dst + dst_id + (2 * dst_stride)),
                   _mm_bsrli_si128(_mm256_castsi256_si128(out_r0r2r1r3), 8));
  _mm_storel_epi64((__m128i *)(dst + dst_id + (3 * dst_stride)),
                   _mm_bsrli_si128(out_r1r3, 8));
}

// SIMD implementation to convolve a block of pixels with origin-symmetric,
// wiener non-separable filter corresponds to CONFIG_WIENER_NONSEP loop
// restoration. DIAMOND shape with 13/12-tap or 6-tap filter is used for
// convolution.

// TODO(rachelbarker): Standardize on non-subtract-center code path,
// so that this one can be removed
void av2_convolve_symmetric_subtract_center_highbd_avx2(
    const uint16_t *dgd, int stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_row_end, int block_col_begin,
    int block_col_end) {
  assert(filter_config->subtract_center);
  const int num_rows = block_row_end - block_row_begin;
  const int num_cols = block_col_end - block_col_begin;
  const int num_sym_taps = filter_config->num_pixels / 2;

  // Dispatch to the appropriate SIMD function for the selected filter shape
  // and block size. If none are applicable, fall back to C
  if (num_rows == 4 && num_cols == 4 && num_sym_taps == 6 &&
      (filter_config->config == wienerns_simd_config_uv_from_uv ||
       !memcmp(wienerns_simd_config_uv_from_uv, filter_config->config,
               filter_config->num_pixels * 3 *
                   sizeof(filter_config->config[0][0])))) {
    av2_convolve_symmetric_subtract_center_highbd_6tap_avx2(
        dgd, stride, filter_config, filter, dst, dst_stride, bit_depth,
        block_row_begin, block_col_begin);
  } else if (num_rows == 4 && num_cols == 4 && num_sym_taps == 12 &&
             (filter_config->config == wienerns_simd_config_y ||
              !memcmp(wienerns_simd_config_y, filter_config->config,
                      filter_config->num_pixels * 3 *
                          sizeof(filter_config->config[0][0])))) {
    av2_convolve_symmetric_subtract_center_highbd_12tap_avx2(
        dgd, stride, filter_config, filter, dst, dst_stride, bit_depth,
        block_row_begin, block_col_begin);
  } else {
    av2_convolve_symmetric_subtract_center_highbd_c(
        dgd, stride, filter_config, filter, dst, dst_stride, bit_depth,
        block_row_begin, block_row_end, block_col_begin, block_col_end);
  }
}

// AVX2 intrinsic for convolve wiener non-separable dual loop restoration
// filtering with subtract center off. Two buffers (dgd and dgd_dual) are
// considered for dual filtering, where each buffer goes through 7-tap
// filtering. The output for a particular pixel in a 4x4 block is calculated
// with DIAMOND shaped filter considering a 5x5 grid surrounded by that pixel.
// A total of 14 filter taps required and are expected to be passed as filter
// taps required for dgd(first) buffer followed by dgd_dual(second) buffer
// filter taps. The exact order of filter taps needed is shown below.
// Filter Taps: fc0 fc1 fc2 fc3 fc4 fc5 fc6 fc7 fc8 fc9 fc10 fc11 fc12 fc13.
// Here, 'fc0-fc6' and 'fc7-fc13' are corresponding to filter taps used for
// dgd and dgd_dual buffers respectively. Also, 'fc0-fc5' and 'fc7-fc12' are the
// symmetric taps and 'fc6' and 'fc13' are the center taps correspond to dgd and
// dgd_dual buffers respectively.
//
// The following describes the algorithm briefly.
// 7-tap filtering for dgd (first) buffer:
// Load Source Data:
// dgd_ra0 = a0 a1 a2 a3 a4 a5 a6 a7
// dgd_rb0 = b0 b1 b2 b3 b4 b5 b6 b7
// dgd_rc0 = c0 c1 c2 c3 c4 c5 c6 c7
// dgd_rd0 = d0 d1 d2 d3 d4 d5 d6 d7
// dgd_re0 = e0 e1 e2 e3 e4 e5 e6 e7
// dgd_rf0 = f0 f1 f2 f3 f4 f5 f6 f7
// dgd_rg0 = g0 g1 g2 g3 g4 g5 g6 g7
// The output for a pixel located at c2 position is calculated as below.
// dgd_output_c2 = ((a2+e2)*fc4 + (b1+d3)*fc2 + (b2+d2)*fc0 + (b3+d1)*fc3 +
// (c0+c4)*fc5 + (c1+c3)*fc1 + c2*fc6(singleton_tap)
//
// 7-tap filtering for dgd_dual (second) buffer:
// Load Source Data:
// dgd_dual_ra0 = a0 a1 a2 a3 a4 a5 a6 a7
// dgd_dual_rb0 = b0 b1 b2 b3 b4 b5 b6 b7
// dgd_dual_rc0 = c0 c1 c2 c3 c4 c5 c6 c7
// dgd_dual_rd0 = d0 d1 d2 d3 d4 d5 d6 d7
// dgd_dual_re0 = e0 e1 e2 e3 e4 e5 e6 e7
// dgd_dual_rf0 = f0 f1 f2 f3 f4 f5 f6 f7
// dgd_dual_rg0 = g0 g1 g2 g3 g4 g5 g6 g7
// dgd_dual_output_c2 = (a2+e2)*fc11 + (b1+d3)*fc9 + (b2+d2)*fc7 + (b3+d1)*fc10
// + (c0+c4)*fc12 + (c1+c3)*fc8 + c2*fc13((singleton_tap_dual)
// The final output corresponding to c2 is calculated as below.
// output_c2 = dgd_output_c2 + dgd_dual_output_c2
// The source registers are unpacked such that the output corresponding to 2
// rows will be produced in a single register (i.e., processing 2 rows
// simultaneously).
//
// Example:
// The dgd(first) buffer output corresponding to fc4 of rows 0 and 1 is achieved
// like below.
// __m256i src_reg3 = a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
// __m256i filter_4 = fc4 fc4 fc4 fc4 fc4 fc4 fc4 fc4 | fc4 fc4 fc4 fc4 fc4
// fc4 fc4 fc4
//  __m256i out_f4_01 = _mm256_madd_epi16(src_reg3, filter_4);
//                   = (a2*fc4+e2*fc4) (a3*fc4+e3*fc4) .. | (b2*fc4+f2*fc4)
//                   . .
// Here, out_f4_01 contains partial output of rows 0 and 1 corresponding to
// fc4.
//
/*
// Should be same as wienerns_simd_config_uv_from_uv
const int config_7tap_uv_from_uv_avx2[][3] = {
  { 1, 0, 0 },   { -1, 0, 0 }, { 0, 1, 1 },  { 0, -1, 1 }, { 1, 1, 2 },
  { -1, -1, 2 }, { -1, 1, 3 }, { 1, -1, 3 }, { 2, 0, 4 },  { -2, 0, 4 },
  { 0, 2, 5 },   { 0, -2, 5 }, { 0, 0, 18 },
};

// Should be same as wienerns_simd_config_uv_from_y
const int config_13tap_uv_from_y_avx2[][3] = {
  { 1, 0, 6 },    { -1, 0, 7 },  { 0, 1, 8 },   { 0, -1, 9 }, { 1, 1, 10 },
  { -1, -1, 11 }, { -1, 1, 12 }, { 1, -1, 13 }, { 2, 0, 14 },  { -2, 0, 15 },
  { 0, 2, 16 },   { 0, -2, 17 }, { 0, 0, 19 },
};
*/

void av2_convolve_symmetric_dual_highbd_7plus13tap_avx2(
    const uint16_t *dgd, int dgd_stride, const uint16_t *dgd_dual,
    int dgd_dual_stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  // Derive singleton_tap and singleton_tap_dual and repalce center tap filter
  // values with the same.
  int32_t singleton_tap = 1 << filter_config->prec_bits;
  int32_t singleton_tap_dual = 0;
  if (filter_config->num_pixels % 2) {
    const int singleton_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    singleton_tap += filter[singleton_tap_index];
  }

  if (filter_config->num_pixels2 % 2) {
    const int last_config = filter_config->num_pixels2 - 1;
    const int singleton_tap_index =
        filter_config->config2[last_config][NONSEP_BUF_POS];
    singleton_tap_dual += filter[singleton_tap_index];
  }

  // Prepare filter coefficients for dgd(first) buffer 7-tap filtering
  // fc0 fc1 fc2 fc3 fc4 fc5 fc18(center_tap) x
  __m128i filter_coeff = _mm_loadu_si128((__m128i const *)(filter));
  // Replace the center_tap with derived singleton_tap.
  const __m128i center_tap1 = _mm_set1_epi16(singleton_tap);
  const __m128i filter_coeff_dgd =
      _mm_blend_epi16(filter_coeff, center_tap1, 0x40);

  // Prepare filter coefficients for dgd_dual(second) buffer 13-tap filtering
  // Currently we only support the case where there is a center tap in the
  // in-plane filter, so that the dual filter taps are at indices:
  // fc6 fc7 fc8 fc9 fc10 fc11 fc12 fc13 | fc14 fc15 fc16 fc17 center x x x
  assert(filter_config->num_pixels == 13);
  const __m128i filter_coeff_dual_lo =
      _mm_loadu_si128((__m128i const *)(filter + 6));
  __m128i filter_coeff_dual_hi =
      _mm_bsrli_si128(_mm_loadu_si128((__m128i const *)(filter + 12)), 4);
  const __m128i center_tap2 = _mm_set1_epi16(singleton_tap_dual);
  filter_coeff_dual_hi =
      _mm_blend_epi16(filter_coeff_dual_hi, center_tap2, 0x10);

  // Initialize the output registers with zero.
  __m256i accum_out_r0r1 = _mm256_setzero_si256();
  __m256i accum_out_r2r3 = _mm256_setzero_si256();

  // 7-tap filtering for dgd (first) buffer.
  apply_7tap_filtering_with_subtract_center_off(
      dgd, dgd_stride, filter_coeff_dgd, &accum_out_r0r1, &accum_out_r2r3,
      block_row_begin, block_col_begin);

  // 7-tap filtering for dgd_dual (second) buffer.
  apply_asym_13tap_filtering_with_subtract_center_off(
      dgd_dual, dgd_dual_stride, filter_coeff_dual_lo, filter_coeff_dual_hi,
      &accum_out_r0r1, &accum_out_r2r3, block_row_begin, block_col_begin);

  // Store the output after rounding and clipping.
  round_and_store_avx2(dst, dst_stride, filter_config, bit_depth,
                       accum_out_r0r1, accum_out_r2r3, block_row_begin,
                       block_col_begin);
}

void av2_convolve_symmetric_dual_highbd_avx2(
    const uint16_t *dgd, int dgd_stride, const uint16_t *dgd_dual,
    int dgd_dual_stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_row_end, int block_col_begin,
    int block_col_end) {
  assert(!filter_config->subtract_center);

  const int num_rows = block_row_end - block_row_begin;
  const int num_cols = block_col_end - block_col_begin;
  assert(num_rows >= 0 && num_cols >= 0);

  if (num_rows != 4 || num_cols != 4) {
    av2_convolve_symmetric_dual_highbd_c(
        dgd, dgd_stride, dgd_dual, dgd_dual_stride, filter_config, filter, dst,
        dst_stride, bit_depth, block_row_begin, block_row_end, block_col_begin,
        block_col_end);
    return;
  }
  // const int num_sym_taps = filter_config->num_pixels / 2;
  // const int num_taps_dual = filter_config->num_pixels2;

  // Dispatch to the appropriate SIMD function for the selected filter shape
  // and block size. If none are applicable, fall back to C
  // TODO(rachelbarker): Add fast path for reduced-length filters which don't
  // use any cross-plane filtering
  if ((wienerns_simd_config_uv_from_uv == filter_config->config &&
       wienerns_simd_config_uv_from_y == filter_config->config2) ||
      (!memcmp(wienerns_simd_config_uv_from_uv, filter_config->config,
               filter_config->num_pixels * 3 *
                   sizeof(filter_config->config[0][0])) &&
       !memcmp(wienerns_simd_config_uv_from_y, filter_config->config,
               filter_config->num_pixels2 * 3 *
                   sizeof(filter_config->config2[0][0])))) {
    av2_convolve_symmetric_dual_highbd_7plus13tap_avx2(
        dgd, dgd_stride, dgd_dual, dgd_dual_stride, filter_config, filter, dst,
        dst_stride, bit_depth, block_row_begin, block_col_begin);
    return;
  }
  av2_convolve_symmetric_dual_highbd_c(
      dgd, dgd_stride, dgd_dual, dgd_dual_stride, filter_config, filter, dst,
      dst_stride, bit_depth, block_row_begin, block_row_end, block_col_begin,
      block_col_end);
}

// AVX2 intrinsic for convolve wiener non-separable dual loop restoration
// filtering. The output for a particular pixel in a 4x4 block is calculated
// with DIAMOND shaped filter considering a 5x5 grid surrounded by that pixel.
// Filter Coefficients: fc0 fc1 fc2 fc3 fc4 fc5 f6 f7 f8 f9 f10 f11
// 6-tap filtering for dgd (first) buffer:
// dgd_reg_a = a0 a1 a2 a3 a4 a5 a6 a7
// dgd_reg_b = b0 b1 b2 b3 b4 b5 b6 b7
// dgd_reg_c = c0 c1 c2 c3 c4 c5 c6 c7
// dgd_reg_d = d0 d1 d2 d3 d4 d5 d6 d7
// dgd_reg_e = e0 e1 e2 e3 e4 e5 e6 e7
// The output for a pixel located at c2 position is calculated as below.
// dgd_output_c2 = ((a2-c2)+(e2-c2))*fc4 + ((b1-c2+d3-c2))*fc2 +
// (b2-c2+d2-c2)*fc0 + (b3-c2+d1-c2)*fc3 + (c0-c2+c4-c2)*fc5 +
// (c1-c2+c3-c2)*fc1 + c2*singleton_tap + dc_offset
//
// 6-tap filtering for dgd_dual (second) buffer:
// dgd_dual_reg_a = a0 a1 a2 a3 a4 a5 a6 a7
// dgd_dual_reg_b = b0 b1 b2 b3 b4 b5 b6 b7
// dgd_dual_reg_c = c0 c1 c2 c3 c4 c5 c6 c7
// dgd_dual_reg_d = d0 d1 d2 d3 d4 d5 d6 d7
// dgd_dual_reg_e = e0 e1 e2 e3 e4 e5 e6 e7
// dgd_dual_output_c2 = ((a2-c2)+(e2-c2))*fc10 + ((b1-c2+d3-c2))*fc8 +
// (b2-c2+d2-c2)*fc6 + (b3-c2+d1-c2)*fc9 + (c0-c2+c4-c2)*fc11 +
// (c1-c2+c3-c2)*fc7
// output_c2 = dgd_output_c2 + dgd_dual_output_c2
// The source registers are unpacked such that the output corresponding to 2
// rows will be produced in a single register (i.e., processing 2 rows
// simultaneously).
//
// Example:
// The output corresponding to fc4 of rows 0 and 1 is achieved like below.
// __m256i centerpixel_row01 = c2 c2 c3 c3 c4 c4 c5 c5 | d2 d2 d3 d3 d4 d4 d5 d5
// __m256i src_reg3 = a2 e2 a3 e3 a4 e4 a5 e5 | b2 f2 b3 f3 b4 f4 b5 f5
// __m256i filter_4 = fc4 fc4 fc4 fc4 fc4 fc4 fc4 fc4 | fc4 fc4 fc4 fc4 fc4 fc4
// fc4 fc4
// __m256 src_reg3 = _mm256_sub_epi16(src_reg3, centerpixel_row01);
//  __m256i out_f4_01 = _mm256_madd_epi16(src_reg3, filter_4);
//                   = ((a2-c2)*fc4+(e2-c2)*fc4) (a3-c3)*fc4+(e3-c3)*fc4) .. |
//                   (b2-d2)*fc4 +(f2-d2)*fc4) . .
// Here, out_f4_01 contains partial output of rows 0 and 1 corresponding to fc4.

void av2_convolve_symmetric_dual_subtract_center_highbd_6plus12tap_avx2(
    const uint16_t *dgd, int dgd_stride, const uint16_t *dgd_dual,
    int dgd_dual_stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_col_begin) {
  const int32_t singleton_tap = 1 << filter_config->prec_bits;
  int32_t dc_offset = 0;
  if (filter_config->num_pixels % 2) {
    const int dc_offset_tap_index =
        filter_config->config[filter_config->num_pixels - 1][NONSEP_BUF_POS];
    dc_offset = filter[dc_offset_tap_index];
  }

  // Prepare filter coefficients for dgd buffer 6-tap filtering
  // fc0 fc1 fc2 fc3 fc4 fc5 center_tap x
  __m128i filter_coeff = _mm_loadu_si128((__m128i const *)(filter));
  // Replace the center_tap with derived singleton_tap.
  const __m128i center_tap = _mm_set1_epi16(singleton_tap);
  const __m128i filter_coeff_dgd =
      _mm_blend_epi16(filter_coeff, center_tap, 0x40);

  // Prepare filter coefficients for dgd_dual buffer 12-tap filtering
  // Currently we only support the case without a DC coefficient, so that the
  // dual filter taps are at indices:
  // fc6 fc7 fc8 fc9 fc10 fc11 fc12 fc13 | fc14 fc15 fc16 fc17 x x x x
  assert(filter_config->num_pixels == 12);
  const __m128i filter_coeff_dual_lo =
      _mm_loadu_si128((__m128i const *)(filter + 6));
  const __m128i filter_coeff_dual_hi =
      _mm_bsrli_si128(_mm_loadu_si128((__m128i const *)(filter + 10)), 8);

  // Initialize the output registers with zero
  __m256i accum_out_r0r1 = _mm256_setzero_si256();
  __m256i accum_out_r2r3 = _mm256_setzero_si256();

  // 6-tap filtering for dgd (first) buffer
  apply_6tap_filtering(dgd, dgd_stride, filter_coeff_dgd, &accum_out_r0r1,
                       &accum_out_r2r3, block_row_begin, block_col_begin);

  // 6-tap filtering for dgd_dual (second) buffer
  apply_asym_12tap_filtering(dgd_dual, dgd_dual_stride, filter_coeff_dual_lo,
                             filter_coeff_dual_hi, &accum_out_r0r1,
                             &accum_out_r2r3, block_row_begin, block_col_begin);

  // Offset addition
  const __m128i offset_reg = _mm_set1_epi32(dc_offset);
  const __m256i ofs = _mm256_inserti128_si256(
      _mm256_castsi128_si256(offset_reg), offset_reg, 1);
  accum_out_r0r1 = _mm256_add_epi32(accum_out_r0r1, ofs);
  accum_out_r2r3 = _mm256_add_epi32(accum_out_r2r3, ofs);

  // Store the output after rounding and clipping
  round_and_store_avx2(dst, dst_stride, filter_config, bit_depth,
                       accum_out_r0r1, accum_out_r2r3, block_row_begin,
                       block_col_begin);
}

// TODO(rachelbarker): Standardize on non-subtract-center code path,
// so that this one can be removed
void av2_convolve_symmetric_dual_subtract_center_highbd_avx2(
    const uint16_t *dgd, int dgd_stride, const uint16_t *dgd_dual,
    int dgd_dual_stride, const NonsepFilterConfig *filter_config,
    const int16_t *filter, uint16_t *dst, int dst_stride, int bit_depth,
    int block_row_begin, int block_row_end, int block_col_begin,
    int block_col_end) {
  assert(filter_config->subtract_center);

  const int num_rows = block_row_end - block_row_begin;
  const int num_cols = block_col_end - block_col_begin;
  const int num_sym_taps = filter_config->num_pixels / 2;
  const int num_taps_dual = filter_config->num_pixels2;

  // Dispatch to the appropriate SIMD function for the selected filter shape
  // and block size. If none are applicable, fall back to C
  if (num_rows == 4 && num_cols == 4 && num_sym_taps == 6 &&
      num_taps_dual == 12 &&
      (filter_config->config == wienerns_simd_config_uv_from_uv ||
       !memcmp(wienerns_simd_config_uv_from_uv, filter_config->config,
               filter_config->num_pixels * 3 *
                   sizeof(filter_config->config[0][0]))) &&
      (filter_config->config == wienerns_simd_config_uv_from_y ||
       !memcmp(wienerns_simd_config_uv_from_y, filter_config->config2,
               filter_config->num_pixels2 * 3 *
                   sizeof(filter_config->config2[0][0])))) {
    av2_convolve_symmetric_dual_subtract_center_highbd_6plus12tap_avx2(
        dgd, dgd_stride, dgd_dual, dgd_dual_stride, filter_config, filter, dst,
        dst_stride, bit_depth, block_row_begin, block_col_begin);
  } else {
    av2_convolve_symmetric_dual_subtract_center_highbd_c(
        dgd, dgd_stride, dgd_dual, dgd_dual_stride, filter_config, filter, dst,
        dst_stride, bit_depth, block_row_begin, block_row_end, block_col_begin,
        block_col_end);
  }
}

/* Computes the gradient across different directions for a given pixel using 3
 * tap filter. The following shows gradient calculation
 * A0 V0 D0
 * H0 C  H1
 * D1 V1 A1
 * At a given pixel position C,
 * Horzontal gradient = H0 - 2*C + H1
 * Vertical gradient = V0 - 2*C + V1
 * Diagonal gradient = D0 - 2*C + D1
 * Anti diagonal gradient = A0 - 2*C + A1
 * Feature lines buffers are updated here by taking the absolute of these
 * gradient information.
 */
void calc_gradient_in_various_directions_avx2(
    int16_t *feature_line_buffers[], int row, int buffer_row,
    const uint16_t *dgd, int dgd_stride, int width, int col_begin, int col_end,
    int feature_length, int buffer_col) {
  const int buffer_row_0 = buffer_row;
  const int buffer_row_1 = buffer_row_0 + feature_length;
  const int buffer_row_2 = buffer_row_1 + feature_length;
  const int buffer_row_3 = buffer_row_2 + feature_length;
  const int16_t *line_buf1 = feature_line_buffers[buffer_row_1];
  const int16_t *line_buf2 = feature_line_buffers[buffer_row_2];
  const int16_t *line_buf3 = feature_line_buffers[buffer_row_3];
  const int tot_width = col_end - col_begin;
  // Width for which SIMD is possible due to constrains of odd width.
  const int pos_simd_width = AVMMIN(tot_width, width + 3 - 2);
  const uint16_t *cur_row = dgd + (row * dgd_stride) + col_begin;
  const uint16_t *prev_row = cur_row - dgd_stride;
  const uint16_t *next_row = cur_row + dgd_stride;

  // Process width multiples of 16.
  for (int wd = 0; wd + 16 < pos_simd_width; wd += 16) {
    // p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 p16
    const __m256i src_p = _mm256_loadu_si256((__m256i const *)(prev_row));
    // c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16
    const __m256i src_c = _mm256_loadu_si256((__m256i const *)(cur_row));
    // n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12 n13 n14 n15 n16
    const __m256i src_n = _mm256_loadu_si256((__m256i const *)(next_row));

    // p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15
    const __m256i src_pm1 = _mm256_loadu_si256((__m256i const *)(prev_row - 1));
    // c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15
    // n0 n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12 n13 n14 n15
    const __m256i src_nm1 = _mm256_loadu_si256((__m256i const *)(next_row - 1));

    // p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 p16 p17
    const __m256i src_pp1 = _mm256_loadu_si256((__m256i const *)(prev_row + 1));
    // n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12 n13 n14 n15 n16 n17
    const __m256i src_np1 = _mm256_loadu_si256((__m256i const *)(next_row + 1));

    // Horizontal Gradient calculation
    const __m256i base_val = _mm256_add_epi16(src_c, src_c);

    // Vertical Gradient calculation
    const __m256i partial_vert_diff = _mm256_add_epi16(src_p, src_n);
    const __m256i vert_diff = _mm256_sub_epi16(partial_vert_diff, base_val);
    const __m256i abs_vert_diff = _mm256_abs_epi16(vert_diff);
    _mm256_storeu_si256((__m256i *)(line_buf1 + wd), abs_vert_diff);

    // Diagonal Gradient calculation
    const __m256i partial_diag_diff = _mm256_add_epi16(src_pm1, src_np1);
    const __m256i diag_diff = _mm256_sub_epi16(partial_diag_diff, base_val);
    const __m256i abs_diag_diff = _mm256_abs_epi16(diag_diff);
    _mm256_storeu_si256((__m256i *)(line_buf3 + wd), abs_diag_diff);

    // Anti-Diagonal Gradient calculation
    const __m256i partial_anti_diag_diff = _mm256_add_epi16(src_pp1, src_nm1);
    const __m256i anti_diag_diff =
        _mm256_sub_epi16(partial_anti_diag_diff, base_val);
    const __m256i abs_anti_diag_diff = _mm256_abs_epi16(anti_diag_diff);
    _mm256_storeu_si256((__m256i *)(line_buf2 + wd), abs_anti_diag_diff);

    cur_row += 16;
    prev_row += 16;
    next_row += 16;
    buffer_col += 16;
  }

  // Invoke C function to process remaining width.
  const int remaining_width = tot_width - buffer_col;
  if (remaining_width)
    calc_gradient_in_various_directions_c(
        feature_line_buffers, row, buffer_row, dgd, dgd_stride, width,
        buffer_col - 1, col_end, feature_length, buffer_col);
}

// Prepare feature_sum_buffers for the remaining width
static void prepare_feature_sum_bufs_4_avx2(int *feature_sum_buffers[],
                                            int16_t *feature_line_buffers[],
                                            int feature_length, int buffer_row,
                                            int buffer_col) {
  const int buffer_row_0 = buffer_row;
  const int buffer_row_1 = buffer_row_0 + feature_length;
  const int buffer_row_2 = buffer_row_1 + feature_length;
  const int buffer_row_3 = buffer_row_2 + feature_length;
#if defined(__GCC__)
#pragma GCC ivdep
#endif
  const int16_t *line_buf1 = &feature_line_buffers[buffer_row_1][buffer_col];
  const int16_t *line_buf2 = &feature_line_buffers[buffer_row_2][buffer_col];
  const int16_t *line_buf3 = &feature_line_buffers[buffer_row_3][buffer_col];
  int *sum_buf1 = &feature_sum_buffers[1][buffer_col];
  int *sum_buf2 = &feature_sum_buffers[2][buffer_col];
  int *sum_buf3 = &feature_sum_buffers[3][buffer_col];

  const __m128i line1 = _mm_loadu_si64((__m128i const *)line_buf1);
  const __m128i line2 = _mm_loadu_si64((__m128i const *)line_buf2);
  const __m128i line3 = _mm_loadu_si64((__m128i const *)line_buf3);
  const __m128i line11 = _mm_cvtepi16_epi32(line1);
  const __m128i line22 = _mm_cvtepi16_epi32(line2);
  const __m128i line33 = _mm_cvtepi16_epi32(line3);

  const __m128i sum1 = _mm_loadu_si128((__m128i const *)sum_buf1);
  const __m128i sum2 = _mm_loadu_si128((__m128i const *)sum_buf2);
  const __m128i sum3 = _mm_loadu_si128((__m128i const *)sum_buf3);

  const __m128i sub1 = _mm_sub_epi32(sum1, line11);
  const __m128i sub2 = _mm_sub_epi32(sum2, line22);
  const __m128i sub3 = _mm_sub_epi32(sum3, line33);

  _mm_storeu_si128((__m128i *)(sum_buf1), sub1);
  _mm_storeu_si128((__m128i *)(sum_buf2), sub2);
  _mm_storeu_si128((__m128i *)(sum_buf3), sub3);
}

// Update feature_sum_buffers for the remaining width
static void update_feature_sum_bufs_4_avx2(int *feature_sum_buffers[],
                                           int16_t *feature_line_buffers[],
                                           int feature_length, int buffer_row,
                                           int buffer_col) {
  const int buffer_row_0 = buffer_row;
  const int buffer_row_1 = buffer_row_0 + feature_length;
  const int buffer_row_2 = buffer_row_1 + feature_length;
  const int buffer_row_3 = buffer_row_2 + feature_length;
  const int16_t *line_buf1 = &feature_line_buffers[buffer_row_1][buffer_col];
  const int16_t *line_buf2 = &feature_line_buffers[buffer_row_2][buffer_col];
  const int16_t *line_buf3 = &feature_line_buffers[buffer_row_3][buffer_col];
  int *sum_buf1 = &feature_sum_buffers[1][buffer_col];
  int *sum_buf2 = &feature_sum_buffers[2][buffer_col];
  int *sum_buf3 = &feature_sum_buffers[3][buffer_col];

  const __m128i line1 = _mm_loadu_si64((__m128i const *)line_buf1);
  const __m128i line2 = _mm_loadu_si64((__m128i const *)line_buf2);
  const __m128i line3 = _mm_loadu_si64((__m128i const *)line_buf3);
  const __m128i line11 = _mm_cvtepi16_epi32(line1);
  const __m128i line22 = _mm_cvtepi16_epi32(line2);
  const __m128i line33 = _mm_cvtepi16_epi32(line3);

  const __m128i sum1 = _mm_loadu_si128((__m128i const *)sum_buf1);
  const __m128i sum2 = _mm_loadu_si128((__m128i const *)sum_buf2);
  const __m128i sum3 = _mm_loadu_si128((__m128i const *)sum_buf3);

  const __m128i sub1 = _mm_add_epi32(sum1, line11);
  const __m128i sub2 = _mm_add_epi32(sum2, line22);
  const __m128i sub3 = _mm_add_epi32(sum3, line33);

  _mm_storeu_si128((__m128i *)(sum_buf1), sub1);
  _mm_storeu_si128((__m128i *)(sum_buf2), sub2);
  _mm_storeu_si128((__m128i *)(sum_buf3), sub3);
}

void prepare_feature_sum_bufs_avx2(int *feature_sum_buffers[],
                                   int16_t *feature_line_buffers[],
                                   int feature_length, int buffer_row,
                                   int col_begin, int col_end, int buffer_col) {
  const int buffer_row_0 = buffer_row;
  const int buffer_row_1 = buffer_row_0 + feature_length;
  const int buffer_row_2 = buffer_row_1 + feature_length;
  const int buffer_row_3 = buffer_row_2 + feature_length;
  const int16_t *line_buf1 = feature_line_buffers[buffer_row_1];
  const int16_t *line_buf2 = feature_line_buffers[buffer_row_2];
  const int16_t *line_buf3 = feature_line_buffers[buffer_row_3];
  int *sum_buf1 = feature_sum_buffers[1];
  int *sum_buf2 = feature_sum_buffers[2];
  int *sum_buf3 = feature_sum_buffers[3];
  const int tot_width = col_end - col_begin;

  // Process width multiples of 8.
  for (int wd = 0; wd + 8 < tot_width; wd += 8) {
    const __m128i line1 = _mm_loadu_si128((__m128i const *)(line_buf1 + wd));
    const __m128i line2 = _mm_loadu_si128((__m128i const *)(line_buf2 + wd));
    const __m128i line3 = _mm_loadu_si128((__m128i const *)(line_buf3 + wd));
    const __m256i sum1 = _mm256_loadu_si256((__m256i const *)(sum_buf1 + wd));
    const __m256i sum2 = _mm256_loadu_si256((__m256i const *)(sum_buf2 + wd));
    const __m256i sum3 = _mm256_loadu_si256((__m256i const *)(sum_buf3 + wd));

    const __m256i line11 = _mm256_cvtepi16_epi32(line1);
    const __m256i line22 = _mm256_cvtepi16_epi32(line2);
    const __m256i line33 = _mm256_cvtepi16_epi32(line3);

    const __m256i sub1 = _mm256_sub_epi32(sum1, line11);
    const __m256i sub2 = _mm256_sub_epi32(sum2, line22);
    const __m256i sub3 = _mm256_sub_epi32(sum3, line33);

    _mm256_storeu_si256((__m256i *)(sum_buf1 + wd), sub1);
    _mm256_storeu_si256((__m256i *)(sum_buf2 + wd), sub2);
    _mm256_storeu_si256((__m256i *)(sum_buf3 + wd), sub3);
    buffer_col += 8;
  }

  // Invoke C function to process remaining width.
  int remaining_width = tot_width - buffer_col;
  const int remain_width_mul4 = (remaining_width >> 2) << 2;
  remaining_width -= remain_width_mul4;
  if (remain_width_mul4)
    prepare_feature_sum_bufs_4_avx2(feature_sum_buffers, feature_line_buffers,
                                    feature_length, buffer_row, buffer_col);
  if (remaining_width)
    prepare_feature_sum_bufs_c(feature_sum_buffers, feature_line_buffers,
                               feature_length, buffer_row, 0, remaining_width,
                               buffer_col + remain_width_mul4);
}

static void update_feature_sum_bufs_avx2(int *feature_sum_buffers[],
                                         int16_t *feature_line_buffers[],
                                         int feature_length, int buffer_row,
                                         int col_begin, int col_end,
                                         int buffer_col) {
  const int buffer_row_0 = buffer_row;
  const int buffer_row_1 = buffer_row_0 + feature_length;
  const int buffer_row_2 = buffer_row_1 + feature_length;
  const int buffer_row_3 = buffer_row_2 + feature_length;
  const int16_t *line_buf1 = feature_line_buffers[buffer_row_1];
  const int16_t *line_buf2 = feature_line_buffers[buffer_row_2];
  const int16_t *line_buf3 = feature_line_buffers[buffer_row_3];
  int *sum_buf1 = feature_sum_buffers[1];
  int *sum_buf2 = feature_sum_buffers[2];
  int *sum_buf3 = feature_sum_buffers[3];
  const int tot_width = col_end - col_begin;

  for (int wd = 0; wd + 8 < tot_width; wd += 8) {
    const __m128i line1 = _mm_loadu_si128((__m128i const *)(line_buf1 + wd));
    const __m128i line2 = _mm_loadu_si128((__m128i const *)(line_buf2 + wd));
    const __m128i line3 = _mm_loadu_si128((__m128i const *)(line_buf3 + wd));
    const __m256i fsum1 = _mm256_loadu_si256((__m256i const *)(sum_buf1 + wd));
    const __m256i fsum2 = _mm256_loadu_si256((__m256i const *)(sum_buf2 + wd));
    const __m256i fsum3 = _mm256_loadu_si256((__m256i const *)(sum_buf3 + wd));

    const __m256i line11 = _mm256_cvtepi16_epi32(line1);
    const __m256i line22 = _mm256_cvtepi16_epi32(line2);
    const __m256i line33 = _mm256_cvtepi16_epi32(line3);

    const __m256i sub1 = _mm256_add_epi32(fsum1, line11);
    const __m256i sub2 = _mm256_add_epi32(fsum2, line22);
    const __m256i sub3 = _mm256_add_epi32(fsum3, line33);

    _mm256_storeu_si256((__m256i *)(sum_buf1 + wd), sub1);
    _mm256_storeu_si256((__m256i *)(sum_buf2 + wd), sub2);
    _mm256_storeu_si256((__m256i *)(sum_buf3 + wd), sub3);
    buffer_col += 8;
  }

  // Invoke C function to process remaining width.
  int remaining_width = tot_width - buffer_col;
  const int remain_width_mul4 = (remaining_width >> 2) << 2;
  remaining_width -= remain_width_mul4;
  if (remain_width_mul4)
    update_feature_sum_bufs_4_avx2(feature_sum_buffers, feature_line_buffers,
                                   feature_length, buffer_row, buffer_col);
  if (remaining_width)
    update_feature_sum_bufs_c(feature_sum_buffers, feature_line_buffers,
                              feature_length, buffer_row, 0, remaining_width,
                              buffer_col + remain_width_mul4);
}

void fill_directional_feature_buffers_highbd_avx2(
    int *feature_sum_buffers[], int16_t *feature_line_buffers[], int row,
    int buffer_row, const uint16_t *dgd, int dgd_stride, int width,
    int feature_lead, int feature_lag) {
  const int feature_length = feature_lead + feature_lag + 1;
  const int col_begin = -feature_lead;
  const int col_end = width + feature_lag;
  int buffer_col = 0;
  // In the below AVX2 functions, total width to be processed is calculated
  // using col_end-col_begin. Hence, this assert is needed for width to be >= 0
  // always.
  assert(col_begin <= col_end);

  // Preparation of feature sum buffers by subtracting the feature line buffers.
  prepare_feature_sum_bufs_avx2(feature_sum_buffers, feature_line_buffers,
                                feature_length, buffer_row, col_begin, col_end,
                                buffer_col);

  // Compute the gradient across different directions.
  calc_gradient_in_various_directions_avx2(
      feature_line_buffers, row, buffer_row, dgd, dgd_stride, width, col_begin,
      col_end, feature_length, buffer_col);

  // Update the feature sum buffers with updated feature line buffers.
  update_feature_sum_bufs_avx2(feature_sum_buffers, feature_line_buffers,
                               feature_length, buffer_row, col_begin, col_end,
                               buffer_col);
}

static const uint8_t shuffle_mask_low[32] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
                                              2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5,
                                              5, 5, 6, 6, 6, 6, 7, 7, 7, 7 };

static const uint8_t shuffle_mask_high[32] = { 8,  8,  8,  8,  9,  9,  9,  9,
                                               10, 10, 10, 10, 11, 11, 11, 11,
                                               12, 12, 12, 12, 13, 13, 13, 13,
                                               14, 14, 14, 14, 15, 15, 15, 15 };

static AVM_INLINE void set_left_and_right_tskip_sum(int8_t *tskip_sum_buffer,
                                                    int col_begin, int width,
                                                    int col_end,
                                                    int8_t left_tskip,
                                                    int8_t right_tskip) {
  int buffer_col = 0;
  for (int col = col_begin; col < 0; ++col) {
    tskip_sum_buffer[buffer_col] = left_tskip;
    ++buffer_col;
  }

  buffer_col += width;
  for (int col = width; col < col_end; ++col) {
    tskip_sum_buffer[buffer_col] = right_tskip;
    ++buffer_col;
  }
}

void av2_fill_tskip_sum_buffer_avx2(int row, const uint8_t *tskip,
                                    int tskip_stride, int8_t *tskip_sum_buffer,
                                    int width, int height, int tskip_lead,
                                    int tskip_lag, bool use_strict_bounds) {
  if ((width != 64) && (width != 32)) {
    av2_fill_tskip_sum_buffer_c(row, tskip, tskip_stride, tskip_sum_buffer,
                                width, height, tskip_lead, tskip_lag,
                                use_strict_bounds);
    return;
  }
  // TODO(oguleryuz): tskip needs boundary extension.
  assert(use_strict_bounds == true);
  const int col_begin = -tskip_lead;
  const int col_end = width + tskip_lag;

  const int clamped_row = AVMMAX(AVMMIN(row, height - 1), 0);
  const int tskip_id_base_add = (clamped_row >> MI_SIZE_LOG2) * tskip_stride;

  const int tskip_length = tskip_lead + tskip_lag + 1;
  int subtract_row = row - tskip_length;
  int tskip_id_base_sub = 0;

  if (subtract_row >= -tskip_lead) {
    assert(subtract_row <= height - 1);
    subtract_row = subtract_row >= 0 ? subtract_row : 0;
    tskip_id_base_sub = (subtract_row >> MI_SIZE_LOG2) * tskip_stride;
  }

  if (width == 64) {
    __m128i tx_skip_add =
        _mm_loadu_si128((__m128i *)(tskip + tskip_id_base_add));

    if (subtract_row >= -tskip_lead) {
      const __m128i tx_skip_sub =
          _mm_loadu_si128((__m128i *)(tskip + tskip_id_base_sub));
      tx_skip_add = _mm_sub_epi8(tx_skip_add, tx_skip_sub);
    }

    // tx_skip_add
    // a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15
    const __m256i tx_skip_add_temp = _mm256_broadcastsi128_si256(tx_skip_add);
    // tx_skip_add_low
    // a0 a0 a0 a0 a1 a1 a1 a1 a2 a2 a2 a2 a3 a3 a3 a3
    // a4 a4 a4 a4 a5 a5 a5 a5 a6 a6 a6 a6 a7 a7 a7 a7
    const __m256i tx_skip_add_low = _mm256_shuffle_epi8(
        tx_skip_add_temp, _mm256_loadu_si256((__m256i *)shuffle_mask_low));
    // tx_skip_add_high
    // a8  a8  a8  a8  a9  a9  a9  a9  a10 a10 a10 a10 a11 a11 a11 a11
    // a12 a12 a12 a12 a13 a13 a13 a13 a14 a14 a14 a14 a15 a15 a15 a15
    const __m256i tx_skip_add_high = _mm256_shuffle_epi8(
        tx_skip_add_temp, _mm256_loadu_si256((__m256i *)shuffle_mask_high));

    const __m256i tskip_0 =
        _mm256_loadu_si256((__m256i *)(tskip_sum_buffer - col_begin));
    const __m256i tskip_1 =
        _mm256_loadu_si256((__m256i *)(tskip_sum_buffer - col_begin + 32));

    const __m256i tskip_sum_low = _mm256_add_epi8(tskip_0, tx_skip_add_low);
    const __m256i tskip_sum_high = _mm256_add_epi8(tskip_1, tx_skip_add_high);

    _mm256_storeu_si256((__m256i *)(tskip_sum_buffer - col_begin),
                        tskip_sum_low);
    _mm256_storeu_si256((__m256i *)(tskip_sum_buffer - col_begin + 32),
                        tskip_sum_high);

    // Extend data from [col_begin, 0) and [width, col_end).
    const int8_t left_tskip = _mm256_extract_epi8(tskip_sum_low, 0);
    if (col_begin == -1 && (col_end == (width + 4))) {
      *((int8_t *)tskip_sum_buffer) = left_tskip;
      int32_t col_end_value = _mm256_extract_epi32(tskip_sum_high, 7);
      memcpy((tskip_sum_buffer - col_begin + width), &col_end_value,
             sizeof(int32_t));
    } else {
      const int8_t right_tskip = _mm256_extract_epi8(tskip_sum_high, 31);
      set_left_and_right_tskip_sum(tskip_sum_buffer, col_begin, width, col_end,
                                   left_tskip, right_tskip);
    }
  } else if (width == 32) {
    __m128i tx_skip_add =
        _mm_loadl_epi64((__m128i *)(tskip + tskip_id_base_add));

    if (subtract_row >= -tskip_lead) {
      const __m128i tx_skip_sub =
          _mm_loadl_epi64((__m128i *)(tskip + tskip_id_base_sub));
      tx_skip_add = _mm_sub_epi8(tx_skip_add, tx_skip_sub);
    }

    const __m256i tx_skip_add_temp = _mm256_broadcastsi128_si256(tx_skip_add);
    // tx_skip_add_low
    // a0 a0 a0 a0 a1 a1 a1 a1 a2 a2 a2 a2 a3 a3 a3 a3
    // a4 a4 a4 a4 a5 a5 a5 a5 a6 a6 a6 a6 a7 a7 a7 a7
    const __m256i tx_skip_add_low = _mm256_shuffle_epi8(
        tx_skip_add_temp, _mm256_loadu_si256((__m256i *)shuffle_mask_low));

    const __m256i tskip_0 =
        _mm256_loadu_si256((__m256i *)(tskip_sum_buffer - col_begin));
    const __m256i tskip_sum_low = _mm256_add_epi8(tskip_0, tx_skip_add_low);

    _mm256_storeu_si256((__m256i *)(tskip_sum_buffer - col_begin),
                        tskip_sum_low);

    // Extend data from [col_begin, 0) and [width, col_end).
    const int8_t left_tskip = _mm256_extract_epi8(tskip_sum_low, 0);
    const int8_t right_tskip = _mm256_extract_epi8(tskip_sum_low, 31);
    if (col_begin == -1 && (col_end == (width + 1))) {
      *((int8_t *)tskip_sum_buffer) = left_tskip;
      *((int8_t *)(tskip_sum_buffer - col_begin + width)) = right_tskip;
    } else {
      set_left_and_right_tskip_sum(tskip_sum_buffer, col_begin, width, col_end,
                                   left_tskip, right_tskip);
    }
  }
}

#define LOAD_INPUT_DATA(cb, cl)                      \
  /*A0 A1 A2 A3 A4 A5 A6 A7*/                        \
  src_A = _mm256_loadu_si256((__m256i *)(lb0 + cb)); \
  /* B0 B1 B2 B3 B4 B5 B6 B7 */                      \
  src_B = _mm256_loadu_si256((__m256i *)(lb1 + cb)); \
  /* C0 C1 C2 C3 C4 C5 C6 C7 */                      \
  src_C = _mm256_loadu_si256((__m256i *)(lb2 + cb)); \
  /* D0 D1 D2 D3 D4 D5 D6 D7 */                      \
  src_D = _mm256_loadu_si256((__m256i *)(lb3 + cb)); \
  /* a0 a1 a2 a3 a4 a5 a6 a7 */                      \
  src_a = _mm256_loadu_si256((__m256i *)(lb0 + cl)); \
  /* b0 b1 b2 b3 b4 b5 b6 b7*/                       \
  src_b = _mm256_loadu_si256((__m256i *)(lb1 + cl)); \
  /* c0 c1 c2 c3 c4 c5 c6 c7 */                      \
  src_c = _mm256_loadu_si256((__m256i *)(lb2 + cl)); \
  /* d0 d1 d2 d3 d4 d5 d6 d7 */                      \
  src_d = _mm256_loadu_si256((__m256i *)(lb3 + cl));

static AVM_INLINE void process_feature_accum_wd8(__m256i src_A, __m256i src_B,
                                                 __m256i src_C, __m256i src_D,
                                                 __m256i src_a, __m256i src_b,
                                                 __m256i src_c, __m256i src_d,
                                                 __m256i *result) {
  // ra0 ra1 ra2 ra3 ra4 ra5 ra6 ra7
  const __m256i diff_Aa = _mm256_sub_epi32(src_A, src_a);
  // rb0 rb1 rb2 rb3 rb4 rb5 rb6 rb7
  const __m256i diff_Bb = _mm256_sub_epi32(src_B, src_b);
  // rc0 rc1 rc2 rc3 rc4 rc5 rc6 rc7
  const __m256i diff_Cc = _mm256_sub_epi32(src_C, src_c);
  // rd0 rd1 rd2 rd3 rd4 rd5 rd6 rd7
  const __m256i diff_Dd = _mm256_sub_epi32(src_D, src_d);
  // ra0+ra1 ra2+ra3 rb0+rb1 rb2+rb3 ra4+ra5 ra6+ra7 rb4+rb5 rb6+rb7
  const __m256i result0 = _mm256_hadd_epi32(diff_Aa, diff_Bb);
  // rc0+rc1 rc2+rc3 rd0+rd1 rd2+rd3 rc4+rc5 rc6+rc7 rd4+rd5 rd6+rd7
  const __m256i result1 = _mm256_hadd_epi32(diff_Cc, diff_Dd);
  // ra0+ra1+ra2+ra3 rb0+rb1+rb2+rb3 rc0+rc1+rc2+rc3 rd0+rd1+rd2+rd3  --
  // ra4+ra5+ra6+ra7 rb4+rb5+rb6+rb7 rc4+rc5+rc6+rc7 rd4+rd5+rd6+rd7
  *result = _mm256_hadd_epi32(result0, result1);
}

static AVM_INLINE void unpack_results_and_store(
    int dir_feature_accum[][PC_WIENER_FEATURE_ACC_SIZE], __m256i result0,
    __m256i result1, int cur_col) {
  const int cur_idx = cur_col / 4;
  const int *sb0 = dir_feature_accum[0];
  const int *sb1 = dir_feature_accum[1];
  const int *sb2 = dir_feature_accum[2];
  const int *sb3 = dir_feature_accum[3];

  const __m128i accumu_r0 = _mm_set1_epi32(dir_feature_accum[0][cur_idx]);
  const __m128i accumu_r1 = _mm_set1_epi32(dir_feature_accum[1][cur_idx]);
  const __m128i accumu_r2 = _mm_set1_epi32(dir_feature_accum[2][cur_idx]);
  const __m128i accumu_r3 = _mm_set1_epi32(dir_feature_accum[3][cur_idx]);

  // ra0+ra1+ra2+ra3 rb0+rb1+rb2+rb3 rc0+rc1+rc2+rc3 rd0+rd1+rd2+rd3
  const __m128i f1 = _mm256_castsi256_si128(result0);
  // ra4+ra5+ra6+ra7 rb4+rb5+rb6+rb7 rc4+rc5+rc6+rc7 rd4+rd5+rd6+rd7
  const __m128i fresult01 = _mm256_extractf128_si256(result0, 1);
  // a0-a7 b0-b7 c0-c7 d0-d7
  const __m128i f2 = _mm_add_epi32(f1, fresult01);
  // ra8+ra9+ra10+ra11 rb8+rb9+rb10+rb11 rc8+rc9+rc10+rc11 rd8+rd9+rd10+rd11
  const __m128i fresult10 = _mm256_castsi256_si128(result1);
  // a0-a11 b0-b11 c0-c11 d0-d11
  const __m128i f3 = _mm_add_epi32(f2, fresult10);
  // ra12+ra13+ra14+ra15 rb12+rb13+rb14+rb15 rc12+rc13+rc14+rc15
  // rd12+rd13+rd14+rd15
  const __m128i fresult11 = _mm256_extractf128_si256(result1, 1);
  // a0-a15 b0-b15 c0-c15 d0-d15
  const __m128i f4 = _mm_add_epi32(f3, fresult11);

  // ra0-ra3 ra0-ra7 rb0-rb3 rb0-rb7
  const __m128i r00 = _mm_unpacklo_epi32(f1, f2);
  // rc0-rc3 rc0-rc7 rd0-rd3 rd0-rd7
  const __m128i r20 = _mm_unpackhi_epi32(f1, f2);
  // ra0-ra11  ra0-ra15 rb0-rb11 rb0-rb15
  const __m128i r01 = _mm_unpacklo_epi32(f3, f4);
  // rc0-rc11  rc0-rc15 rd0-rd11 rd0-rd15
  const __m128i r21 = _mm_unpackhi_epi32(f3, f4);
  // ra0-ra3 ra0-ra7 ra0-ra11  ra0-ra15
  __m128i r0 = _mm_unpacklo_epi64(r00, r01);
  // rb0-rb3 rb0-rb7 rb0-rb11  rb0-rb15
  __m128i r1 = _mm_unpackhi_epi64(r00, r01);
  // rc0-rc3 rc0-rc7 rc0-rc11  rc0-rc15
  __m128i r2 = _mm_unpacklo_epi64(r20, r21);
  // rd0-rd3 rd0-rd7 rd0-rd11  rd0-rd15
  __m128i r3 = _mm_unpackhi_epi64(r20, r21);

  r0 = _mm_add_epi32(r0, accumu_r0);
  r1 = _mm_add_epi32(r1, accumu_r1);
  r2 = _mm_add_epi32(r2, accumu_r2);
  r3 = _mm_add_epi32(r3, accumu_r3);

  _mm_storeu_si128((__m128i *)(sb0 + cur_idx + 1), r0);
  _mm_storeu_si128((__m128i *)(sb1 + cur_idx + 1), r1);
  _mm_storeu_si128((__m128i *)(sb2 + cur_idx + 1), r2);
  _mm_storeu_si128((__m128i *)(sb3 + cur_idx + 1), r3);
}

static AVM_INLINE void process_feature_accum_wd16(
    int dir_feature_accum[][PC_WIENER_FEATURE_ACC_SIZE], int *feature_sum_buf[],
    int cur_col, int cb, int feature_length) {
  const int cl = cb - feature_length;
  const int *lb0 = feature_sum_buf[0];
  const int *lb1 = feature_sum_buf[1];
  const int *lb2 = feature_sum_buf[2];
  const int *lb3 = feature_sum_buf[3];
  __m256i src_A, src_B, src_C, src_D, src_a, src_b, src_c, src_d;

  // Process the first 8 pixels.
  LOAD_INPUT_DATA(cb, cl)
  __m256i result0 = _mm256_set1_epi16(0);
  process_feature_accum_wd8(src_A, src_B, src_C, src_D, src_a, src_b, src_c,
                            src_d, &result0);

  // Process the next 8 pixels.
  LOAD_INPUT_DATA(cb + 8, cl + 8)
  __m256i result1 = _mm256_set1_epi16(0);
  process_feature_accum_wd8(src_A, src_B, src_C, src_D, src_a, src_b, src_c,
                            src_d, &result1);

  unpack_results_and_store(dir_feature_accum, result0, result1, cur_col);
}

static AVM_INLINE void process_feature_accum_wd15(
    int dir_feature_accum[][PC_WIENER_FEATURE_ACC_SIZE], int *feature_sum_buf[],
    int cur_col, int cb, int feature_length) {
  const int cl = cb - feature_length;
  const int *lb0 = feature_sum_buf[0];
  const int *lb1 = feature_sum_buf[1];
  const int *lb2 = feature_sum_buf[2];
  const int *lb3 = feature_sum_buf[3];
  __m256i src_A, src_B, src_C, src_D, src_a, src_b, src_c, src_d;

  // Process the first 8 pixels.
  LOAD_INPUT_DATA(cb, cl);
  __m256i result0 = _mm256_set1_epi16(0);
  process_feature_accum_wd8(src_A, src_B, src_C, src_D, src_a, src_b, src_c,
                            src_d, &result0);

  // Process the next 8 pixels.
  LOAD_INPUT_DATA(cb + 8, cl + 8);
  const __m256i zero = _mm256_setzero_si256();
  src_A = _mm256_blend_epi32(src_A, zero, 0x80);
  src_B = _mm256_blend_epi32(src_B, zero, 0x80);
  src_C = _mm256_blend_epi32(src_C, zero, 0x80);
  src_D = _mm256_blend_epi32(src_D, zero, 0x80);
  src_a = _mm256_blend_epi32(src_a, zero, 0x80);
  src_b = _mm256_blend_epi32(src_b, zero, 0x80);
  src_c = _mm256_blend_epi32(src_c, zero, 0x80);
  src_d = _mm256_blend_epi32(src_d, zero, 0x80);
  __m256i result1 = zero;
  process_feature_accum_wd8(src_A, src_B, src_C, src_D, src_a, src_b, src_c,
                            src_d, &result1);

  unpack_results_and_store(dir_feature_accum, result0, result1, cur_col);
}

void av2_fill_directional_feature_accumulators_avx2(
    int dir_feature_accum[NUM_PC_WIENER_FEATURES][PC_WIENER_FEATURE_ACC_SIZE],
    int *feature_sum_buf[NUM_PC_WIENER_FEATURES], int width, int col_offset,
    int feature_lead, int feature_lag) {
  // SIMD is specifically implemented for pc_wiener block size equal to 4x4 and
  // restoration unit size must be 64x64 for luma or 32x32 for chroma plane.
  if ((PC_WIENER_BLOCK_SIZE != 4) || ((width != 64) && (width != 32))) {
    // TODO(oguleryuz): Reduce the cases where we punt to c-code.
    av2_fill_directional_feature_accumulators_c(
        dir_feature_accum, feature_sum_buf, width, col_offset, feature_lead,
        feature_lag);
    return;
  }

  // Process width equal to 0 case here.
  int cur_col = 0;
  const int feature_length = feature_lead + feature_lag + 1;
  int cb = cur_col + col_offset + feature_lead;
  for (int k = 0; k < NUM_PC_WIENER_FEATURES; k++) {
    dir_feature_accum[k][0] += feature_sum_buf[k][cb];
  }
  ++cb;

  // Process the remaining width here.
  if (width == 64) {
    // Process next 16 (i.e., 1 to 16) pixels here.
    cur_col = 1;
    process_feature_accum_wd16(dir_feature_accum, feature_sum_buf, cur_col, cb,
                               feature_length);

    // Process next 16 (i.e., 17 to 32) pixels here.
    cur_col += 16;
    cb += 16;
    process_feature_accum_wd16(dir_feature_accum, feature_sum_buf, cur_col, cb,
                               feature_length);

    // Process next 16 (i.e., 33 to 48) pixels here.
    cur_col += 16;
    cb += 16;
    process_feature_accum_wd16(dir_feature_accum, feature_sum_buf, cur_col, cb,
                               feature_length);

    // Process remaining 15 (i.e., 49 to 63) pixels here.
    cur_col += 16;
    cb += 16;
    process_feature_accum_wd15(dir_feature_accum, feature_sum_buf, cur_col, cb,
                               feature_length);
  } else if (width == 32) {
    // Process next 16 pixels here.
    cur_col = 1;
    process_feature_accum_wd16(dir_feature_accum, feature_sum_buf, cur_col, cb,
                               feature_length);

    // Process remaining 15 pixels here.
    cur_col += 16;
    cb += 16;
    process_feature_accum_wd15(dir_feature_accum, feature_sum_buf, cur_col, cb,
                               feature_length);
  } else {
    // For any other case, C support is added. So this assert should not be
    // invoked.
    assert(0);
  }
}

// To make last 8bit element of 256bit register to zero.
static const int8_t blend_mask[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                       0, 0, 0, 0, 0, 0, 0, 0, 0, -1 };

static AVM_INLINE void process_accumulation_wd32(const __m256i src_A,
                                                 const __m256i src_a,
                                                 const __m128i accum_reg,
                                                 int16_t *tskip_out) {
  // d1 - - - - d32
  const __m256i diff_Aa = _mm256_sub_epi8(src_A, src_a);
  const __m256i diff0 = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(diff_Aa));
  const __m256i diff1 =
      _mm256_cvtepi8_epi16(_mm256_extractf128_si256(diff_Aa, 1));
  // d1+d2 d3+d4 d5+d6 d7+d8 d17+d18 d19+d20 d21+d22 d23+d24 |
  // d9+d10 d11+d12 d13+d14 d15+d16 d25+d26 d27+d28 d29+d30 d31+d32
  const __m256i r1 = _mm256_hadd_epi16(diff0, diff1);
  // d1+d2 d3+d4 d5+d6 d7+d8 d9+d10 d11+d12 d13+d14 d15+d16 |
  // d17+d18 d19+d20 d21+d22 d23+d24 d25+d26 d27+d28 d29+d30 d31+d32
  __m256i r2 = _mm256_permute4x64_epi64(r1, 0xd8);
  // sum of: d1d4 d5d8 d9d12 d13d16 d17d20 d21d24 d25d28 d29d32
  r2 = _mm256_hadd_epi16(
      r2, _mm256_castsi128_si256(_mm256_extractf128_si256(r2, 1)));
  // sum of: d1d4 d5d8 d9d12 d13d16 d17d20 d21d24 d25d28 d29d32
  // const __m256i r2 = _mm256_permute4x64_epi64(r1, 0x54);
  // d1d4 d5d8  d9d12 d13d16 d17d20 d21d24 d25d28 d29d32 | x
  const __m128i low128 = _mm256_castsi256_si128(r2);
  //  0   d1d4  d5d8   d9d12  d13d16 d17d20 d21d24 d25d28
  __m128i tmp_shift = _mm_bslli_si128(low128, 2);
  // d1d4 d1d8  d5d12  d9d16  d13d20 d17d24 d21d28 d25d32
  const __m128i result0 = _mm_add_epi16(low128, tmp_shift);
  //   0    0   d1d4   d1d8   d5d12  d9d16  d13d20 d17d24
  tmp_shift = _mm_bslli_si128(result0, 4);
  // d1d4 d1d8  d1d12  d1d16  d5d20  d9d24  d13d28 d17d32
  const __m128i result1 = _mm_add_epi16(result0, tmp_shift);
  //  0     0     0      0    d1d4   d1d8   d1d12  d1d16
  tmp_shift = _mm_bslli_si128(result1, 8);
  // d1d4 d1d8  d1d12  d1d16  d1d20  d1d24  d1d28 d1d32
  const __m128i result2 = _mm_add_epi16(result1, tmp_shift);
  // Add the 0th result to all the values
  const __m128i result = _mm_add_epi16(accum_reg, result2);
  _mm_storeu_si128((__m128i *)(tskip_out), result);
}

void av2_fill_tskip_feature_accumulator_avx2(
    int16_t tskip_feature_accum[PC_WIENER_FEATURE_ACC_SIZE],
    int8_t *tskip_sum_buf, int width, int col_offset, int tskip_lead,
    int tskip_lag) {
  // SIMD is specifically implemented for pc_wiener block size equal to 4x4 and
  // restoration unit size must be 64x64 for luma or 32x32 for chroma plane.
  if ((PC_WIENER_BLOCK_SIZE != 4) || ((width != 64) && (width != 32))) {
    // TODO(oguleryuz): Reduce the cases where we punt to c-code.
    av2_fill_tskip_feature_accumulator_c(tskip_feature_accum, tskip_sum_buf,
                                         width, col_offset, tskip_lead,
                                         tskip_lag);
    return;
  }

  // Process pixel 0 case here.
  int col_base = col_offset + tskip_lead;
  const int tskip_length = tskip_lead + tskip_lag + 1;
  assert(col_base >= 0);
  tskip_feature_accum[0] += tskip_sum_buf[col_base];
  col_base++;
  int cl = col_base - tskip_length;

  // Process the remaining width here.
  if (width == 64) {
    // Processing of the next 32 pixels.
    const __m256i src_A =
        _mm256_loadu_si256((__m256i const *)(tskip_sum_buf + col_base));
    const __m256i src_a =
        _mm256_loadu_si256((__m256i const *)(tskip_sum_buf + cl));
    __m128i accum_reg = _mm_set1_epi16(tskip_feature_accum[0]);
    process_accumulation_wd32(src_A, src_a, accum_reg, &tskip_feature_accum[1]);

    // Process the remianing 31 pixels here. Eventhough 31 pixels are processed
    // here, load 32 here and make the last 8bit of 256 bit register to zero.
    // Load will not overflow as enough elements will be there in the
    // tskip_sum_buffer.
    col_base += 32;
    cl = col_base - tskip_length;
    __m256i src_B =
        _mm256_loadu_si256((__m256i const *)(tskip_sum_buf + col_base));
    __m256i src_b = _mm256_loadu_si256((__m256i const *)(tskip_sum_buf + cl));
    const __m256i blend_reg = _mm256_loadu_si256((__m256i *)blend_mask);
    const __m256i zero = _mm256_setzero_si256();
    src_B = _mm256_blendv_epi8(src_B, zero, blend_reg);
    src_b = _mm256_blendv_epi8(src_b, zero, blend_reg);
    accum_reg = _mm_set1_epi16(tskip_feature_accum[8]);
    process_accumulation_wd32(src_B, src_b, accum_reg, &tskip_feature_accum[9]);
  } else if (width == 32) {
    // Process the remianing 31 pixels here. Eventhough 31 pixels are processed
    // here, load 32 here and make the last 8bit of 256 bit register to zero.
    // Load will not overflow as enough elements will be there in the
    // tskip_sum_buffer.
    __m256i src_A =
        _mm256_loadu_si256((__m256i const *)(tskip_sum_buf + col_base));
    __m256i src_a = _mm256_loadu_si256((__m256i const *)(tskip_sum_buf + cl));
    const __m256i blend_reg = _mm256_loadu_si256((__m256i *)blend_mask);
    const __m256i zero = _mm256_setzero_si256();
    __m128i accum_reg = _mm_set1_epi16(tskip_feature_accum[0]);

    src_A = _mm256_blendv_epi8(src_A, zero, blend_reg);
    src_a = _mm256_blendv_epi8(src_a, zero, blend_reg);
    process_accumulation_wd32(src_A, src_a, accum_reg, &tskip_feature_accum[1]);
  } else {
    // For any other case, C support is added. So this assert should not be
    // invoked.
    assert(0);
  }
}

static const uint8_t shuffle_mask[32] = { 0, 1, 4, 5, 8, 9, 12, 13,
                                          0, 1, 4, 5, 8, 9, 12, 13,
                                          0, 1, 4, 5, 8, 9, 12, 13,
                                          0, 1, 4, 5, 8, 9, 12, 13 };

// AVX2 implementation for luma buffer of cross-component wienerns
void calc_wienerns_ds_luma_420_avx2(const uint16_t *src, int src_stride,
                                    uint16_t *const dst, int dst_stride,
                                    int ds_type, int height_uv, int width_uv,
                                    int ss_x, int ss_y, int col_start) {
  assert(ss_x == 1 && ss_y == 1);
  (void)col_start;
  const __m256i shuffle_reg = _mm256_loadu_si256((__m256i *)shuffle_mask);
  const __m256i zero = _mm256_setzero_si256();
  const int width_offset = (width_uv >> 4) << 4;
  const int width_remainder = width_uv - width_offset;
  if (!width_offset) {
    calc_wienerns_ds_luma_420_c(src, src_stride, dst, dst_stride, ds_type,
                                height_uv, width_uv, ss_x, ss_y, 0);
    return;
  }
  if (ds_type == 1) {
    for (int r = 0; r < height_uv; ++r) {
      const uint16_t *src_0 = &src[2 * r * src_stride];
      const uint16_t *src_1 = &src[(2 * r + 1) * src_stride];
      for (int c = 0; c < width_offset; c += 16) {
        // 00 01 02 03 04 05 06 07 | 08 09 010 011 012 013 014 015
        const __m256i src_a0 = _mm256_loadu_si256((__m256i *)(src_0 + c * 2));
        // 016 017 018 019 020 021 022 023 | 024 025 026 027 028 029 030 031
        const __m256i src_a1 =
            _mm256_loadu_si256((__m256i *)(src_0 + c * 2 + 16));
        const __m256i src_b0 = _mm256_loadu_si256((__m256i *)(src_1 + c * 2));
        const __m256i src_b1 =
            _mm256_loadu_si256((__m256i *)(src_1 + c * 2 + 16));

        // 00 02 04 06 08 010 012 014 | x x x x x x x x
        const __m256i src_a_even_0 = _mm256_permute4x64_epi64(
            _mm256_shuffle_epi8(src_a0, shuffle_reg), 0x08);
        // 016 018 020 022 024 026 028 030 | x x x x x x x x
        const __m256i src_a_even_1 = _mm256_permute4x64_epi64(
            _mm256_shuffle_epi8(src_a1, shuffle_reg), 0x08);
        // 00 02 04 06 08 010 012 014 | 016 018 020 022 024 026 028 030
        const __m256i src_a_even =
            _mm256_permute2x128_si256(src_a_even_0, src_a_even_1, 0x20);
        // 00 02 04 06 016 018 020 022
        const __m256i src_00 = _mm256_unpacklo_epi16(src_a_even, zero);
        // 08 010 012 014 024 026 028 030
        const __m256i src_01 = _mm256_unpackhi_epi16(src_a_even, zero);

        // 10 12 14 16 18 110 112 114 | x x x x x x x x
        const __m256i src_b_even_0 = _mm256_permute4x64_epi64(
            _mm256_shuffle_epi8(src_b0, shuffle_reg), 0x08);
        // 116 118 120 122 124 126 128 130 | x x x x x x x x
        const __m256i src_b_even_1 = _mm256_permute4x64_epi64(
            _mm256_shuffle_epi8(src_b1, shuffle_reg), 0x08);
        // 10 12 14 16 18 110 112 114 | 116 118 120 122 124 126 128 130
        const __m256i src_b_even =
            _mm256_permute2x128_si256(src_b_even_0, src_b_even_1, 0x20);
        // 10 12 14 16 116 118 120 122
        const __m256i src_10 = _mm256_unpacklo_epi16(src_b_even, zero);
        // 18 110 112 114 124 126 128 130
        const __m256i src_11 = _mm256_unpackhi_epi16(src_b_even, zero);

        const __m256i result_0 =
            _mm256_srli_epi32(_mm256_add_epi32(src_00, src_10), 1);
        const __m256i result_1 =
            _mm256_srli_epi32(_mm256_add_epi32(src_01, src_11), 1);
        // 10 12 14 16 18 110 112 114 116 118 120 122 124 126 128 130
        const __m256i result = _mm256_packus_epi32(result_0, result_1);
        _mm256_storeu_si256((__m256i *)(&dst[r * dst_stride + c]), result);
      }
    }
  } else if (ds_type == 2) {
    for (int r = 0; r < height_uv; ++r) {
      const uint16_t *src_0 = &src[(1 + ss_y) * r * src_stride];
      for (int c = 0; c < width_offset; c += 16) {
        // 00 01 02 03 04 05 06 07 | 08 09 010 011 012 013 014 015
        const __m256i src_a =
            _mm256_loadu_si256((__m256i const *)(src_0 + c * 2));
        const __m256i src_b =
            _mm256_loadu_si256((__m256i const *)(src_0 + c * 2 + 16));

        // 00 02 04 06 00 02 04 06 | 08 010 012 014 08 010 012 014
        const __m256i src_a_even = _mm256_permute4x64_epi64(
            _mm256_shuffle_epi8(src_a, shuffle_reg), 0x08);
        // 016 018 020 022 016 018 020 022 | 024 026 028 030 024 026 028 030
        const __m256i src_b_even = _mm256_permute4x64_epi64(
            _mm256_shuffle_epi8(src_b, shuffle_reg), 0x08);
        const __m256i result =
            _mm256_permute2x128_si256(src_a_even, src_b_even, 0x20);
        _mm256_storeu_si256((__m256i *)(&dst[r * dst_stride + c]), result);
      }
    }
  } else {
    for (int r = 0; r < height_uv; ++r) {
      const uint16_t *src_0 = &src[2 * r * src_stride];
      const uint16_t *src_1 = &src[(2 * r + 1) * src_stride];
      for (int c = 0; c < width_offset; c += 16) {
        // 00 01 02 03 04 05 06 07 | 08 09 010 011 012 013 014 015
        const __m256i src_a0 = _mm256_loadu_si256((__m256i *)(src_0 + c * 2));
        // 016 017 018 019 020 021 022 023 | 024 025 026 027 028 029 030 031
        const __m256i src_a1 =
            _mm256_loadu_si256((__m256i *)(src_0 + c * 2 + 16));
        // 10 11 12 13 14 15 16 17 | 18 19 110 111 112 113 114 115
        const __m256i src_b0 = _mm256_loadu_si256((__m256i *)(src_1 + c * 2));
        // 116 117 118 119 120 121 122 123 | 124 125 126 127 128 129 130 131
        const __m256i src_b1 =
            _mm256_loadu_si256((__m256i *)(src_1 + c * 2 + 16));

        // 00 01 02 03 | 08 09 010 011
        const __m256i src_a00 = _mm256_unpacklo_epi16(src_a0, zero);
        // 04 05 06 07 | 012 013 014 015
        const __m256i src_a01 = _mm256_unpackhi_epi16(src_a0, zero);
        // 016 017 018 019 | 024 025 026 027
        const __m256i src_a10 = _mm256_unpacklo_epi16(src_a1, zero);
        // 020 021 022 023 | 028 029 030 031
        const __m256i src_a11 = _mm256_unpackhi_epi16(src_a1, zero);
        // 10 11 12 13 | 18 19 110 111
        const __m256i src_b00 = _mm256_unpacklo_epi16(src_b0, zero);
        // 14 15 16 17 | 112 113 114 115
        const __m256i src_b01 = _mm256_unpackhi_epi16(src_b0, zero);
        // 116 117 118 119 | 124 125 126 127
        const __m256i src_b10 = _mm256_unpacklo_epi16(src_b1, zero);
        // 120 121 122 123 | 128 129 130 131
        const __m256i src_b11 = _mm256_unpackhi_epi16(src_b1, zero);

        // 00 01 | 02 03 | 04 05 | 06 07
        const __m256i src_a_0 = _mm256_hadd_epi32(src_a00, src_a01);
        // 08 09 | 010 011 | 012 013 | 014 015
        const __m256i src_a_1 = _mm256_hadd_epi32(src_a10, src_a11);
        // 00 01 | 02 03 | 04 05 | 06 07
        const __m256i src_b_0 = _mm256_hadd_epi32(src_b00, src_b01);
        // 08 09 | 010 011 | 012 013 | 014 015
        const __m256i src_b_1 = _mm256_hadd_epi32(src_b10, src_b11);

        // 00 01 | 02 03 | 04 05 | 06 07
        const __m256i res_0 =
            _mm256_srli_epi32(_mm256_add_epi32(src_a_0, src_b_0), 2);
        // 08 09 | 010 011 | 012 013 | 014 015
        const __m256i res_1 =
            _mm256_srli_epi32(_mm256_add_epi32(src_a_1, src_b_1), 2);
        // 00 01 02 03 | 08 09 010 011 | 04 05 06 07 |012 013 014 015
        const __m256i res = _mm256_packus_epi32(res_0, res_1);
        _mm256_storeu_si256((__m256i *)(&dst[r * dst_stride + c]),
                            _mm256_permute4x64_epi64(res, 0XD8));
      }
    }
  }
  if (width_remainder)
    calc_wienerns_ds_luma_420_c(src, src_stride, dst, dst_stride, ds_type,
                                height_uv, width_uv, ss_x, ss_y, width_offset);
}
