12tap filter for av1_highbd_convolve_2d_sr_ssse3()

SpeedTest: 12x compared to C-code

Change-Id: If11b3f47bf0ee556c8b15c7d6328ac11c419b6de
diff --git a/av1/common/convolve.c b/av1/common/convolve.c
index 054c33e..63dda39 100644
--- a/av1/common/convolve.c
+++ b/av1/common/convolve.c
@@ -1025,11 +1025,6 @@
     const int subpel_y_qn, ConvolveParams *conv_params, int bd) {
   const bool need_x = subpel_x_qn != 0;
   const bool need_y = subpel_y_qn != 0;
-  // Filters with taps > 8 are only for encoder side use.
-  const int filter_x_taps_gt8 =
-      (filter_params_x == NULL) ? 0 : ((filter_params_x->taps > 8) ? 1 : 0);
-  const int filter_y_taps_gt8 =
-      (filter_params_y == NULL) ? 0 : ((filter_params_y->taps > 8) ? 1 : 0);
 
   if (!need_x && !need_y) {
     aom_highbd_convolve_copy(src, src_stride, dst, dst_stride, w, h);
@@ -1041,15 +1036,9 @@
                              filter_params_y, subpel_y_qn, bd);
   } else {
     assert(need_x && need_y);
-    if (filter_x_taps_gt8 || filter_y_taps_gt8) {
-      av1_highbd_convolve_2d_sr_c(src, src_stride, dst, dst_stride, w, h,
-                                  filter_params_x, filter_params_y, subpel_x_qn,
-                                  subpel_y_qn, conv_params, bd);
-    } else {
-      av1_highbd_convolve_2d_sr(src, src_stride, dst, dst_stride, w, h,
-                                filter_params_x, filter_params_y, subpel_x_qn,
-                                subpel_y_qn, conv_params, bd);
-    }
+    av1_highbd_convolve_2d_sr(src, src_stride, dst, dst_stride, w, h,
+                              filter_params_x, filter_params_y, subpel_x_qn,
+                              subpel_y_qn, conv_params, bd);
   }
 }
 
diff --git a/av1/common/x86/highbd_convolve_2d_avx2.c b/av1/common/x86/highbd_convolve_2d_avx2.c
index 12d1962..12046e4 100644
--- a/av1/common/x86/highbd_convolve_2d_avx2.c
+++ b/av1/common/x86/highbd_convolve_2d_avx2.c
@@ -20,6 +20,12 @@
 #include "aom_dsp/aom_filter.h"
 #include "av1/common/convolve.h"
 
