FFmpeg
Loading...
Searching...
No Matches
ops_asmgen.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 Ramiro Polla
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include "ops_asmgen.h"
22
23/*********************************************************************/
24/* Helpers functions. */
25
26/* Looping when s->use_vh is set. */
27#define LOOP_VH(s, mask, idx) if (s->use_vh) LOOP(mask, idx)
28#define LOOP_MASK_VH(s, p, idx) if (s->use_vh) LOOP_MASK(p, idx)
29#define LOOP_MASK_BWD_VH(s, p, idx) if (s->use_vh) LOOP_MASK_BWD(p, idx)
30
31/* Inline rasm comments. */
32#define CMT(comment) rasm_annotate(r, comment)
33#define CMTF(fmt, ...) rasm_annotatef(r, (char[128]){0}, 128, fmt, __VA_ARGS__)
34
35/* Reshape input/output vector registers for current SwsOp. */
36static void reshape_io_vectors(SwsAArch64OpRegs *regs, int el_count, int el_size)
37{
38 for (int i = 0; i < 4; i++) {
39 regs->sl[i] = a64op_make_vec(a64op_vec_n(regs->sl[i]), el_count, el_size);
40 regs->sh[i] = a64op_make_vec(a64op_vec_n(regs->sh[i]), el_count, el_size);
41 regs->dl[i] = a64op_make_vec(a64op_vec_n(regs->dl[i]), el_count, el_size);
42 regs->dh[i] = a64op_make_vec(a64op_vec_n(regs->dh[i]), el_count, el_size);
43 }
44}
45
46/* Reshape temp vector registers for current SwsOp. */
47static void reshape_temp_vectors(SwsAArch64OpRegs *regs, int el_count, int el_size)
48{
49 for (int i = 0; i < FF_ARRAY_ELEMS(regs->vt); i++)
50 regs->vt[i] = a64op_make_vec(a64op_vec_n(regs->vt[i]), el_count, el_size);
51}
52
53/* Reshape const vector registers for current SwsOp. */
54static void reshape_const_vectors(SwsAArch64OpRegs *regs, int el_count, int el_size)
55{
56 for (int i = 0; i < FF_ARRAY_ELEMS(regs->vk); i++)
57 regs->vk[i] = a64op_make_vec(a64op_vec_n(regs->vk[i]), el_count, el_size);
58}
59
60/*********************************************************************/
61/* Function frame */
62
63static unsigned clobbered_frame_size(unsigned n)
64{
65 return ((n + 1) >> 1) * 16;
66}
67
68static void asmgen_prologue(SwsAArch64Context *s, const RasmOp *regs, unsigned n)
69{
70 RasmContext *r = s->rctx;
71 RasmOp sp = a64op_sp();
72 unsigned frame_size = clobbered_frame_size(n);
73 RasmOp sp_pre = a64op_pre(sp, -frame_size);
74
75 rasm_add_comment(r, "prologue");
76 if (n == 0) {
77 /* no-op */
78 } else if (n == 1) {
79 i_str(r, regs[0], sp_pre);
80 } else {
81 i_stp(r, regs[0], regs[1], sp_pre);
82 for (unsigned i = 2; i + 1 < n; i += 2)
83 i_stp(r, regs[i], regs[i + 1], a64op_off(sp, i * sizeof(uint64_t)));
84 if (n & 1)
85 i_str(r, regs[n - 1], a64op_off(sp, (n - 1) * sizeof(uint64_t)));
86 }
87}
88
89static void asmgen_epilogue(SwsAArch64Context *s, const RasmOp *regs, unsigned n)
90{
91 RasmContext *r = s->rctx;
92 RasmOp sp = a64op_sp();
93 unsigned frame_size = clobbered_frame_size(n);
94 RasmOp sp_post = a64op_post(sp, frame_size);
95
96 rasm_add_comment(r, "epilogue");
97 if (n == 0) {
98 /* no-op */
99 } else if (n == 1) {
100 i_ldr(r, regs[0], sp_post);
101 } else {
102 if (n & 1)
103 i_ldr(r, regs[n - 1], a64op_off(sp, (n - 1) * sizeof(uint64_t)));
104 for (unsigned i = (n & ~1u) - 2; i >= 2; i -= 2)
105 i_ldp(r, regs[i], regs[i + 1], a64op_off(sp, i * sizeof(uint64_t)));
106 i_ldp(r, regs[0], regs[1], sp_post);
107 }
108}
109
110/*********************************************************************/
111/* Callee-saved registers (r19-r28, fp, and lr). */
112#define MAX_SAVED_REGS 12
113
114static void clobber_gpr(RasmOp regs[MAX_SAVED_REGS], unsigned *count,
115 RasmOp gpr)
116{
117 const int n = a64op_gpr_n(gpr);
118 if (n >= 19 && n <= 30)
119 regs[(*count)++] = gpr;
120}
121
122static unsigned clobbered_gprs(const SwsAArch64Context *s,
123 SwsCompMask imask, SwsCompMask omask,
125{
126 unsigned count = 0;
127 clobber_gpr(regs, &count, a64op_lr());
128 LOOP(imask, i) {
129 clobber_gpr(regs, &count, s->in[i]);
130 clobber_gpr(regs, &count, s->in_bump[i]);
131 }
132 LOOP(omask, i) {
133 clobber_gpr(regs, &count, s->out[i]);
134 clobber_gpr(regs, &count, s->out_bump[i]);
135 }
136 return count;
137}
138
140{
141 RasmContext *r = s->rctx;
142
143 /**
144 * The process function for aarch64 works similarly to the x86 backend.
145 * The description in x86/ops_include.asm mostly holds as well here.
146 */
147
148 /* Function prologue */
149 RasmOp saved_regs[MAX_SAVED_REGS];
150 unsigned nsaved = clobbered_gprs(s, imask, omask, saved_regs);
151 if (nsaved)
152 asmgen_prologue(s, saved_regs, nsaved);
153
154 /* Load values from exec. */
155 RasmOp exec_in[4];
156 RasmOp exec_in_bump[4];
157 RasmOp exec_out[4];
158 RasmOp exec_out_bump[4];
159 LOOP(imask, i) { exec_in [i] = a64op_off(s->exec, offsetof_exec_in + (i * sizeof(uint8_t *))); }
160 LOOP(imask, i) { exec_in_bump [i] = a64op_off(s->exec, offsetof_exec_in_bump + (i * sizeof(uint8_t *))); }
161 LOOP(omask, i) { exec_out [i] = a64op_off(s->exec, offsetof_exec_out + (i * sizeof(uint8_t *))); }
162 LOOP(omask, i) { exec_out_bump[i] = a64op_off(s->exec, offsetof_exec_out_bump + (i * sizeof(uint8_t *))); }
163 LOOP(imask, i) { i_ldr(r, s->in[i], exec_in [i]); CMTF("in[%u] = exec->in[%u];", i, i); }
164 LOOP(omask, i) { i_ldr(r, s->out[i], exec_out[i]); CMTF("out[%u] = exec->out[%u];", i, i); }
165 LOOP(imask, i) { i_ldr(r, s->in_bump[i], exec_in_bump[i]); CMTF("in_bump[%u] = exec->in_bump[%u];", i, i); }
166 LOOP(omask, i) { i_ldr(r, s->out_bump[i], exec_out_bump[i]); CMTF("out_bump[%u] = exec->out_bump[%u];", i, i); }
167
168 /* Setup. */
169 s->setup = rasm_get_current_node(r);
170
171 int first_row = rasm_new_label(r, NULL);
172 int next_row = rasm_new_label(r, NULL);
173 int next_block = rasm_new_label(r, NULL);
174
175 /* Jump to first row (skips padding). */
176 i_b (r, rasm_op_label(first_row)); CMT("goto first_row;");
177
178 /* Perform padding, preparing for next row. */
179 rasm_add_label(r, next_row); CMT("next_row:");
180 LOOP(imask, i) { i_add(r, s->in[i], s->in[i], s->in_bump[i]); CMTF("in[%u] += in_bump[%u];", i, i); }
181 LOOP(omask, i) { i_add(r, s->out[i], s->out[i], s->out_bump[i]); CMTF("out[%u] += out_bump[%u];", i, i); }
182
183 /* First row (reset x). */
184 rasm_add_label(r, first_row); CMT("first_row:");
185 i_mov(r, s->bx, s->bx_start); CMT("bx = bx_start;");
186
187 /* Main loop. */
188 rasm_add_label(r, next_block); CMT("next_block:");
189 s->loop = rasm_get_current_node(r);
190
191 /* Perform horizontal loop. */
192 i_add(r, s->bx, s->bx, IMM(1)); CMT("bx += 1;");
193 i_cmp(r, s->bx, s->bx_end); CMT("if (bx != bx_end)");
194 i_bne(r, next_block); CMT(" goto next_block;");
195
196 /* Perform vertical loop. */
197 i_add(r, s->y, s->y, IMM(1)); CMT("y += 1;");
198 i_cmp(r, s->y, s->y_end); CMT("if (y != y_end)");
199 i_bne(r, next_row); CMT(" goto next_row;");
200
201 /* Function epilogue */
202 if (nsaved)
203 asmgen_epilogue(s, saved_regs, nsaved);
204
205 i_ret(r);
206}
207
208/*********************************************************************/
209/* gather raw pixels from planes */
210/* SWS_UOP_READ_BIT */
211/* SWS_UOP_READ_NIBBLE */
212/* SWS_UOP_READ_PACKED */
213/* SWS_UOP_READ_PLANAR */
214
216 SwsAArch64OpRegs *regs)
217{
218 RasmContext *r = s->rctx;
219 AArch64VecViews dl[1] = { a64op_vec_views(regs->dl[0]) };
220 AArch64VecViews shift_vec = a64op_vec_views(regs->vk[0]);
221 AArch64VecViews bitmask_vec = a64op_vec_views(regs->vk[1]);
222
223 AArch64VecViews vtmp = a64op_vec_views(regs->vt[0]);
224 RasmOp wtmp = a64op_w(s->tmp0);
225
226 /* Note that shift_vec has negative values, so that using it with
227 * ushl actually performs a right shift. */
228 if (p->block_size == 16) {
229 i_ldrh(r, wtmp, a64op_post(s->in[0], 2)); CMT("uint16_t tmp = *in[0]++;");
230 i_dup (r, dl[0].b8, wtmp); CMT("vl[0].lo = broadcast(tmp);");
231 i_lsr (r, wtmp, wtmp, IMM(8)); CMT("tmp >>= 8;");
232 i_dup (r, vtmp.b8, wtmp); CMT("vtmp.lo = broadcast(tmp);");
233 i_ins (r, dl[0].de[1], vtmp.de[0]); CMT("vl[0].hi = vtmp.lo;");
234 i_ushl(r, dl[0].b16, dl[0].b16, shift_vec.b16); CMT("vl[0] <<= shift_vec;");
235 i_and (r, dl[0].b16, dl[0].b16, bitmask_vec.b16); CMT("vl[0] &= bitmask_vec;");
236 } else {
237 i_ldrb(r, wtmp, a64op_post(s->in[0], 1)); CMT("uint8_t tmp = *in[0]++;");
238 i_dup (r, dl[0].b8, wtmp); CMT("vl[0].lo = broadcast(tmp);");
239 i_ushl(r, dl[0].b8, dl[0].b8, shift_vec.b8); CMT("vl[0] <<= shift_vec;");
240 i_and (r, dl[0].b8, dl[0].b8, bitmask_vec.b8); CMT("vl[0] &= bitmask_vec;");
241 }
242}
243
245 SwsAArch64OpRegs *regs)
246{
247 RasmContext *r = s->rctx;
248 AArch64VecViews dl[1] = { a64op_vec_views(regs->dl[0]) };
250
251 AArch64VecViews vtmp = a64op_vec_views(regs->vt[0]);
252
253 if (p->block_size == 8) {
254 i_ldr (r, dl[0].s, a64op_post(s->in[0], 4)); CMT("vl[0] = *in[0]++;");
255 i_ushr(r, vtmp.b8, dl[0].b8, IMM(4)); CMT("vtmp.lo = vl[0] >> 4;");
256 i_and (r, dl[0].b8, dl[0].b8, nibble_mask.b8); CMT("vl[0].lo &= nibble_mask;");
257 i_zip1(r, dl[0].b8, vtmp.b8, dl[0].b8); CMT("interleave");
258 } else {
259 i_ldr (r, dl[0].d, a64op_post(s->in[0], 8)); CMT("vl[0] = *in[0]++;");
260 i_ushr(r, vtmp.b8, dl[0].b8, IMM(4)); CMT("vtmp.lo = vl[0] >> 4;");
261 i_and (r, dl[0].b8, dl[0].b8, nibble_mask.b8); CMT("vl[0].lo &= nibble_mask;");
262 i_zip1(r, dl[0].b16, vtmp.b16, dl[0].b16); CMT("interleave");
263 }
264}
265
267{
268 RasmContext *r = s->rctx;
269
270 switch (p->mask) {
271 case SWS_COMP_ELEMS(2): i_ld2(r, vv_2(vx[0], vx[1]), a64op_post(s->in[0], s->vec_size * 2)); break;
272 case SWS_COMP_ELEMS(3): i_ld3(r, vv_3(vx[0], vx[1], vx[2]), a64op_post(s->in[0], s->vec_size * 3)); break;
273 case SWS_COMP_ELEMS(4): i_ld4(r, vv_4(vx[0], vx[1], vx[2], vx[3]), a64op_post(s->in[0], s->vec_size * 4)); break;
274 }
275}
276
278 SwsAArch64OpRegs *regs)
279{
280 av_assert0(p->mask != 0x0001);
281 asmgen_op_read_packed_n(s, p, regs->dl);
282 if (s->use_vh)
283 asmgen_op_read_packed_n(s, p, regs->dh);
284}
285
287 SwsAArch64OpRegs *regs)
288{
289 RasmContext *r = s->rctx;
290 AArch64VecViews dl[4] = A64OP_VEC_VIEWS4(regs->dl);
291 AArch64VecViews dh[4] = A64OP_VEC_VIEWS4(regs->dh);
292
293 LOOP_MASK(p, i) {
294 switch ((s->use_vh ? 0x100 : 0) | s->vec_size) {
295 case 0x008: i_ldr(r, dl[i].d, a64op_post(s->in[i], s->vec_size * 1)); break;
296 case 0x010: i_ldr(r, dl[i].q, a64op_post(s->in[i], s->vec_size * 1)); break;
297 case 0x108: i_ldp(r, dl[i].d, dh[i].d, a64op_post(s->in[i], s->vec_size * 2)); break;
298 case 0x110: i_ldp(r, dl[i].q, dh[i].q, a64op_post(s->in[i], s->vec_size * 2)); break;
299 }
300 }
301}
302
303/*********************************************************************/
304/* write raw pixels to planes */
305/* SWS_UOP_WRITE_BIT */
306/* SWS_UOP_WRITE_NIBBLE */
307/* SWS_UOP_WRITE_PACKED */
308/* SWS_UOP_WRITE_PLANAR */
309
311 SwsAArch64OpRegs *regs)
312{
313 RasmContext *r = s->rctx;
314 AArch64VecViews sl[1] = { a64op_vec_views(regs->sl[0]) };
315 AArch64VecViews shift_vec = a64op_vec_views(regs->vk[0]);
316
317 AArch64VecViews vtmp0 = a64op_vec_views(regs->vt[0]);
318 AArch64VecViews vtmp1 = a64op_vec_views(regs->vt[1]);
319
320 if (p->block_size == 8) {
321 i_ushl(r, sl[0].b8, sl[0].b8, shift_vec.b8); CMT("vl[0] <<= shift_vec;");
322 i_addv(r, vtmp0.b, sl[0].b8); CMT("vtmp0[0] = add_across(vl[0].lo);");
323 i_str (r, vtmp0.b, a64op_post(s->out[0], 1)); CMT("*out[0]++ = vtmp0;");
324 } else {
325 i_ushl(r, sl[0].b16, sl[0].b16, shift_vec.b16); CMT("vl[0] <<= shift_vec;");
326 i_addv(r, vtmp0.b, sl[0].b8); CMT("vtmp0[0] = add_across(vl[0].lo);");
327 i_ins (r, vtmp1.de[0], sl[0].de[1]); CMT("vtmp1.lo = vl[0].hi;");
328 i_addv(r, vtmp1.b, vtmp1.b8); CMT("vtmp1[0] = add_across(vtmp1);");
329 i_ins (r, vtmp0.be[1], vtmp1.be[0]); CMT("vtmp0[1] = vtmp1[0];");
330 i_str (r, vtmp0.h, a64op_post(s->out[0], 2)); CMT("*out[0]++ = vtmp0;");
331 }
332}
333
335 SwsAArch64OpRegs *regs)
336{
337 RasmContext *r = s->rctx;
338 AArch64VecViews sl[4] = A64OP_VEC_VIEWS4(regs->sl);
339 AArch64VecViews vtmp0 = a64op_vec_views(regs->vt[0]);
340 AArch64VecViews vtmp1 = a64op_vec_views(regs->vt[1]);
341
342 if (p->block_size == 8) {
343 i_shl (r, vtmp0.h4, sl[0].h4, IMM(4));
344 i_ushr(r, vtmp1.h4, sl[0].h4, IMM(8));
345 i_orr (r, sl[0].b8, vtmp0.b8, vtmp1.b8);
346 i_xtn (r, vtmp0.b8, sl[0].h8);
347 i_str (r, vtmp0.s, a64op_post(s->out[0], 4));
348 } else {
349 i_shl (r, vtmp0.h8, sl[0].h8, IMM(4));
350 i_ushr(r, vtmp1.h8, sl[0].h8, IMM(8));
351 i_orr (r, sl[0].b16, vtmp0.b16, vtmp1.b16);
352 i_xtn (r, vtmp0.b8, sl[0].h8);
353 i_str (r, vtmp0.d, a64op_post(s->out[0], 8));
354 }
355}
356
358{
359 RasmContext *r = s->rctx;
360
361 switch (p->mask) {
362 case SWS_COMP_ELEMS(2): i_st2(r, vv_2(vx[0], vx[1]), a64op_post(s->out[0], s->vec_size * 2)); break;
363 case SWS_COMP_ELEMS(3): i_st3(r, vv_3(vx[0], vx[1], vx[2]), a64op_post(s->out[0], s->vec_size * 3)); break;
364 case SWS_COMP_ELEMS(4): i_st4(r, vv_4(vx[0], vx[1], vx[2], vx[3]), a64op_post(s->out[0], s->vec_size * 4)); break;
365 }
366}
367
369 SwsAArch64OpRegs *regs)
370{
371 av_assert0(p->mask != 0x0001);
372 asmgen_op_write_packed_n(s, p, regs->sl);
373 if (s->use_vh)
374 asmgen_op_write_packed_n(s, p, regs->sh);
375}
376
378 SwsAArch64OpRegs *regs)
379{
380 RasmContext *r = s->rctx;
381 AArch64VecViews sl[4] = A64OP_VEC_VIEWS4(regs->sl);
382 AArch64VecViews sh[4] = A64OP_VEC_VIEWS4(regs->sh);
383
384 LOOP_MASK(p, i) {
385 switch ((s->use_vh ? 0x100 : 0) | s->vec_size) {
386 case 0x008: i_str(r, sl[i].d, a64op_post(s->out[i], s->vec_size * 1)); break;
387 case 0x010: i_str(r, sl[i].q, a64op_post(s->out[i], s->vec_size * 1)); break;
388 case 0x108: i_stp(r, sl[i].d, sh[i].d, a64op_post(s->out[i], s->vec_size * 2)); break;
389 case 0x110: i_stp(r, sl[i].q, sh[i].q, a64op_post(s->out[i], s->vec_size * 2)); break;
390 }
391 }
392}
393
394/*********************************************************************/
395/* swap byte order (for differing endianness) */
396/* SWS_UOP_SWAP_BYTES */
397
399 SwsAArch64OpRegs *regs)
400{
401 RasmContext *r = s->rctx;
402 AArch64VecViews sl[4] = A64OP_VEC_VIEWS4(regs->sl);
403 AArch64VecViews sh[4] = A64OP_VEC_VIEWS4(regs->sh);
404 AArch64VecViews dl[4] = A64OP_VEC_VIEWS4(regs->dl);
405 AArch64VecViews dh[4] = A64OP_VEC_VIEWS4(regs->dh);
406
407 switch (ff_sws_pixel_type_size(p->type)) {
408 case sizeof(uint16_t):
409 LOOP_MASK (p, i) i_rev16(r, dl[i].b16, sl[i].b16);
410 LOOP_MASK_VH(s, p, i) i_rev16(r, dh[i].b16, sh[i].b16);
411 break;
412 case sizeof(uint32_t):
413 LOOP_MASK (p, i) i_rev32(r, dl[i].b16, sl[i].b16);
414 LOOP_MASK_VH(s, p, i) i_rev32(r, dh[i].b16, sh[i].b16);
415 break;
416 }
417}
418
419/*********************************************************************/
420/* rearrange channel order, or duplicate channels */
421/* SWS_UOP_PERMUTE */
422/* SWS_UOP_COPY */
423
424static const char *print_swizzle_v(char buf[8], int8_t n, uint8_t vh)
425{
426 if (n == -1)
427 snprintf(buf, sizeof(char[8]), "vtmp%c", vh ? 'h' : 'l');
428 else
429 snprintf(buf, sizeof(char[8]), "v%c[%u]", vh ? 'h' : 'l', n);
430 return buf;
431}
432#define PRINT_SWIZZLE_V(n, vh) print_swizzle_v((char[8]){ 0 }, n, vh)
433
434static RasmOp swizzle_a64op(SwsAArch64OpRegs *regs, int8_t n, uint8_t vh, bool dst)
435{
436 if (n == -1)
437 return regs->vt[vh];
438 if (vh)
439 return dst ? regs->dh[n] : regs->sh[n];
440 return dst ? regs->dl[n] : regs->sl[n];
441}
442
444 int8_t dst, int8_t src)
445{
446 RasmContext *r = s->rctx;
447 RasmOp src_op[2] = { swizzle_a64op(regs, src, 0, false), swizzle_a64op(regs, src, 1, false) };
448 RasmOp dst_op[2] = { swizzle_a64op(regs, dst, 0, true), swizzle_a64op(regs, dst, 1, true) };
449
450 i_mov (r, dst_op[0], src_op[0]); CMTF("%s = %s;", PRINT_SWIZZLE_V(dst, 0), PRINT_SWIZZLE_V(src, 0));
451 if (s->use_vh) {
452 i_mov(r, dst_op[1], src_op[1]); CMTF("%s = %s;", PRINT_SWIZZLE_V(dst, 1), PRINT_SWIZZLE_V(src, 1));
453 }
454}
455
457 SwsAArch64OpRegs *regs)
458{
459 for (int i = 0; i < p->par.move.num_moves; i++)
460 swizzle_emit(s, regs, p->par.move.dst[i], p->par.move.src[i]);
461}
462
463/*********************************************************************/
464/* split tightly packed data into components */
465/* SWS_UOP_UNPACK */
466
468 SwsAArch64OpRegs *regs)
469{
470 RasmContext *r = s->rctx;
471 RasmOp *sl = regs->sl;
472 RasmOp *sh = regs->sh;
473 RasmOp *dl = regs->dl;
474 RasmOp *dh = regs->dh;
475 RasmOp *vmask = regs->vk;
476
477 const int offsets[4] = {
478 p->par.pack.pattern[3] + p->par.pack.pattern[2] + p->par.pack.pattern[1],
479 p->par.pack.pattern[3] + p->par.pack.pattern[2],
480 p->par.pack.pattern[3],
481 0
482 };
483
484 /* Loop backwards to avoid clobbering component 0. */
485 LOOP_MASK_BWD (p, i) {
486 if (offsets[i]) {
487 i_ushr (r, dl[i], sl[0], IMM(offsets[i])); CMTF("vl[%u] >>= %u;", i, offsets[i]);
488 } else if (i) {
489 i_mov16b(r, dl[i], sl[0]); CMTF("vl[%u] = vl[0];", i);
490 }
491 }
492 LOOP_MASK_BWD_VH(s, p, i) {
493 if (offsets[i]) {
494 i_ushr (r, dh[i], sh[0], IMM(offsets[i])); CMTF("vh[%u] >>= %u;", i, offsets[i]);
495 } else if (i) {
496 i_mov16b(r, dh[i], sh[0]); CMTF("vh[%u] = vh[0];", i);
497 }
498 }
499
500 /* Apply masks. */
501 LOOP_MASK_BWD (p, i) { i_and16b(r, dl[i], dl[i], vmask[i]); CMTF("vl[%u] &= 0x%x;", i, (1u << p->par.pack.pattern[i]) - 1); }
502 LOOP_MASK_BWD_VH(s, p, i) { i_and16b(r, dh[i], dh[i], vmask[i]); CMTF("vh[%u] &= 0x%x;", i, (1u << p->par.pack.pattern[i]) - 1); }
503}
504
505/*********************************************************************/
506/* compress components into tightly packed data */
507/* SWS_UOP_PACK */
508
510 SwsAArch64OpRegs *regs)
511{
512 RasmContext *r = s->rctx;
513 RasmOp *sl = regs->sl;
514 RasmOp *sh = regs->sh;
515 RasmOp *dl = regs->dl;
516 RasmOp *dh = regs->dh;
517
518 const int offsets[4] = {
519 p->par.pack.pattern[3] + p->par.pack.pattern[2] + p->par.pack.pattern[1],
520 p->par.pack.pattern[3] + p->par.pack.pattern[2],
521 p->par.pack.pattern[3],
522 0
523 };
524 SwsCompMask offset_mask = 0;
525 LOOP_MASK(p, i) {
526 if (offsets[i])
527 offset_mask |= SWS_COMP(i);
528 }
529
530 /* Perform left shift. */
531 LOOP (offset_mask, i) { i_shl(r, dl[i], sl[i], IMM(offsets[i])); CMTF("vl[%u] <<= %u;", i, offsets[i]); }
532 LOOP_VH(s, offset_mask, i) { i_shl(r, dh[i], sh[i], IMM(offsets[i])); CMTF("vh[%u] <<= %u;", i, offsets[i]); }
533 LOOP (offset_mask, i) { sl[i] = dl[i]; }
534 LOOP_VH(s, offset_mask, i) { sh[i] = dh[i]; }
535
536 /* Combine components. */
537 for (int i = 0; i < 4; i++) {
538 sl[i] = v_16b(sl[i]);
539 sh[i] = v_16b(sh[i]);
540 dl[i] = v_16b(dl[i]);
541 dh[i] = v_16b(dh[i]);
542 }
543 LOOP_MASK (p, i) {
544 if (i != 0) {
545 i_orr16b (r, dl[0], sl[0], sl[i]); CMTF("vl[0] |= vl[%u];", i);
546 if (s->use_vh) {
547 i_orr16b(r, dh[0], sh[0], sh[i]); CMTF("vh[0] |= vh[%u];", i);
548 }
549 }
550 }
551}
552
553/*********************************************************************/
554/* logical left shift of raw pixel values */
555/* SWS_UOP_LSHIFT */
556
558 SwsAArch64OpRegs *regs)
559{
560 uint8_t shift = p->par.shift.amount;
561 RasmContext *r = s->rctx;
562 RasmOp *sl = regs->sl;
563 RasmOp *sh = regs->sh;
564 RasmOp *dl = regs->dl;
565 RasmOp *dh = regs->dh;
566
567 LOOP_MASK (p, i) { i_shl(r, dl[i], sl[i], IMM(shift)); CMTF("vl[%u] <<= %u;", i, shift); }
568 LOOP_MASK_VH(s, p, i) { i_shl(r, dh[i], sh[i], IMM(shift)); CMTF("vh[%u] <<= %u;", i, shift); }
569}
570
571/*********************************************************************/
572/* right shift of raw pixel values */
573/* SWS_UOP_RSHIFT */
574
576 SwsAArch64OpRegs *regs)
577{
578 uint8_t shift = p->par.shift.amount;
579 RasmContext *r = s->rctx;
580 RasmOp *sl = regs->sl;
581 RasmOp *sh = regs->sh;
582 RasmOp *dl = regs->dl;
583 RasmOp *dh = regs->dh;
584
585 LOOP_MASK (p, i) { i_ushr(r, dl[i], sl[i], IMM(shift)); CMTF("vl[%u] >>= %u;", i, shift); }
586 LOOP_MASK_VH(s, p, i) { i_ushr(r, dh[i], sh[i], IMM(shift)); CMTF("vh[%u] >>= %u;", i, shift); }
587}
588
589/*********************************************************************/
590/* clear pixel values */
591/* SWS_UOP_CLEAR */
592
594 RasmOp *vx, RasmOp *vk, int i, const char *vx_str)
595{
596 RasmContext *r = s->rctx;
597 RasmOp clear_vec = vk[0];
598 if (p->par.clear.zero & SWS_COMP(i)) {
599 i_movi(r, vx[i], IMM(0)); CMTF("%s[%u] = 0;", vx_str, i);
600 } else if (p->par.clear.one & SWS_COMP(i)) {
601 if (p->block_size * ff_sws_pixel_type_size(p->type) == 8) {
602 i_movi(r, v_8b (vx[i]), IMM(0xff));
603 } else {
604 i_movi(r, v_16b(vx[i]), IMM(0xff));
605 }
606 CMTF("%s[%u] = UINT_MAX;", vx_str, i);
607 } else {
608 i_dup (r, vx[i], a64op_elem(clear_vec, i)); CMTF("%s[%u] = broadcast(clear_vec[%u]);", vx_str, i, i);
609 }
610}
611
613 SwsAArch64OpRegs *regs)
614{
615 RasmOp *dl = regs->dl;
616 RasmOp *dh = regs->dh;
617 RasmOp *vk = regs->vk;
618
619 LOOP_MASK (p, i) { emit_clear(s, p, dl, vk, i, "vl"); }
620 LOOP_MASK_VH(s, p, i) { emit_clear(s, p, dh, vk, i, "vh"); }
621}
622
623/*********************************************************************/
624/* convert (cast) between formats */
625/* SWS_UOP_TO_U8 */
626/* SWS_UOP_TO_U16 */
627/* SWS_UOP_TO_U32 */
628/* SWS_UOP_TO_F32 */
629
631 SwsAArch64OpRegs *regs)
632{
633 RasmContext *r = s->rctx;
634 AArch64VecViews sl[4] = A64OP_VEC_VIEWS4(regs->sl);
635 AArch64VecViews sh[4] = A64OP_VEC_VIEWS4(regs->sh);
636 AArch64VecViews dl[4] = A64OP_VEC_VIEWS4(regs->dl);
637 AArch64VecViews dh[4] = A64OP_VEC_VIEWS4(regs->dh);
638
639 /**
640 * Since each instruction in the convert operation needs specific
641 * element types, it is simpler to use arrangement specifiers for
642 * each operand instead of reshaping all vectors.
643 */
644
645 size_t src_el_size = s->el_size;
646 SwsPixelType to_type;
647 switch (p->uop) {
648 case SWS_UOP_TO_U8: to_type = SWS_PIXEL_U8; break;
649 case SWS_UOP_TO_U16: to_type = SWS_PIXEL_U16; break;
650 case SWS_UOP_TO_U32: to_type = SWS_PIXEL_U32; break;
651 case SWS_UOP_TO_F32: to_type = SWS_PIXEL_F32; break;
652 default:
653 av_assert0(!"Invalid uop!");
654 break;
655 }
656 size_t dst_el_size = ff_sws_pixel_type_size(to_type);
657
658 /**
659 * This function assumes block_size is either 8 or 16, and that
660 * we're always using the most amount of vector registers possible.
661 * Therefore, u32 always uses the high vector bank.
662 */
663 if (p->type == SWS_PIXEL_F32) {
664 rasm_add_comment(r, "f32 -> u32");
665 LOOP_MASK(p, i) i_fcvtzu(r, dl[i].s4, sl[i].s4);
666 LOOP_MASK(p, i) i_fcvtzu(r, dh[i].s4, sh[i].s4);
667 memcpy(sl, dl, sizeof(sl));
668 memcpy(sh, dh, sizeof(sh));
669 }
670
671 if (p->block_size == 8) {
672 if (src_el_size == 1 && dst_el_size > src_el_size) {
673 rasm_add_comment(r, "u8 -> u16");
674 LOOP_MASK(p, i) i_uxtl (r, dl[i].h8, sl[i].b8);
675 memcpy(sl, dl, sizeof(sl));
676 src_el_size = 2;
677 } else if (src_el_size == 4 && dst_el_size < src_el_size) {
678 rasm_add_comment(r, "u32 -> u16");
679 LOOP_MASK(p, i) i_xtn (r, dl[i].h4, sl[i].s4);
680 LOOP_MASK(p, i) i_xtn (r, dh[i].h4, sh[i].s4);
681 LOOP_MASK(p, i) i_ins (r, dl[i].de[1], sh[i].de[0]);
682 memcpy(sl, dl, sizeof(sl));
683 memcpy(sh, dh, sizeof(sh));
684 src_el_size = 2;
685 }
686 if (src_el_size == 2 && dst_el_size == 4) {
687 rasm_add_comment(r, "u16 -> u32");
688 LOOP_MASK(p, i) i_uxtl2(r, dh[i].s4, sl[i].h8);
689 LOOP_MASK(p, i) i_uxtl (r, dl[i].s4, sl[i].h4);
690 memcpy(sl, dl, sizeof(sl));
691 memcpy(sh, dh, sizeof(sh));
692 src_el_size = 4;
693 } else if (src_el_size == 2 && dst_el_size == 1) {
694 rasm_add_comment(r, "u16 -> u8");
695 LOOP_MASK(p, i) i_xtn (r, dl[i].b8, sl[i].h8);
696 memcpy(sl, dl, sizeof(sl));
697 src_el_size = 1;
698 }
699 } else /* if (p->block_size == 16) */ {
700 if (src_el_size == 1 && dst_el_size == 2) {
701 rasm_add_comment(r, "u8 -> u16");
702 LOOP_MASK(p, i) i_uxtl2(r, dh[i].h8, sl[i].b16);
703 LOOP_MASK(p, i) i_uxtl (r, dl[i].h8, sl[i].b8);
704 memcpy(sl, dl, sizeof(sl));
705 memcpy(sh, dh, sizeof(sh));
706 } else if (src_el_size == 2 && dst_el_size == 1) {
707 rasm_add_comment(r, "u16 -> u8");
708 LOOP_MASK(p, i) i_xtn (r, dl[i].b8, sl[i].h8);
709 LOOP_MASK(p, i) i_xtn (r, dh[i].b8, sh[i].h8);
710 LOOP_MASK(p, i) i_ins (r, dl[i].de[1], sh[i].de[0]);
711 memcpy(sl, dl, sizeof(sl));
712 }
713 }
714
715 /* See comment above for high vector bank usage for u32. */
716 if (to_type == SWS_PIXEL_F32) {
717 rasm_add_comment(r, "u32 -> f32");
718 LOOP_MASK(p, i) i_ucvtf(r, dl[i].s4, sl[i].s4);
719 LOOP_MASK(p, i) i_ucvtf(r, dh[i].s4, sh[i].s4);
720 }
721}
722
723/*********************************************************************/
724/* expand integers to the full range */
725/* SWS_UOP_EXPAND_PAIR */
726/* SWS_UOP_EXPAND_QUAD */
727
729 SwsAArch64OpRegs *regs)
730{
731 RasmContext *r = s->rctx;
732 RasmOp *sl = regs->sl;
733 RasmOp *dl = regs->dl;
734 RasmOp *dh = regs->dh;
735
736 size_t src_el_size = s->el_size;
737 SwsPixelType to_type;
738 switch (p->uop) {
739 case SWS_UOP_EXPAND_PAIR: to_type = SWS_PIXEL_U16; break;
740 case SWS_UOP_EXPAND_QUAD: to_type = SWS_PIXEL_U32; break;
741 default:
742 av_assert0(!"Invalid uop!");
743 break;
744 }
745 size_t dst_el_size = ff_sws_pixel_type_size(to_type);
746 size_t dst_total_size = p->block_size * dst_el_size;
747 size_t dst_vec_size = FFMIN(dst_total_size, 16);
748
749 if (!s->use_vh)
750 s->use_vh = (dst_vec_size != dst_total_size);
751
752 if (src_el_size == 1) {
753 rasm_add_comment(r, "u8 -> u16");
754 reshape_io_vectors(regs, 16, 1);
755 LOOP_MASK_VH(s, p, i) i_zip2(r, dh[i], sl[i], sl[i]);
756 LOOP_MASK (p, i) i_zip1(r, dl[i], sl[i], sl[i]);
757 sl = dl;
758 }
759 if (dst_el_size == 4) {
760 rasm_add_comment(r, "u16 -> u32");
761 reshape_io_vectors(regs, 8, 2);
762 LOOP_MASK_VH(s, p, i) i_zip2(r, dh[i], sl[i], sl[i]);
763 LOOP_MASK (p, i) i_zip1(r, dl[i], sl[i], sl[i]);
764 }
765}
766
767/*********************************************************************/
768/* numeric minimum */
769/* SWS_UOP_MIN */
770
772 SwsAArch64OpRegs *regs)
773{
774 RasmContext *r = s->rctx;
775 RasmOp *sl = regs->sl;
776 RasmOp *sh = regs->sh;
777 RasmOp *dl = regs->dl;
778 RasmOp *dh = regs->dh;
779 RasmOp *vk = regs->vk;
780
781 if (p->type == SWS_PIXEL_F32) {
782 LOOP_MASK (p, i) { i_fmin(r, dl[i], sl[i], vk[i]); CMTF("vl[%u] = min(vl[%u], vmin%u);", i, i, i); }
783 LOOP_MASK_VH(s, p, i) { i_fmin(r, dh[i], sh[i], vk[i]); CMTF("vh[%u] = min(vh[%u], vmin%u);", i, i, i); }
784 } else {
785 LOOP_MASK (p, i) { i_umin(r, dl[i], sl[i], vk[i]); CMTF("vl[%u] = min(vl[%u], vmin%u);", i, i, i); }
786 LOOP_MASK_VH(s, p, i) { i_umin(r, dh[i], sh[i], vk[i]); CMTF("vh[%u] = min(vh[%u], vmin%u);", i, i, i); }
787 }
788}
789
790/*********************************************************************/
791/* numeric maximum */
792/* SWS_UOP_MAX */
793
795 SwsAArch64OpRegs *regs)
796{
797 RasmContext *r = s->rctx;
798 RasmOp *sl = regs->sl;
799 RasmOp *sh = regs->sh;
800 RasmOp *dl = regs->dl;
801 RasmOp *dh = regs->dh;
802 RasmOp *vk = regs->vk;
803
804 if (p->type == SWS_PIXEL_F32) {
805 LOOP_MASK (p, i) { i_fmax(r, dl[i], sl[i], vk[i]); CMTF("vl[%u] = max(vl[%u], vmax%u);", i, i, i); }
806 LOOP_MASK_VH(s, p, i) { i_fmax(r, dh[i], sh[i], vk[i]); CMTF("vh[%u] = max(vh[%u], vmax%u);", i, i, i); }
807 } else {
808 LOOP_MASK (p, i) { i_umax(r, dl[i], sl[i], vk[i]); CMTF("vl[%u] = max(vl[%u], vmax%u);", i, i, i); }
809 LOOP_MASK_VH(s, p, i) { i_umax(r, dh[i], sh[i], vk[i]); CMTF("vh[%u] = max(vh[%u], vmax%u);", i, i, i); }
810 }
811}
812
813/*********************************************************************/
814/* multiplication by scalar */
815/* SWS_UOP_SCALE */
816
818 SwsAArch64OpRegs *regs)
819{
820 RasmContext *r = s->rctx;
821 RasmOp *sl = regs->sl;
822 RasmOp *sh = regs->sh;
823 RasmOp *dl = regs->dl;
824 RasmOp *dh = regs->dh;
825 RasmOp scale_vec = regs->vk[0];
826
827 if (p->type == SWS_PIXEL_F32) {
828 LOOP_MASK (p, i) { i_fmul(r, dl[i], sl[i], scale_vec); CMTF("vl[%u] *= scale_vec;", i); }
829 LOOP_MASK_VH(s, p, i) { i_fmul(r, dh[i], sh[i], scale_vec); CMTF("vh[%u] *= scale_vec;", i); }
830 } else {
831 LOOP_MASK (p, i) { i_mul (r, dl[i], sl[i], scale_vec); CMTF("vl[%u] *= scale_vec;", i); }
832 LOOP_MASK_VH(s, p, i) { i_mul (r, dh[i], sh[i], scale_vec); CMTF("vh[%u] *= scale_vec;", i); }
833 }
834}
835
836/*********************************************************************/
837/* generalized linear affine transform */
838/* SWS_UOP_LINEAR */
839/* SWS_UOP_LINEAR_FMA */
840
842 SwsAArch64OpRegs *regs, bool vh_pass)
843{
844 RasmContext *r = s->rctx;
845 /**
846 * The intermediate registers for fmul+fadd (for when SWS_BITEXACT
847 * is set) start from temp vector 8.
848 */
849 RasmOp *vt = regs->vt;
850 RasmOp *vtmp = &vt[8];
851 RasmOp *sx = vh_pass ? regs->sh : regs->sl;
852 RasmOp *dx = vh_pass ? regs->dh : regs->dl;
853 char cvh = vh_pass ? 'h' : 'l';
854
855 if (vh_pass && !s->use_vh)
856 return;
857
858 /**
859 * The non-zero coefficients have been packed in aarch64_setup_linear()
860 * in sequential order into the individual lanes of the coefficient
861 * vector registers. We must follow the same order of execution here.
862 */
863 LOOP_MASK(p, i) {
864 bool first = true;
865 RasmNode *pre_mul = rasm_get_current_node(r);
866 for (int j = 0; j < 5; j++) {
867 bool is_offset = (j == 0);
868 int src_j = is_offset ? 4 : (j - 1);
869 if (p->par.lin.zero & SWS_MASK(i, src_j))
870 continue;
871 RasmOp vsrc = sx[src_j];
872 RasmOp vcoeff = regs->linear_vcoeff[i][j];
873 if (first && is_offset) {
874 i_dup (r, dx[i], vcoeff); CMTF("v%c[%u] = broadcast(offset[%u]);", cvh, i, i);
875 } else if (first && !is_offset) {
876 if (p->par.lin.one & SWS_MASK(i, src_j)) {
877 i_mov16b(r, dx[i], vsrc); CMTF("v%c[%u] = vsrc%c[%u];", cvh, i, cvh, src_j);
878 } else {
879 i_fmul (r, dx[i], vsrc, vcoeff); CMTF("v%c[%u] = vsrc%c[%u] * coeff[%u][%u];", cvh, i, cvh, src_j, i, src_j);
880 }
881 } else if (p->uop == SWS_UOP_LINEAR_FMA) {
882 /**
883 * Most modern aarch64 cores have a fastpath for sequences
884 * of fmla instructions. This means that even if the coefficient
885 * is 1, it is still faster to use fmla by 1 instead of fadd.
886 */
887 i_fmla(r, dx[i], vsrc, vcoeff); CMTF("v%c[%u] += vsrc%c[%u] * coeff[%u][%u];", cvh, i, cvh, src_j, i, src_j);
888 } else {
889 /**
890 * Split the multiply-accumulate into fmul+fadd. All
891 * multiplications are performed first into temporary
892 * registers, and only then added to the destination,
893 * to reduce the dependency chain.
894 * There is no need to perform multiplications by 1.
895 */
896 if (!(p->par.lin.one & SWS_MASK(i, src_j))) {
897 pre_mul = rasm_set_current_node(r, pre_mul);
898 i_fmul(r, vtmp[src_j], vsrc, vcoeff); CMTF("vtmp[%u] = vsrc%c[%u] * coeff[%u][%u];", src_j, cvh, src_j, i, src_j);
899 pre_mul = rasm_set_current_node(r, pre_mul);
900 i_fadd(r, dx[i], dx[i], vtmp[src_j]); CMTF("v%c[%u] += vtmp[%u];", cvh, i, src_j);
901 } else {
902 i_fadd(r, dx[i], dx[i], vsrc); CMTF("v%c[%u] += vsrc%c[%u];", cvh, i, cvh, src_j);
903 }
904 }
905 first = false;
906 }
907 }
908}
909
911 SwsAArch64OpRegs *regs)
912{
913 /* Perform linear passes for low and high vector banks. */
914 linear_pass(s, p, regs, false);
915 linear_pass(s, p, regs, true);
916}
917
918/*********************************************************************/
919/* add dithering noise */
920/* SWS_UOP_DITHER */
921
923 SwsAArch64OpRegs *regs)
924{
925 RasmContext *r = s->rctx;
926 RasmOp *sl = regs->sl;
927 RasmOp *sh = regs->sh;
928 RasmOp *dl = regs->dl;
929 RasmOp *dh = regs->dh;
930 RasmOp src_ptr = regs->dither_ptr;
931
932 RasmOp ptr = s->tmp0;
933 RasmOp tmp1 = s->tmp1;
934 RasmOp wtmp1 = a64op_w(tmp1);
935 RasmOp dither_vl = regs->vt[0];
936 RasmOp dither_vh = regs->vt[1];
937 RasmOp bx64 = a64op_x(s->bx);
938 RasmOp y64 = a64op_x(s->y);
939
940 /**
941 * For a description of the matrix buffer layout, read the comments
942 * in aarch64_setup_dither() in aarch64/ops.c.
943 */
944
945 /**
946 * Sort components by y_offset value so that we can start dithering
947 * with the smallest value, and increment the pointer upwards for
948 * each new offset. The dither matrix is over-allocated and may be
949 * over-read at the top, but it cannot be over-read before the start
950 * of the buffer. Since we only mask the y offset once, this would
951 * be an issue if we tried to subtract a value larger than the
952 * initial y_offset.
953 */
954 int sorted[4];
955 int n_comps = 0;
956 /* Very cheap bucket sort. */
957 int max_offset = 0;
958 LOOP_MASK(p, i)
959 max_offset = FFMAX(max_offset, p->par.dither.y_offset[i]);
960 for (int y_off = 0; y_off <= max_offset; y_off++) {
961 LOOP_MASK(p, i) {
962 if (p->par.dither.y_offset[i] == y_off)
963 sorted[n_comps++] = i;
964 }
965 }
966
967 /**
968 * We use ubfiz to mask and shift left in one single instruction:
969 * ubfiz <Wd>, <Wn>, #<lsb>, #<width>
970 * Wd = (Wn & ((1 << width) - 1)) << lsb;
971 *
972 * Given:
973 * block_size = 8, log2(block_size) = 3
974 * dither_size = 16, log2(dither_size) = 4, dither_mask = 0b1111
975 * sizeof(float) = 4, log2(sizeof(float)) = 2
976 *
977 * Suppose we have bx = 0bvvvv. To get x, we left shift by
978 * log2(block_size) and end up with 0bvvvv000. Then we mask against
979 * dither_mask, and end up with 0bv000. Finally we multiply by
980 * sizeof(float), which is the same as shifting left by
981 * log2(sizeof(float)). The result is 0bv00000.
982 *
983 * Therefore:
984 * width = log2(dither_size) - log2(block_size)
985 * lsb = log2(block_size) + log2(sizeof(float))
986 */
987 const int block_size_log2 = (p->block_size == 16) ? 4 : 3;
988 const int dither_size_log2 = p->par.dither.size_log2;
989 const int sizeof_float_log2 = 2;
990 if (dither_size_log2 != block_size_log2) {
991 RasmOp lsb = IMM(block_size_log2 + sizeof_float_log2);
992 RasmOp width = IMM(dither_size_log2 - block_size_log2);
993 i_ubfiz(r, tmp1, bx64, lsb, width); CMT("tmp1 = (bx & ((dither_size / block_size) - 1)) * block_size * sizeof(float);");
994 i_add (r, ptr, src_ptr, tmp1); CMT("ptr += tmp1;");
995 src_ptr = ptr;
996 }
997
998 int last_y_off = -1;
999 int prev_i = 0;
1000 for (int sorted_i = 0; sorted_i < n_comps; sorted_i++) {
1001 int i = sorted[sorted_i];
1002 uint8_t y_off = p->par.dither.y_offset[i];
1003 bool do_load = (y_off != last_y_off);
1004
1005 if (last_y_off < 0) {
1006 /* On the first run, calculate pointer inside dither_matrix. */
1007 RasmOp lsb = IMM(dither_size_log2 + sizeof_float_log2);
1008 RasmOp width = IMM(dither_size_log2);
1009 /**
1010 * The ubfiz instruction for the y offset performs masking
1011 * by the dither matrix size and shifts by the stride.
1012 */
1013 if (y_off == 0) {
1014 i_ubfiz(r, tmp1, y64, lsb, width); CMT("tmp1 = (y & (dither_size - 1)) * dither_size * sizeof(float);");
1015 } else {
1016 i_add (r, wtmp1, s->y, IMM(y_off)); CMTF("tmp1 = y + y_off[%u];", i);
1017 i_ubfiz(r, tmp1, tmp1, lsb, width); CMT("tmp1 = (tmp1 & (dither_size - 1)) * dither_size * sizeof(float);");
1018 }
1019 i_add(r, ptr, src_ptr, tmp1); CMT("ptr += tmp1;");
1020 } else if (do_load) {
1021 /**
1022 * On subsequent runs, just increment the pointer.
1023 * The matrix is over-allocated, so we don't risk
1024 * overreading.
1025 */
1026 int delta = (y_off - last_y_off) * (1 << dither_size_log2) * sizeof(float);
1027 i_add(r, ptr, ptr, IMM(delta)); CMTF("ptr += (y_off[%u] - y_off[%u]) * dither_size * sizeof(float);", i, prev_i);
1028 }
1029
1030 if (do_load) {
1031 RasmOp dither_vlq = v_q(dither_vl);
1032 RasmOp dither_vhq = v_q(dither_vh);
1033 i_ldp (r, dither_vlq, dither_vhq, a64op_base(ptr)); CMT("{ ditherl, ditherh } = *ptr;");
1034 }
1035
1036 i_fadd (r, dl[i], sl[i], dither_vl); CMTF("vl[%u] += vditherl;", i);
1037 if (s->use_vh) {
1038 i_fadd(r, dh[i], sh[i], dither_vh); CMTF("vh[%u] += vditherh;", i);
1039 }
1040 sl = dl;
1041 sh = dh;
1042
1043 last_y_off = y_off;
1044 prev_i = i;
1045 }
1046}
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
static const uint8_t frame_size[4]
Definition g723_1.h:222
static const int offsets[]
Definition hevc_pel.c:34
#define r
Definition input.c:42
static int shift(int a, int b)
Definition bonk.c:261
#define FFMIN(a, b)
Definition macros.h:49
#define FFMAX(a, b)
Definition macros.h:47
static void asmgen_op_write_planar(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:377
static void asmgen_op_clear(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:612
static const char * print_swizzle_v(char buf[8], int8_t n, uint8_t vh)
Definition ops_asmgen.c:424
static void asmgen_op_read_packed(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:277
static void asmgen_op_read_nibble(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:244
static RasmOp swizzle_a64op(SwsAArch64OpRegs *regs, int8_t n, uint8_t vh, bool dst)
Definition ops_asmgen.c:434
#define CMT(comment)
Definition ops_asmgen.c:32
static void reshape_io_vectors(SwsAArch64OpRegs *regs, int el_count, int el_size)
Definition ops_asmgen.c:36
static void asmgen_op_write_nibble(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:334
static void linear_pass(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs, bool vh_pass)
Definition ops_asmgen.c:841
static void clobber_gpr(RasmOp regs[MAX_SAVED_REGS], unsigned *count, RasmOp gpr)
Definition ops_asmgen.c:114
static void asmgen_op_move(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:456
static void reshape_temp_vectors(SwsAArch64OpRegs *regs, int el_count, int el_size)
Definition ops_asmgen.c:47
static void asmgen_op_unpack(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:467
static void reshape_const_vectors(SwsAArch64OpRegs *regs, int el_count, int el_size)
Definition ops_asmgen.c:54
static void asmgen_op_dither(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:922
static void asmgen_process(SwsAArch64Context *s, SwsCompMask imask, SwsCompMask omask)
Definition ops_asmgen.c:139
static void asmgen_op_lshift(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:557
static unsigned clobbered_frame_size(unsigned n)
Definition ops_asmgen.c:63
static void asmgen_op_linear(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:910
static void asmgen_op_min(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:771
static void asmgen_op_convert(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:630
static void emit_clear(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, RasmOp *vx, RasmOp *vk, int i, const char *vx_str)
Definition ops_asmgen.c:593
#define PRINT_SWIZZLE_V(n, vh)
Definition ops_asmgen.c:432
static void asmgen_op_swap_bytes(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:398
#define MAX_SAVED_REGS
Definition ops_asmgen.c:112
static void asmgen_op_read_planar(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:286
#define LOOP_MASK_VH(s, p, idx)
Definition ops_asmgen.c:28
static void asmgen_op_expand(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:728
static void asmgen_op_scale(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:817
static void asmgen_op_write_packed_n(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, RasmOp *vx)
Definition ops_asmgen.c:357
#define LOOP_VH(s, mask, idx)
Definition ops_asmgen.c:27
static void asmgen_op_rshift(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:575
#define CMTF(fmt,...)
Definition ops_asmgen.c:33
static void asmgen_op_read_bit(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:215
static void asmgen_prologue(SwsAArch64Context *s, const RasmOp *regs, unsigned n)
Definition ops_asmgen.c:68
static void asmgen_op_write_bit(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:310
#define LOOP_MASK_BWD_VH(s, p, idx)
Definition ops_asmgen.c:29
static void asmgen_epilogue(SwsAArch64Context *s, const RasmOp *regs, unsigned n)
Definition ops_asmgen.c:89
static void asmgen_op_max(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:794
static void swizzle_emit(SwsAArch64Context *s, SwsAArch64OpRegs *regs, int8_t dst, int8_t src)
Definition ops_asmgen.c:443
static void asmgen_op_write_packed(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:368
static void asmgen_op_pack(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, SwsAArch64OpRegs *regs)
Definition ops_asmgen.c:509
static void asmgen_op_read_packed_n(SwsAArch64Context *s, const SwsAArch64OpImplParams *p, RasmOp *vx)
Definition ops_asmgen.c:266
static unsigned clobbered_gprs(const SwsAArch64Context *s, SwsCompMask imask, SwsCompMask omask, RasmOp regs[MAX_SAVED_REGS])
Definition ops_asmgen.c:122
#define LOOP(mask, idx)
Definition ops_impl.h:56
static uint16_t nibble_mask(SwsCompMask mask)
Definition ops_impl.h:34
#define offsetof_exec_out_bump
Definition ops_impl.h:84
#define LOOP_MASK_BWD(p, idx)
Definition ops_impl.h:64
#define offsetof_exec_in
These values will be used by ops_asmgen to access fields inside of SwsOpExec and SwsOpImpl.
Definition ops_impl.h:81
#define offsetof_exec_out
Definition ops_impl.h:82
#define LOOP_MASK(p, idx)
Definition ops_impl.h:63
#define offsetof_exec_in_bump
Definition ops_impl.h:83
RasmNode * rasm_get_current_node(RasmContext *rctx)
Definition rasm.c:194
AArch64VecViews a64op_vec_views(RasmOp op)
Definition rasm.c:330
RasmNode * rasm_add_label(RasmContext *rctx, int id)
Definition rasm.c:146
RasmNode * rasm_add_comment(RasmContext *rctx, const char *comment)
Definition rasm.c:117
int rasm_new_label(RasmContext *rctx, const char *name)
Allocate a new label ID with the given name.
Definition rasm.c:282
RasmNode * rasm_set_current_node(RasmContext *rctx, RasmNode *node)
Definition rasm.c:199
#define i_mov16b(rctx, op0, op1)
Definition rasm.h:627
static RasmOp a64op_lr(void)
Definition rasm.h:356
#define i_dup(rctx, op0, op1)
Definition rasm.h:557
#define i_stp(rctx, op0, op1, op2)
Definition rasm.h:587
#define i_and16b(rctx, op0, op1, op2)
Definition rasm.h:626
#define i_ins(rctx, op0, op1)
Definition rasm.h:564
#define i_uxtl(rctx, op0, op1)
Definition rasm.h:601
static RasmOp a64op_base(RasmOp op)
Definition rasm.h:506
#define i_ld4(rctx, op0, op1)
Definition rasm.h:569
static uint8_t a64op_gpr_n(RasmOp op)
Definition rasm.h:351
#define i_st3(rctx, op0, op1)
Definition rasm.h:585
static RasmOp a64op_sp(void)
Definition rasm.h:357
static RasmOp v_8b(RasmOp op)
Definition rasm.h:437
#define i_ld2(rctx, op0, op1)
Definition rasm.h:567
#define i_orr16b(rctx, op0, op1, op2)
Definition rasm.h:628
#define i_fadd(rctx, op0, op1, op2)
Definition rasm.h:558
#define i_ucvtf(rctx, op0, op1)
Definition rasm.h:593
static RasmOp vv_4(RasmOp op0, RasmOp op1, RasmOp op2, RasmOp op3)
Definition rasm.h:449
static RasmOp a64op_x(RasmOp op)
Definition rasm.h:361
#define i_rev32(rctx, op0, op1)
Definition rasm.h:581
#define i_ldp(rctx, op0, op1, op2)
Definition rasm.h:570
#define i_zip1(rctx, op0, op1, op2)
Definition rasm.h:604
#define i_ushl(rctx, op0, op1, op2)
Definition rasm.h:597
#define i_movi(rctx, op0, op1)
Definition rasm.h:576
#define i_fcvtzu(rctx, op0, op1)
Definition rasm.h:559
static RasmOp a64op_pre(RasmOp op, int16_t imm)
Definition rasm.h:508
static RasmOp a64op_elem(RasmOp op, uint8_t idx)
Definition rasm.h:422
#define i_and(rctx, op0, op1, op2)
Definition rasm.h:550
#define i_lsr(rctx, op0, op1, op2)
Definition rasm.h:574
#define i_fmla(rctx, op0, op1, op2)
Definition rasm.h:562
#define i_ushr(rctx, op0, op1, op2)
Definition rasm.h:600
#define i_shl(rctx, op0, op1, op2)
Definition rasm.h:582
static RasmOp v_q(RasmOp op)
Definition rasm.h:434
#define i_xtn(rctx, op0, op1)
Definition rasm.h:603
#define i_b(rctx, op0)
Definition rasm.h:551
#define i_fmin(rctx, op0, op1, op2)
Definition rasm.h:561
#define i_ubfiz(rctx, op0, op1, op2, op3)
Definition rasm.h:592
#define i_uxtl2(rctx, op0, op1)
Definition rasm.h:602
#define i_bne(rctx, id)
Definition rasm.h:609
static RasmOp a64op_off(RasmOp op, int16_t imm)
Definition rasm.h:507
static RasmOp rasm_op_label(int id)
Definition rasm.h:96
#define i_ldr(rctx, op0, op1)
Definition rasm.h:571
#define i_mul(rctx, op0, op1, op2)
Definition rasm.h:577
#define i_fmul(rctx, op0, op1, op2)
Definition rasm.h:563
static RasmOp vv_2(RasmOp op0, RasmOp op1)
Definition rasm.h:447
#define i_st4(rctx, op0, op1)
Definition rasm.h:586
#define i_st2(rctx, op0, op1)
Definition rasm.h:584
#define i_orr(rctx, op0, op1, op2)
Definition rasm.h:578
static RasmOp v_16b(RasmOp op)
Definition rasm.h:438
static RasmOp a64op_post(RasmOp op, int16_t imm)
Definition rasm.h:509
#define i_ldrh(rctx, op0, op1)
Definition rasm.h:573
#define i_zip2(rctx, op0, op1, op2)
Definition rasm.h:605
static uint8_t a64op_vec_n(RasmOp op)
Definition rasm.h:377
#define A64OP_VEC_VIEWS4(op)
Definition rasm.h:479
#define i_add(rctx, op0, op1, op2)
Definition rasm.h:547
#define i_ldrb(rctx, op0, op1)
Definition rasm.h:572
#define i_umax(rctx, op0, op1, op2)
Definition rasm.h:594
#define i_ret(rctx)
Definition rasm.h:579
#define i_addv(rctx, op0, op1)
Definition rasm.h:548
static RasmOp vv_3(RasmOp op0, RasmOp op1, RasmOp op2)
Definition rasm.h:448
static RasmOp a64op_w(RasmOp op)
Definition rasm.h:360
static RasmOp a64op_make_vec(uint8_t n, uint8_t el_count, uint8_t el_size)
Definition rasm.h:366
#define i_str(rctx, op0, op1)
Definition rasm.h:588
#define i_mov(rctx, op0, op1)
Definition rasm.h:575
#define i_rev16(rctx, op0, op1)
Definition rasm.h:580
#define i_fmax(rctx, op0, op1, op2)
Definition rasm.h:560
#define i_umin(rctx, op0, op1, op2)
Definition rasm.h:595
#define i_ld3(rctx, op0, op1)
Definition rasm.h:568
#define i_cmp(rctx, op0, op1)
Definition rasm.h:555
#define IMM(val)
Definition rasm.h:92
#define FF_ARRAY_ELEMS(a)
#define snprintf
Definition snprintf.h:34
This helper structure is used to mimic the assembler syntax for vector register modifiers.
Definition rasm.h:456
RasmOp h
Definition rasm.h:459
RasmOp b16
Definition rasm.h:465
RasmOp be[2]
Definition rasm.h:472
RasmOp d
Definition rasm.h:461
RasmOp b8
Definition rasm.h:464
RasmOp h4
Definition rasm.h:466
RasmOp de[2]
Definition rasm.h:473
RasmOp b
Definition rasm.h:458
RasmOp h8
Definition rasm.h:467
RasmOp s
Definition rasm.h:460
SwsAArch64OpImplParams describes the parameters for an SwsUOpType operation.
Definition ops_impl.h:47
RasmOp vt[12]
Definition ops_asmgen.h:32
RasmOp linear_vcoeff[4][5]
Definition ops_asmgen.h:38
#define src
Definition vp8dsp.c:248
#define width
Definition dsp.h:89
Runtime assembler for AArch64.
Definition rasm.h:44
SwsPixelType
Definition uops.h:39
@ SWS_PIXEL_F32
Definition uops.h:44
@ SWS_PIXEL_U32
Definition uops.h:43
@ SWS_PIXEL_U16
Definition uops.h:42
@ SWS_PIXEL_U8
Definition uops.h:41
#define SWS_COMP(X)
Definition uops.h:97
#define SWS_COMP_ELEMS(N)
Definition uops.h:100
#define SWS_MASK(I, J)
Definition uops.h:231
@ SWS_UOP_TO_U8
Definition uops.h:159
@ SWS_UOP_EXPAND_QUAD
Definition uops.h:158
@ SWS_UOP_LINEAR_FMA
Definition uops.h:177
@ SWS_UOP_EXPAND_PAIR
Definition uops.h:157
@ SWS_UOP_TO_U16
Definition uops.h:160
@ SWS_UOP_TO_U32
Definition uops.h:161
@ SWS_UOP_TO_F32
Definition uops.h:162
uint8_t SwsCompMask
Bit-mask of components.
Definition uops.h:93
static av_const int ff_sws_pixel_type_size(SwsPixelType type)
Definition uops.h:50
float delta