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"] 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# LINT.IfChange 17// void xnn_${DATATYPE.lower()}_gemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8c4__aarch32_neondot_ld64( 18// size_t mr, r0 19// size_t nc, r1 20// size_t kc, r2 -> r5 21// const uint8_t*restrict a, r3 22// size_t a_stride, sp + 80 -> (r7) 23// const void*restrict w, sp + 84 -> r9 24// uint8_t*restrict c, sp + 88 -> r11 25// size_t cm_stride, sp + 92 -> (r6) 26// size_t cn_stride, sp + 96 -> r7 27// ${PARAMS_UNION} params) sp + 100 -> (r5) 28 29// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. 30 31// Register usage 32// A0 r3 d0 33// A1 r12 d1 34// A2 r10 d2 35// A3 r0 d3 36 37// B r9 q2 q3 q4 q5 38 39// C0 r11 d16-d17 q8 d18-d19 q9 40// C1 r4 d20-d21 q10 d22-d23 q11 41// C2 r8 d24-d25 q12 d26-d27 q13 42// C3 r6 d28-d29 q14 d30-d31 q15 43 44// unused q7 45 46$if REQUANTIZATION == "RNDNU": 47 // params structure is 16 bytes 48 // struct { 49 // int32_t right_pre_shift; d12[0] 50 // int32_t multiplier; d12[1] 51 // int32_t right_post_shift; d13[0] 52 // int16_t output_zero_point; d13[2] 53 // int8_t output_min; d13[6] 54 // int8_t output_max; d13[7] 55 // } rndnu_neon; 56$else: 57 // params structure is 4 bytes 58 // struct { 59 // int16_t output_zero_point; d13[2] 60 // int8_t output_min; d13[6] 61 // int8_t output_max; d13[7] 62 // } xnn_qs8_minmax_params.neonv8; 63 64// iOS does not support 32 bit ARM with Neon DotProduct. 65#ifndef __APPLE__ 66BEGIN_FUNCTION xnn_${DATATYPE.lower()}_gemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8c4__aarch32_neondot_ld64 67 # Push 80 bytes 68 PUSH {r4, r5, r6, r7, r8, r9, r10, r11} // 32 69 VPUSH {d8-d13} // +48 = 80 70 71 LDR r7, [sp, 80] // a_stride 72 ADD r2, r2, 3 // kc = (kc + 3) & ~3 73 LDR r11, [sp, 88] // c 74 LDR r6, [sp, 92] // cm_stride 75 LDR r9, [sp, 84] // w 76 BIC r2, r2, 3 77 LDR r5, [sp, 100] // params 78 79 # Clamp A and C pointers 80 CMP r0, 2 // if mr >= 2 81 ADD r12, r3, r7 // a1 = a0 + a_stride 82 ADD r4, r11, r6 // c1 = c0 + cm_stride 83 MOVLO r12, r3 // a1 84 MOVLO r4, r11 // c1 85 // if mr > 2 86 ADD r10, r12, r7 // a2 = a1 + a_stride 87 ADD r8, r4, r6 // c2 = c1 + cm_stride 88 MOVLS r10, r12 // a2 89 MOVLS r8, r4 // c2 90 91 CMP r0, 4 // if mr >=4 92 ADD r0, r10, r7 // a3 = a2 + a_stride 93 ADD r6, r8, r6 // c3 = c2 + cm_stride 94 MOVLO r0, r10 // a3 95 MOVLO r6, r8 // c3 96 97 # Load params values 98 $if REQUANTIZATION == "RNDNU": 99 VLDM r5, {d12-d13} // RNDNU params 100 $else: 101 VLD1.32 {d13[]}, [r5] // QC8 params 102 LDR r7, [sp, 96] // cn_stride 103 104 .p2align 3 1050: 106 # Load initial bias from w into accumulators 107 VLDM r9!, {d16-d19} // Bias 108 SUBS r5, r2, 8 // k = kc - 8 109 VMOV q10, q8 110 VMOV q11, q9 111 VMOV q12, q8 112 VMOV q13, q9 113 VMOV q14, q8 114 VMOV q15, q9 115 BLO 3f // less than 8 channels? 116 117 # Main loop - 8 bytes of A. 118 # 16 SDOT, 4 LD64 A, 4 LD128 B 119 .p2align 3 1201: 121 VLD1.8 {d0}, [r3]! // A0 122 VLD1.8 {q2}, [r9]! // B0 123 VLD1.8 {d1}, [r12]! // A1 124 VLD1.8 {q3}, [r9]! // B1 125 VLD1.8 {d2}, [r10]! // A2 126 VLD1.8 {q4}, [r9]! // B2 127 VLD1.8 {d3}, [r0]! // A3 128 VLD1.8 {q5}, [r9]! // B3 129 SUBS r5, r5, 8 130 131 VSDOT.S8 q8, q2, d0[0] 132 VSDOT.S8 q9, q3, d0[0] 133 VSDOT.S8 q10, q2, d1[0] 134 VSDOT.S8 q11, q3, d1[0] 135 VSDOT.S8 q12, q2, d2[0] 136 VSDOT.S8 q13, q3, d2[0] 137 VSDOT.S8 q14, q2, d3[0] 138 VSDOT.S8 q15, q3, d3[0] 139 140 VSDOT.S8 q8, q4, d0[1] 141 VSDOT.S8 q9, q5, d0[1] 142 VSDOT.S8 q10, q4, d1[1] 143 VSDOT.S8 q11, q5, d1[1] 144 VSDOT.S8 q12, q4, d2[1] 145 VSDOT.S8 q13, q5, d2[1] 146 VSDOT.S8 q14, q4, d3[1] 147 VSDOT.S8 q15, q5, d3[1] 148 BHS 1b 149 150 # Is there a remainder?- 4 bytes of A 151 ADDS r5, r5, 8 152 BNE 3f 153 1542: 155 $if REQUANTIZATION == "RNDNU": 156 # RNDNU quantization 157 VDUP.32 q0, d12[0] // right_pre_shift 158 159 VQSHL.S32 q8, q8, q0 160 VQSHL.S32 q9, q9, q0 161 VQSHL.S32 q10, q10, q0 162 VQSHL.S32 q11, q11, q0 163 VQSHL.S32 q12, q12, q0 164 VQSHL.S32 q13, q13, q0 165 VQSHL.S32 q14, q14, q0 166 VQSHL.S32 q15, q15, q0 167 168 VDUP.32 q2, d13[0] // right_post_shift 169 170 VQDMULH.S32 q8, q8, d12[1] // multiplier 171 VQDMULH.S32 q9, q9, d12[1] 172 VQDMULH.S32 q10, q10, d12[1] 173 VQDMULH.S32 q11, q11, d12[1] 174 VQDMULH.S32 q12, q12, d12[1] 175 VQDMULH.S32 q13, q13, d12[1] 176 VQDMULH.S32 q14, q14, d12[1] 177 VQDMULH.S32 q15, q15, d12[1] 178 179 VRSHL.S32 q8, q8, q2 180 VRSHL.S32 q9, q9, q2 181 VRSHL.S32 q10, q10, q2 182 VRSHL.S32 q11, q11, q2 183 VRSHL.S32 q12, q12, q2 184 VRSHL.S32 q13, q13, q2 185 VRSHL.S32 q14, q14, q2 186 VRSHL.S32 q15, q15, q2 187 $else: 188 # QC8 FP32 quantization 189 VLD1.8 {q0-q1}, [r9]! 190 191 VCVT.F32.S32 q8, q8 192 VCVT.F32.S32 q9, q9 193 VCVT.F32.S32 q10, q10 194 VCVT.F32.S32 q11, q11 195 VCVT.F32.S32 q12, q12 196 VCVT.F32.S32 q13, q13 197 VCVT.F32.S32 q14, q14 198 VCVT.F32.S32 q15, q15 199 200 VMUL.F32 q8, q8, q0 // multiplier 201 VMUL.F32 q9, q9, q1 202 VMUL.F32 q10, q10, q0 203 VMUL.F32 q11, q11, q1 204 VMUL.F32 q12, q12, q0 205 VMUL.F32 q13, q13, q1 206 VMUL.F32 q14, q14, q0 207 VMUL.F32 q15, q15, q1 208 209 VCVTN.S32.F32 q8, q8 210 VCVTN.S32.F32 q9, q9 211 VCVTN.S32.F32 q10, q10 212 VCVTN.S32.F32 q11, q11 213 VCVTN.S32.F32 q12, q12 214 VCVTN.S32.F32 q13, q13 215 VCVTN.S32.F32 q14, q14 216 VCVTN.S32.F32 q15, q15 217 218 VDUP.16 q0, d13[2] // output_zero_point 219 220 VQMOVN.S32 d16, q8 221 VQMOVN.S32 d17, q9 222 VQMOVN.S32 d18, q10 223 VQMOVN.S32 d19, q11 224 VQMOVN.S32 d20, q12 225 VQMOVN.S32 d21, q13 226 VQMOVN.S32 d22, q14 227 VQMOVN.S32 d23, q15 228 229 VQADD.S16 q8, q8, q0 230 VQADD.S16 q9, q9, q0 231 VQADD.S16 q10, q10, q0 232 VQADD.S16 q11, q11, q0 233 234 VDUP.8 q12, d13[6] // output_min 235 236 VQMOVN.S16 d0, q8 237 VQMOVN.S16 d1, q9 238 VQMOVN.S16 d2, q10 239 VQMOVN.S16 d3, q11 240 241 VDUP.8 q13, d13[7] // output_max 242 243 VMAX.S8 q0, q0, q12 244 VMAX.S8 q1, q1, q12 245 246 SUBS r1, r1, 8 247 248 VMIN.S8 q0, q0, q13 249 VMIN.S8 q1, q1, q13 250 251 # Store full 4 x 8 252 BLO 4f 253 VST1.8 {d0}, [r11], r7 254 SUB r3, r3, r2 255 VST1.8 {d1}, [r4], r7 256 SUB r12, r12, r2 257 VST1.8 {d2}, [r8], r7 258 SUB r10, r10, r2 259 VST1.8 {d3}, [r6], r7 260 SUB r0, r0, r2 261 BHI 0b 262 263 VPOP {d8-d13} 264 POP {r4, r5, r6, r7, r8, r9, r10, r11} 265 BX lr 266 267 # Remainder- 4 bytes of A 268 .p2align 3 2693: 270 VLD1.32 {d0[0]}, [r3]! // A0 271 VLD1.32 {q2}, [r9]! // B0 272 VLD1.32 {d1[0]}, [r12]! // A1 273 VLD1.32 {q3}, [r9]! // B1 274 VLD1.32 {d2[0]}, [r10]! // A2 275 VLD1.32 {d3[0]}, [r0]! // A3 276 277 VSDOT.S8 q8, q2, d0[0] 278 VSDOT.S8 q9, q3, d0[0] 279 VSDOT.S8 q10, q2, d1[0] 280 VSDOT.S8 q11, q3, d1[0] 281 VSDOT.S8 q12, q2, d2[0] 282 VSDOT.S8 q13, q3, d2[0] 283 VSDOT.S8 q14, q2, d3[0] 284 VSDOT.S8 q15, q3, d3[0] 285 B 2b 286 287 # Store odd width 288 .p2align 3 2894: 290 TST r1, 4 291 BEQ 5f 292 VST1.32 {d0[0]}, [r11]! 293 VST1.32 {d1[0]}, [r4]! 294 VST1.32 {d2[0]}, [r8]! 295 VST1.32 {d3[0]}, [r6]! 296 VEXT.8 q0, q0, q0, 4 297 VEXT.8 q1, q1, q1, 4 2985: 299 TST r1, 2 300 BEQ 6f 301 VST1.16 {d0[0]}, [r11]! 302 VST1.16 {d1[0]}, [r4]! 303 VST1.16 {d2[0]}, [r8]! 304 VST1.16 {d3[0]}, [r6]! 305 VEXT.8 q0, q0, q0, 2 306 VEXT.8 q1, q1, q1, 2 307 3086: 309 TST r1, 1 310 BEQ 7f 311 VST1.8 {d0[0]}, [r11] 312 VST1.8 {d1[0]}, [r4] 313 VST1.8 {d2[0]}, [r8] 314 VST1.8 {d3[0]}, [r6] 315 3167: 317 VPOP {d8-d13} 318 POP {r4, r5, r6, r7, r8, r9, r10, r11} 319 BX lr 320 321END_FUNCTION xnn_${DATATYPE.lower()}_gemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8c4__aarch32_neondot_ld64 322# LINT.ThenChange(4x8c4-rndnu-aarch32-neondot-ld64.cc) 323#endif // __APPLE__ 324 325#ifdef __ELF__ 326.section ".note.GNU-stack","",%progbits 327#endif 328 329