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