+void av1_highbd_convolve_2d_sr_ssse3(
+    const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w,
+    int h, const InterpFilterParams *filter_params_x,
+    const InterpFilterParams *filter_params_y, const int subpel_x_qn,
+    const int subpel_y_qn, ConvolveParams *conv_params, int bd);
+
 void av1_highbd_convolve_2d_sr_avx2(const uint16_t *src, int src_stride,
                                     uint16_t *dst, int dst_stride, int w, int h,
                                     const InterpFilterParams *filter_params_x,
@@ -27,6 +33,13 @@
                                     const int subpel_x_qn,
                                     const int subpel_y_qn,
                                     ConvolveParams *conv_params, int bd) {
+  if (filter_params_x->taps == 12) {
+    av1_highbd_convolve_2d_sr_ssse3(src, src_stride, dst, dst_stride, w, h,
+                                    filter_params_x, filter_params_y,
+                                    subpel_x_qn, subpel_y_qn, conv_params, bd);
+    return;
+  }
+
   DECLARE_ALIGNED(32, int16_t, im_block[(MAX_SB_SIZE + MAX_FILTER_TAP) * 8]);
   int im_h = h + filter_params_y->taps - 1;
   int im_stride = 8;
diff --git a/av1/common/x86/highbd_convolve_2d_ssse3.c b/av1/common/x86/highbd_convolve_2d_ssse3.c
index 5318fca..148543f 100644
--- a/av1/common/x86/highbd_convolve_2d_ssse3.c
+++ b/av1/common/x86/highbd_convolve_2d_ssse3.c
@@ -18,6 +18,7 @@
 #include "aom_dsp/aom_filter.h"
 #include "aom_dsp/x86/convolve_sse2.h"
 #include "av1/common/convolve.h"
+#include "aom_dsp/x86/convolve_common_intrin.h"
 
 void av1_highbd_convolve_2d_sr_ssse3(
     const uint16_t *src, int src_stride, uint16_t *dst, int dst_stride, int w,
@@ -35,7 +36,6 @@
   // Check that, even with 12-bit input, the intermediate values will fit
   // into an unsigned 16-bit intermediate array.
   assert(bd + FILTER_BITS + 2 - conv_params->round_0 <= 16);
-  __m128i coeffs_x[4], coeffs_y[4], s[16];
 
   const __m128i round_const_x = _mm_set1_epi32(
       ((1 << conv_params->round_0) >> 1) + (1 << (bd + FILTER_BITS - 1)));
@@ -54,163 +54,360 @@
       _mm_set1_epi16(bd == 10 ? 1023 : (bd == 12 ? 4095 : 255));
   const __m128i zero = _mm_setzero_si128();
 
-  prepare_coeffs(filter_params_x, subpel_x_qn, coeffs_x);
-  prepare_coeffs(filter_params_y, subpel_y_qn, coeffs_y);
+  if (filter_params_x->taps == 12) {
+    __m128i coeffs_x[6], coeffs_y[6], s[24];
+    prepare_coeffs_12tap(filter_params_x, subpel_x_qn, coeffs_x);
+    prepare_coeffs_12tap(filter_params_y, subpel_y_qn, coeffs_y);
 
-  for (j = 0; j < w; j += 8) {
-    /* Horizontal filter */
-    {
-      for (i = 0; i < im_h; i += 1) {
-        const __m128i row00 =
-            _mm_loadu_si128((__m128i *)&src_ptr[i * src_stride + j]);
-        const __m128i row01 =
-            _mm_loadu_si128((__m128i *)&src_ptr[i * src_stride + (j + 8)]);
+    for (j = 0; j < w; j += 8) {
+      /* Horizontal filter */
+      {
+        for (i = 0; i < im_h; i += 1) {
+          const __m128i row00 =
+              _mm_loadu_si128((__m128i *)&src_ptr[i * src_stride + j]);
+          const __m128i row01 =
+              _mm_loadu_si128((__m128i *)&src_ptr[i * src_stride + (j + 8)]);
+          const __m128i row02 =
+              _mm_loadu_si128((__m128i *)&src_ptr[i * src_stride + (j + 16)]);
 
-        // even pixels
-        s[0] = _mm_alignr_epi8(row01, row00, 0);
-        s[1] = _mm_alignr_epi8(row01, row00, 4);
-        s[2] = _mm_alignr_epi8(row01, row00, 8);
-        s[3] = _mm_alignr_epi8(row01, row00, 12);
+          // even pixels
+          s[0] = _mm_alignr_epi8(row01, row00, 0);
+          s[1] = _mm_alignr_epi8(row01, row00, 4);
+          s[2] = _mm_alignr_epi8(row01, row00, 8);
+          s[3] = _mm_alignr_epi8(row01, row00, 12);
+          s[4] = _mm_alignr_epi8(row02, row01, 0);
+          s[5] = _mm_alignr_epi8(row02, row01, 4);
 
-        __m128i res_even = convolve(s, coeffs_x);
-        res_even = _mm_sra_epi32(_mm_add_epi32(res_even, round_const_x),
-                                 round_shift_x);
+          __m128i res_even = convolve_12tap(s, coeffs_x);
+          res_even = _mm_sra_epi32(_mm_add_epi32(res_even, round_const_x),
+                                   round_shift_x);
 
-        // odd pixels
-        s[0] = _mm_alignr_epi8(row01, row00, 2);
-        s[1] = _mm_alignr_epi8(row01, row00, 6);
-        s[2] = _mm_alignr_epi8(row01, row00, 10);
-        s[3] = _mm_alignr_epi8(row01, row00, 14);
+          // odd pixels
+          s[0] = _mm_alignr_epi8(row01, row00, 2);
+          s[1] = _mm_alignr_epi8(row01, row00, 6);
+          s[2] = _mm_alignr_epi8(row01, row00, 10);
+          s[3] = _mm_alignr_epi8(row01, row00, 14);
+          s[4] = _mm_alignr_epi8(row02, row01, 2);
+          s[5] = _mm_alignr_epi8(row02, row01, 6);
 
-        __m128i res_odd = convolve(s, coeffs_x);
-        res_odd =
-            _mm_sra_epi32(_mm_add_epi32(res_odd, round_const_x), round_shift_x);
+          __m128i res_odd = convolve_12tap(s, coeffs_x);
+          res_odd = _mm_sra_epi32(_mm_add_epi32(res_odd, round_const_x),
+                                  round_shift_x);
 
-        __m128i res_even1 = _mm_packs_epi32(res_even, res_even);
-        __m128i res_odd1 = _mm_packs_epi32(res_odd, res_odd);
-        __m128i res = _mm_unpacklo_epi16(res_even1, res_odd1);
+          __m128i res_even1 = _mm_packs_epi32(res_even, res_even);
+          __m128i res_odd1 = _mm_packs_epi32(res_odd, res_odd);
+          __m128i res = _mm_unpacklo_epi16(res_even1, res_odd1);
 
-        _mm_store_si128((__m128i *)&im_block[i * im_stride], res);
+          _mm_store_si128((__m128i *)&im_block[i * im_stride], res);
+        }
+      }
+
+      /* Vertical filter */
+      {
+        __m128i s0 = _mm_loadu_si128((__m128i *)(im_block + 0 * im_stride));
+        __m128i s1 = _mm_loadu_si128((__m128i *)(im_block + 1 * im_stride));
+        __m128i s2 = _mm_loadu_si128((__m128i *)(im_block + 2 * im_stride));
+        __m128i s3 = _mm_loadu_si128((__m128i *)(im_block + 3 * im_stride));
+        __m128i s4 = _mm_loadu_si128((__m128i *)(im_block + 4 * im_stride));
+        __m128i s5 = _mm_loadu_si128((__m128i *)(im_block + 5 * im_stride));
+        __m128i s6 = _mm_loadu_si128((__m128i *)(im_block + 6 * im_stride));
+        __m128i s7 = _mm_loadu_si128((__m128i *)(im_block + 7 * im_stride));
+        __m128i s8 = _mm_loadu_si128((__m128i *)(im_block + 8 * im_stride));
+        __m128i s9 = _mm_loadu_si128((__m128i *)(im_block + 9 * im_stride));
+        __m128i s10 = _mm_loadu_si128((__m128i *)(im_block + 10 * im_stride));
+
+        s[0] = _mm_unpacklo_epi16(s0, s1);
+        s[1] = _mm_unpacklo_epi16(s2, s3);
+        s[2] = _mm_unpacklo_epi16(s4, s5);
+        s[3] = _mm_unpacklo_epi16(s6, s7);
+        s[4] = _mm_unpacklo_epi16(s8, s9);
+
+        s[6] = _mm_unpackhi_epi16(s0, s1);
+        s[7] = _mm_unpackhi_epi16(s2, s3);
+        s[8] = _mm_unpackhi_epi16(s4, s5);
+        s[9] = _mm_unpackhi_epi16(s6, s7);
+        s[10] = _mm_unpackhi_epi16(s8, s9);
+
+        s[12] = _mm_unpacklo_epi16(s1, s2);
+        s[13] = _mm_unpacklo_epi16(s3, s4);
+        s[14] = _mm_unpacklo_epi16(s5, s6);
+        s[15] = _mm_unpacklo_epi16(s7, s8);
+        s[16] = _mm_unpacklo_epi16(s9, s10);
+
+        s[18] = _mm_unpackhi_epi16(s1, s2);
+        s[19] = _mm_unpackhi_epi16(s3, s4);
+        s[20] = _mm_unpackhi_epi16(s5, s6);
+        s[21] = _mm_unpackhi_epi16(s7, s8);
+        s[22] = _mm_unpackhi_epi16(s9, s10);
+
+        for (i = 0; i < h; i += 2) {
+          const int16_t *data = &im_block[i * im_stride];
+
+          __m128i s11 = _mm_loadu_si128((__m128i *)(data + 11 * im_stride));
+          __m128i s12 = _mm_loadu_si128((__m128i *)(data + 12 * im_stride));
+
+          s[5] = _mm_unpacklo_epi16(s10, s11);
+          s[11] = _mm_unpackhi_epi16(s10, s11);
+
+          s[17] = _mm_unpacklo_epi16(s11, s12);
+          s[23] = _mm_unpackhi_epi16(s11, s12);
+
+          const __m128i res_a0 = convolve_12tap(s, coeffs_y);
+          __m128i res_a_round0 = _mm_sra_epi32(
+              _mm_add_epi32(res_a0, round_const_y), round_shift_y);
+          res_a_round0 = _mm_sra_epi32(
+              _mm_add_epi32(res_a_round0, round_const_bits), round_shift_bits);
+
+          const __m128i res_a1 = convolve_12tap(s + 12, coeffs_y);
+          __m128i res_a_round1 = _mm_sra_epi32(
+              _mm_add_epi32(res_a1, round_const_y), round_shift_y);
+          res_a_round1 = _mm_sra_epi32(
+              _mm_add_epi32(res_a_round1, round_const_bits), round_shift_bits);
+
+          if (w - j > 4) {
+            const __m128i res_b0 = convolve_12tap(s + 6, coeffs_y);
+            __m128i res_b_round0 = _mm_sra_epi32(
+                _mm_add_epi32(res_b0, round_const_y), round_shift_y);
+            res_b_round0 =
+                _mm_sra_epi32(_mm_add_epi32(res_b_round0, round_const_bits),
+                              round_shift_bits);
+
+            const __m128i res_b1 = convolve_12tap(s + 18, coeffs_y);
+            __m128i res_b_round1 = _mm_sra_epi32(
+                _mm_add_epi32(res_b1, round_const_y), round_shift_y);
+            res_b_round1 =
+                _mm_sra_epi32(_mm_add_epi32(res_b_round1, round_const_bits),
+                              round_shift_bits);
+
+            __m128i res_16bit0 = _mm_packs_epi32(res_a_round0, res_b_round0);
+            res_16bit0 = _mm_min_epi16(res_16bit0, clip_pixel);
+            res_16bit0 = _mm_max_epi16(res_16bit0, zero);
+
+            __m128i res_16bit1 = _mm_packs_epi32(res_a_round1, res_b_round1);
+            res_16bit1 = _mm_min_epi16(res_16bit1, clip_pixel);
+            res_16bit1 = _mm_max_epi16(res_16bit1, zero);
+
+            _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res_16bit0);
+            _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j + dst_stride],
+                             res_16bit1);
+          } else if (w == 4) {
+            res_a_round0 = _mm_packs_epi32(res_a_round0, res_a_round0);
+            res_a_round0 = _mm_min_epi16(res_a_round0, clip_pixel);
+            res_a_round0 = _mm_max_epi16(res_a_round0, zero);
+
+            res_a_round1 = _mm_packs_epi32(res_a_round1, res_a_round1);
+            res_a_round1 = _mm_min_epi16(res_a_round1, clip_pixel);
+            res_a_round1 = _mm_max_epi16(res_a_round1, zero);
+
+            _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j], res_a_round0);
+            _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j + dst_stride],
+                             res_a_round1);
+          } else {
+            res_a_round0 = _mm_packs_epi32(res_a_round0, res_a_round0);
+            res_a_round0 = _mm_min_epi16(res_a_round0, clip_pixel);
+            res_a_round0 = _mm_max_epi16(res_a_round0, zero);
+
+            res_a_round1 = _mm_packs_epi32(res_a_round1, res_a_round1);
+            res_a_round1 = _mm_min_epi16(res_a_round1, clip_pixel);
+            res_a_round1 = _mm_max_epi16(res_a_round1, zero);
+
+            *((uint32_t *)(&dst[i * dst_stride + j])) =
+                _mm_cvtsi128_si32(res_a_round0);
+
+            *((uint32_t *)(&dst[i * dst_stride + j + dst_stride])) =
+                _mm_cvtsi128_si32(res_a_round1);
+          }
+          s[0] = s[1];
+          s[1] = s[2];
+          s[2] = s[3];
+          s[3] = s[4];
+          s[4] = s[5];
+
+          s[6] = s[7];
+          s[7] = s[8];
+          s[8] = s[9];
+          s[9] = s[10];
+          s[10] = s[11];
+
+          s[12] = s[13];
+          s[13] = s[14];
+          s[14] = s[15];
+          s[15] = s[16];
+          s[16] = s[17];
+
+          s[18] = s[19];
+          s[19] = s[20];
+          s[20] = s[21];
+          s[21] = s[22];
+          s[22] = s[23];
+
+          s10 = s12;
+        }
       }
     }
