xref: /aosp_15_r20/external/XNNPACK/src/qs8-gemm/4x8-aarch32-neon-mlal-lane-cortex-a53.S.in (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
1// Copyright 2021 Google LLC
2//
3// This source code is licensed under the BSD-style license found in the
4// LICENSE file in the root directory of this source tree.
5
6$assert REQUANTIZATION in ["FP32", "RNDNU"]
7$assert not CHANNELWISE or REQUANTIZATION == "FP32"
8$assert DATATYPE in ["QC8", "QS8", "QU8"]
9$assert DATATYPE != "QC8" or REQUANTIZATION == "FP32"
10
11#include <xnnpack/assembly.h>
12
13.syntax unified
14
15$PARAMS_UNION = "xnn_qs8_minmax_params" if CHANNELWISE else "xnn_qs8_conv_minmax_params"
16$ISA = "neonv8" if ARMV8 else "neon"
17$XMIN = "VMIN.U8" if DATATYPE == "QU8" else "VMIN.S8"
18$XMAX = "VMAX.U8" if DATATYPE == "QU8" else "VMAX.S8"
19$XXTL = "VMOVL.U8" if DATATYPE == "QU8" else "VMOVL.S8"
20$SQXTXN = "VQMOVUN.S16" if DATATYPE == "QU8" else "VQMOVN.S16"
21$XINT8_T = "uint8_t" if DATATYPE == "QU8" else "int8_t"
22// void xnn_${DATATYPE.lower()}_gemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_a53(
23//     size_t mr,                            r0
24//     size_t nc,                            r1
25//     size_t kc,                            (r2) -> sp + 56 -> r5
26//     const ${XINT8_T}*restrict a,              r3
27//     size_t a_stride,           sp + 96 -> (r7)
28//     const void*restrict w,     sp + 100 -> r9
29//     ${XINT8_T}*restrict c,         sp + 104 -> r11
30//     size_t cm_stride,          sp + 108 -> (r6)
31//     size_t cn_stride,          sp + 112 -> r7
32//     ${PARAMS_UNION} params)  sp + 116 -> (r5)
33
34// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved.
35
36// Register usage
37// A0   r3  d0-d1 q0
38// A1  r12  d2-d3 q1
39// A2  r10  d4-d5 q2
40// A3   r0  d6-d7 q3
41
42// B    r9  d8-d9 q4 q5
43
44// C0  r11 d16-d17  q8  d18-d19  q9
45// C1   r4 d20-d21 q10  d22-d23 q11
46// C2   r8 d24-d25 q12  d26-d27 q13
47// C3   r6 d28-d29 q14  d30-d31 q15
48
49// r2,r14 A53 gpr temporary loads
50// Unused d15
51
52$if REQUANTIZATION == "RNDNU" and DATATYPE != "QU8":
53  // params structure is 16 bytes
54  //  struct {
55  //    int32_t right_pre_shift;    d12[0]
56  //    int32_t multiplier;         d12[1]
57  //    int32_t right_post_shift;   d13[0]
58  //    int16_t output_zero_point;  d13[2]
59  //    int8_t output_min;          d13[6]
60  //    int8_t output_max;          d13[7]
61  //  } rndnu_neon;
62$elif REQUANTIZATION == "RNDNU" and DATATYPE == "QU8":
63  # params structure is 20 bytes
64  #  struct {
65  #    uint8_t kernel_zero_point[4];  d14
66  #    int32_t right_pre_shift;       d12[0]
67  #    int32_t multiplier;            d12[1]
68  #    int32_t right_post_shift;      d13[0]
69  #    int16_t output_zero_point;     d13[2]
70  #    uint8_t output_min;            d13[6]
71  #    uint8_t output_max;            d13[7]
72  #  } rndnu_neon;
73$elif DATATYPE == "QC8" and not ARMV8:
74  // params structure is 10 bytes
75  // struct {
76  //   float magic_bias;                           d12[0]
77  //   int32_t magic_bias_less_output_zero_point;  d12[1]
78  //   int8_t output_min;                          d13[6]
79  //   int8_t output_max;                          d13[7]
80  // } xnn_qs8_minmax_params.neon;
81$else:
82  // params structure is 4 bytes
83  //  struct {
84  //    int16_t output_zero_point;  d13[2]
85  //    int8_t output_min;          d13[6]
86  //    int8_t output_max;          d13[7]
87  //  } xnn_qs8_minmax_params.neonv8;
88
89BEGIN_FUNCTION xnn_${DATATYPE.lower()}_gemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_a53
90        # Push 96 bytes
91        PUSH    {r2, r4, r5, r6, r7, r8, r9, r10, r11, lr}  // 40
92        $if DATATYPE == "QU8":
93          VPUSH   {d8-d14}                            // +56 = 96
94        $else:
95          SUB     sp, sp, 8                           // +8
96          VPUSH   {d8-d13}                            // +48 = 96
97
98        LDR     r7, [sp, 96]            // a_stride
99        LDR     r11, [sp, 104]          // c
100        LDR     r6, [sp, 108]           // cm_stride
101        LDR     r9, [sp, 100]           // w
102        LDR     r5, [sp, 116]           // params
103
104        # Clamp A and C pointers
105        CMP     r0, 2                   // if mr >= 2
106        ADD     r12, r3, r7             //   a1 = a0 + a_stride
107        ADD     r4, r11, r6             //   c1 = c0 + cm_stride
108        MOVLO   r12, r3                 // a1
109        MOVLO   r4, r11                 // c1
110                                        // if mr > 2
111        ADD     r10, r12, r7            //   a2 = a1 + a_stride
112        ADD     r8, r4, r6              //   c2 = c1 + cm_stride
113        MOVLS   r10, r12                // a2
114        MOVLS   r8, r4                  // c2
115
116        CMP     r0, 4                   // if mr >=4
117        ADD     r0, r10, r7             //   a3 = a2 + a_stride
118        ADD     r6, r8, r6              //   c3 = c2 + cm_stride
119        MOVLO   r0, r10                 // a3
120        MOVLO   r6, r8                  // c3
121
122        # Load params values
123        $if DATATYPE == "QU8":
124          VLD1.32 {d14[]}, [r5]!          // QU8 kernel_zero_point
125        $if REQUANTIZATION == "RNDNU":
126          VLDM    r5, {d12-d13}           // RNDNU params
127        $elif DATATYPE == "QC8" and ARMV8:
128          VLD1.32 {d13[]}, [r5]           // QC8 neonv8 params
129        $elif DATATYPE == "QC8" and not ARMV8:
130          VLDM    r5!, {d12}              // QC8 neon params
131          VLD1.16 {d13[]}, [r5]           // output_min/max
132        LDR     r7, [sp, 112]            // cn_stride
133
134        $if PREFETCH:
135          PLD     [r9,  64]               // Prefetch B
136          PLD     [r9, 128]
137          PLD     [r9, 192]
138          PLD     [r9, 256]
139          PLD     [r9, 320]
140          PLD     [r9, 384]
141
142        .p2align 3
1430:
144        # Load initial bias from w into accumulators
145        VLDM    r9!, {d16-d19}          // Bias
146        SUBS    r5, r2, 8               // k = kc - 8
147
148        $if PREFETCH:
149          VMOV    q10, q8
150          PLD     [r3,  64]               // Prefetch A
151          VMOV    q11, q9
152          PLD     [r12, 64]
153          VMOV    q12, q8
154          PLD     [r10, 64]
155          VMOV    q13, q9
156          PLD     [r0,  64]
157          VMOV    q14, q8
158          VMOV    q15, q9
159        $else:
160          VMOV    q10, q8
161          VMOV    q11, q9
162          VMOV    q12, q8
163          VMOV    q13, q9
164          VMOV    q14, q8
165          VMOV    q15, q9
166        BLO     4f                      // less than 8 channels?
167
168        // Prologue - load 4A's and B0
169        VLD1.8  {d0},  [r3]!            // A0
170        VLD1.8  {d2}, [r12]!            // A1
171        VLD1.8  {d4}, [r10]!            // A2
172        VLD1.8  {d6},  [r0]!            // A3
173        VLD1.8  {d8},  [r9]!            // B0
174
175        SUBS    r5, r5, 8               // k = k - 8
176        BLO     2f                      // less than 8 channels?
177
178        // Main loop - 8 bytes
179        // 64 bytes for weights.
180        // 5 VMOVL = 4 A and 1 B = 5 cycles
181        // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles
182        // 1 blocks with VLD B, VMLA = 9 cycles
183        // total = 84 cycles
184        .p2align 3
1851:
186        // Extend - 5 cycles
187        ${XXTL} q0, d0
188        $if PREFETCH:
189          PLD     [r3, 128]
190        $if DATATYPE == "QU8":
191          VSUBL.U8 q4, d8, d14
192        $else:
193          VMOVL.S8 q4, d8
194        $if PREFETCH:
195          PLD     [r9, 448]
196        ${XXTL} q1, d2
197        $if PREFETCH:
198          PLD     [r12, 128]
199        ${XXTL} q2, d4
200        $if PREFETCH:
201          PLD     [r0, 128]
202        ${XXTL} q3, d6
203        $if PREFETCH:
204          PLD     [r10, 128]
205
206        // BLOCK 0 - 10 cycles
207        VLD1.8  {d10},  [r9]!           // B1
208        VMLAL.S16 q8, d8, d0[0]
209        VMLAL.S16 q9, d9, d0[0]
210        VMLAL.S16 q10, d8, d2[0]
211        VMLAL.S16 q11, d9, d2[0]
212        $if DATATYPE == "QU8":
213          VSUBL.U8 q5, d10, d14
214        $else:
215          VMOVL.S8 q5, d10
216        VMLAL.S16 q12, d8, d4[0]
217        VMLAL.S16 q13, d9, d4[0]
218        VMLAL.S16 q14, d8, d6[0]
219        VMLAL.S16 q15, d9, d6[0]
220
221        // BLOCK 1 - 10 cycles
222        VLD1.8  {d8},  [r9]!            // B2
223        VMLAL.S16 q8, d10, d0[1]
224        VMLAL.S16 q9, d11, d0[1]
225        VMLAL.S16 q10, d10, d2[1]
226        VMLAL.S16 q11, d11, d2[1]
227        $if DATATYPE == "QU8":
228          VSUBL.U8 q4, d8, d14
229        $else:
230          VMOVL.S8 q4, d8
231        VMLAL.S16 q12, d10, d4[1]
232        VMLAL.S16 q13, d11, d4[1]
233        VMLAL.S16 q14, d10, d6[1]
234        VMLAL.S16 q15, d11, d6[1]
235
236        // BLOCK 2 - 10 cycles
237        VLD1.8  {d10},  [r9]!           // B3
238        VMLAL.S16 q8, d8, d0[2]
239        VMLAL.S16 q9, d9, d0[2]
240        VMLAL.S16 q10, d8, d2[2]
241        VMLAL.S16 q11, d9, d2[2]
242        $if DATATYPE == "QU8":
243          VSUBL.U8 q5, d10, d14
244        $else:
245          VMOVL.S8 q5, d10
246        VMLAL.S16 q12, d8, d4[2]
247        VMLAL.S16 q13, d9, d4[2]
248        VMLAL.S16 q14, d8, d6[2]
249        VMLAL.S16 q15, d9, d6[2]
250
251        // BLOCK 3 - 10 cycles
252        VLD1.8  {d8},  [r9]!            // B4
253        VMLAL.S16 q8, d10, d0[3]
254        VMLAL.S16 q9, d11, d0[3]
255        VMLAL.S16 q10, d10, d2[3]
256        VMLAL.S16 q11, d11, d2[3]
257        $if DATATYPE == "QU8":
258          VSUBL.U8 q4, d8, d14
259        $else:
260          VMOVL.S8 q4, d8
261        VMLAL.S16 q12, d10, d4[3]
262        LDR     r2, [r3]                // A0 low
263        VMLAL.S16 q13, d11, d4[3]
264        LDR     r14, [r3, 4]            // A0 high
265        VMLAL.S16 q14, d10, d6[3]
266        ADD     r3, r3, 8
267        VMLAL.S16 q15, d11, d6[3]
268
269        // BLOCK 4 - 10 cycles
270        VLD1.8  {d10},  [r9]!           // B5
271        VMOV    d0, r2, r14             // A0 VMOV
272        VMLAL.S16 q8, d8, d1[0]
273        VMLAL.S16 q9, d9, d1[0]
274        VMLAL.S16 q10, d8, d3[0]
275        VMLAL.S16 q11, d9, d3[0]
276        $if DATATYPE == "QU8":
277          VSUBL.U8 q5, d10, d14
278        $else:
279          VMOVL.S8 q5, d10
280        VMLAL.S16 q12, d8, d5[0]
281        LDR     r2, [r12]               // A1 low
282        VMLAL.S16 q13, d9, d5[0]
283        LDR     r14, [r12, 4]           // A1 high
284        VMLAL.S16 q14, d8, d7[0]
285        ADD     r12, r12, 8
286        VMLAL.S16 q15, d9, d7[0]
287
288        // BLOCK 5 - 10 cycles
289        VLD1.8  {d8},  [r9]!            // B6
290        VMOV    d2, r2, r14             // A1 VMOV
291        VMLAL.S16 q8, d10, d1[1]
292        VMLAL.S16 q9, d11, d1[1]
293        VMLAL.S16 q10, d10, d3[1]
294        VMLAL.S16 q11, d11, d3[1]
295        $if DATATYPE == "QU8":
296          VSUBL.U8 q4, d8, d14
297        $else:
298          VMOVL.S8 q4, d8
299        VMLAL.S16 q12, d10, d5[1]
300        LDR     r2, [r10]               // A2 low
301        VMLAL.S16 q13, d11, d5[1]
302        LDR     r14, [r10, 4]           // A2 high
303        VMLAL.S16 q14, d10, d7[1]
304        ADD     r10, r10, 8
305        VMLAL.S16 q15, d11, d7[1]
306
307        // BLOCK 6 - 10 cycles
308        VLD1.8  {d10},  [r9]!           // B7
309        VMOV    d4, r2, r14             // A2 VMOV
310        VMLAL.S16 q8, d8, d1[2]
311        VMLAL.S16 q9, d9, d1[2]
312        VMLAL.S16 q10, d8, d3[2]
313        VMLAL.S16 q11, d9, d3[2]
314        $if DATATYPE == "QU8":
315          VSUBL.U8 q5, d10, d14
316        $else:
317          VMOVL.S8 q5, d10
318        VMLAL.S16 q12, d8, d5[2]
319        LDR     r2, [r0]                // A3 low
320        VMLAL.S16 q13, d9, d5[2]
321        LDR     r14, [r0, 4]            // A3 high
322        VMLAL.S16 q14, d8, d7[2]
323        ADD     r0, r0, 8
324        VMLAL.S16 q15, d9, d7[2]
325
326        // BLOCK 7 - 9 cycles
327        VLD1.8  {d8},  [r9]!            // B0
328        VMOV    d6, r2, r14             // A3 VMOV
329        VMLAL.S16 q8, d10, d1[3]
330        VMLAL.S16 q9, d11, d1[3]
331        VMLAL.S16 q10, d10, d3[3]
332        VMLAL.S16 q11, d11, d3[3]
333        VMLAL.S16 q12, d10, d5[3]
334        VMLAL.S16 q13, d11, d5[3]
335        SUBS    r5, r5, 8
336        VMLAL.S16 q14, d10, d7[3]
337        VMLAL.S16 q15, d11, d7[3]
338        BHS     1b
339
340        // Epilogue
341
342        .p2align 3
3432:
344        ${XXTL} q0, d0
345        $if DATATYPE == "QU8":
346          VSUBL.U8 q4, d8, d14
347        $else:
348          VMOVL.S8 q4, d8
349        ${XXTL} q1, d2
350        ${XXTL} q2, d4
351        ${XXTL} q3, d6
352
353        VLD1.8  {d10},  [r9]!           // B1
354        VMLAL.S16 q8, d8, d0[0]
355        VMLAL.S16 q9, d9, d0[0]
356        VMLAL.S16 q10, d8, d2[0]
357        VMLAL.S16 q11, d9, d2[0]
358        $if DATATYPE == "QU8":
359          VSUBL.U8 q5, d10, d14
360        $else:
361          VMOVL.S8 q5, d10
362        VMLAL.S16 q12, d8, d4[0]
363        VMLAL.S16 q13, d9, d4[0]
364        VMLAL.S16 q14, d8, d6[0]
365        VMLAL.S16 q15, d9, d6[0]
366
367        VLD1.8  {d8},  [r9]!            // B2
368        VMLAL.S16 q8, d10, d0[1]
369        VMLAL.S16 q9, d11, d0[1]
370        VMLAL.S16 q10, d10, d2[1]
371        VMLAL.S16 q11, d11, d2[1]
372        $if DATATYPE == "QU8":
373          VSUBL.U8 q4, d8, d14
374        $else:
375          VMOVL.S8 q4, d8
376        VMLAL.S16 q12, d10, d4[1]
377        VMLAL.S16 q13, d11, d4[1]
378        VMLAL.S16 q14, d10, d6[1]
379        VMLAL.S16 q15, d11, d6[1]
380
381        VLD1.8  {d10},  [r9]!           // B3
382        VMLAL.S16 q8, d8, d0[2]
383        VMLAL.S16 q9, d9, d0[2]
384        VMLAL.S16 q10, d8, d2[2]
385        VMLAL.S16 q11, d9, d2[2]
386        $if DATATYPE == "QU8":
387          VSUBL.U8 q5, d10, d14
388        $else:
389          VMOVL.S8 q5, d10
390        VMLAL.S16 q12, d8, d4[2]
391        VMLAL.S16 q13, d9, d4[2]
392        VMLAL.S16 q14, d8, d6[2]
393        VMLAL.S16 q15, d9, d6[2]
394
395        VLD1.8  {d8},  [r9]!            // B4
396        VMLAL.S16 q8, d10, d0[3]
397        VMLAL.S16 q9, d11, d0[3]
398        VMLAL.S16 q10, d10, d2[3]
399        VMLAL.S16 q11, d11, d2[3]
400        $if DATATYPE == "QU8":
401          VSUBL.U8 q4, d8, d14
402        $else:
403          VMOVL.S8 q4, d8
404        VMLAL.S16 q12, d10, d4[3]
405        VMLAL.S16 q13, d11, d4[3]
406        VMLAL.S16 q14, d10, d6[3]
407        VMLAL.S16 q15, d11, d6[3]
408
409        VLD1.8  {d10},  [r9]!           // B5
410        VMLAL.S16 q8, d8, d1[0]
411        VMLAL.S16 q9, d9, d1[0]
412        VMLAL.S16 q10, d8, d3[0]
413        VMLAL.S16 q11, d9, d3[0]
414        $if DATATYPE == "QU8":
415          VSUBL.U8 q5, d10, d14
416        $else:
417          VMOVL.S8 q5, d10
418        VMLAL.S16 q12, d8, d5[0]
419        VMLAL.S16 q13, d9, d5[0]
420        VMLAL.S16 q14, d8, d7[0]
421        VMLAL.S16 q15, d9, d7[0]
422
423        VLD1.8  {d8},  [r9]!            // B6
424        VMLAL.S16 q8, d10, d1[1]
425        VMLAL.S16 q9, d11, d1[1]
426        VMLAL.S16 q10, d10, d3[1]
427        VMLAL.S16 q11, d11, d3[1]
428        $if DATATYPE == "QU8":
429          VSUBL.U8 q4, d8, d14
430        $else:
431          VMOVL.S8 q4, d8
432        VMLAL.S16 q12, d10, d5[1]
433        VMLAL.S16 q13, d11, d5[1]
434        VMLAL.S16 q14, d10, d7[1]
435        VMLAL.S16 q15, d11, d7[1]
436
437        VLD1.8  {d10},  [r9]!           // B7
438        VMLAL.S16 q8, d8, d1[2]
439        VMLAL.S16 q9, d9, d1[2]
440        VMLAL.S16 q10, d8, d3[2]
441        VMLAL.S16 q11, d9, d3[2]
442        $if DATATYPE == "QU8":
443          VSUBL.U8 q5, d10, d14
444        $else:
445          VMOVL.S8 q5, d10
446        VMLAL.S16 q12, d8, d5[2]
447        VMLAL.S16 q13, d9, d5[2]
448        VMLAL.S16 q14, d8, d7[2]
449        VMLAL.S16 q15, d9, d7[2]
450
451        VMLAL.S16 q8, d10, d1[3]
452        VMLAL.S16 q9, d11, d1[3]
453        VMLAL.S16 q10, d10, d3[3]
454        VMLAL.S16 q11, d11, d3[3]
455        VMLAL.S16 q12, d10, d5[3]
456        VMLAL.S16 q13, d11, d5[3]
457        ADDS    r5, r5, 8
458        VMLAL.S16 q14, d10, d7[3]
459        VMLAL.S16 q15, d11, d7[3]
460
461        # Is there a remainder?- 1-7 bytes of A
462        BNE     4f
463
4643:
465        $if REQUANTIZATION == "RNDNU":
466          # RNDNU quantization
467          VDUP.32 q0, d12[0]              // right_pre_shift
468
469          VQSHL.S32 q8,  q8, q0
470          VQSHL.S32 q9,  q9, q0
471          VQSHL.S32 q10, q10, q0
472          VQSHL.S32 q11, q11, q0
473          VQSHL.S32 q12, q12, q0
474          VQSHL.S32 q13, q13, q0
475          VQSHL.S32 q14, q14, q0
476          VQSHL.S32 q15, q15, q0
477
478          VDUP.32 q2, d13[0]              // right_post_shift
479
480          VQDMULH.S32 q8,  q8, d12[1]     // multiplier
481          VQDMULH.S32 q9,  q9, d12[1]
482          VQDMULH.S32 q10, q10, d12[1]
483          VQDMULH.S32 q11, q11, d12[1]
484          VQDMULH.S32 q12, q12, d12[1]
485          VQDMULH.S32 q13, q13, d12[1]
486          VQDMULH.S32 q14, q14, d12[1]
487          VQDMULH.S32 q15, q15, d12[1]
488
489          VRSHL.S32 q8,  q8, q2
490          VRSHL.S32 q9,  q9, q2
491          VRSHL.S32 q10, q10, q2
492          VRSHL.S32 q11, q11, q2
493          VRSHL.S32 q12, q12, q2
494          VRSHL.S32 q13, q13, q2
495          VRSHL.S32 q14, q14, q2
496          VRSHL.S32 q15, q15, q2
497        $elif DATATYPE == "QC8" and ARMV8:
498          # QC8 FP32 quantization
499          VLD1.8  {q0-q1},  [r9]!
500
501          VCVT.F32.S32 q8,  q8
502          VCVT.F32.S32 q9,  q9
503          VCVT.F32.S32 q10, q10
504          VCVT.F32.S32 q11, q11
505          VCVT.F32.S32 q12, q12
506          VCVT.F32.S32 q13, q13
507          VCVT.F32.S32 q14, q14
508          VCVT.F32.S32 q15, q15
509
510          VMUL.F32 q8,  q8, q0            // multiplier
511          VMUL.F32 q9,  q9, q1
512          VMUL.F32 q10, q10, q0
513          VMUL.F32 q11, q11, q1
514          VMUL.F32 q12, q12, q0
515          VMUL.F32 q13, q13, q1
516          VMUL.F32 q14, q14, q0
517          VMUL.F32 q15, q15, q1
518
519          VCVTN.S32.F32 q8,  q8
520          VCVTN.S32.F32 q9,  q9
521          VCVTN.S32.F32 q10, q10
522          VCVTN.S32.F32 q11, q11
523          VCVTN.S32.F32 q12, q12
524          VCVTN.S32.F32 q13, q13
525          VCVTN.S32.F32 q14, q14
526          VCVTN.S32.F32 q15, q15
527        $elif DATATYPE == "QC8" and not ARMV8:
528          # QC8 FP32 quantization
529          VLD1.8  {q0-q1},  [r9]!
530
531          VDUP.32 q2, d12[0]              // magic_bias
532          VDUP.32 q3, d12[1]              // magic_bias_less_output_zero_point
533
534          VCVT.F32.S32 q8,  q8
535          VCVT.F32.S32 q9,  q9
536          VCVT.F32.S32 q10, q10
537          VCVT.F32.S32 q11, q11
538          VCVT.F32.S32 q12, q12
539          VCVT.F32.S32 q13, q13
540          VCVT.F32.S32 q14, q14
541          VCVT.F32.S32 q15, q15
542
543          VMUL.F32 q8,  q8, q0            // multiplier
544          VMUL.F32 q9,  q9, q1
545          VMUL.F32 q10, q10, q0
546          VMUL.F32 q11, q11, q1
547          VMUL.F32 q12, q12, q0
548          VMUL.F32 q13, q13, q1
549          VMUL.F32 q14, q14, q0
550          VMUL.F32 q15, q15, q1
551
552          VADD.F32 q8,  q8, q2            // magic_bias
553          VADD.F32 q9,  q9, q2
554          VADD.F32 q10, q10, q2
555          VADD.F32 q11, q11, q2
556          VADD.F32 q12, q12, q2
557          VADD.F32 q13, q13, q2
558          VADD.F32 q14, q14, q2
559          VADD.F32 q15, q15, q2
560
561          VQSUB.S32 q8,  q8, q3           // magic_bias_less_output_zero_point
562          VQSUB.S32 q9,  q9, q3
563          VQSUB.S32 q10, q10, q3
564          VQSUB.S32 q11, q11, q3
565          VQSUB.S32 q12, q12, q3
566          VQSUB.S32 q13, q13, q3
567          VQSUB.S32 q14, q14, q3
568          VQSUB.S32 q15, q15, q3
569
570        $if DATATYPE != "QC8" or ARMV8:
571          VDUP.16 q0, d13[2]              // output_zero_point
572
573        VQMOVN.S32 d16, q8
574        VQMOVN.S32 d17, q9
575        VQMOVN.S32 d18, q10
576        VQMOVN.S32 d19, q11
577        VQMOVN.S32 d20, q12
578        VQMOVN.S32 d21, q13
579        VQMOVN.S32 d22, q14
580        VQMOVN.S32 d23, q15
581
582        $if DATATYPE != "QC8" or ARMV8:
583          VQADD.S16 q8,  q8, q0
584          VQADD.S16 q9,  q9, q0
585          VQADD.S16 q10, q10, q0
586          VQADD.S16 q11, q11, q0
587
588        VDUP.8  q12, d13[6]             // output_min
589
590        ${SQXTXN} d0,  q8
591        ${SQXTXN} d1,  q9
592        ${SQXTXN} d2, q10
593        ${SQXTXN} d3, q11
594
595        VDUP.8  q13, d13[7]             // output_max
596
597        ${XMAX} q0, q0, q12
598        ${XMAX} q1, q1, q12
599
600        LDR     r2, [sp, 56]            // kc
601        SUBS    r1, r1, 8
602
603        ${XMIN} q0, q0, q13
604        ${XMIN} q1, q1, q13
605
606        # Store full 4 x 8
607        BLO     5f
608        VST1.8  {d0}, [r11], r7
609        SUB     r3, r3, r2
610        VST1.8  {d1}, [r4], r7
611        SUB     r12, r12, r2
612        VST1.8  {d2}, [r8], r7
613        SUB     r10, r10, r2
614        VST1.8  {d3}, [r6], r7
615        SUB     r0, r0, r2
616        BHI     0b
617
618        $if DATATYPE == "QU8":
619          VPOP    {d8-d14}
620          ADD     sp, sp, 4               // skip r2
621        $else:
622          VPOP    {d8-d13}
623          ADD     sp, sp, 12              // skip pad of 8 + r2
624        POP     {r4, r5, r6, r7, r8, r9, r10, r11, pc}
625
626        # Remainder- 1 to 7 bytes of A
627        .p2align 3
6284:
629        AND     r5, r5, 7               // kc remainder 1 to 7
630
631        VLD1.8  {d0},  [r3], r5
632        VLD1.8  {d8},  [r9]!
633        VLD1.8  {d2}, [r12], r5
634        VLD1.8  {d4}, [r10], r5
635        VLD1.8  {d6},  [r0], r5
636
637        ${XXTL} q0, d0
638        $if DATATYPE == "QU8":
639          VSUBL.U8 q4, d8, d14
640        $else:
641          VMOVL.S8 q4, d8
642        ${XXTL} q1, d2
643        ${XXTL} q2, d4
644        ${XXTL} q3, d6
645        VMLAL.S16 q8, d8, d0[0]
646        VMLAL.S16 q9, d9, d0[0]
647        VMLAL.S16 q10, d8, d2[0]
648        VMLAL.S16 q11, d9, d2[0]
649        VMLAL.S16 q12, d8, d4[0]
650        VMLAL.S16 q13, d9, d4[0]
651        VMLAL.S16 q14, d8, d6[0]
652        VMLAL.S16 q15, d9, d6[0]
653        CMP     r5, 2
654        BLO     3b
655
656        VLD1.8  {d8},  [r9]!
657        $if DATATYPE == "QU8":
658          VSUBL.U8 q4, d8, d14
659        $else:
660          VMOVL.S8 q4, d8
661        VMLAL.S16 q8, d8, d0[1]
662        VMLAL.S16 q9, d9, d0[1]
663        VMLAL.S16 q10, d8, d2[1]
664        VMLAL.S16 q11, d9, d2[1]
665        VMLAL.S16 q12, d8, d4[1]
666        VMLAL.S16 q13, d9, d4[1]
667        VMLAL.S16 q14, d8, d6[1]
668        VMLAL.S16 q15, d9, d6[1]
669        BEQ     3b
670
671        VLD1.8  {d8},  [r9]!
672        $if DATATYPE == "QU8":
673          VSUBL.U8 q4, d8, d14
674        $else:
675          VMOVL.S8 q4, d8
676        VMLAL.S16 q8, d8, d0[2]
677        VMLAL.S16 q9, d9, d0[2]
678        VMLAL.S16 q10, d8, d2[2]
679        VMLAL.S16 q11, d9, d2[2]
680        VMLAL.S16 q12, d8, d4[2]
681        VMLAL.S16 q13, d9, d4[2]
682        VMLAL.S16 q14, d8, d6[2]
683        VMLAL.S16 q15, d9, d6[2]
684        CMP     r5, 4
685        BLO     3b
686
687        VLD1.8  {d8},  [r9]!
688        $if DATATYPE == "QU8":
689          VSUBL.U8 q4, d8, d14
690        $else:
691          VMOVL.S8 q4, d8
692        VMLAL.S16 q8, d8, d0[3]
693        VMLAL.S16 q9, d9, d0[3]
694        VMLAL.S16 q10, d8, d2[3]
695        VMLAL.S16 q11, d9, d2[3]
696        VMLAL.S16 q12, d8, d4[3]
697        VMLAL.S16 q13, d9, d4[3]
698        VMLAL.S16 q14, d8, d6[3]
699        VMLAL.S16 q15, d9, d6[3]
700        BEQ     3b
701
702        VLD1.8  {d8},  [r9]!
703        $if DATATYPE == "QU8":
704          VSUBL.U8 q4, d8, d14
705        $else:
706          VMOVL.S8 q4, d8
707        VMLAL.S16 q8, d8, d1[0]
708        VMLAL.S16 q9, d9, d1[0]
709        VMLAL.S16 q10, d8, d3[0]
710        VMLAL.S16 q11, d9, d3[0]
711        VMLAL.S16 q12, d8, d5[0]
712        VMLAL.S16 q13, d9, d5[0]
713        VMLAL.S16 q14, d8, d7[0]
714        VMLAL.S16 q15, d9, d7[0]
715        CMP     r5, 6
716        BLO     3b
717
718        VLD1.8  {d8},  [r9]!
719        $if DATATYPE == "QU8":
720          VSUBL.U8 q4, d8, d14
721        $else:
722          VMOVL.S8 q4, d8
723        VMLAL.S16 q8, d8, d1[1]
724        VMLAL.S16 q9, d9, d1[1]
725        VMLAL.S16 q10, d8, d3[1]
726        VMLAL.S16 q11, d9, d3[1]
727        VMLAL.S16 q12, d8, d5[1]
728        VMLAL.S16 q13, d9, d5[1]
729        VMLAL.S16 q14, d8, d7[1]
730        VMLAL.S16 q15, d9, d7[1]
731        BEQ     3b
732
733        VLD1.8  {d8},  [r9]!
734        $if DATATYPE == "QU8":
735          VSUBL.U8 q4, d8, d14
736        $else:
737          VMOVL.S8 q4, d8
738        VMLAL.S16 q8, d8, d1[2]
739        VMLAL.S16 q9, d9, d1[2]
740        VMLAL.S16 q10, d8, d3[2]
741        VMLAL.S16 q11, d9, d3[2]
742        VMLAL.S16 q12, d8, d5[2]
743        VMLAL.S16 q13, d9, d5[2]
744        VMLAL.S16 q14, d8, d7[2]
745        VMLAL.S16 q15, d9, d7[2]
746        B       3b
747
748        # Store odd width
749        .p2align 3
7505:
751        TST     r1, 4
752        BEQ     6f
753        VST1.32 {d0[0]}, [r11]!
754        VST1.32 {d1[0]}, [r4]!
755        VST1.32 {d2[0]}, [r8]!
756        VST1.32 {d3[0]}, [r6]!
757        VEXT.8  q0, q0, q0, 4
758        VEXT.8  q1, q1, q1, 4
7596:
760        TST     r1, 2
761        BEQ     7f
762        VST1.16 {d0[0]}, [r11]!
763        VST1.16 {d1[0]}, [r4]!
764        VST1.16 {d2[0]}, [r8]!
765        VST1.16 {d3[0]}, [r6]!
766        VEXT.8  q0, q0, q0, 2
767        VEXT.8  q1, q1, q1, 2
768
7697:
770        TST     r1, 1
771        BEQ     8f
772        VST1.8  {d0[0]}, [r11]
773        VST1.8  {d1[0]}, [r4]
774        VST1.8  {d2[0]}, [r8]
775        VST1.8  {d3[0]}, [r6]
776
7778:
778        $if DATATYPE == "QU8":
779          VPOP    {d8-d14}
780          ADD     sp, sp, 4               // skip r2
781        $else:
782          VPOP    {d8-d13}
783          ADD     sp, sp, 12              // skip pad of 8 + r2
784        POP     {r4, r5, r6, r7, r8, r9, r10, r11, pc}
785
786
787END_FUNCTION xnn_${DATATYPE.lower()}_gemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_a53
788
789#ifdef __ELF__
790.section ".note.GNU-stack","",%progbits
791#endif
792
793