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", "QU8"] 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$ISA = "neonv8" if ARMV8 else "neon" 17$CPU = "a35" if ARMV8 else "a7" 18$XMIN = "VMIN.U8" if DATATYPE == "QU8" else "VMIN.S8" 19$XMAX = "VMAX.U8" if DATATYPE == "QU8" else "VMAX.S8" 20$XXTL = "VMOVL.U8" if DATATYPE == "QU8" else "VMOVL.S8" 21$SQXTXN = "VQMOVUN.S16" if DATATYPE == "QU8" else "VQMOVN.S16" 22$XINT8_T = "uint8_t" if DATATYPE == "QU8" else "int8_t" 23// void xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_${CPU}( 24// size_t mr, (r0) 25// size_t nc, r1 -> sp + 56 26// size_t kc, (r2) -> r5 -> sp + 60 27// size_t ks, (r3) -> sp + 64 -> r14 28// const ${XINT8_T}**restrict a, sp + 104 -> r2 29// const void*restrict w, sp + 108 -> r9 30// ${XINT8_T}*restrict c, sp + 112 -> r11 31// size_t cm_stride, sp + 116 -> (r6) 32// size_t cn_stride, sp + 120 -> (r7) 33// size_t a_offset, sp + 124 -> (r5) 34// const ${XINT8_T}* zero, sp + 128 -> (r7) 35// ${PARAMS_UNION}*params); sp + 132 -> (r5) 36 37// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. 38 39// Register usage 40// A0 r3 d0-d1 q0 41// A1 r12 d2-d3 q1 42// A2 r10 d4-d5 q2 43// A3 r0 d6-d7 q3 44 45// B r9 d8-d9 q4 q5 46 47// C0 r11 d16-d17 q8 d18-d19 q9 48// C1 r4 d20-d21 q10 d22-d23 q11 49// C2 r8 d24-d25 q12 d26-d27 q13 50// C3 r6 d28-d29 q14 d30-d31 q15 51 52// Unused d15 53 54$if REQUANTIZATION == "RNDNU" and DATATYPE != "QU8": 55 // params structure is 16 bytes 56 // struct { 57 // int32_t right_pre_shift; d12[0] 58 // int32_t multiplier; d12[1] 59 // int32_t right_post_shift; d13[0] 60 // int16_t output_zero_point; d13[2] 61 // int8_t output_min; d13[6] 62 // int8_t output_max; d13[7] 63 // } rndnu_neon; 64$elif REQUANTIZATION == "RNDNU" and DATATYPE == "QU8": 65 // params structure is 20 bytes 66 // struct { 67 // uint8_t kernel_zero_point[4]; d14 68 // int32_t right_pre_shift; d12[0] 69 // int32_t multiplier; d12[1] 70 // int32_t right_post_shift; d13[0] 71 // int16_t output_zero_point; d13[2] 72 // uint8_t output_min; d13[6] 73 // uint8_t output_max; d13[7] 74 // } rndnu_neon; 75$elif DATATYPE == "QC8" and not ARMV8: 76 // params structure is 10 bytes 77 // struct { 78 // float magic_bias; d12[0] 79 // int32_t magic_bias_less_output_zero_point; d12[1] 80 // int8_t output_min; d13[6] 81 // int8_t output_max; d13[7] 82 // } xnn_qs8_minmax_params.neon; 83$else: 84 // params structure is 4 bytes 85 // struct { 86 // int16_t output_zero_point; d13[2] 87 // int8_t output_min; d13[6] 88 // int8_t output_max; d13[7] 89 // } xnn_qs8_minmax_params.neonv8; 90 91BEGIN_FUNCTION xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_${CPU} 92 # Push 104 bytes 93 # r1, r2 will be reloaded in outer loop. r3 is ks 94 PUSH {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +48 95 $if DATATYPE == "QU8": 96 VPUSH {d8-d14} // +56 = 104 97 $else: 98 SUB sp, sp, 8 // +8 99 VPUSH {d8-d13} // +48 = 104 100 101 LDR r11, [sp, 112] // c 102 LDR r6, [sp, 116] // cm_stride 103 LDR r2, [sp, 104] // a 104 LDR r9, [sp, 108] // w 105 LDR r5, [sp, 132] // params 106 MOV r14, r3 // p = ks 107 108 # Clamp C pointers 109 CMP r0, 2 // if mr >= 2 110 ADD r4, r11, r6 // c1 = c0 + cm_stride 111 MOVLO r4, r11 // c1 112 // if mr > 2 113 ADD r8, r4, r6 // c2 = c1 + cm_stride 114 MOVLS r8, r4 // c2 115 CMP r0, 4 // if mr >=4 116 ADD r6, r8, r6 // c3 = c2 + cm_stride 117 MOVLO r6, r8 // c3 118 119 # Load params values 120 $if DATATYPE == "QU8": 121 VLD1.32 {d14[]}, [r5]! // QU8 kernel_zero_point 122 $if REQUANTIZATION == "RNDNU": 123 VLDM r5, {d12-d13} // RNDNU params 124 $elif DATATYPE == "QC8" and ARMV8: 125 VLD1.32 {d13[]}, [r5] // QC8 neonv8 params 126 $elif DATATYPE == "QC8" and not ARMV8: 127 VLDM r5!, {d12} // QC8 neon params 128 VLD1.16 {d13[]}, [r5] 129 130 $if PREFETCH: 131 PLD [r9, 64] // Prefetch B 132 PLD [r9, 128] 133 PLD [r9, 192] 134 PLD [r9, 256] 135 PLD [r9, 320] 136 PLD [r9, 384] 137 138 .p2align 3 1390: 140 # Load initial bias from w into accumulators 141 VLDM r9!, {d16-d19} // Bias 142 VMOV q10, q8 143 VMOV q11, q9 144 STR r1, [sp, 56] // save nc 145 VMOV q12, q8 146 VMOV q13, q9 147 VMOV q14, q8 148 VMOV q15, q9 149 150 .p2align 3 1511: 152 # Load next 4 A pointers 153 LDR r3, [r2, 0] 154 LDR r12, [r2, 4] 155 LDR r10, [r2, 8] 156 LDR r0, [r2, 12] 157 158 # Add a_offset 159 LDR r5, [sp, 124] // a_offset 160 LDR r7, [sp, 128] // zero 161 ADD r2, r2, 16 162 CMP r3, r7 // if a0 == zero 163 ADD r3, r3, r5 // a0 += a_offset 164 MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset 165 CMP r12, r7 // if a1 == zero 166 ADD r12, r12, r5 // a1 += a_offset 167 MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset 168 CMP r10, r7 // if a2 == zero 169 ADD r10, r10, r5 // a2 += a_offset 170 MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset 171 CMP r0, r7 // if a3 == zero 172 ADD r0, r0, r5 // a3 += a_offset 173 LDR r5, [sp, 60] // kc 174 MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset 175 SUBS r5, r5, 8 // kc - 8 176 BLO 5f // less than 8 channels? 177 178 // Prologue - load 4A's and B0 179 VLD1.8 {d0}, [r3]! // A0 180 VLD1.8 {d8}, [r9]! // B0 181 SUBS r5, r5, 8 // k = k - 8 182 VLD1.8 {d2}, [r12]! // A1 183 VLD1.8 {d4}, [r10]! // A2 184 VLD1.8 {d6}, [r0]! // A3 185 186 BLO 3f // less than 8 channels? 187 188 // Main loop - 8 bytes 189 // 64 bytes for weights. 190 // 5 VMOVL = 4 A and 1 B = 5 cycles 191 // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles 192 // 1 blocks with VLD B, VMLA = 9 cycles 193 // total = 84 cycles 194 .p2align 3 1952: 196 // Extend - 5 cycles 197 ${XXTL} q0, d0 198 $if DATATYPE == "QU8": 199 VSUBL.U8 q4, d8, d14 200 $else: 201 VMOVL.S8 q4, d8 202 $if PREFETCH: 203 PLD [r9, 448] 204 ${XXTL} q1, d2 205 ${XXTL} q2, d4 206 ${XXTL} q3, d6 207 208 // BLOCK 0 - 10 cycles 209 VLD1.8 {d10}, [r9]! // B1 210 VMLAL.S16 q8, d8, d0[0] 211 VMLAL.S16 q9, d9, d0[0] 212 VMLAL.S16 q10, d8, d2[0] 213 VMLAL.S16 q11, d9, d2[0] 214 $if DATATYPE == "QU8": 215 VSUBL.U8 q5, d10, d14 216 $else: 217 VMOVL.S8 q5, d10 218 VMLAL.S16 q12, d8, d4[0] 219 VMLAL.S16 q13, d9, d4[0] 220 VMLAL.S16 q14, d8, d6[0] 221 VMLAL.S16 q15, d9, d6[0] 222 223 // BLOCK 1 - 10 cycles 224 VLD1.8 {d8}, [r9]! // B2 225 VMLAL.S16 q8, d10, d0[1] 226 VMLAL.S16 q9, d11, d0[1] 227 VMLAL.S16 q10, d10, d2[1] 228 VMLAL.S16 q11, d11, d2[1] 229 $if DATATYPE == "QU8": 230 VSUBL.U8 q4, d8, d14 231 $else: 232 VMOVL.S8 q4, d8 233 VMLAL.S16 q12, d10, d4[1] 234 VMLAL.S16 q13, d11, d4[1] 235 VMLAL.S16 q14, d10, d6[1] 236 VMLAL.S16 q15, d11, d6[1] 237 238 // BLOCK 2 - 10 cycles 239 VLD1.8 {d10}, [r9]! // B3 240 VMLAL.S16 q8, d8, d0[2] 241 VMLAL.S16 q9, d9, d0[2] 242 VMLAL.S16 q10, d8, d2[2] 243 VMLAL.S16 q11, d9, d2[2] 244 $if DATATYPE == "QU8": 245 VSUBL.U8 q5, d10, d14 246 $else: 247 VMOVL.S8 q5, d10 248 VMLAL.S16 q12, d8, d4[2] 249 VMLAL.S16 q13, d9, d4[2] 250 VMLAL.S16 q14, d8, d6[2] 251 VMLAL.S16 q15, d9, d6[2] 252 253 // BLOCK 3 - 10 cycles 254 VLD1.8 {d8}, [r9]! // B4 255 VMLAL.S16 q8, d10, d0[3] 256 VMLAL.S16 q9, d11, d0[3] 257 VMLAL.S16 q10, d10, d2[3] 258 VMLAL.S16 q11, d11, d2[3] 259 VLD1.8 {d0}, [r3]! // A0 260 $if DATATYPE == "QU8": 261 VSUBL.U8 q4, d8, d14 262 $else: 263 VMOVL.S8 q4, d8 264 VMLAL.S16 q12, d10, d4[3] 265 VMLAL.S16 q13, d11, d4[3] 266 VMLAL.S16 q14, d10, d6[3] 267 VMLAL.S16 q15, d11, d6[3] 268 269 // BLOCK 4 - 10 cycles 270 VLD1.8 {d10}, [r9]! // B5 271 VMLAL.S16 q8, d8, d1[0] 272 VMLAL.S16 q9, d9, d1[0] 273 VMLAL.S16 q10, d8, d3[0] 274 VMLAL.S16 q11, d9, d3[0] 275 VLD1.8 {d2}, [r12]! // A1 276 $if DATATYPE == "QU8": 277 VSUBL.U8 q5, d10, d14 278 $else: 279 VMOVL.S8 q5, d10 280 VMLAL.S16 q12, d8, d5[0] 281 VMLAL.S16 q13, d9, d5[0] 282 VMLAL.S16 q14, d8, d7[0] 283 VMLAL.S16 q15, d9, d7[0] 284 285 // BLOCK 5 - 10 cycles 286 VLD1.8 {d8}, [r9]! // B6 287 VMLAL.S16 q8, d10, d1[1] 288 VMLAL.S16 q9, d11, d1[1] 289 VMLAL.S16 q10, d10, d3[1] 290 VMLAL.S16 q11, d11, d3[1] 291 VLD1.8 {d4}, [r10]! // A2 292 $if DATATYPE == "QU8": 293 VSUBL.U8 q4, d8, d14 294 $else: 295 VMOVL.S8 q4, d8 296 VMLAL.S16 q12, d10, d5[1] 297 VMLAL.S16 q13, d11, d5[1] 298 VMLAL.S16 q14, d10, d7[1] 299 VMLAL.S16 q15, d11, d7[1] 300 301 // BLOCK 6 - 10 cycles 302 VLD1.8 {d10}, [r9]! // B7 303 VMLAL.S16 q8, d8, d1[2] 304 VMLAL.S16 q9, d9, d1[2] 305 VMLAL.S16 q10, d8, d3[2] 306 VMLAL.S16 q11, d9, d3[2] 307 VLD1.8 {d6}, [r0]! // A3 308 $if DATATYPE == "QU8": 309 VSUBL.U8 q5, d10, d14 310 $else: 311 VMOVL.S8 q5, d10 312 VMLAL.S16 q12, d8, d5[2] 313 VMLAL.S16 q13, d9, d5[2] 314 VMLAL.S16 q14, d8, d7[2] 315 VMLAL.S16 q15, d9, d7[2] 316 317 // BLOCK 7 - 9 cycles 318 VLD1.8 {d8}, [r9]! // B0 319 VMLAL.S16 q8, d10, d1[3] 320 VMLAL.S16 q9, d11, d1[3] 321 VMLAL.S16 q10, d10, d3[3] 322 VMLAL.S16 q11, d11, d3[3] 323 VMLAL.S16 q12, d10, d5[3] 324 VMLAL.S16 q13, d11, d5[3] 325 SUBS r5, r5, 8 326 VMLAL.S16 q14, d10, d7[3] 327 VMLAL.S16 q15, d11, d7[3] 328 BHS 2b 329 330 // Epilogue 331 332 .p2align 3 3333: 334 ${XXTL} q0, d0 335 $if DATATYPE == "QU8": 336 VSUBL.U8 q4, d8, d14 337 $else: 338 VMOVL.S8 q4, d8 339 ${XXTL} q1, d2 340 ${XXTL} q2, d4 341 ${XXTL} q3, d6 342 343 VLD1.8 {d10}, [r9]! // B1 344 VMLAL.S16 q8, d8, d0[0] 345 VMLAL.S16 q9, d9, d0[0] 346 VMLAL.S16 q10, d8, d2[0] 347 VMLAL.S16 q11, d9, d2[0] 348 $if DATATYPE == "QU8": 349 VSUBL.U8 q5, d10, d14 350 $else: 351 VMOVL.S8 q5, d10 352 VMLAL.S16 q12, d8, d4[0] 353 VMLAL.S16 q13, d9, d4[0] 354 VMLAL.S16 q14, d8, d6[0] 355 VMLAL.S16 q15, d9, d6[0] 356 357 VLD1.8 {d8}, [r9]! // B2 358 VMLAL.S16 q8, d10, d0[1] 359 VMLAL.S16 q9, d11, d0[1] 360 VMLAL.S16 q10, d10, d2[1] 361 VMLAL.S16 q11, d11, d2[1] 362 $if DATATYPE == "QU8": 363 VSUBL.U8 q4, d8, d14 364 $else: 365 VMOVL.S8 q4, d8 366 VMLAL.S16 q12, d10, d4[1] 367 VMLAL.S16 q13, d11, d4[1] 368 VMLAL.S16 q14, d10, d6[1] 369 VMLAL.S16 q15, d11, d6[1] 370 371 VLD1.8 {d10}, [r9]! // B3 372 VMLAL.S16 q8, d8, d0[2] 373 VMLAL.S16 q9, d9, d0[2] 374 VMLAL.S16 q10, d8, d2[2] 375 VMLAL.S16 q11, d9, d2[2] 376 $if DATATYPE == "QU8": 377 VSUBL.U8 q5, d10, d14 378 $else: 379 VMOVL.S8 q5, d10 380 VMLAL.S16 q12, d8, d4[2] 381 VMLAL.S16 q13, d9, d4[2] 382 VMLAL.S16 q14, d8, d6[2] 383 VMLAL.S16 q15, d9, d6[2] 384 385 VLD1.8 {d8}, [r9]! // B4 386 VMLAL.S16 q8, d10, d0[3] 387 VMLAL.S16 q9, d11, d0[3] 388 VMLAL.S16 q10, d10, d2[3] 389 VMLAL.S16 q11, d11, d2[3] 390 $if DATATYPE == "QU8": 391 VSUBL.U8 q4, d8, d14 392 $else: 393 VMOVL.S8 q4, d8 394 VMLAL.S16 q12, d10, d4[3] 395 VMLAL.S16 q13, d11, d4[3] 396 VMLAL.S16 q14, d10, d6[3] 397 VMLAL.S16 q15, d11, d6[3] 398 399 VLD1.8 {d10}, [r9]! // B5 400 VMLAL.S16 q8, d8, d1[0] 401 VMLAL.S16 q9, d9, d1[0] 402 VMLAL.S16 q10, d8, d3[0] 403 VMLAL.S16 q11, d9, d3[0] 404 $if DATATYPE == "QU8": 405 VSUBL.U8 q5, d10, d14 406 $else: 407 VMOVL.S8 q5, d10 408 VMLAL.S16 q12, d8, d5[0] 409 VMLAL.S16 q13, d9, d5[0] 410 VMLAL.S16 q14, d8, d7[0] 411 VMLAL.S16 q15, d9, d7[0] 412 413 VLD1.8 {d8}, [r9]! // B6 414 VMLAL.S16 q8, d10, d1[1] 415 VMLAL.S16 q9, d11, d1[1] 416 VMLAL.S16 q10, d10, d3[1] 417 VMLAL.S16 q11, d11, d3[1] 418 $if DATATYPE == "QU8": 419 VSUBL.U8 q4, d8, d14 420 $else: 421 VMOVL.S8 q4, d8 422 VMLAL.S16 q12, d10, d5[1] 423 VMLAL.S16 q13, d11, d5[1] 424 VMLAL.S16 q14, d10, d7[1] 425 VMLAL.S16 q15, d11, d7[1] 426 427 VLD1.8 {d10}, [r9]! // B7 428 VMLAL.S16 q8, d8, d1[2] 429 VMLAL.S16 q9, d9, d1[2] 430 VMLAL.S16 q10, d8, d3[2] 431 VMLAL.S16 q11, d9, d3[2] 432 $if DATATYPE == "QU8": 433 VSUBL.U8 q5, d10, d14 434 $else: 435 VMOVL.S8 q5, d10 436 VMLAL.S16 q12, d8, d5[2] 437 VMLAL.S16 q13, d9, d5[2] 438 VMLAL.S16 q14, d8, d7[2] 439 VMLAL.S16 q15, d9, d7[2] 440 441 VMLAL.S16 q8, d10, d1[3] 442 VMLAL.S16 q9, d11, d1[3] 443 VMLAL.S16 q10, d10, d3[3] 444 VMLAL.S16 q11, d11, d3[3] 445 VMLAL.S16 q12, d10, d5[3] 446 VMLAL.S16 q13, d11, d5[3] 447 ADDS r5, r5, 8 448 VMLAL.S16 q14, d10, d7[3] 449 VMLAL.S16 q15, d11, d7[3] 450 451 # Is there a remainder?- 1-7 bytes of A 452 BNE 6f 453 4544: 455 # ks loop 456 SUBS r14, r14, 16 // ks -= MR * sizeof(void*) 457 BHI 1b 458 459 LDR r7, [sp, 120] // cn_stride 460 LDR r14, [sp, 64] // p = ks 461 462 $if REQUANTIZATION == "RNDNU": 463 # RNDNU quantization 464 VDUP.32 q0, d12[0] // right_pre_shift 465 466 VQSHL.S32 q8, q8, q0 467 VQSHL.S32 q9, q9, q0 468 VQSHL.S32 q10, q10, q0 469 VQSHL.S32 q11, q11, q0 470 VQSHL.S32 q12, q12, q0 471 VQSHL.S32 q13, q13, q0 472 VQSHL.S32 q14, q14, q0 473 VQSHL.S32 q15, q15, q0 474 475 VDUP.32 q2, d13[0] // right_post_shift 476 477 VQDMULH.S32 q8, q8, d12[1] // multiplier 478 VQDMULH.S32 q9, q9, d12[1] 479 VQDMULH.S32 q10, q10, d12[1] 480 VQDMULH.S32 q11, q11, d12[1] 481 VQDMULH.S32 q12, q12, d12[1] 482 VQDMULH.S32 q13, q13, d12[1] 483 VQDMULH.S32 q14, q14, d12[1] 484 VQDMULH.S32 q15, q15, d12[1] 485 486 VRSHL.S32 q8, q8, q2 487 VRSHL.S32 q9, q9, q2 488 VRSHL.S32 q10, q10, q2 489 VRSHL.S32 q11, q11, q2 490 VRSHL.S32 q12, q12, q2 491 VRSHL.S32 q13, q13, q2 492 VRSHL.S32 q14, q14, q2 493 VRSHL.S32 q15, q15, q2 494 $elif DATATYPE == "QC8" and ARMV8: 495 # QC8 FP32 quantization 496 VLD1.8 {q0-q1}, [r9]! 497 498 VCVT.F32.S32 q8, q8 499 VCVT.F32.S32 q9, q9 500 VCVT.F32.S32 q10, q10 501 VCVT.F32.S32 q11, q11 502 VCVT.F32.S32 q12, q12 503 VCVT.F32.S32 q13, q13 504 VCVT.F32.S32 q14, q14 505 VCVT.F32.S32 q15, q15 506 507 VMUL.F32 q8, q8, q0 // multiplier 508 VMUL.F32 q9, q9, q1 509 VMUL.F32 q10, q10, q0 510 VMUL.F32 q11, q11, q1 511 VMUL.F32 q12, q12, q0 512 VMUL.F32 q13, q13, q1 513 VMUL.F32 q14, q14, q0 514 VMUL.F32 q15, q15, q1 515 516 VCVTN.S32.F32 q8, q8 517 VCVTN.S32.F32 q9, q9 518 VCVTN.S32.F32 q10, q10 519 VCVTN.S32.F32 q11, q11 520 VCVTN.S32.F32 q12, q12 521 VCVTN.S32.F32 q13, q13 522 VCVTN.S32.F32 q14, q14 523 VCVTN.S32.F32 q15, q15 524 $elif DATATYPE == "QC8" and not ARMV8: 525 # QC8 FP32 quantization 526 VLD1.8 {q0-q1}, [r9]! 527 528 VDUP.32 q2, d12[0] // magic_bias 529 VDUP.32 q3, d12[1] // magic_bias_less_output_zero_point 530 531 VCVT.F32.S32 q8, q8 532 VCVT.F32.S32 q9, q9 533 VCVT.F32.S32 q10, q10 534 VCVT.F32.S32 q11, q11 535 VCVT.F32.S32 q12, q12 536 VCVT.F32.S32 q13, q13 537 VCVT.F32.S32 q14, q14 538 VCVT.F32.S32 q15, q15 539 540 VMUL.F32 q8, q8, q0 // multiplier 541 VMUL.F32 q9, q9, q1 542 VMUL.F32 q10, q10, q0 543 VMUL.F32 q11, q11, q1 544 VMUL.F32 q12, q12, q0 545 VMUL.F32 q13, q13, q1 546 VMUL.F32 q14, q14, q0 547 VMUL.F32 q15, q15, q1 548 549 VADD.F32 q8, q8, q2 // magic_bias 550 VADD.F32 q9, q9, q2 551 VADD.F32 q10, q10, q2 552 VADD.F32 q11, q11, q2 553 VADD.F32 q12, q12, q2 554 VADD.F32 q13, q13, q2 555 VADD.F32 q14, q14, q2 556 VADD.F32 q15, q15, q2 557 558 VQSUB.S32 q8, q8, q3 // magic_bias_less_output_zero_point 559 VQSUB.S32 q9, q9, q3 560 VQSUB.S32 q10, q10, q3 561 VQSUB.S32 q11, q11, q3 562 VQSUB.S32 q12, q12, q3 563 VQSUB.S32 q13, q13, q3 564 VQSUB.S32 q14, q14, q3 565 VQSUB.S32 q15, q15, q3 566 567 $if DATATYPE != "QC8" or ARMV8: 568 VDUP.16 q0, d13[2] // output_zero_point 569 570 VQMOVN.S32 d16, q8 571 VQMOVN.S32 d17, q9 572 VQMOVN.S32 d18, q10 573 VQMOVN.S32 d19, q11 574 VQMOVN.S32 d20, q12 575 VQMOVN.S32 d21, q13 576 VQMOVN.S32 d22, q14 577 VQMOVN.S32 d23, q15 578 579 $if DATATYPE != "QC8" or ARMV8: 580 VQADD.S16 q8, q8, q0 581 VQADD.S16 q9, q9, q0 582 VQADD.S16 q10, q10, q0 583 VQADD.S16 q11, q11, q0 584 585 LDR r1, [sp, 56] // restore nc 586 VDUP.8 q12, d13[6] // output_min 587 588 ${SQXTXN} d0, q8 589 ${SQXTXN} d1, q9 590 ${SQXTXN} d2, q10 591 ${SQXTXN} d3, q11 592 593 VDUP.8 q13, d13[7] // output_max 594 595 ${XMAX} q0, q0, q12 596 ${XMAX} q1, q1, q12 597 598 SUBS r1, r1, 8 // nc -= 8 599 600 ${XMIN} q0, q0, q13 601 ${XMIN} q1, q1, q13 602 603 # Store full 4 x 8 604 BLO 7f 605 VST1.8 {d3}, [r6], r7 606 VST1.8 {d2}, [r8], r7 607 VST1.8 {d1}, [r4], r7 608 VST1.8 {d0}, [r11], r7 609 SUB r2, r2, r14 // a -= ks 610 BHI 0b 611 612 $if DATATYPE == "QU8": 613 VPOP {d8-d14} 614 ADD sp, sp, 12 // skip r1, r2, r3 615 $else: 616 VPOP {d8-d13} 617 ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 618 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 619 620 # Remainder- 1 to 7 bytes of A 621 .p2align 3 6225: 623 AND r5, r5, 7 // kc remainder 1 to 7 6246: 625 VLD1.8 {d0}, [r3] 626 VLD1.8 {d8}, [r9]! 627 VLD1.8 {d2}, [r12] 628 VLD1.8 {d4}, [r10] 629 VLD1.8 {d6}, [r0] 630 631 ${XXTL} q0, d0 632 $if DATATYPE == "QU8": 633 VSUBL.U8 q4, d8, d14 634 $else: 635 VMOVL.S8 q4, d8 636 ${XXTL} q1, d2 637 ${XXTL} q2, d4 638 ${XXTL} q3, d6 639 VMLAL.S16 q8, d8, d0[0] 640 VMLAL.S16 q9, d9, d0[0] 641 VMLAL.S16 q10, d8, d2[0] 642 VMLAL.S16 q11, d9, d2[0] 643 VMLAL.S16 q12, d8, d4[0] 644 VMLAL.S16 q13, d9, d4[0] 645 VMLAL.S16 q14, d8, d6[0] 646 VMLAL.S16 q15, d9, d6[0] 647 CMP r5, 2 648 BLO 4b 649 650 VLD1.8 {d8}, [r9]! 651 $if DATATYPE == "QU8": 652 VSUBL.U8 q4, d8, d14 653 $else: 654 VMOVL.S8 q4, d8 655 VMLAL.S16 q8, d8, d0[1] 656 VMLAL.S16 q9, d9, d0[1] 657 VMLAL.S16 q10, d8, d2[1] 658 VMLAL.S16 q11, d9, d2[1] 659 VMLAL.S16 q12, d8, d4[1] 660 VMLAL.S16 q13, d9, d4[1] 661 VMLAL.S16 q14, d8, d6[1] 662 VMLAL.S16 q15, d9, d6[1] 663 BEQ 4b 664 665 VLD1.8 {d8}, [r9]! 666 $if DATATYPE == "QU8": 667 VSUBL.U8 q4, d8, d14 668 $else: 669 VMOVL.S8 q4, d8 670 VMLAL.S16 q8, d8, d0[2] 671 VMLAL.S16 q9, d9, d0[2] 672 VMLAL.S16 q10, d8, d2[2] 673 VMLAL.S16 q11, d9, d2[2] 674 VMLAL.S16 q12, d8, d4[2] 675 VMLAL.S16 q13, d9, d4[2] 676 VMLAL.S16 q14, d8, d6[2] 677 VMLAL.S16 q15, d9, d6[2] 678 CMP r5, 4 679 BLO 4b 680 681 VLD1.8 {d8}, [r9]! 682 $if DATATYPE == "QU8": 683 VSUBL.U8 q4, d8, d14 684 $else: 685 VMOVL.S8 q4, d8 686 VMLAL.S16 q8, d8, d0[3] 687 VMLAL.S16 q9, d9, d0[3] 688 VMLAL.S16 q10, d8, d2[3] 689 VMLAL.S16 q11, d9, d2[3] 690 VMLAL.S16 q12, d8, d4[3] 691 VMLAL.S16 q13, d9, d4[3] 692 VMLAL.S16 q14, d8, d6[3] 693 VMLAL.S16 q15, d9, d6[3] 694 BEQ 4b 695 696 VLD1.8 {d8}, [r9]! 697 $if DATATYPE == "QU8": 698 VSUBL.U8 q4, d8, d14 699 $else: 700 VMOVL.S8 q4, d8 701 VMLAL.S16 q8, d8, d1[0] 702 VMLAL.S16 q9, d9, d1[0] 703 VMLAL.S16 q10, d8, d3[0] 704 VMLAL.S16 q11, d9, d3[0] 705 VMLAL.S16 q12, d8, d5[0] 706 VMLAL.S16 q13, d9, d5[0] 707 VMLAL.S16 q14, d8, d7[0] 708 VMLAL.S16 q15, d9, d7[0] 709 CMP r5, 6 710 BLO 4b 711 712 VLD1.8 {d8}, [r9]! 713 $if DATATYPE == "QU8": 714 VSUBL.U8 q4, d8, d14 715 $else: 716 VMOVL.S8 q4, d8 717 VMLAL.S16 q8, d8, d1[1] 718 VMLAL.S16 q9, d9, d1[1] 719 VMLAL.S16 q10, d8, d3[1] 720 VMLAL.S16 q11, d9, d3[1] 721 VMLAL.S16 q12, d8, d5[1] 722 VMLAL.S16 q13, d9, d5[1] 723 VMLAL.S16 q14, d8, d7[1] 724 VMLAL.S16 q15, d9, d7[1] 725 BEQ 4b 726 727 VLD1.8 {d8}, [r9]! 728 $if DATATYPE == "QU8": 729 VSUBL.U8 q4, d8, d14 730 $else: 731 VMOVL.S8 q4, d8 732 VMLAL.S16 q8, d8, d1[2] 733 VMLAL.S16 q9, d9, d1[2] 734 VMLAL.S16 q10, d8, d3[2] 735 VMLAL.S16 q11, d9, d3[2] 736 VMLAL.S16 q12, d8, d5[2] 737 VMLAL.S16 q13, d9, d5[2] 738 VMLAL.S16 q14, d8, d7[2] 739 VMLAL.S16 q15, d9, d7[2] 740 B 4b 741 742 # Store odd width 743 .p2align 3 7447: 745 TST r1, 4 746 BEQ 8f 747 VST1.32 {d3[0]}, [r6]! 748 VST1.32 {d2[0]}, [r8]! 749 VST1.32 {d1[0]}, [r4]! 750 VST1.32 {d0[0]}, [r11]! 751 VEXT.8 q1, q1, q1, 4 752 VEXT.8 q0, q0, q0, 4 7538: 754 TST r1, 2 755 BEQ 9f 756 VST1.16 {d3[0]}, [r6]! 757 VST1.16 {d2[0]}, [r8]! 758 VST1.16 {d1[0]}, [r4]! 759 VST1.16 {d0[0]}, [r11]! 760 VEXT.8 q1, q1, q1, 2 761 VEXT.8 q0, q0, q0, 2 762 7639: 764 TST r1, 1 765 BEQ 10f 766 VST1.8 {d3[0]}, [r6] 767 VST1.8 {d2[0]}, [r8] 768 VST1.8 {d1[0]}, [r4] 769 VST1.8 {d0[0]}, [r11] 770 77110: 772 $if DATATYPE == "QU8": 773 VPOP {d8-d14} 774 ADD sp, sp, 12 // skip r1, r2, r3 775 $else: 776 VPOP {d8-d13} 777 ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 778 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 779 780END_FUNCTION xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_${CPU} 781 782#ifdef __ELF__ 783.section ".note.GNU-stack","",%progbits 784#endif 785