-    /* Vertical filter */
-    {
-      __m128i s0 = _mm_loadu_si128((__m128i *)(im_block + 0 * im_stride));
-      __m128i s1 = _mm_loadu_si128((__m128i *)(im_block + 1 * im_stride));
-      __m128i s2 = _mm_loadu_si128((__m128i *)(im_block + 2 * im_stride));
-      __m128i s3 = _mm_loadu_si128((__m128i *)(im_block + 3 * im_stride));
-      __m128i s4 = _mm_loadu_si128((__m128i *)(im_block + 4 * im_stride));
-      __m128i s5 = _mm_loadu_si128((__m128i *)(im_block + 5 * im_stride));
-      __m128i s6 = _mm_loadu_si128((__m128i *)(im_block + 6 * im_stride));
+  } else {
+    __m128i coeffs_x[4], coeffs_y[4], s[16];
+    prepare_coeffs(filter_params_x, subpel_x_qn, coeffs_x);
+    prepare_coeffs(filter_params_y, subpel_y_qn, coeffs_y);
 
-      s[0] = _mm_unpacklo_epi16(s0, s1);
-      s[1] = _mm_unpacklo_epi16(s2, s3);
-      s[2] = _mm_unpacklo_epi16(s4, s5);
+    for (j = 0; j < w; j += 8) {
+      /* Horizontal filter */
+      {
+        for (i = 0; i < im_h; i += 1) {
+          const __m128i row00 =
+              _mm_loadu_si128((__m128i *)&src_ptr[i * src_stride + j]);
+          const __m128i row01 =
+              _mm_loadu_si128((__m128i *)&src_ptr[i * src_stride + (j + 8)]);
 
-      s[4] = _mm_unpackhi_epi16(s0, s1);
-      s[5] = _mm_unpackhi_epi16(s2, s3);
-      s[6] = _mm_unpackhi_epi16(s4, s5);
+          // even pixels
+          s[0] = _mm_alignr_epi8(row01, row00, 0);
+          s[1] = _mm_alignr_epi8(row01, row00, 4);
+          s[2] = _mm_alignr_epi8(row01, row00, 8);
+          s[3] = _mm_alignr_epi8(row01, row00, 12);
 
-      s[0 + 8] = _mm_unpacklo_epi16(s1, s2);
-      s[1 + 8] = _mm_unpacklo_epi16(s3, s4);
-      s[2 + 8] = _mm_unpacklo_epi16(s5, s6);
+          __m128i res_even = convolve(s, coeffs_x);
+          res_even = _mm_sra_epi32(_mm_add_epi32(res_even, round_const_x),
+                                   round_shift_x);
 
-      s[4 + 8] = _mm_unpackhi_epi16(s1, s2);
-      s[5 + 8] = _mm_unpackhi_epi16(s3, s4);
-      s[6 + 8] = _mm_unpackhi_epi16(s5, s6);
+          // odd pixels
+          s[0] = _mm_alignr_epi8(row01, row00, 2);
+          s[1] = _mm_alignr_epi8(row01, row00, 6);
+          s[2] = _mm_alignr_epi8(row01, row00, 10);
+          s[3] = _mm_alignr_epi8(row01, row00, 14);
 
-      for (i = 0; i < h; i += 2) {
-        const int16_t *data = &im_block[i * im_stride];
+          __m128i res_odd = convolve(s, coeffs_x);
+          res_odd = _mm_sra_epi32(_mm_add_epi32(res_odd, round_const_x),
+                                  round_shift_x);
 
-        __m128i s7 = _mm_loadu_si128((__m128i *)(data + 7 * im_stride));
-        __m128i s8 = _mm_loadu_si128((__m128i *)(data + 8 * im_stride));
+          __m128i res_even1 = _mm_packs_epi32(res_even, res_even);
+          __m128i res_odd1 = _mm_packs_epi32(res_odd, res_odd);
+          __m128i res = _mm_unpacklo_epi16(res_even1, res_odd1);
 
-        s[3] = _mm_unpacklo_epi16(s6, s7);
-        s[7] = _mm_unpackhi_epi16(s6, s7);
-
-        s[3 + 8] = _mm_unpacklo_epi16(s7, s8);
-        s[7 + 8] = _mm_unpackhi_epi16(s7, s8);
-
-        const __m128i res_a0 = convolve(s, coeffs_y);
-        __m128i res_a_round0 =
-            _mm_sra_epi32(_mm_add_epi32(res_a0, round_const_y), round_shift_y);
-        res_a_round0 = _mm_sra_epi32(
-            _mm_add_epi32(res_a_round0, round_const_bits), round_shift_bits);
-
-        const __m128i res_a1 = convolve(s + 8, coeffs_y);
-        __m128i res_a_round1 =
-            _mm_sra_epi32(_mm_add_epi32(res_a1, round_const_y), round_shift_y);
-        res_a_round1 = _mm_sra_epi32(
-            _mm_add_epi32(res_a_round1, round_const_bits), round_shift_bits);
-
-        if (w - j > 4) {
-          const __m128i res_b0 = convolve(s + 4, coeffs_y);
-          __m128i res_b_round0 = _mm_sra_epi32(
-              _mm_add_epi32(res_b0, round_const_y), round_shift_y);
-          res_b_round0 = _mm_sra_epi32(
-              _mm_add_epi32(res_b_round0, round_const_bits), round_shift_bits);
-
-          const __m128i res_b1 = convolve(s + 4 + 8, coeffs_y);
-          __m128i res_b_round1 = _mm_sra_epi32(
-              _mm_add_epi32(res_b1, round_const_y), round_shift_y);
-          res_b_round1 = _mm_sra_epi32(
-              _mm_add_epi32(res_b_round1, round_const_bits), round_shift_bits);
-
-          __m128i res_16bit0 = _mm_packs_epi32(res_a_round0, res_b_round0);
-          res_16bit0 = _mm_min_epi16(res_16bit0, clip_pixel);
-          res_16bit0 = _mm_max_epi16(res_16bit0, zero);
-
-          __m128i res_16bit1 = _mm_packs_epi32(res_a_round1, res_b_round1);
-          res_16bit1 = _mm_min_epi16(res_16bit1, clip_pixel);
-          res_16bit1 = _mm_max_epi16(res_16bit1, zero);
-
-          _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res_16bit0);
-          _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j + dst_stride],
-                           res_16bit1);
-        } else if (w == 4) {
-          res_a_round0 = _mm_packs_epi32(res_a_round0, res_a_round0);
-          res_a_round0 = _mm_min_epi16(res_a_round0, clip_pixel);
-          res_a_round0 = _mm_max_epi16(res_a_round0, zero);
-
-          res_a_round1 = _mm_packs_epi32(res_a_round1, res_a_round1);
-          res_a_round1 = _mm_min_epi16(res_a_round1, clip_pixel);
-          res_a_round1 = _mm_max_epi16(res_a_round1, zero);
-
-          _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j], res_a_round0);
-          _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j + dst_stride],
-                           res_a_round1);
-        } else {
-          res_a_round0 = _mm_packs_epi32(res_a_round0, res_a_round0);
-          res_a_round0 = _mm_min_epi16(res_a_round0, clip_pixel);
-          res_a_round0 = _mm_max_epi16(res_a_round0, zero);
-
-          res_a_round1 = _mm_packs_epi32(res_a_round1, res_a_round1);
-          res_a_round1 = _mm_min_epi16(res_a_round1, clip_pixel);
-          res_a_round1 = _mm_max_epi16(res_a_round1, zero);
-
-          *((uint32_t *)(&dst[i * dst_stride + j])) =
-              _mm_cvtsi128_si32(res_a_round0);
-
-          *((uint32_t *)(&dst[i * dst_stride + j + dst_stride])) =
-              _mm_cvtsi128_si32(res_a_round1);
+          _mm_store_si128((__m128i *)&im_block[i * im_stride], res);
         }
