Coverage Report

Created: 2026-09-14 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/libde265/libde265/x86/transform-avx512.cc
Line
Count
Source
1
/*
2
 * H.265 video codec.
3
 * Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libde265.
6
 *
7
 * libde265 is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libde265 is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libde265.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
// AVX-512 (F + BW) inverse transform for 32x32, 8-bit.
22
//
23
// A full 32-column row fits in one zmm (32 int16), so the vertical pass
24
// processes all 32 columns at once (twice the AVX2 width). The per-column
25
// butterfly is the same arithmetic as the AVX2/SSE versions: every 512-bit op
26
// acts independently per 128-bit lane, so each of the 4 lanes does what the SSE
27
// does for 8 columns -> 32 columns, bit-identical. The even part reuses a
28
// 16-point core (a 32-pt inverse DCT's even half is a 16-pt inverse DCT of the
29
// even rows). The 32x32 transpose between passes is done at 256-bit width using
30
// the proven AVX2 path (split each zmm into its two ymm halves, transpose,
31
// recombine) since the transpose is shuffle-bound, not the hot path.
32
33
#include "x86/transform-avx512.h"
34
35
#ifdef HAVE_CONFIG_H
36
#include "config.h"
37
#endif
38
39
#if HAVE_AVX512
40
41
#include <immintrin.h>
42
#include "x86/transform-dct-tables.h"   // idct_T16_{1,2,3}, idct_T32
43
44
namespace {
45
46
0
#define shift_1st 7
47
0
#define add_1st   (1 << (shift_1st - 1))   // 64
48
0
#define shift_2nd 12
49
0
#define add_2nd   (1 << (shift_2nd - 1))   // 2048
50
51
// broadcast a 128-bit (8 int16) madd pattern to all four 128-bit lanes
52
0
static inline __m512i bc(const int16_t* p) {
53
0
  return _mm512_broadcast_i32x4(_mm_load_si128((const __m128i*)p));
54
0
}
55
56
// --- 256-bit transpose helpers (identical to transform-avx2.cc) ----------
57
58
0
static inline void transpose8x8_lanes(__m256i a[8]) {
59
0
  __m256i b0=_mm256_unpacklo_epi16(a[0],a[1]), b1=_mm256_unpackhi_epi16(a[0],a[1]);
60
0
  __m256i b2=_mm256_unpacklo_epi16(a[2],a[3]), b3=_mm256_unpackhi_epi16(a[2],a[3]);
61
0
  __m256i b4=_mm256_unpacklo_epi16(a[4],a[5]), b5=_mm256_unpackhi_epi16(a[4],a[5]);
62
0
  __m256i b6=_mm256_unpacklo_epi16(a[6],a[7]), b7=_mm256_unpackhi_epi16(a[6],a[7]);
63
0
  __m256i c0=_mm256_unpacklo_epi32(b0,b2), c1=_mm256_unpackhi_epi32(b0,b2);
64
0
  __m256i c2=_mm256_unpacklo_epi32(b1,b3), c3=_mm256_unpackhi_epi32(b1,b3);
65
0
  __m256i c4=_mm256_unpacklo_epi32(b4,b6), c5=_mm256_unpackhi_epi32(b4,b6);
66
0
  __m256i c6=_mm256_unpacklo_epi32(b5,b7), c7=_mm256_unpackhi_epi32(b5,b7);
67
0
  a[0]=_mm256_unpacklo_epi64(c0,c4); a[1]=_mm256_unpackhi_epi64(c0,c4);
68
0
  a[2]=_mm256_unpacklo_epi64(c1,c5); a[3]=_mm256_unpackhi_epi64(c1,c5);
69
0
  a[4]=_mm256_unpacklo_epi64(c2,c6); a[5]=_mm256_unpackhi_epi64(c2,c6);
70
0
  a[6]=_mm256_unpacklo_epi64(c3,c7); a[7]=_mm256_unpackhi_epi64(c3,c7);
71
0
}
72
0
static inline void transpose16x16(__m256i S[16]) {
73
0
  __m256i top[8], bot[8];
74
0
  for (int r=0;r<8;r++){ top[r]=S[r]; bot[r]=S[r+8]; }
75
0
  transpose8x8_lanes(top); transpose8x8_lanes(bot);
76
0
  for (int r=0;r<8;r++){
77
0
    S[r]   = _mm256_permute2x128_si256(top[r], bot[r], 0x20);
78
0
    S[r+8] = _mm256_permute2x128_si256(top[r], bot[r], 0x31);
79
0
  }
80
0
}
81
0
static inline void transpose32x32_ymm(__m256i Lo[32], __m256i Hi[32]) {
82
0
  __m256i A[16],B[16],C[16],D[16];
83
0
  for (int r=0;r<16;r++){ A[r]=Lo[r]; B[r]=Hi[r]; C[r]=Lo[r+16]; D[r]=Hi[r+16]; }
84
0
  transpose16x16(A); transpose16x16(B); transpose16x16(C); transpose16x16(D);
85
0
  for (int r=0;r<16;r++){ Lo[r]=A[r]; Hi[r]=C[r]; Lo[r+16]=B[r]; Hi[r+16]=D[r]; }
86
0
}
87
88
// 32x32 transpose of zmm rows, via the 256-bit path.
89
0
static inline void transpose32x32_z(__m512i S[32]) {
90
0
  __m256i Lo[32], Hi[32];
91
0
  for (int r=0;r<32;r++){
92
0
    Lo[r]=_mm512_castsi512_si256(S[r]);
93
0
    Hi[r]=_mm512_extracti64x4_epi64(S[r], 1);
94
0
  }
95
0
  transpose32x32_ymm(Lo, Hi);
96
0
  for (int r=0;r<32;r++)
97
0
    S[r]=_mm512_inserti64x4(_mm512_castsi256_si512(Lo[r]), Hi[r], 1);
98
0
}
99
100
// --- 16-point core (32 columns) ------------------------------------------
101
102
0
static inline void idct16_core_512(const __m512i S[16], __m512i resL[16], __m512i resH[16]) {
103
0
  const __m512i T00=bc(idct_T16_1[0][0]),T01=bc(idct_T16_1[0][1]),T02=bc(idct_T16_1[0][2]),T03=bc(idct_T16_1[0][3]);
104
0
  const __m512i T04=bc(idct_T16_1[0][4]),T05=bc(idct_T16_1[0][5]),T06=bc(idct_T16_1[0][6]),T07=bc(idct_T16_1[0][7]);
105
0
  const __m512i T10=bc(idct_T16_1[1][0]),T11=bc(idct_T16_1[1][1]),T12=bc(idct_T16_1[1][2]),T13=bc(idct_T16_1[1][3]);
106
0
  const __m512i T14=bc(idct_T16_1[1][4]),T15=bc(idct_T16_1[1][5]),T16=bc(idct_T16_1[1][6]),T17=bc(idct_T16_1[1][7]);
107
0
  const __m512i T20=bc(idct_T16_1[2][0]),T21=bc(idct_T16_1[2][1]),T22=bc(idct_T16_1[2][2]),T23=bc(idct_T16_1[2][3]);
108
0
  const __m512i T24=bc(idct_T16_1[2][4]),T25=bc(idct_T16_1[2][5]),T26=bc(idct_T16_1[2][6]),T27=bc(idct_T16_1[2][7]);
109
0
  const __m512i T30=bc(idct_T16_1[3][0]),T31=bc(idct_T16_1[3][1]),T32_=bc(idct_T16_1[3][2]),T33=bc(idct_T16_1[3][3]);
110
0
  const __m512i T34=bc(idct_T16_1[3][4]),T35=bc(idct_T16_1[3][5]),T36=bc(idct_T16_1[3][6]),T37=bc(idct_T16_1[3][7]);
111
0
  const __m512i U00=bc(idct_T16_2[0][0]),U01=bc(idct_T16_2[0][1]),U02=bc(idct_T16_2[0][2]),U03=bc(idct_T16_2[0][3]);
112
0
  const __m512i U10=bc(idct_T16_2[1][0]),U11=bc(idct_T16_2[1][1]),U12=bc(idct_T16_2[1][2]),U13=bc(idct_T16_2[1][3]);
113
0
  const __m512i V00=bc(idct_T16_3[0][0]),V01=bc(idct_T16_3[0][1]),V10=bc(idct_T16_3[1][0]),V11=bc(idct_T16_3[1][1]);
114
115
0
  __m512i m0,m1,m2,m3,m4,m5,m6,m7;
116
0
  __m512i E0l,E1l,E2l,E3l,E0h,E1h,E2h,E3h;
117
0
  __m512i O0l,O1l,O2l,O3l,O4l,O5l,O6l,O7l,O0h,O1h,O2h,O3h,O4h,O5h,O6h,O7h;
118
0
  __m512i E00l,E01l,E00h,E01h,EE0l,EE1l,EE2l,EE3l,EE0h,EE1h,EE2h,EE3h;
119
0
  __m512i E4l,E5l,E6l,E7l,E4h,E5h,E6h,E7h;
120
121
0
  m0=_mm512_unpacklo_epi16(S[1],S[3]);  E0l=_mm512_madd_epi16(m0,T00);
122
0
  m1=_mm512_unpackhi_epi16(S[1],S[3]);  E0h=_mm512_madd_epi16(m1,T00);
123
0
  m2=_mm512_unpacklo_epi16(S[5],S[7]);  E1l=_mm512_madd_epi16(m2,T10);
124
0
  m3=_mm512_unpackhi_epi16(S[5],S[7]);  E1h=_mm512_madd_epi16(m3,T10);
125
0
  m4=_mm512_unpacklo_epi16(S[9],S[11]); E2l=_mm512_madd_epi16(m4,T20);
126
0
  m5=_mm512_unpackhi_epi16(S[9],S[11]); E2h=_mm512_madd_epi16(m5,T20);
127
0
  m6=_mm512_unpacklo_epi16(S[13],S[15]);E3l=_mm512_madd_epi16(m6,T30);
128
0
  m7=_mm512_unpackhi_epi16(S[13],S[15]);E3h=_mm512_madd_epi16(m7,T30);
129
0
  O0l=_mm512_add_epi32(_mm512_add_epi32(E0l,E1l),_mm512_add_epi32(E2l,E3l));
130
0
  O0h=_mm512_add_epi32(_mm512_add_epi32(E0h,E1h),_mm512_add_epi32(E2h,E3h));
131
132
0
  E0l=_mm512_madd_epi16(m0,T01);E0h=_mm512_madd_epi16(m1,T01);E1l=_mm512_madd_epi16(m2,T11);E1h=_mm512_madd_epi16(m3,T11);
133
0
  E2l=_mm512_madd_epi16(m4,T21);E2h=_mm512_madd_epi16(m5,T21);E3l=_mm512_madd_epi16(m6,T31);E3h=_mm512_madd_epi16(m7,T31);
134
0
  O1l=_mm512_add_epi32(_mm512_add_epi32(E0l,E1l),_mm512_add_epi32(E2l,E3l));
135
0
  O1h=_mm512_add_epi32(_mm512_add_epi32(E0h,E1h),_mm512_add_epi32(E2h,E3h));
136
137
0
  E0l=_mm512_madd_epi16(m0,T02);E0h=_mm512_madd_epi16(m1,T02);E1l=_mm512_madd_epi16(m2,T12);E1h=_mm512_madd_epi16(m3,T12);
138
0
  E2l=_mm512_madd_epi16(m4,T22);E2h=_mm512_madd_epi16(m5,T22);E3l=_mm512_madd_epi16(m6,T32_);E3h=_mm512_madd_epi16(m7,T32_);
139
0
  O2l=_mm512_add_epi32(_mm512_add_epi32(E0l,E1l),_mm512_add_epi32(E2l,E3l));
140
0
  O2h=_mm512_add_epi32(_mm512_add_epi32(E0h,E1h),_mm512_add_epi32(E2h,E3h));
141
142
0
  E0l=_mm512_madd_epi16(m0,T03);E0h=_mm512_madd_epi16(m1,T03);E1l=_mm512_madd_epi16(m2,T13);E1h=_mm512_madd_epi16(m3,T13);
143
0
  E2l=_mm512_madd_epi16(m4,T23);E2h=_mm512_madd_epi16(m5,T23);E3l=_mm512_madd_epi16(m6,T33);E3h=_mm512_madd_epi16(m7,T33);
144
0
  O3l=_mm512_add_epi32(_mm512_add_epi32(E0l,E1l),_mm512_add_epi32(E2l,E3l));
145
0
  O3h=_mm512_add_epi32(_mm512_add_epi32(E0h,E1h),_mm512_add_epi32(E2h,E3h));
146
147
0
  E0l=_mm512_madd_epi16(m0,T04);E0h=_mm512_madd_epi16(m1,T04);E1l=_mm512_madd_epi16(m2,T14);E1h=_mm512_madd_epi16(m3,T14);
148
0
  E2l=_mm512_madd_epi16(m4,T24);E2h=_mm512_madd_epi16(m5,T24);E3l=_mm512_madd_epi16(m6,T34);E3h=_mm512_madd_epi16(m7,T34);
149
0
  O4l=_mm512_add_epi32(_mm512_add_epi32(E0l,E1l),_mm512_add_epi32(E2l,E3l));
150
0
  O4h=_mm512_add_epi32(_mm512_add_epi32(E0h,E1h),_mm512_add_epi32(E2h,E3h));
151
152
0
  E0l=_mm512_madd_epi16(m0,T05);E0h=_mm512_madd_epi16(m1,T05);E1l=_mm512_madd_epi16(m2,T15);E1h=_mm512_madd_epi16(m3,T15);
153
0
  E2l=_mm512_madd_epi16(m4,T25);E2h=_mm512_madd_epi16(m5,T25);E3l=_mm512_madd_epi16(m6,T35);E3h=_mm512_madd_epi16(m7,T35);
154
0
  O5l=_mm512_add_epi32(_mm512_add_epi32(E0l,E1l),_mm512_add_epi32(E2l,E3l));
155
0
  O5h=_mm512_add_epi32(_mm512_add_epi32(E0h,E1h),_mm512_add_epi32(E2h,E3h));
156
157
0
  E0l=_mm512_madd_epi16(m0,T06);E0h=_mm512_madd_epi16(m1,T06);E1l=_mm512_madd_epi16(m2,T16);E1h=_mm512_madd_epi16(m3,T16);
158
0
  E2l=_mm512_madd_epi16(m4,T26);E2h=_mm512_madd_epi16(m5,T26);E3l=_mm512_madd_epi16(m6,T36);E3h=_mm512_madd_epi16(m7,T36);
159
0
  O6l=_mm512_add_epi32(_mm512_add_epi32(E0l,E1l),_mm512_add_epi32(E2l,E3l));
160
0
  O6h=_mm512_add_epi32(_mm512_add_epi32(E0h,E1h),_mm512_add_epi32(E2h,E3h));
161
162
0
  E0l=_mm512_madd_epi16(m0,T07);E0h=_mm512_madd_epi16(m1,T07);E1l=_mm512_madd_epi16(m2,T17);E1h=_mm512_madd_epi16(m3,T17);
163
0
  E2l=_mm512_madd_epi16(m4,T27);E2h=_mm512_madd_epi16(m5,T27);E3l=_mm512_madd_epi16(m6,T37);E3h=_mm512_madd_epi16(m7,T37);
164
0
  O7l=_mm512_add_epi32(_mm512_add_epi32(E0l,E1l),_mm512_add_epi32(E2l,E3l));
165
0
  O7h=_mm512_add_epi32(_mm512_add_epi32(E0h,E1h),_mm512_add_epi32(E2h,E3h));
166
167
  // even part
168
0
  m0=_mm512_unpacklo_epi16(S[2],S[6]);  E0l=_mm512_madd_epi16(m0,U00);
169
0
  m1=_mm512_unpackhi_epi16(S[2],S[6]);  E0h=_mm512_madd_epi16(m1,U00);
170
0
  m2=_mm512_unpacklo_epi16(S[10],S[14]);E0l=_mm512_add_epi32(E0l,_mm512_madd_epi16(m2,U10));
171
0
  m3=_mm512_unpackhi_epi16(S[10],S[14]);E0h=_mm512_add_epi32(E0h,_mm512_madd_epi16(m3,U10));
172
0
  E1l=_mm512_madd_epi16(m0,U01);E1h=_mm512_madd_epi16(m1,U01);
173
0
  E1l=_mm512_add_epi32(E1l,_mm512_madd_epi16(m2,U11));E1h=_mm512_add_epi32(E1h,_mm512_madd_epi16(m3,U11));
174
0
  E2l=_mm512_madd_epi16(m0,U02);E2h=_mm512_madd_epi16(m1,U02);
175
0
  E2l=_mm512_add_epi32(E2l,_mm512_madd_epi16(m2,U12));E2h=_mm512_add_epi32(E2h,_mm512_madd_epi16(m3,U12));
176
0
  E3l=_mm512_madd_epi16(m0,U03);E3h=_mm512_madd_epi16(m1,U03);
177
0
  E3l=_mm512_add_epi32(E3l,_mm512_madd_epi16(m2,U13));E3h=_mm512_add_epi32(E3h,_mm512_madd_epi16(m3,U13));
178
179
0
  m0=_mm512_unpacklo_epi16(S[4],S[12]); E00l=_mm512_madd_epi16(m0,V00);
180
0
  m1=_mm512_unpackhi_epi16(S[4],S[12]); E00h=_mm512_madd_epi16(m1,V00);
181
0
  m2=_mm512_unpacklo_epi16(S[0],S[8]);  EE0l=_mm512_madd_epi16(m2,V10);
182
0
  m3=_mm512_unpackhi_epi16(S[0],S[8]);  EE0h=_mm512_madd_epi16(m3,V10);
183
0
  E01l=_mm512_madd_epi16(m0,V01);E01h=_mm512_madd_epi16(m1,V01);
184
0
  EE1l=_mm512_madd_epi16(m2,V11);EE1h=_mm512_madd_epi16(m3,V11);
185
186
0
  EE2l=_mm512_sub_epi32(EE1l,E01l);EE3l=_mm512_sub_epi32(EE0l,E00l);
187
0
  EE2h=_mm512_sub_epi32(EE1h,E01h);EE3h=_mm512_sub_epi32(EE0h,E00h);
188
0
  EE0l=_mm512_add_epi32(EE0l,E00l);EE1l=_mm512_add_epi32(EE1l,E01l);
189
0
  EE0h=_mm512_add_epi32(EE0h,E00h);EE1h=_mm512_add_epi32(EE1h,E01h);
190
191
0
  E4l=_mm512_sub_epi32(EE3l,E3l); E5l=_mm512_sub_epi32(EE2l,E2l);
192
0
  E6l=_mm512_sub_epi32(EE1l,E1l); E7l=_mm512_sub_epi32(EE0l,E0l);
193
0
  E4h=_mm512_sub_epi32(EE3h,E3h); E5h=_mm512_sub_epi32(EE2h,E2h);
194
0
  E6h=_mm512_sub_epi32(EE1h,E1h); E7h=_mm512_sub_epi32(EE0h,E0h);
195
0
  E0l=_mm512_add_epi32(EE0l,E0l); E1l=_mm512_add_epi32(EE1l,E1l);
196
0
  E2l=_mm512_add_epi32(EE2l,E2l); E3l=_mm512_add_epi32(EE3l,E3l);
197
0
  E0h=_mm512_add_epi32(EE0h,E0h); E1h=_mm512_add_epi32(EE1h,E1h);
198
0
  E2h=_mm512_add_epi32(EE2h,E2h); E3h=_mm512_add_epi32(EE3h,E3h);
199
200
0
  resL[0]=_mm512_add_epi32(E0l,O0l); resH[0]=_mm512_add_epi32(E0h,O0h);
201
0
  resL[1]=_mm512_add_epi32(E1l,O1l); resH[1]=_mm512_add_epi32(E1h,O1h);
202
0
  resL[2]=_mm512_add_epi32(E2l,O2l); resH[2]=_mm512_add_epi32(E2h,O2h);
203
0
  resL[3]=_mm512_add_epi32(E3l,O3l); resH[3]=_mm512_add_epi32(E3h,O3h);
204
0
  resL[4]=_mm512_add_epi32(E4l,O4l); resH[4]=_mm512_add_epi32(E4h,O4h);
205
0
  resL[5]=_mm512_add_epi32(E5l,O5l); resH[5]=_mm512_add_epi32(E5h,O5h);
206
0
  resL[6]=_mm512_add_epi32(E6l,O6l); resH[6]=_mm512_add_epi32(E6h,O6h);
207
0
  resL[7]=_mm512_add_epi32(E7l,O7l); resH[7]=_mm512_add_epi32(E7h,O7h);
208
0
  resL[15]=_mm512_sub_epi32(E0l,O0l); resH[15]=_mm512_sub_epi32(E0h,O0h);
209
0
  resL[14]=_mm512_sub_epi32(E1l,O1l); resH[14]=_mm512_sub_epi32(E1h,O1h);
210
0
  resL[13]=_mm512_sub_epi32(E2l,O2l); resH[13]=_mm512_sub_epi32(E2h,O2h);
211
0
  resL[12]=_mm512_sub_epi32(E3l,O3l); resH[12]=_mm512_sub_epi32(E3h,O3h);
212
0
  resL[11]=_mm512_sub_epi32(E4l,O4l); resH[11]=_mm512_sub_epi32(E4h,O4h);
213
0
  resL[10]=_mm512_sub_epi32(E5l,O5l); resH[10]=_mm512_sub_epi32(E5h,O5h);
214
0
  resL[9] =_mm512_sub_epi32(E6l,O6l); resH[9] =_mm512_sub_epi32(E6h,O6h);
215
0
  resL[8] =_mm512_sub_epi32(E7l,O7l); resH[8] =_mm512_sub_epi32(E7h,O7h);
216
0
}
217
218
0
static inline __m512i round_shift_pack(__m512i lo, __m512i hi, __m512i vr, int shift) {
219
0
  return _mm512_packs_epi32(
220
0
      _mm512_srai_epi32(_mm512_add_epi32(lo,vr),shift),
221
0
      _mm512_srai_epi32(_mm512_add_epi32(hi,vr),shift));
222
0
}
223
224
// 1-D 32-point inverse transform in place on S[0..31] (32 columns).
225
0
static inline void idct32_vpass_512(__m512i S[32], int add, int shift) {
226
0
  __m512i ev[16];
227
0
  for (int i=0;i<16;i++) ev[i]=S[2*i];
228
0
  __m512i EL[16], EH[16];
229
0
  idct16_core_512(ev, EL, EH);
230
231
0
  __m512i pl[8], ph[8];
232
0
  for (int g=0; g<8; g++) {
233
0
    pl[g]=_mm512_unpacklo_epi16(S[4*g+1], S[4*g+3]);
234
0
    ph[g]=_mm512_unpackhi_epi16(S[4*g+1], S[4*g+3]);
235
0
  }
236
237
0
  const __m512i vr = _mm512_set1_epi32(add);
238
0
  for (int k=0;k<16;k++) {
239
0
    __m512i OL=_mm512_madd_epi16(pl[0], bc(idct_T32[0][k]));
240
0
    __m512i OH=_mm512_madd_epi16(ph[0], bc(idct_T32[0][k]));
241
0
    for (int g=1; g<8; g++) {
242
0
      OL=_mm512_add_epi32(OL,_mm512_madd_epi16(pl[g], bc(idct_T32[g][k])));
243
0
      OH=_mm512_add_epi32(OH,_mm512_madd_epi16(ph[g], bc(idct_T32[g][k])));
244
0
    }
245
0
    S[k]    = round_shift_pack(_mm512_add_epi32(EL[k],OL), _mm512_add_epi32(EH[k],OH), vr, shift);
246
0
    S[31-k] = round_shift_pack(_mm512_sub_epi32(EL[k],OL), _mm512_sub_epi32(EH[k],OH), vr, shift);
247
0
  }
248
0
}
249
250
} // namespace
251
252
253
void transform_32x32_add_8_avx512(uint8_t *dst, const int16_t *coeffs, ptrdiff_t stride)
254
0
{
255
0
  __m512i S[32];
256
0
  for (int r=0;r<32;r++) S[r]=_mm512_loadu_si512((const void*)(coeffs + r*32));
257
258
0
  idct32_vpass_512(S, add_1st, shift_1st);
259
0
  transpose32x32_z(S);
260
0
  idct32_vpass_512(S, add_2nd, shift_2nd);
261
0
  transpose32x32_z(S);
262
263
  // index to gather the low qword of each 128-bit lane into a contiguous 256
264
0
  const __m512i gather = _mm512_setr_epi64(0,2,4,6, 1,3,5,7);
265
0
  for (int r=0;r<32;r++) {
266
0
    uint8_t* d = dst + r*stride;
267
0
    __m512i pred = _mm512_cvtepu8_epi16(_mm256_loadu_si256((const __m256i*)d)); // 32 int16
268
0
    __m512i sum  = _mm512_adds_epi16(S[r], pred);
269
0
    __m512i pk   = _mm512_packus_epi16(sum, sum);          // per lane: [c|c]
270
0
    pk = _mm512_permutexvar_epi64(gather, pk);             // low 256 = c0..c31
271
0
    _mm256_storeu_si256((__m256i*)d, _mm512_castsi512_si256(pk));
272
0
  }
273
0
}
274
275
#endif // HAVE_AVX512