Commit 0ab25b5217 for aom

commit 0ab25b52179e747593899b7e2cc392a4ccb199dd
Author: Lin Zheng <linzhen@google.com>
Date:   Tue Nov 3 19:19:31 2026 +0000

    Fix nightly build failures

    Fix unguarded AVX2 target attributes causing SIGILL on non-AVX2 hosts

    Change-Id: I093693984924d89c82df7590fa72a6e64f0956c4

diff --git a/av1/common/av1_rtcd_defs.pl b/av1/common/av1_rtcd_defs.pl
index 10e611ab12..b906ba03fd 100644
--- a/av1/common/av1_rtcd_defs.pl
+++ b/av1/common/av1_rtcd_defs.pl
@@ -486,6 +486,9 @@ if (aom_config("CONFIG_AV1_ENCODER") eq "yes") {
   add_proto qw/void av1_get_horver_correlation_full/, "const int16_t *diff, int stride, int w, int h, float *hcorr, float *vcorr";
   specialize qw/av1_get_horver_correlation_full sse4_1 avx2 neon/;

+  add_proto qw/void av1_get_energy_distribution_finer/, "const int16_t *diff, int stride, int bw, int bh, float *hordist, float *verdist";
+  specialize qw/av1_get_energy_distribution_finer avx2/;
+
   add_proto qw/void av1_interp_cubic_rate_dist/, "const double *p1, const double *p2, double x, double rate_dist_f[2]";
   specialize qw/av1_interp_cubic_rate_dist sse2/;

diff --git a/av1/encoder/rd.c b/av1/encoder/rd.c
index cb5dfd4813..1263d35dcd 100644
--- a/av1/encoder/rd.c
+++ b/av1/encoder/rd.c
@@ -1460,9 +1460,6 @@ void av1_set_rd_speed_thresholds(AV1_COMP *cpi) {
   rd->thresh_mult[THR_D45_PRED] = 2500;
 }

-#if HAVE_AVX2 && (defined(__GNUC__) || defined(__clang__))
-__attribute__((target("avx2")))
-#endif
 static inline void update_thr_fact(int (*factor_buf)[MAX_MODES],
                                    THR_MODES best_mode_index,
                                    THR_MODES mode_start, THR_MODES mode_end,
@@ -1483,9 +1480,6 @@ static inline void update_thr_fact(int (*factor_buf)[MAX_MODES],
   }
 }

-#if HAVE_AVX2 && (defined(__GNUC__) || defined(__clang__))
-__attribute__((target("avx2")))
-#endif
 void av1_update_rd_thresh_fact(
     const AV1_COMMON *const cm, int (*factor_buf)[MAX_MODES],
     int use_adaptive_rd_thresh, BLOCK_SIZE bsize, THR_MODES best_mode_index,
diff --git a/av1/encoder/tx_search.c b/av1/encoder/tx_search.c
index e07ee5884f..3f31343f4c 100644
--- a/av1/encoder/tx_search.c
+++ b/av1/encoder/tx_search.c
@@ -11,6 +11,8 @@

 #include <inttypes.h>

+#include "config/av1_rtcd.h"
+
 #include "av1/common/cfl.h"
 #include "av1/common/reconintra.h"
 #include "av1/encoder/block.h"
@@ -23,9 +25,6 @@
 #include "av1/encoder/tx_prune_model_weights.h"
 #include "av1/encoder/tx_search.h"
 #include "av1/encoder/txb_rdopt.h"
-#if HAVE_AVX2 && (defined(__x86_64__) || defined(_M_X64))
-#include <immintrin.h>
-#endif

 #define PROB_THRESH_OFFSET_TX_TYPE 100

@@ -1457,169 +1456,24 @@ static inline float get_adaptive_thresholds(
   return prune_2D_adaptive_thresholds[tx_size][pruning_aggressiveness];
 }

-#if HAVE_AVX2 && defined(__x86_64__) && \
-    (defined(__GNUC__) || defined(__clang__))
-__attribute__((target("avx2")))
-#endif
-static inline void get_energy_distribution_finer(const int16_t *diff,
-                                                 int stride, int bw, int bh,
-                                                 float *hordist,
-                                                 float *verdist) {
+void av1_get_energy_distribution_finer_c(const int16_t *diff, int stride,
+                                         int bw, int bh, float *hordist,
+                                         float *verdist) {
   const int w_shift = bw <= 8 ? 0 : 1;
   const int h_shift = bh <= 8 ? 0 : 1;
   const int esq_w = bw >> w_shift;
   const int esq_h = bh >> h_shift;
+  assert(esq_w <= 16);
+  assert(esq_h <= 16);
   uint32_t col_sum[16] = { 0 };
   uint32_t row_sum[16] = { 0 };
   uint64_t total = 0;
-  int i, j;
-
-#if HAVE_AVX2 && defined(__x86_64__) && \
-    (defined(__GNUC__) || defined(__clang__))
-  if (bw == 4) {
-    __m128i v_col = _mm_setzero_si128();
-    if (h_shift == 0) {
-      for (i = 0; i < esq_h; i++) {
-        const __m128i d = _mm_cvtepi16_epi32(
-            _mm_loadl_epi64((const __m128i *)(diff + i * stride)));
-        const __m128i sq = _mm_mullo_epi32(d, d);
-        v_col = _mm_add_epi32(v_col, sq);
-        const __m128i s1 = _mm_hadd_epi32(sq, sq);
-        const __m128i s2 = _mm_hadd_epi32(s1, s1);
-        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
-      }
-    } else {
-      for (i = 0; i < esq_h; i++) {
-        const __m128i d0 =
-            _mm_loadl_epi64((const __m128i *)(diff + (2 * i) * stride));
-        const __m128i d1 =
-            _mm_loadl_epi64((const __m128i *)(diff + (2 * i + 1) * stride));
-        const __m128i d01 = _mm_unpacklo_epi16(d0, d1);
-        const __m128i sq = _mm_madd_epi16(d01, d01);
-        v_col = _mm_add_epi32(v_col, sq);
-        const __m128i s1 = _mm_hadd_epi32(sq, sq);
-        const __m128i s2 = _mm_hadd_epi32(s1, s1);
-        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
-      }
-    }
-    _mm_storeu_si128((__m128i *)col_sum, v_col);
-    const __m128i t1 = _mm_hadd_epi32(v_col, v_col);
-    const __m128i t2 = _mm_hadd_epi32(t1, t1);
-    total = (uint32_t)_mm_cvtsi128_si32(t2);
-  } else if (bw == 8) {
-    __m256i v_col = _mm256_setzero_si256();
-    if (h_shift == 0) {
-      for (i = 0; i < esq_h; i++) {
-        const __m256i d = _mm256_cvtepi16_epi32(
-            _mm_loadu_si128((const __m128i *)(diff + i * stride)));
-        const __m256i sq = _mm256_mullo_epi32(d, d);
-        v_col = _mm256_add_epi32(v_col, sq);
-        const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
-                                            _mm256_extracti128_si256(sq, 1));
-        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
-        const __m128i s2 = _mm_hadd_epi32(s1, s1);
-        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
-      }
-    } else {
-      for (i = 0; i < esq_h; i++) {
-        const __m128i d0 =
-            _mm_loadu_si128((const __m128i *)(diff + (2 * i) * stride));
-        const __m128i d1 =
-            _mm_loadu_si128((const __m128i *)(diff + (2 * i + 1) * stride));
-        const __m256i d0_256 = _mm256_cvtepi16_epi32(d0);
-        const __m256i d1_256 = _mm256_cvtepi16_epi32(d1);
-        const __m256i sq = _mm256_add_epi32(_mm256_mullo_epi32(d0_256, d0_256),
-                                            _mm256_mullo_epi32(d1_256, d1_256));
-        v_col = _mm256_add_epi32(v_col, sq);
-        const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
-                                            _mm256_extracti128_si256(sq, 1));
-        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
-        const __m128i s2 = _mm_hadd_epi32(s1, s1);
-        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
-      }
-    }
-    _mm256_storeu_si256((__m256i *)col_sum, v_col);
-    const __m128i c128 = _mm_add_epi32(_mm256_castsi256_si128(v_col),
-                                       _mm256_extracti128_si256(v_col, 1));
-    const __m128i t1 = _mm_hadd_epi32(c128, c128);
-    const __m128i t2 = _mm_hadd_epi32(t1, t1);
-    total = (uint32_t)_mm_cvtsi128_si32(t2);
-  } else if (bw == 16) {
-    __m256i v_col = _mm256_setzero_si256();
-    if (h_shift == 0) {
-      for (i = 0; i < esq_h; i++) {
-        const __m256i d =
-            _mm256_loadu_si256((const __m256i *)(diff + i * stride));
-        const __m256i sq = _mm256_madd_epi16(d, d);
-        v_col = _mm256_add_epi32(v_col, sq);
-        const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
-                                            _mm256_extracti128_si256(sq, 1));
-        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
-        const __m128i s2 = _mm_hadd_epi32(s1, s1);
-        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
-      }
-    } else {
-      for (i = 0; i < esq_h; i++) {
-        const __m256i d0 =
-            _mm256_loadu_si256((const __m256i *)(diff + (2 * i) * stride));
-        const __m256i d1 =
-            _mm256_loadu_si256((const __m256i *)(diff + (2 * i + 1) * stride));
-        const __m256i sq = _mm256_add_epi32(_mm256_madd_epi16(d0, d0),
-                                            _mm256_madd_epi16(d1, d1));
-        v_col = _mm256_add_epi32(v_col, sq);
-        const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
-                                            _mm256_extracti128_si256(sq, 1));
-        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
-        const __m128i s2 = _mm_hadd_epi32(s1, s1);
-        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
-      }
-    }
-    _mm256_storeu_si256((__m256i *)col_sum, v_col);
-    const __m128i c128 = _mm_add_epi32(_mm256_castsi256_si128(v_col),
-                                       _mm256_extracti128_si256(v_col, 1));
-    const __m128i t1 = _mm_hadd_epi32(c128, c128);
-    const __m128i t2 = _mm_hadd_epi32(t1, t1);
-    total = (uint32_t)_mm_cvtsi128_si32(t2);
-  } else {
-    __m256i v_col_lo = _mm256_setzero_si256();
-    __m256i v_col_hi = _mm256_setzero_si256();
-    for (i = 0; i < esq_h; i++) {
-      const int16_t *r0 = diff + (2 * i) * stride;
-      const int16_t *r1 = diff + (2 * i + 1) * stride;
-      const __m256i d0_lo = _mm256_loadu_si256((const __m256i *)r0);
-      const __m256i d0_hi = _mm256_loadu_si256((const __m256i *)(r0 + 16));
-      const __m256i d1_lo = _mm256_loadu_si256((const __m256i *)r1);
-      const __m256i d1_hi = _mm256_loadu_si256((const __m256i *)(r1 + 16));
-      const __m256i sq_lo = _mm256_add_epi32(_mm256_madd_epi16(d0_lo, d0_lo),
-                                             _mm256_madd_epi16(d1_lo, d1_lo));
-      const __m256i sq_hi = _mm256_add_epi32(_mm256_madd_epi16(d0_hi, d0_hi),
-                                             _mm256_madd_epi16(d1_hi, d1_hi));
-      v_col_lo = _mm256_add_epi32(v_col_lo, sq_lo);
-      v_col_hi = _mm256_add_epi32(v_col_hi, sq_hi);
-      const __m256i sq_sum = _mm256_add_epi32(sq_lo, sq_hi);
-      const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq_sum),
-                                          _mm256_extracti128_si256(sq_sum, 1));
-      const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
-      const __m128i s2 = _mm_hadd_epi32(s1, s1);
-      row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
-    }
-    _mm256_storeu_si256((__m256i *)col_sum, v_col_lo);
-    _mm256_storeu_si256((__m256i *)(col_sum + 8), v_col_hi);
-    const __m256i v_col_all = _mm256_add_epi32(v_col_lo, v_col_hi);
-    const __m128i c128 = _mm_add_epi32(_mm256_castsi256_si128(v_col_all),
-                                       _mm256_extracti128_si256(v_col_all, 1));
-    const __m128i t1 = _mm_hadd_epi32(c128, c128);
-    const __m128i t2 = _mm_hadd_epi32(t1, t1);
-    total = (uint32_t)_mm_cvtsi128_si32(t2);
-  }
-#else
-  memset(col_sum, 0, esq_w * sizeof(col_sum[0]));
-  memset(row_sum, 0, esq_h * sizeof(row_sum[0]));
-  for (i = 0; i < bh; i++) {
+
+  for (int i = 0; i < bh; i++) {
     const int16_t *cur_diff_row = diff + i * stride;
     uint32_t r_sum = 0;
     if (w_shift) {
-      for (j = 0; j < bw; j += 2) {
+      for (int j = 0; j < bw; j += 2) {
         const uint32_t sq =
             (uint32_t)(cur_diff_row[j] * cur_diff_row[j] +
                        cur_diff_row[j + 1] * cur_diff_row[j + 1]);
@@ -1627,7 +1481,7 @@ static inline void get_energy_distribution_finer(const int16_t *diff,
         r_sum += sq;
       }
     } else {
-      for (j = 0; j < bw; j++) {
+      for (int j = 0; j < bw; j++) {
         const uint32_t sq = (uint32_t)(cur_diff_row[j] * cur_diff_row[j]);
         col_sum[j] += sq;
         r_sum += sq;
@@ -1636,20 +1490,19 @@ static inline void get_energy_distribution_finer(const int16_t *diff,
     row_sum[i >> h_shift] += r_sum;
     total += r_sum;
   }
-#endif

   // Output hordist and verdist arrays are normalized 1D projections of esq
   if (total == 0) {
-    float hor_val = 1.0f / esq_w;
-    for (j = 0; j < esq_w - 1; j++) hordist[j] = hor_val;
-    float ver_val = 1.0f / esq_h;
-    for (i = 0; i < esq_h - 1; i++) verdist[i] = ver_val;
+    const float hor_val = 1.0f / esq_w;
+    for (int j = 0; j < esq_w - 1; j++) hordist[j] = hor_val;
+    const float ver_val = 1.0f / esq_h;
+    for (int i = 0; i < esq_h - 1; i++) verdist[i] = ver_val;
     return;
   }

   const float e_recip = 1.0f / (float)total;
-  for (j = 0; j < esq_w - 1; j++) hordist[j] = (float)col_sum[j] * e_recip;
-  for (i = 0; i < esq_h - 1; i++) verdist[i] = (float)row_sum[i] * e_recip;
+  for (int j = 0; j < esq_w - 1; j++) hordist[j] = (float)col_sum[j] * e_recip;
+  for (int i = 0; i < esq_h - 1; i++) verdist[i] = (float)row_sum[i] * e_recip;
 }

 static inline bool check_bit_mask(uint16_t mask, int val) {
@@ -1664,10 +1517,6 @@ static inline void unset_bit_mask(uint16_t *mask, int val) {
   *mask &= ~(1 << val);
 }

-#if HAVE_AVX2 && defined(__x86_64__) && \
-    (defined(__GNUC__) || defined(__clang__))
-__attribute__((target("avx2")))
-#endif
 static void prune_tx_2D(MACROBLOCK *x, BLOCK_SIZE bsize, TX_SIZE tx_size,
                         int blk_row, int blk_col, TxSetType tx_set_type,
                         TX_TYPE_PRUNE_MODE prune_2d_txfm_mode, int *txk_map,
@@ -1705,8 +1554,8 @@ static void prune_tx_2D(MACROBLOCK *x, BLOCK_SIZE bsize, TX_SIZE tx_size,
   const struct macroblock_plane *const p = &x->plane[0];
   const int diff_stride = block_size_wide[bsize];
   const int16_t *diff = p->src_diff + 4 * blk_row * diff_stride + 4 * blk_col;
-  get_energy_distribution_finer(diff, diff_stride, bw, bh, hfeatures,
-                                vfeatures);
+  av1_get_energy_distribution_finer(diff, diff_stride, bw, bh, hfeatures,
+                                    vfeatures);

   av1_get_horver_correlation_full(diff, diff_stride, bw, bh,
                                   &hfeatures[hfeatures_num - 1],
diff --git a/av1/encoder/x86/rdopt_avx2.c b/av1/encoder/x86/rdopt_avx2.c
index 71dd0ea16a..b7498d1a7b 100644
--- a/av1/encoder/x86/rdopt_avx2.c
+++ b/av1/encoder/x86/rdopt_avx2.c
@@ -252,3 +252,201 @@ void av1_get_horver_correlation_full_avx2(const int16_t *diff, int stride,
     *vcorr = 1.0;
   }
 }
+
+static inline uint64_t xx_cvtsi128_si64(__m128i a) {
+#if AOM_ARCH_X86_64
+  return (uint64_t)_mm_cvtsi128_si64(a);
+#else
+  uint64_t tmp;
+  _mm_storel_epi64((__m128i *)&tmp, a);
+  return tmp;
+#endif
+}
+
+static inline uint64_t sum_u32x4_to_u64(__m128i v) {
+  const __m256i v64 = _mm256_cvtepu32_epi64(v);
+  const __m128i sum2 = _mm_add_epi64(_mm256_castsi256_si128(v64),
+                                     _mm256_extracti128_si256(v64, 1));
+  const __m128i sum1 = _mm_add_epi64(sum2, _mm_srli_si128(sum2, 8));
+  return xx_cvtsi128_si64(sum1);
+}
+
+static inline uint64_t sum_u32x8_to_u64(__m256i v) {
+  const __m256i lo64 = _mm256_cvtepu32_epi64(_mm256_castsi256_si128(v));
+  const __m256i hi64 = _mm256_cvtepu32_epi64(_mm256_extracti128_si256(v, 1));
+  const __m256i sum4 = _mm256_add_epi64(lo64, hi64);
+  const __m128i sum2 = _mm_add_epi64(_mm256_castsi256_si128(sum4),
+                                     _mm256_extracti128_si256(sum4, 1));
+  const __m128i sum1 = _mm_add_epi64(sum2, _mm_srli_si128(sum2, 8));
+  return xx_cvtsi128_si64(sum1);
+}
+
+void av1_get_energy_distribution_finer_avx2(const int16_t *diff, int stride,
+                                            int bw, int bh, float *hordist,
+                                            float *verdist) {
+  const int w_shift = bw <= 8 ? 0 : 1;
+  const int h_shift = bh <= 8 ? 0 : 1;
+  const int esq_w = bw >> w_shift;
+  const int esq_h = bh >> h_shift;
+  assert(esq_w <= 16);
+  assert(esq_h <= 16);
+  uint32_t col_sum[16] = { 0 };
+  uint32_t row_sum[16] = { 0 };
+  uint64_t total = 0;
+
+  if (bw == 4) {
+    __m128i v_col = _mm_setzero_si128();
+    if (h_shift == 0) {
+      for (int i = 0; i < esq_h; ++i) {
+        const __m128i d = _mm_cvtepi16_epi32(
+            _mm_loadl_epi64((const __m128i *)(diff + i * stride)));
+        const __m128i sq = _mm_mullo_epi32(d, d);
+        v_col = _mm_add_epi32(v_col, sq);
+        const __m128i s1 = _mm_hadd_epi32(sq, sq);
+        const __m128i s2 = _mm_hadd_epi32(s1, s1);
+        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+      }
+    } else {
+      for (int i = 0; i < esq_h; ++i) {
+        const __m128i d0 =
+            _mm_loadl_epi64((const __m128i *)(diff + (2 * i) * stride));
+        const __m128i d1 =
+            _mm_loadl_epi64((const __m128i *)(diff + (2 * i + 1) * stride));
+        const __m128i d01 = _mm_unpacklo_epi16(d0, d1);
+        const __m128i sq = _mm_madd_epi16(d01, d01);
+        v_col = _mm_add_epi32(v_col, sq);
+        const __m128i s1 = _mm_hadd_epi32(sq, sq);
+        const __m128i s2 = _mm_hadd_epi32(s1, s1);
+        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+      }
+    }
+    _mm_storeu_si128((__m128i *)col_sum, v_col);
+    total = sum_u32x4_to_u64(v_col);
+  } else if (bw == 8) {
+    __m256i v_col = _mm256_setzero_si256();
+    if (h_shift == 0) {
+      for (int i = 0; i < esq_h; ++i) {
+        const __m256i d = _mm256_cvtepi16_epi32(
+            _mm_loadu_si128((const __m128i *)(diff + i * stride)));
+        const __m256i sq = _mm256_mullo_epi32(d, d);
+        v_col = _mm256_add_epi32(v_col, sq);
+        const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
+                                            _mm256_extracti128_si256(sq, 1));
+        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+        const __m128i s2 = _mm_hadd_epi32(s1, s1);
+        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+      }
+    } else {
+      for (int i = 0; i < esq_h; ++i) {
+        const __m128i d0 =
+            _mm_loadu_si128((const __m128i *)(diff + (2 * i) * stride));
+        const __m128i d1 =
+            _mm_loadu_si128((const __m128i *)(diff + (2 * i + 1) * stride));
+        const __m256i d0_256 = _mm256_cvtepi16_epi32(d0);
+        const __m256i d1_256 = _mm256_cvtepi16_epi32(d1);
+        const __m256i sq = _mm256_add_epi32(_mm256_mullo_epi32(d0_256, d0_256),
+                                            _mm256_mullo_epi32(d1_256, d1_256));
+        v_col = _mm256_add_epi32(v_col, sq);
+        const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
+                                            _mm256_extracti128_si256(sq, 1));
+        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+        const __m128i s2 = _mm_hadd_epi32(s1, s1);
+        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+      }
+    }
+    _mm256_storeu_si256((__m256i *)col_sum, v_col);
+    total = sum_u32x8_to_u64(v_col);
+  } else if (bw == 16) {
+    __m256i v_col = _mm256_setzero_si256();
+    if (h_shift == 0) {
+      for (int i = 0; i < esq_h; ++i) {
+        const __m256i d =
+            _mm256_loadu_si256((const __m256i *)(diff + i * stride));
+        const __m256i sq = _mm256_madd_epi16(d, d);
+        v_col = _mm256_add_epi32(v_col, sq);
+        const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
+                                            _mm256_extracti128_si256(sq, 1));
+        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+        const __m128i s2 = _mm_hadd_epi32(s1, s1);
+        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+      }
+    } else {
+      for (int i = 0; i < esq_h; ++i) {
+        const __m256i d0 =
+            _mm256_loadu_si256((const __m256i *)(diff + (2 * i) * stride));
+        const __m256i d1 =
+            _mm256_loadu_si256((const __m256i *)(diff + (2 * i + 1) * stride));
+        const __m256i sq = _mm256_add_epi32(_mm256_madd_epi16(d0, d0),
+                                            _mm256_madd_epi16(d1, d1));
+        v_col = _mm256_add_epi32(v_col, sq);
+        const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
+                                            _mm256_extracti128_si256(sq, 1));
+        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+        const __m128i s2 = _mm_hadd_epi32(s1, s1);
+        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+      }
+    }
+    _mm256_storeu_si256((__m256i *)col_sum, v_col);
+    total = sum_u32x8_to_u64(v_col);
+  } else {
+    assert(bw == 32);
+    __m256i v_col_lo = _mm256_setzero_si256();
+    __m256i v_col_hi = _mm256_setzero_si256();
+    if (h_shift == 0) {
+      for (int i = 0; i < esq_h; ++i) {
+        const int16_t *r0 = diff + i * stride;
+        const __m256i d0_lo = _mm256_loadu_si256((const __m256i *)r0);
+        const __m256i d0_hi = _mm256_loadu_si256((const __m256i *)(r0 + 16));
+        const __m256i sq_lo = _mm256_madd_epi16(d0_lo, d0_lo);
+        const __m256i sq_hi = _mm256_madd_epi16(d0_hi, d0_hi);
+        v_col_lo = _mm256_add_epi32(v_col_lo, sq_lo);
+        v_col_hi = _mm256_add_epi32(v_col_hi, sq_hi);
+        const __m256i sq_sum = _mm256_add_epi32(sq_lo, sq_hi);
+        const __m128i sq128 =
+            _mm_add_epi32(_mm256_castsi256_si128(sq_sum),
+                          _mm256_extracti128_si256(sq_sum, 1));
+        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+        const __m128i s2 = _mm_hadd_epi32(s1, s1);
+        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+      }
+    } else {
+      for (int i = 0; i < esq_h; ++i) {
+        const int16_t *r0 = diff + (2 * i) * stride;
+        const int16_t *r1 = diff + (2 * i + 1) * stride;
+        const __m256i d0_lo = _mm256_loadu_si256((const __m256i *)r0);
+        const __m256i d0_hi = _mm256_loadu_si256((const __m256i *)(r0 + 16));
+        const __m256i d1_lo = _mm256_loadu_si256((const __m256i *)r1);
+        const __m256i d1_hi = _mm256_loadu_si256((const __m256i *)(r1 + 16));
+        const __m256i sq_lo = _mm256_add_epi32(_mm256_madd_epi16(d0_lo, d0_lo),
+                                               _mm256_madd_epi16(d1_lo, d1_lo));
+        const __m256i sq_hi = _mm256_add_epi32(_mm256_madd_epi16(d0_hi, d0_hi),
+                                               _mm256_madd_epi16(d1_hi, d1_hi));
+        v_col_lo = _mm256_add_epi32(v_col_lo, sq_lo);
+        v_col_hi = _mm256_add_epi32(v_col_hi, sq_hi);
+        const __m256i sq_sum = _mm256_add_epi32(sq_lo, sq_hi);
+        const __m128i sq128 =
+            _mm_add_epi32(_mm256_castsi256_si128(sq_sum),
+                          _mm256_extracti128_si256(sq_sum, 1));
+        const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+        const __m128i s2 = _mm_hadd_epi32(s1, s1);
+        row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+      }
+    }
+    _mm256_storeu_si256((__m256i *)col_sum, v_col_lo);
+    _mm256_storeu_si256((__m256i *)(col_sum + 8), v_col_hi);
+    total = sum_u32x8_to_u64(v_col_lo) + sum_u32x8_to_u64(v_col_hi);
+  }
+
+  // Output hordist and verdist arrays are normalized 1D projections of esq
+  if (total == 0) {
+    const float hor_val = 1.0f / esq_w;
+    for (int j = 0; j < esq_w - 1; ++j) hordist[j] = hor_val;
+    const float ver_val = 1.0f / esq_h;
+    for (int i = 0; i < esq_h - 1; ++i) verdist[i] = ver_val;
+    return;
+  }
+
+  const float e_recip = 1.0f / (float)total;
+  for (int j = 0; j < esq_w - 1; ++j) hordist[j] = (float)col_sum[j] * e_recip;
+  for (int i = 0; i < esq_h - 1; ++i) verdist[i] = (float)row_sum[i] * e_recip;
+}
diff --git a/test/horver_correlation_test.cc b/test/horver_correlation_test.cc
index c9212de185..476d2e00e4 100644
--- a/test/horver_correlation_test.cc
+++ b/test/horver_correlation_test.cc
@@ -151,4 +151,122 @@ INSTANTIATE_TEST_SUITE_P(
     AVX2, HorverTest, ::testing::Values(av1_get_horver_correlation_full_avx2));
 #endif  // HAVE_AVX2

+using EnergyDistFunc = void (*)(const int16_t *diff, int stride, int bw, int bh,
+                                float *hordist, float *verdist);
+
+class EnergyDistributionTest : public ::testing::TestWithParam<EnergyDistFunc> {
+ public:
+  void SetUp() override {
+    data_buf_ = (int16_t *)aom_malloc(MAX_SB_SQUARE * sizeof(int16_t));
+    ASSERT_NE(data_buf_, nullptr);
+    target_func_ = GetParam();
+  }
+  void TearDown() override { aom_free(data_buf_); }
+  void RunRandomTest();
+  void RunZeroTest();
+  void RunExtremeValuesTest();
+
+ private:
+  EnergyDistFunc target_func_;
+  ACMRandom rng_;
+  int16_t *data_buf_;
+};
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EnergyDistributionTest);
+
+void EnergyDistributionTest::RunRandomTest() {
+  for (int tx_size = 0; tx_size < TX_SIZES_ALL; ++tx_size) {
+    const int bw = tx_size_wide[tx_size];
+    const int bh = tx_size_high[tx_size];
+    if (bw > 32 || bh > 32) continue;
+    const int esq_w = bw <= 8 ? bw : bw / 2;
+    const int esq_h = bh <= 8 ? bh : bh / 2;
+    for (int iter = 0; iter < 500 && !HasFatalFailure(); ++iter) {
+      float hdist_ref[16] = { 0 }, vdist_ref[16] = { 0 };
+      float hdist_test[16] = { 0 }, vdist_test[16] = { 0 };
+
+      for (int i = 0; i < MAX_SB_SQUARE; ++i) {
+        data_buf_[i] = (rng_.Rand16() % (1 << 13)) - (1 << 12);
+      }
+
+      av1_get_energy_distribution_finer_c(data_buf_, MAX_SB_SIZE, bw, bh,
+                                          hdist_ref, vdist_ref);
+      API_REGISTER_STATE_CHECK(
+          target_func_(data_buf_, MAX_SB_SIZE, bw, bh, hdist_test, vdist_test));
+
+      for (int j = 0; j < esq_w - 1; ++j) {
+        ASSERT_FLOAT_EQ(hdist_ref[j], hdist_test[j])
+            << "hordist mismatch at " << j << " (" << bw << "x" << bh << ")";
+      }
+      for (int i = 0; i < esq_h - 1; ++i) {
+        ASSERT_FLOAT_EQ(vdist_ref[i], vdist_test[i])
+            << "verdist mismatch at " << i << " (" << bw << "x" << bh << ")";
+      }
+    }
+  }
+}
+
+void EnergyDistributionTest::RunZeroTest() {
+  memset(data_buf_, 0, MAX_SB_SQUARE * sizeof(int16_t));
+  for (int tx_size = 0; tx_size < TX_SIZES_ALL; ++tx_size) {
+    const int bw = tx_size_wide[tx_size];
+    const int bh = tx_size_high[tx_size];
+    if (bw > 32 || bh > 32) continue;
+    const int esq_w = bw <= 8 ? bw : bw / 2;
+    const int esq_h = bh <= 8 ? bh : bh / 2;
+    float hdist_ref[16] = { 0 }, vdist_ref[16] = { 0 };
+    float hdist_test[16] = { 0 }, vdist_test[16] = { 0 };
+
+    av1_get_energy_distribution_finer_c(data_buf_, MAX_SB_SIZE, bw, bh,
+                                        hdist_ref, vdist_ref);
+    API_REGISTER_STATE_CHECK(
+        target_func_(data_buf_, MAX_SB_SIZE, bw, bh, hdist_test, vdist_test));
+
+    for (int j = 0; j < esq_w - 1; ++j) {
+      ASSERT_FLOAT_EQ(hdist_ref[j], hdist_test[j]);
+    }
+    for (int i = 0; i < esq_h - 1; ++i) {
+      ASSERT_FLOAT_EQ(vdist_ref[i], vdist_test[i]);
+    }
+  }
+}
+
+void EnergyDistributionTest::RunExtremeValuesTest() {
+  for (int i = 0; i < MAX_SB_SQUARE; ++i) {
+    data_buf_[i] = (1 << 12) - 1;
+  }
+  for (int tx_size = 0; tx_size < TX_SIZES_ALL; ++tx_size) {
+    const int bw = tx_size_wide[tx_size];
+    const int bh = tx_size_high[tx_size];
+    if (bw > 32 || bh > 32) continue;
+    const int esq_w = bw <= 8 ? bw : bw / 2;
+    const int esq_h = bh <= 8 ? bh : bh / 2;
+    float hdist_ref[16] = { 0 }, vdist_ref[16] = { 0 };
+    float hdist_test[16] = { 0 }, vdist_test[16] = { 0 };
+
+    av1_get_energy_distribution_finer_c(data_buf_, MAX_SB_SIZE, bw, bh,
+                                        hdist_ref, vdist_ref);
+    API_REGISTER_STATE_CHECK(
+        target_func_(data_buf_, MAX_SB_SIZE, bw, bh, hdist_test, vdist_test));
+
+    for (int j = 0; j < esq_w - 1; ++j) {
+      ASSERT_FLOAT_EQ(hdist_ref[j], hdist_test[j]);
+    }
+    for (int i = 0; i < esq_h - 1; ++i) {
+      ASSERT_FLOAT_EQ(vdist_ref[i], vdist_test[i]);
+    }
+  }
+}
+
+TEST_P(EnergyDistributionTest, RandomValues) { RunRandomTest(); }
+
+TEST_P(EnergyDistributionTest, ZeroValues) { RunZeroTest(); }
+
+TEST_P(EnergyDistributionTest, ExtremeValues) { RunExtremeValuesTest(); }
+
+#if HAVE_AVX2
+INSTANTIATE_TEST_SUITE_P(
+    AVX2, EnergyDistributionTest,
+    ::testing::Values(av1_get_energy_distribution_finer_avx2));
+#endif  // HAVE_AVX2
+
 }  // namespace