-        s[0] = s[1];
-        s[1] = s[2];
-        s[2] = s[3];
+      }
 
-        s[4] = s[5];
-        s[5] = s[6];
-        s[6] = s[7];
+      /* Vertical filter */
+      {
+        __m128i s0 = _mm_loadu_si128((__m128i *)(im_block + 0 * im_stride));
+        __m128i s1 = _mm_loadu_si128((__m128i *)(im_block + 1 * im_stride));
+        __m128i s2 = _mm_loadu_si128((__m128i *)(im_block + 2 * im_stride));
+        __m128i s3 = _mm_loadu_si128((__m128i *)(im_block + 3 * im_stride));
+        __m128i s4 = _mm_loadu_si128((__m128i *)(im_block + 4 * im_stride));
+        __m128i s5 = _mm_loadu_si128((__m128i *)(im_block + 5 * im_stride));
+        __m128i s6 = _mm_loadu_si128((__m128i *)(im_block + 6 * im_stride));
 
-        s[0 + 8] = s[1 + 8];
-        s[1 + 8] = s[2 + 8];
-        s[2 + 8] = s[3 + 8];
+        s[0] = _mm_unpacklo_epi16(s0, s1);
+        s[1] = _mm_unpacklo_epi16(s2, s3);
+        s[2] = _mm_unpacklo_epi16(s4, s5);
 
-        s[4 + 8] = s[5 + 8];
-        s[5 + 8] = s[6 + 8];
-        s[6 + 8] = s[7 + 8];
+        s[4] = _mm_unpackhi_epi16(s0, s1);
+        s[5] = _mm_unpackhi_epi16(s2, s3);
+        s[6] = _mm_unpackhi_epi16(s4, s5);
 
-        s6 = s8;
+        s[0 + 8] = _mm_unpacklo_epi16(s1, s2);
+        s[1 + 8] = _mm_unpacklo_epi16(s3, s4);
+        s[2 + 8] = _mm_unpacklo_epi16(s5, s6);
+
+        s[4 + 8] = _mm_unpackhi_epi16(s1, s2);
+        s[5 + 8] = _mm_unpackhi_epi16(s3, s4);
+        s[6 + 8] = _mm_unpackhi_epi16(s5, s6);
+
+        for (i = 0; i < h; i += 2) {
+          const int16_t *data = &im_block[i * im_stride];
+
+          __m128i s7 = _mm_loadu_si128((__m128i *)(data + 7 * im_stride));
+          __m128i s8 = _mm_loadu_si128((__m128i *)(data + 8 * im_stride));
+
+          s[3] = _mm_unpacklo_epi16(s6, s7);
+          s[7] = _mm_unpackhi_epi16(s6, s7);
+
+          s[3 + 8] = _mm_unpacklo_epi16(s7, s8);
+          s[7 + 8] = _mm_unpackhi_epi16(s7, s8);
+
+          const __m128i res_a0 = convolve(s, coeffs_y);
+          __m128i res_a_round0 = _mm_sra_epi32(
+              _mm_add_epi32(res_a0, round_const_y), round_shift_y);
+          res_a_round0 = _mm_sra_epi32(
+              _mm_add_epi32(res_a_round0, round_const_bits), round_shift_bits);
+
+          const __m128i res_a1 = convolve(s + 8, coeffs_y);
+          __m128i res_a_round1 = _mm_sra_epi32(
+              _mm_add_epi32(res_a1, round_const_y), round_shift_y);
+          res_a_round1 = _mm_sra_epi32(
+              _mm_add_epi32(res_a_round1, round_const_bits), round_shift_bits);
+
+          if (w - j > 4) {
+            const __m128i res_b0 = convolve(s + 4, coeffs_y);
+            __m128i res_b_round0 = _mm_sra_epi32(
+                _mm_add_epi32(res_b0, round_const_y), round_shift_y);
+            res_b_round0 =
+                _mm_sra_epi32(_mm_add_epi32(res_b_round0, round_const_bits),
+                              round_shift_bits);
+
+            const __m128i res_b1 = convolve(s + 4 + 8, coeffs_y);
+            __m128i res_b_round1 = _mm_sra_epi32(
+                _mm_add_epi32(res_b1, round_const_y), round_shift_y);
+            res_b_round1 =
+                _mm_sra_epi32(_mm_add_epi32(res_b_round1, round_const_bits),
+                              round_shift_bits);
+
+            __m128i res_16bit0 = _mm_packs_epi32(res_a_round0, res_b_round0);
+            res_16bit0 = _mm_min_epi16(res_16bit0, clip_pixel);
+            res_16bit0 = _mm_max_epi16(res_16bit0, zero);
+
+            __m128i res_16bit1 = _mm_packs_epi32(res_a_round1, res_b_round1);
+            res_16bit1 = _mm_min_epi16(res_16bit1, clip_pixel);
+            res_16bit1 = _mm_max_epi16(res_16bit1, zero);
+
+            _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j], res_16bit0);
+            _mm_storeu_si128((__m128i *)&dst[i * dst_stride + j + dst_stride],
+                             res_16bit1);
+          } else if (w == 4) {
+            res_a_round0 = _mm_packs_epi32(res_a_round0, res_a_round0);
+            res_a_round0 = _mm_min_epi16(res_a_round0, clip_pixel);
+            res_a_round0 = _mm_max_epi16(res_a_round0, zero);
+
+            res_a_round1 = _mm_packs_epi32(res_a_round1, res_a_round1);
+            res_a_round1 = _mm_min_epi16(res_a_round1, clip_pixel);
+            res_a_round1 = _mm_max_epi16(res_a_round1, zero);
+
+            _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j], res_a_round0);
+            _mm_storel_epi64((__m128i *)&dst[i * dst_stride + j + dst_stride],
+                             res_a_round1);
+          } else {
+            res_a_round0 = _mm_packs_epi32(res_a_round0, res_a_round0);
+            res_a_round0 = _mm_min_epi16(res_a_round0, clip_pixel);
+            res_a_round0 = _mm_max_epi16(res_a_round0, zero);
+
+            res_a_round1 = _mm_packs_epi32(res_a_round1, res_a_round1);
+            res_a_round1 = _mm_min_epi16(res_a_round1, clip_pixel);
+            res_a_round1 = _mm_max_epi16(res_a_round1, zero);
+
+            *((uint32_t *)(&dst[i * dst_stride + j])) =
+                _mm_cvtsi128_si32(res_a_round0);
+
+            *((uint32_t *)(&dst[i * dst_stride + j + dst_stride])) =
+                _mm_cvtsi128_si32(res_a_round1);
+          }
+          s[0] = s[1];
+          s[1] = s[2];
+          s[2] = s[3];
+
+          s[4] = s[5];
+          s[5] = s[6];
+          s[6] = s[7];
+
+          s[0 + 8] = s[1 + 8];
+          s[1 + 8] = s[2 + 8];
+          s[2 + 8] = s[3 + 8];
+
+          s[4 + 8] = s[5 + 8];
+          s[5 + 8] = s[6 + 8];
+          s[6 + 8] = s[7 + 8];
+
+          s6 = s8;
+        }
       }
     }
   }
