1// Copyright 2019 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#include <xnnpack/assembly.h>
7
8.syntax unified
9
10// void xnn_f32_gemm_minmax_ukernel_4x8__aarch32_neon_cortex_a53(
11//     size_t mr,                            r0
12//     size_t nc,                            r1
13//     size_t kc,                            r2 -> r5 -> sp + 0
14//     const uint8_t*restrict a,             r3
15//     size_t a_stride,          sp + 100 -> (r7)
16//     const void*restrict w,    sp + 104 -> r9
17//     uint8_t*restrict c,       sp + 108 -> r11
18//     size_t cm_stride,         sp + 112 -> (r6)
19//     size_t cn_stride,         sp + 116 -> (r0)
20//     const union xnn_f32_minmax_params params[restrict XNN_MIN_ELEMENTS(1)])  sp + 120 -> (r5)
21
22
23// inner loop registers
24// r0, r2   scratch temporaries for loads
25// r14 (lr) unused
26
27// A0   r3  d0
28// A1  r12  d1
29// A2  r10  d2
30// A3   r7  d3
31
32// B    r9  d8,  d9, d10, d11
33// B       d12, d13, d14, d15
34
35// C0  r11 d16-d17  q8  d18-d19  q9
36// C1   r4 d20-d21 q10  d22-d23 q11
37// C2   r8 d24-d25 q12  d26-d27 q13
38// C3   r6 d28-d29 q14  d30-d31 q15
39
40// Clamp (r5) d4 d5 d6 d7
41
42BEGIN_FUNCTION xnn_f32_gemm_minmax_ukernel_4x8__aarch32_neon_cortex_a53
43        .arm
44#ifndef __APPLE__
45        .arch armv7-a
46        .fpu neon
47#endif
48        // Push 100 bytes
49        // r2 will be reloaded in outer loop
50        VPUSH  {d8-d15}                                // 64
51        PUSH   {r2, r4, r5, r6, r7, r8, r9, r10, r11}  // +36 = 100
52
53        LDR     r7, [sp, 100]        // a_stride
54        LDR    r11, [sp, 108]        // c
55        LDR     r6, [sp, 112]        // cm_stride
56        LDR     r9, [sp, 104]        // w
57
58        // Clamp A and C pointers
59        CMP    r0, 2                 // if mr >= 2
60        ADD    r12, r3, r7           //   a1 = a0 + a_stride
61        ADD    r4, r11, r6           //   c1 = c0 + cm_stride
62        MOVLO  r12, r3               // a1
63        MOVLO  r4, r11               // c1
64                                     // if mr > 2
65        ADD    r10, r12, r7          //   a2 = a1 + a_stride
66        ADD    r8, r4, r6            //   c2 = c1 + cm_stride
67        MOVLS  r10, r12              // a2
68        MOVLS  r8, r4                // c2
69
70        CMP    r0, 4                 // if mr >=4
71        ADD    r7, r10, r7           //   a3 = a2 + a_stride
72        ADD    r6, r8, r6            //   c3 = c2 + cm_stride
73        MOVLO  r7, r10               // a3
74        MOVLO  r6, r8                // c3
75
76        .p2align 3
770:
78        # Load initial bias from w into accumulators
79        VLDM        r9!, {d16-d19}   // Bias
80
81        SUBS        r5, r2, 16       // kc - 16
82        PLD         [r3,  0]    // Prefetch A
83        PLD         [r3, 64]
84        VMOV        q10, q8
85        PLD        [r12,  0]
86        PLD        [r12, 64]
87        VMOV        q11, q9
88        PLD        [r10,  0]
89        PLD        [r10, 64]
90        VMOV        q12, q8
91        PLD         [r7,  0]
92        PLD         [r7, 64]
93        VMOV        q13, q9
94        PLD         [r9,   0]  // Prefetch B
95        PLD         [r9,  64]
96        VMOV        q14, q8
97        PLD         [r9, 128]
98        PLD         [r9, 192]
99        VMOV        q15, q9
100        PLD         [r9, 256]
101        PLD         [r9, 320]
102        BLO         4f               // less than 4 channels?
103
104        // Prologue
105        VLD1.32   {d0},  [r3]!       // A0
106        VLD1.32   {d1}, [r12]!       // A1
107        VLD1.32   {d2}, [r10]!       // A2
108        VLD1.32   {d3},  [r7]!       // A3
109        SUBS        r5, r5, 16
110        VLDM        r9, {d8-d11}     // B0
111        LDR         r0, [r9, 56]     // B1 low   VMOV is in BLOCK 0
112        LDR         r2, [r9, 60]     // B1 high
113        VLDR       d13, [r9, 40]     // B1
114
115        BLO         2f               // less than 4 channels?  skip main loop
116
117        # Main loop - 4 floats of A (16 bytes)
118        # 32 FMA + 8 LD64 A + 8 LDR B
119        .p2align 3
1201:
121        # First group of 16 FMA, Second group loads
122        // BLOCK 0
123        VLD1.32    {d4}, [r3]!       // A0
124        VMOV        d15, r0, r2      // b1 VMOV b from second group
125        VMLA.F32     q8, q4, d0[0]
126        LDR          r0, [r12]       // A1 low
127        VMLA.F32    q10, q4, d1[0]
128        LDR          r2, [r12, 4]    // A1 high
129        VMLA.F32    q12, q4, d2[0]
130        PLD         [r3, 128]        // Prefetch A0
131
132        // BLOCK 1
133        VLDR        d12, [r9, 32]    // B1
134        VMOV         d5, r0, r2      // a1 VMOV
135        VMLA.F32    q14, q4, d3[0]
136        LDR          r0, [r9, 72]    // B0 low
137        VMLA.F32     q9, q5, d0[0]
138        LDR          r2, [r9, 76]    // B0 high
139        VMLA.F32    q11, q5, d1[0]
140        PLD         [r12, 128]       // Prefetch A1
141
142        // BLOCK 2
143        VLD1.32    {d6}, [r10]!      // A2
144        VMOV         d9, r0, r2      // b0 VMOV
145        VMLA.F32    q13, q5, d2[0]
146        LDR          r0, [r7]        // A3 low
147        VMLA.F32    q15, q5, d3[0]
148        LDR          r2, [r7, 4]     // A3 high
149        VMLA.F32     q8, q6, d0[1]
150        PLD         [r10, 128]       // Prefetch A2
151
152        // BLOCK 3
153        VLDR        d14, [r9, 48]    // B1
154        VMOV         d7, r0, r2      // a3 VMOV
155        VMLA.F32    q10, q6, d1[1]
156        LDR          r0, [r9, 88]    // B0 low
157        VMLA.F32    q12, q6, d2[1]
158        LDR          r2, [r9, 92]    // B0 high
159        VMLA.F32    q14, q6, d3[1]
160        PLD         [r7, 128]        // Prefetch A3
161
162        // BLOCK 4
163        VLDR         d8, [r9, 64]    // B0
164        VMOV        d11, r0, r2      // B0 VMOV
165        VMLA.F32     q9, q7, d0[1]
166        LDR          r0, [r9, 104]   // B1 low   VMOV is in BLOCK 0
167        VMLA.F32    q11, q7, d1[1]
168        LDR          r2, [r9, 108]   // B1 high
169        VMLA.F32    q13, q7, d2[1]
170        PLD         [r9, 384]        // Prefetch B
171
172        // BLOCK 5
173        VLDR        d10, [r9, 80]    // B0
174        VMOV        d13, r0, r2      // b1 VMOV b from second group
175        VMLA.F32    q15, q7, d3[1]
176        LDR          r0, [r9, 120]   // B1 low   VMOV is in BLOCK 0
177        NOP
178        LDR          r2, [r9, 124]   // B1 high
179        NOP
180        PLD         [r9, 448]        // Prefetch B
181
182        # Second group of 16 FMA, First group of loads
183        // BLOCK 0
184        VLD1.32    {d0}, [r3]!       // A0
185        VMOV        d15, r0, r2      // b1 VMOV b from second group
186        VMLA.F32     q8, q4, d4[0]
187        LDR          r0, [r12, 8]    // A1 low
188        VMLA.F32    q10, q4, d5[0]
189        LDR          r2, [r12, 12]   // A1 high
190        VMLA.F32    q12, q4, d6[0]
191        // NOP
192
193        // BLOCK 1
194        VLDR        d12, [r9, 96]    // B1
195        VMOV         d1, r0, r2      // a1 VMOV
196        VMLA.F32    q14, q4, d7[0]
197        LDR          r0, [r9, 136]   // B0 low
198        VMLA.F32     q9, q5, d4[0]
199        LDR          r2, [r9, 140]   // B0 high
200        VMLA.F32    q11, q5, d5[0]
201        // NOP
202
203        // BLOCK 2
204        VLD1.32    {d2}, [r10]!      // A2
205        VMOV         d9, r0, r2      // b0 VMOV
206        VMLA.F32    q13, q5, d6[0]
207        LDR          r0, [r7, 8]     // A3 low
208        VMLA.F32    q15, q5, d7[0]
209        LDR          r2, [r7, 12]    // A3 high
210        VMLA.F32     q8, q6, d4[1]
211        // NOP
212
213        // BLOCK 3
214        VLDR        d14, [r9, 112]   // B1
215        VMOV         d3, r0, r2      // a3 VMOV
216        VMLA.F32    q10, q6, d5[1]
217        LDR          r0, [r9, 152]   // B0 low
218        VMLA.F32    q12, q6, d6[1]
219        LDR          r2, [r9, 156]   // B0 high
220        VMLA.F32    q14, q6, d7[1]
221        ADD         r12, r12, 16     // A1++
222
223        // BLOCK 4
224        VLDR         d8, [r9, 128]   // B0
225        VMOV        d11, r0, r2      // B0 VMOV
226        VMLA.F32     q9, q7, d4[1]
227        LDR          r0, [r9, 168]   // B1 low
228        VMLA.F32    q11, q7, d5[1]
229        LDR          r2, [r9, 172]   // B1 high
230        VMLA.F32    q13, q7, d6[1]
231        ADD          r7, r7, 16      // A3++
232
233        // BLOCK 5
234        VLDR        d10, [r9, 144]   // B0
235        VMOV        d13, r0, r2      // b1 VMOV b
236        VMLA.F32    q15, q7, d7[1]
237        LDR          r0, [r9, 184]   // B1 low   VMOV is in BLOCK 0
238        SUBS        r5, r5, 16
239        LDR          r2, [r9, 188]   // B1 high
240        ADD         r9, r9, 128      // B++
241        BHS         1b
242
243        # Epilogue - 4 floats of A (16 bytes)
2442:
245        # First group of 16 FMA, Second group loads
246        // BLOCK 0
247        VLD1.32    {d4}, [r3]!       // A0
248        VMOV        d15, r0, r2      // b1 VMOV b from second group
249        VMLA.F32     q8, q4, d0[0]
250        LDR          r0, [r12]       // A1 low
251        VMLA.F32    q10, q4, d1[0]
252        LDR          r2, [r12, 4]    // A1 high
253        VMLA.F32    q12, q4, d2[0]
254        // NOP
255
256        // BLOCK 1
257        VLDR        d12, [r9, 32]    // B1
258        VMOV         d5, r0, r2      // a1 VMOV
259        VMLA.F32    q14, q4, d3[0]
260        LDR          r0, [r9, 72]    // B0 low
261        VMLA.F32     q9, q5, d0[0]
262        LDR          r2, [r9, 76]    // B0 high
263        VMLA.F32    q11, q5, d1[0]
264        // NOP
265
266        // BLOCK 2
267        VLD1.32    {d6}, [r10]!      // A2
268        VMOV         d9, r0, r2      // b0 VMOV
269        VMLA.F32    q13, q5, d2[0]
270        LDR          r0, [r7]        // A3 low
271        VMLA.F32    q15, q5, d3[0]
272        LDR          r2, [r7, 4]     // A3 high
273        VMLA.F32     q8, q6, d0[1]
274        // NOP
275
276        // BLOCK 3
277        VLDR        d14, [r9, 48]    // B1
278        VMOV         d7, r0, r2      // a3 VMOV
279        VMLA.F32    q10, q6, d1[1]
280        LDR          r0, [r9, 88]    // B0 low
281        VMLA.F32    q12, q6, d2[1]
282        LDR          r2, [r9, 92]    // B0 high
283        VMLA.F32    q14, q6, d3[1]
284        // NOP
285
286        // BLOCK 4
287        VLDR         d8, [r9, 64]    // B0
288        VMOV        d11, r0, r2      // B0 VMOV
289        VMLA.F32     q9, q7, d0[1]
290        LDR          r0, [r9, 104]   // B1 low
291        VMLA.F32    q11, q7, d1[1]
292        LDR          r2, [r9, 108]   // B1 high
293        VMLA.F32    q13, q7, d2[1]
294        // NOP
295
296        // BLOCK 5
297        VLDR        d10, [r9, 80]    // B0
298        VMOV        d13, r0, r2      // b1 VMOV b
299        VMLA.F32    q15, q7, d3[1]
300        LDR          r0, [r9, 120]   // B1 low   VMOV is in BLOCK 0
301        NOP
302        LDR          r2, [r9, 124]   // B1 high
303        NOP
304        NOP
305
306        # Second group of 16 FMA, First group of loads
307        // BLOCK 0
308        VLDR        d12, [r9, 96]    // B1
309        VMOV        d15, r0, r2      // b1 VMOV b from second group
310        VMLA.F32     q8, q4, d4[0]
311        VMLA.F32    q10, q4, d5[0]
312        VMLA.F32    q12, q4, d6[0]
313
314        // BLOCK 1
315        VLDR        d14, [r9, 112]   // B1
316        VMLA.F32    q14, q4, d7[0]
317        VMLA.F32     q9, q5, d4[0]
318        VMLA.F32    q11, q5, d5[0]
319        ADD         r12, r12, 8      // A1++
320
321        // BLOCK 2
322        ADD          r7, r7, 8       // A3++ VLDR B1 lands here
323        ADD          r9, r9, 128     // B++
324        VMLA.F32    q13, q5, d6[0]
325        VMLA.F32    q15, q5, d7[0]
326        VMLA.F32     q8, q6, d4[1]
327
328        // BLOCK 3
329        VMLA.F32    q10, q6, d5[1]
330        VMLA.F32    q12, q6, d6[1]
331        VMLA.F32    q14, q6, d7[1]
332        TST          r5, 15
333
334        // BLOCK 4
335        VMLA.F32     q9, q7, d4[1]
336        VMLA.F32    q11, q7, d5[1]
337        VMLA.F32    q13, q7, d6[1]
338
339        // BLOCK 5
340        VMLA.F32    q15, q7, d7[1]
341
342        // Is there a remainder?- 1 to 3 floats of A (4, 8 or 12 bytes)
343        BNE          4f
344
345        .p2align 3
3463:
347        // Load params pointer
348        LDR          r0, [sp, 116]   // cn_stride
349        LDR          r5, [sp, 120]   // params
350        LDR          r2, [sp, 0]     // kc
351        SUBS         r1, r1, 8
352
353        // Load min/max values
354        VLD1.32     {d4[],d5[]}, [r5]!
355        VLD1.32     {d6[],d7[]}, [r5]
356
357        // Clamp
358        VMAX.F32     q8,  q8, q2
359        VMAX.F32     q9,  q9, q2
360        VMAX.F32    q10, q10, q2
361        VMAX.F32    q11, q11, q2
362        VMAX.F32    q12, q12, q2
363        VMAX.F32    q13, q13, q2
364        VMAX.F32    q14, q14, q2
365        VMAX.F32    q15, q15, q2
366        VMIN.F32     q8,  q8, q3
367        VMIN.F32     q9,  q9, q3
368        VMIN.F32    q10, q10, q3
369        VMIN.F32    q11, q11, q3
370        VMIN.F32    q12, q12, q3
371        VMIN.F32    q13, q13, q3
372        VMIN.F32    q14, q14, q3
373        VMIN.F32    q15, q15, q3
374
375        // Store full 4 x 8
376        BLO         6f
377        VST1.32     {d16-d19}, [r11], r0
378        SUB         r7, r7, r2
379        VST1.32     {d20-d23}, [r4], r0
380        SUB         r10, r10, r2
381        VST1.32     {d24-d27}, [r8], r0
382        SUB         r12, r12, r2
383        VST1.32     {d28-d31}, [r6], r0
384        SUB         r3, r3, r2
385        BHI         0b
386
387        ADD         sp, sp, 4
388        POP         {r4, r5, r6, r7, r8, r9, r10, r11}
389        VPOP        {d8-d15}
390        BX          lr
391
392        .p2align 3
3934:
394        // Is there a remainder?- 2 floats of A (8 bytes)
395        TST         r5, 8
396        BEQ         5f
397
398        // Remainder - 2 floats of A (8 bytes)
399        VLD1.32    {d0}, [r3]!       // A0
400        VLDM        r9!, {d8-d11}    // B0
401        VLD1.32    {d1}, [r12]!      // A1
402        VLD1.32    {d2}, [r10]!      // A2
403        VLD1.32    {d3}, [ r7]!      // A3
404
405        VMLA.F32     q8, q4, d0[0]
406        VMLA.F32     q9, q5, d0[0]
407        VMLA.F32    q10, q4, d1[0]
408        VMLA.F32    q11, q5, d1[0]
409        VLDM        r9!, {d12-d15}   // B1
410        VMLA.F32    q12, q4, d2[0]
411        VMLA.F32    q13, q5, d2[0]
412        VMLA.F32    q14, q4, d3[0]
413        VMLA.F32    q15, q5, d3[0]
414        VMLA.F32     q8, q6, d0[1]
415        VMLA.F32     q9, q7, d0[1]
416        VMLA.F32    q10, q6, d1[1]
417        VMLA.F32    q11, q7, d1[1]
418        VMLA.F32    q12, q6, d2[1]
419        VMLA.F32    q13, q7, d2[1]
420        VMLA.F32    q14, q6, d3[1]
421        VMLA.F32    q15, q7, d3[1]
422
423        // Is there a remainder?- 1 floats of A (4 bytes)
424        TST         r5, 4
425        BEQ         3b
426
4275:
428        // Remainder- 1 floats of A (4 bytes)
429        VLDM        r3!,  {s0}       // A0
430        VLDM        r9!, {d8-d11}    // B0
431        VLDM        r12!, {s2}       // A1
432        VLDM        r10!, {s4}       // A2
433        VLDM         r7!, {s6}       // A3
434        VMLA.F32     q8, q4, d0[0]
435        VMLA.F32     q9, q5, d0[0]
436        VMLA.F32    q10, q4, d1[0]
437        VMLA.F32    q11, q5, d1[0]
438        VMLA.F32    q12, q4, d2[0]
439        VMLA.F32    q13, q5, d2[0]
440        VMLA.F32    q14, q4, d3[0]
441        VMLA.F32    q15, q5, d3[0]
442        B           3b
443
444        // Store odd width
4456:
446        TST         r1, 4
447        BEQ         7f
448        VST1.32    {d16-d17}, [r11]!
449        VMOV         q8,  q9
450        VST1.32    {d20-d21},  [r4]!
451        VMOV        q10, q11
452        VST1.32    {d24-d25},  [r8]!
453        VMOV        q12, q13
454        VST1.32    {d28-d29},  [r6]!
455        VMOV        q14, q15
456
4577:
458        TST        r1, 2
459        BEQ        8f
460        VST1.32    {d16}, [r11]!
461        VMOV        d16, d17
462        VST1.32    {d20},  [r4]!
463        VMOV        d20, d21
464        VST1.32    {d24},  [r8]!
465        VMOV        d24, d25
466        VST1.32    {d28},  [r6]!
467        VMOV        d28, d29
468
4698:
470        TST         r1, 1
471        BEQ         9f
472        VST1.32    {d16[0]}, [r11]
473        VST1.32    {d20[0]},  [r4]
474        VST1.32    {d24[0]},  [r8]
475        VST1.32    {d28[0]},  [r6]
476
4779:
478        ADD         sp, sp, 4
479        POP         {r4, r5, r6, r7, r8, r9, r10, r11}
480        VPOP        {d8-d15}
481        BX          lr
482
483END_FUNCTION xnn_f32_gemm_minmax_ukernel_4x8__aarch32_neon_cortex_a53
484
485#ifdef __ELF__
486.section ".note.GNU-stack","",%progbits
487#endif
488