1// Copyright 2022 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_qc8_dwconv_minmax_fp32_ukernel_up16x3__aarch32_neonv8_mla8_cortex_a35(
11//   size_t channels,                          r0, r11
12//   size_t output_width,                      r1
13//   const int8_t** input,                     r2
14//   const void* weights,                      r3
15//   int8_t* output,                           r10, [sp, 88]
16//   size_t input_stride,                      r6,  [sp, 92]
17//   size_t output_increment,                  r12, [sp, 96]
18//   size_t input_offset,                     (r11),[sp, 100]
19//   const int8_t* zero,                       r4,  [sp, 104]
20//   const union xnn_qs8_minmax_params params  r5,  [sp, 108]
21
22// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved.
23
24// Register usage
25// A0   r5  q4
26// A1   r6  q5
27// A2   r8  q6
28
29// B    r7/r3/lr  q12 q13 q14
30
31// C0  r10 q12 q13 q14 q15
32
33// Prod q0 q1 q2 q3
34
35// params structure is 4 bytes
36//   struct {
37//     int16_t output_zero_point;  d20[0] q10
38//     int8_t output_min;          d20[2] q9
39//     int8_t output_max;          d20[3] q11
40//   } xnn_qs8_minmax_params.neonv8;
41
42// r7 temp B
43// r9 B post increment 80 or 16
44
45// unused q7
46
47BEGIN_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up16x3__aarch32_neonv8_mla8_cortex_a35
48        // 88 bytes of stack
49        PUSH    {r4, r5, r6, r7, r8, r9, r10, r11, lr}  // 40
50        SUB     sp, sp, 4
51        VPUSH   {d8, d9, d10, d11, d12, d13}            // 48
52
53        LDR     r5, [sp, 108]           // params
54        LDR     r10, [sp, 88]           // output
55        LDR     r12, [sp, 96]           // output_increment
56        LDR     r4, [sp, 104]           // zero
57
58        VLD1.32 {d20[]}, [r5]            // QC8 params
59        VDUP.8  q9 , d20[2]              // output_min
60        VDUP.8  q11, d20[3]              // output_max
61        VDUP.16 q10, d20[0]              // output_zero_point
62
63        .p2align 3
640:
65        LDR     r11, [sp, 100]           // input_offset
66        LDMIB   r2, {r5, r6}             // i0, i1
67        LDR     r8, [r2]                 // i2
68        CMP     r5, r4                   // i0 == zero?
69        ADDNE   r5, r5, r11              // i0 += input_offset
70        CMP     r6, r4                   // i1 == zero?
71        ADDNE   r6, r6, r11              // i1 += input_offset
72        CMP     r8, r4                   // i2 == zero?
73        ADDNE   r8, r8, r11              // i2 += input_offset
74
75        MOV     lr, r3
76        MOV     r9, 80
77
78        // Is there at least 16 channels for main loop?
79        SUBS    r11, r0, 16
80        BLO     2f
81
82// Main loop - 16 channels
83// lr weights.  r3 reset
84// r0/r11  loop counter.
85// r5 i0
86// r6 i1
87// r8 i2
88// q12 q13 q14 q15   accumulators
89
90        .p2align 3
911:
92        ADD     r7, lr, 64              // skip over bias to get weights
93        VLD1.8  {q4}, [r8]!             // i2
94        VLD1.8  {q12}, [r7]!            // w0
95        VLD1.8  {q5}, [r5]!             // i0
96        VLD1.8  {q13}, [r7]!            // w1
97        VLD1.8  {q6}, [r6]!             // i1
98        VLD1.8  {q14}, [r7]             // w2
99
100        VMULL.S8 q1, d8,  d24           // i2 * w0
101        VMULL.S8 q2, d9,  d25
102        VMLAL.S8 q1, d10, d26           // i0 * w1
103        VMLAL.S8 q2, d11, d27
104        VMULL.S8 q0, d12, d28           // i1 * w2
105        VLD1.8  {q12, q13}, [lr]!       // load bias
106        VMULL.S8 q3, d13, d29
107        VLD1.8  {q14, q15}, [lr], r9
108
109        VADDW.S16 q12, q12, d0
110        VADDW.S16 q13, q13, d1
111        VADDW.S16 q14, q14, d4
112        VADDW.S16 q15, q15, d5
113        VADDW.S16 q12, q12, d2
114        VADDW.S16 q13, q13, d3
115        VADDW.S16 q14, q14, d6
116        VLD1.32 {q0, q1}, [lr]!         // quant per channel scale values
117        VADDW.S16 q15, q15, d7
118        VLD1.32 {q2, q3}, [lr]!
119
120        // QC8 FP32 quantization
121
122        VCVT.F32.S32 q12, q12
123        VCVT.F32.S32 q13, q13
124        VCVT.F32.S32 q14, q14
125        VCVT.F32.S32 q15, q15
126
127        VMUL.F32 q12, q0, q12
128        VMUL.F32 q13, q1, q13
129        VMUL.F32 q14, q2, q14
130        VMUL.F32 q15, q3, q15
131
132        VCVTN.S32.F32 q12, q12
133        VCVTN.S32.F32 q13, q13
134        VCVTN.S32.F32 q14, q14
135        VCVTN.S32.F32 q15, q15
136
137        VQMOVN.S32 d24, q12
138        VQMOVN.S32 d25, q13
139        VQMOVN.S32 d28, q14
140        VQMOVN.S32 d29, q15
141
142        VQADD.S16 q12, q12, q10
143        VQADD.S16 q14, q14, q10
144        VQMOVN.S16 d24, q12
145        VQMOVN.S16 d25, q14
146        VMIN.S8 q12, q12, q11
147        VMAX.S8 q12, q12, q9
148        SUBS    r11, r11, 16
149        VST1.8  {q12}, [r10]!
150        BHS     1b
151
1522:
153        // Is there a remainder channels?  1-15
154        ANDS    r11, r11, 15
155        BNE     4f
156
1573:
158        LDR     r6, [sp, 92]            // input_stride
159        SUBS    r1, r1, 1               // output_width
160        ADD     r10, r10, r12           // output += output_increment
161        ADD     r2, r2, r6              // input += input_stride
162        BNE     0b
163
164        VPOP    {d8, d9, d10, d11, d12, d13}
165        ADD     sp, sp, 4               // pad
166        POP     {r4, r5, r6, r7, r8, r9, r10, r11, pc}
167
168// Small Remainder - 1-8 channels
1694:
170        CMP     r11, 9                  // handle 9 or more
171        ADD     r7, lr, 64              // skip over bias to get weights
172        BHS     5f
173
174        MOV     r9, 16
175
176        VLD1.8  {d8}, [r8]              // i2
177        VLD1.8  {d24}, [r7], r9         // w0
178        VLD1.8  {d10}, [r5]             // i0
179        VLD1.8  {d26}, [r7], r9         // w1
180        VLD1.8  {d12}, [r6]             // i1
181        VLD1.8  {d28}, [r7]             // w2
182
183        VMULL.S8 q1, d8,  d24           // i2 * w0
184        VMLAL.S8 q1, d10, d26           // i0 * w1
185        VMULL.S8 q0, d12, d28           // i1 * w2
186        VLD1.8  {q12, q13}, [lr]        // load bias
187        ADD     lr, lr, 112
188
189        VADDW.S16 q12, q12, d0
190        VADDW.S16 q13, q13, d1
191        VADDW.S16 q12, q12, d2
192        VADDW.S16 q13, q13, d3
193        VLD1.32 {q0, q1}, [lr]          // quant per channel scale values
194
195        // QC8 FP32 quantization
196
197        VCVT.F32.S32 q12, q12
198        VCVT.F32.S32 q13, q13
199
200        VMUL.F32 q12, q0, q12
201        VMUL.F32 q13, q1, q13
202
203        VCVTN.S32.F32 q12, q12
204        VCVTN.S32.F32 q13, q13
205
206        VQMOVN.S32 d24, q12
207        VQMOVN.S32 d25, q13
208
209        VQADD.S16 q12, q12, q10
210        VQMOVN.S16 d24, q12
211        VMIN.S8 d24, d24, d22
212        VMAX.S8 d24, d24, d18
213
214        //  Store 8
215        TST     r11, 8
216        BEQ     6f
217        VST1.8  {d24}, [r10]!
218        B       3b
219
220        .p2align 3
221// Large Remainder - 9-15 channels
222// Process 16 same as main loop, but conditional store
223
2245:
225        VLD1.8  {q4}, [r8]!             // i2
226        VLD1.8  {q12}, [r7]!            // w0
227        VLD1.8  {q5}, [r5]!             // i0
228        VLD1.8  {q13}, [r7]!            // w1
229        VLD1.8  {q6}, [r6]!             // i1
230        VLD1.8  {q14}, [r7]             // w2
231
232        VMULL.S8 q1, d8,  d24           // i2 * w0
233        VMULL.S8 q2, d9,  d25
234        VMLAL.S8 q1, d10, d26           // i0 * w1
235        VMLAL.S8 q2, d11, d27
236        VMULL.S8 q0, d12, d28           // i1 * w2
237        VLD1.8  {q12, q13}, [lr]!       // load bias
238        VMULL.S8 q3, d13, d29
239        VLD1.8  {q14, q15}, [lr], r9
240
241        VADDW.S16 q12, q12, d0
242        VADDW.S16 q13, q13, d1
243        VADDW.S16 q14, q14, d4
244        VADDW.S16 q15, q15, d5
245        VADDW.S16 q12, q12, d2
246        VADDW.S16 q13, q13, d3
247        VADDW.S16 q14, q14, d6
248        VLD1.32 {q0, q1}, [lr]!         // quant per channel scale values
249        VADDW.S16 q15, q15, d7
250        VLD1.32 {q2, q3}, [lr]
251
252        // QC8 FP32 quantization
253
254        VCVT.F32.S32 q12, q12
255        VCVT.F32.S32 q13, q13
256        VCVT.F32.S32 q14, q14
257        VCVT.F32.S32 q15, q15
258
259        VMUL.F32 q12, q0, q12
260        VMUL.F32 q13, q1, q13
261        VMUL.F32 q14, q2, q14
262        VMUL.F32 q15, q3, q15
263
264        VCVTN.S32.F32 q12, q12
265        VCVTN.S32.F32 q13, q13
266        VCVTN.S32.F32 q14, q14
267        VCVTN.S32.F32 q15, q15
268
269        VQMOVN.S32 d24, q12
270        VQMOVN.S32 d25, q13
271        VQMOVN.S32 d28, q14
272        VQMOVN.S32 d29, q15
273
274        VQADD.S16 q12, q12, q10
275        VQADD.S16 q14, q14, q10
276        VQMOVN.S16 d24, q12
277        VQMOVN.S16 d25, q14
278        VMIN.S8 q12, q12, q11
279        VMAX.S8 q12, q12, q9
280
281        // Store 8
282        VST1.8  {d24}, [r10]!
283        VMOV    d24, d25
284
285        // Store 4
2866:
287        TST     r11, 4
288        BEQ     7f
289        VST1.32 {d24[0]}, [r10]!
290        VEXT.8  d24, d24, d24, 4
291
292        // Store 2
2937:
294        TST     r11, 2
295        BEQ     8f
296        VST1.16 {d24[0]}, [r10]!
297        VEXT.8  d24, d24, d24, 2
298
299        // Store 1
3008:
301        TST     r11, 1
302        BEQ     3b
303        VST1.8  {d24[0]}, [r10]!
304        B       3b
305
306
307END_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up16x3__aarch32_neonv8_mla8_cortex_a35
308#ifdef __ELF__
309.section ".note.GNU-stack","",%progbits
310#endif
311