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