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