xref: /aosp_15_r20/external/XNNPACK/src/qc8-dwconv/up8x3-minmax-fp32-aarch32-neonv8-mla8-cortex-a35.S (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
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_up8x3__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, 40]
16//   size_t input_stride,                      r9,  [sp, 44]
17//   size_t output_increment,                  r12, [sp, 48]
18//   size_t input_offset,                      r7,  [sp, 52]
19//   const int8_t* zero,                       r4,  [sp, 56]
20//   const union xnn_qs8_minmax_params params  r5,  [sp, 60]
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  d4
26// A1   r6  d5
27// A2   r8  d6
28
29// B    r3/lr  d7 d16 d17
30
31// C0  r10 q12 q13 q14 q15
32
33// Prod q0 q1
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] d18 q9
39//     int8_t output_max;          d20[3] d19
40//   } xnn_qs8_minmax_params.neonv8;
41
42// unused q4 q5 q6 q7 q11
43
44BEGIN_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up8x3__aarch32_neonv8_mla8_cortex_a35
45        // 40 bytes of stack.  36 + 4 pad
46        PUSH    {r4, r5, r6, r7, r8, r9, r10, r11, lr}  // 40
47        SUB     sp, sp, 4
48        LDR     r5,  [sp, 60]            // params
49        LDR     r10, [sp, 40]            // output
50        LDR     r9,  [sp, 44]            // input_stride
51        LDR     r12, [sp, 48]            // output_increment
52        LDR     r7,  [sp, 52]            // input_offset
53        LDR     r4,  [sp, 56]            // zero
54
55        VLD1.32 {d20[]}, [r5]            // QC8 params
56        VDUP.8  d18, d20[2]              // output_min
57        VDUP.8  d19, d20[3]              // output_max
58        VDUP.16 q10, d20[0]              // output_zero_point
59
60        .p2align 3
610:
62        LDMIB   r2, {r5, r6}             // i0, i1
63        LDR     r8, [r2]                 // i2
64        CMP     r5, r4                   // i0 == zero?
65        ADDNE   r5, r5, r7               // i0 += input_offset
66        CMP     r6, r4                   // i1 == zero?
67        ADDNE   r6, r6, r7               // i1 += input_offset
68        CMP     r8, r4                   // i2 == zero?
69        ADDNE   r8, r8, r7               // i2 += input_offset
70
71        MOV     lr, r3
72        MOV     r11, r0                 // channel count as is, fall into loop
73
74// Main loop - 8 channels
75// lr weights.  r3 reset
76// r0/r11  loop counter.
77// r5 i0
78// r6 i1
79// r8 i2
80// q12 q13 q14 q15   accumulators
81// Weights are:
82//   32 bias - 8 int
83//   24 weights - 3 * 8 byte
84//   32 quant scale - 8 int
85//   88 bytes total
86
87        .p2align 3
881:
89        VLD1.8  {q12, q13}, [lr]!       // load bias
90
91        VLD1.8  {d4}, [r8]!             // i2
92        VLD1.8  {d7}, [lr]!             // w0
93        VLD1.8  {d5}, [r5]!             // i0
94        VLD1.8  {d16}, [lr]!            // w1
95        VLD1.8  {d6}, [r6]!             // i1
96        VLD1.8  {d17}, [lr]!            // w2
97
98        VMULL.S8 q1, d4, d7             // i2 * w0
99        VMLAL.S8 q1, d5, d16            // i0 * w1
100        VMULL.S8 q0, d6, d17            // i1 * w2
101
102
103        VADDW.S16 q12, q12, d0
104        VADDW.S16 q13, q13, d1
105        VADDW.S16 q12, q12, d2
106        VADDW.S16 q13, q13, d3
107
108        VLD1.32 {q0, q1}, [lr]!         // quant per channel scale values
109
110        // QC8 FP32 quantization
111
112        VCVT.F32.S32 q12, q12
113        VCVT.F32.S32 q13, q13
114
115        VMUL.F32 q12, q0, q12
116        VMUL.F32 q13, q1, q13
117
118        VCVTN.S32.F32 q12, q12
119        VCVTN.S32.F32 q13, q13
120
121        VQMOVN.S32 d24, q12
122        VQMOVN.S32 d25, q13
123        SUBS    r11, r11, 8             // 8 channels per loop
124
125        VQADD.S16 q12, q12, q10
126        VQMOVN.S16 d24, q12
127        VMIN.S8 d24, d24, d19
128        VMAX.S8 d24, d24, d18
129
130        BLO     3f                      // less than 8?
131
132        VST1.8  {d24}, [r10]!
133        BHI     1b                      // at least 1, continue loop
134
1352:
136        SUBS    r1, r1, 1               // output_width
137        ADD     r10, r10, r12           // output += output_increment
138        ADD     r2, r2, r9              // input += input_stride
139        BNE     0b
140
141        ADD     sp, sp, 4
142        POP     {r4, r5, r6, r7, r8, r9, r10, r11, pc}
143
144        .p2align 3
145        // Store 4
1463:
147        TST     r11, 4
148        BEQ     4f
149        VST1.32 {d24[0]}, [r10]!
150        VEXT.8  d24, d24, d24, 4
151
152        // Store 2
1534:
154        TST     r11, 2
155        BEQ     5f
156        VST1.16 {d24[0]}, [r10]!
157        VEXT.8  d24, d24, d24, 2
158
159        // Store 1
1605:
161        TST     r11, 1
162        BEQ     2b
163        VST1.8  {d24[0]}, [r10]!
164        B       2b
165
166
167END_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up8x3__aarch32_neonv8_mla8_cortex_a35
168#ifdef __ELF__
169.section ".note.GNU-stack","",%progbits
170#endif
171