diff --git a/test/av1_convolve_test.cc b/test/av1_convolve_test.cc
index dfcdaab..0c90280 100644
--- a/test/av1_convolve_test.cc
+++ b/test/av1_convolve_test.cc
@@ -842,8 +842,11 @@
   void RunTest() {
     for (int sub_x = 0; sub_x < 16; ++sub_x) {
       for (int sub_y = 0; sub_y < 16; ++sub_y) {
-        for (int h_f = EIGHTTAP_REGULAR; h_f < INTERP_FILTERS_ALL; ++h_f) {
-          for (int v_f = EIGHTTAP_REGULAR; v_f < INTERP_FILTERS_ALL; ++v_f) {
+        for (int h_f = EIGHTTAP_REGULAR; h_f <= INTERP_FILTERS_ALL; ++h_f) {
+          for (int v_f = EIGHTTAP_REGULAR; v_f <= INTERP_FILTERS_ALL; ++v_f) {
+            if (((h_f == MULTITAP_SHARP2) && (v_f < MULTITAP_SHARP2)) ||
+                ((h_f < MULTITAP_SHARP2) && (v_f == MULTITAP_SHARP2)))
+              continue;
             TestConvolve(static_cast<InterpFilter>(h_f),
                          static_cast<InterpFilter>(v_f), sub_x, sub_y);
           }
@@ -854,30 +857,17 @@
 
  public:
   void SpeedTest() {
-    for (int h_f = EIGHTTAP_REGULAR; h_f < INTERP_FILTERS_ALL; ++h_f) {
-      for (int v_f = EIGHTTAP_REGULAR; v_f < INTERP_FILTERS_ALL; ++v_f) {
+    for (int h_f = EIGHTTAP_REGULAR; h_f <= INTERP_FILTERS_ALL; ++h_f) {
+      for (int v_f = EIGHTTAP_REGULAR; v_f <= INTERP_FILTERS_ALL; ++v_f) {
+        if (((h_f == MULTITAP_SHARP2) && (v_f < MULTITAP_SHARP2)) ||
+            ((h_f < MULTITAP_SHARP2) && (v_f == MULTITAP_SHARP2)))
+          continue;
         TestConvolveSpeed(static_cast<InterpFilter>(h_f),
                           static_cast<InterpFilter>(v_f), 10000);
       }
     }
   }
 
- public:
-  void RunTest12Tap() {
-    for (int sub_x = 0; sub_x < 16; ++sub_x) {
-      for (int sub_y = 0; sub_y < 16; ++sub_y) {
-        TestConvolve(static_cast<InterpFilter>(MULTITAP_SHARP2),
-                     static_cast<InterpFilter>(MULTITAP_SHARP2), sub_x, sub_y);
-      }
-    }
-  }
-
- public:
-  void SpeedTest12Tap() {
-    TestConvolveSpeed(static_cast<InterpFilter>(MULTITAP_SHARP2),
-                      static_cast<InterpFilter>(MULTITAP_SHARP2), 10000);
-  }
-
  private:
   void TestConvolve(const InterpFilter h_f, const InterpFilter v_f,
                     const int sub_x, const int sub_y) {
@@ -939,12 +929,8 @@
 
 TEST_P(AV1Convolve2DTest, RunTest) { RunTest(); }
 
-TEST_P(AV1Convolve2DTest, RunTest12Tap) { RunTest12Tap(); }
-
 TEST_P(AV1Convolve2DTest, DISABLED_SpeedTest) { SpeedTest(); }
 
-TEST_P(AV1Convolve2DTest, DISABLED_SpeedTest12Tap) { SpeedTest12Tap(); }
-
 INSTANTIATE_TEST_SUITE_P(C, AV1Convolve2DTest,
                          BuildLowbdParams(av1_convolve_2d_sr_c));
 
@@ -980,8 +966,11 @@
   void RunTest() {
     for (int sub_x = 0; sub_x < 16; ++sub_x) {
       for (int sub_y = 0; sub_y < 16; ++sub_y) {
-        for (int h_f = EIGHTTAP_REGULAR; h_f < INTERP_FILTERS_ALL; ++h_f) {
-          for (int v_f = EIGHTTAP_REGULAR; v_f < INTERP_FILTERS_ALL; ++v_f) {
+        for (int h_f = EIGHTTAP_REGULAR; h_f <= INTERP_FILTERS_ALL; ++h_f) {
+          for (int v_f = EIGHTTAP_REGULAR; v_f <= INTERP_FILTERS_ALL; ++v_f) {
+            if (((h_f == MULTITAP_SHARP2) && (v_f < MULTITAP_SHARP2)) ||
+                ((h_f < MULTITAP_SHARP2) && (v_f == MULTITAP_SHARP2)))
+              continue;
             TestConvolve(static_cast<InterpFilter>(h_f),
                          static_cast<InterpFilter>(v_f), sub_x, sub_y);
           }
@@ -990,6 +979,19 @@
     }
   }
 
+ public:
+  void SpeedTest() {
+    for (int h_f = EIGHTTAP_REGULAR; h_f <= INTERP_FILTERS_ALL; ++h_f) {
+      for (int v_f = EIGHTTAP_REGULAR; v_f <= INTERP_FILTERS_ALL; ++v_f) {
+        if (((h_f == MULTITAP_SHARP2) && (v_f < MULTITAP_SHARP2)) ||
+            ((h_f < MULTITAP_SHARP2) && (v_f == MULTITAP_SHARP2)))
+          continue;
+        TestConvolveSpeed(static_cast<InterpFilter>(h_f),
+                          static_cast<InterpFilter>(v_f), 10000);
+      }
+    }
+  }
+
  private:
   void TestConvolve(const InterpFilter h_f, const InterpFilter v_f,
                     const int sub_x, const int sub_y) {
@@ -1015,10 +1017,47 @@
                               &conv_params2, bit_depth);
     AssertOutputBufferEq(reference, test, width, height);
   }
+
+  void TestConvolveSpeed(const InterpFilter h_f, const InterpFilter v_f,
+                         int num_iters) {
+    const int width = GetParam().Block().Width();
+    const int height = GetParam().Block().Height();
+    const int bit_depth = GetParam().BitDepth();
+    const InterpFilterParams *filter_params_x =
+        av1_get_interp_filter_params_with_block_size(h_f, width);
+    const InterpFilterParams *filter_params_y =
+        av1_get_interp_filter_params_with_block_size(v_f, height);
+    const uint16_t *input = FirstRandomInput16(GetParam());
+    DECLARE_ALIGNED(32, uint16_t, reference[MAX_SB_SQUARE]);
+    ConvolveParams conv_params1 = get_conv_params_no_round(0, 0, NULL, 0, 0, 8);
+    aom_usec_timer timer;
+    aom_usec_timer_start(&timer);
+    for (int i = 0; i < num_iters; ++i) {
+      av1_highbd_convolve_2d_sr_c(input, width, reference, kOutputStride, width,
+                                  height, filter_params_x, filter_params_y, 0,
+                                  0, &conv_params1, bit_depth);
+    }
+    aom_usec_timer_mark(&timer);
+    const double time1 = static_cast<double>(aom_usec_timer_elapsed(&timer));
+    DECLARE_ALIGNED(32, uint16_t, test[MAX_SB_SQUARE]);
+    ConvolveParams conv_params2 = get_conv_params_no_round(0, 0, NULL, 0, 0, 8);
+    aom_usec_timer_start(&timer);
+    for (int i = 0; i < num_iters; ++i) {
+      GetParam().TestFunction()(input, width, test, kOutputStride, width,
+                                height, filter_params_x, filter_params_y, 0, 0,
+                                &conv_params2, bit_depth);
+    }
+    aom_usec_timer_mark(&timer);
+    const double time2 = static_cast<double>(aom_usec_timer_elapsed(&timer));
+    printf("%d - %d %3dx%-3d:%7.2f/%7.2fns (%3.2f)\n", h_f, v_f, width, height,
+           time1, time2, time1 / time2);
+  }
 };
 
 TEST_P(AV1Convolve2DHighbdTest, RunTest) { RunTest(); }
 
+TEST_P(AV1Convolve2DHighbdTest, DISABLED_SpeedTest) { SpeedTest(); }
+
 INSTANTIATE_TEST_SUITE_P(C, AV1Convolve2DHighbdTest,
                          BuildHighbdParams(av1_highbd_convolve_2d_sr_c));