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()}_igemm_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 -> sp + 56 25// size_t kc, (r2) -> r5 -> sp + 60 26// size_t ks, (r3) -> sp + 64 -> r14 27// const ${XINT8_T}**restrict a, sp + 104 -> r2 28// const void*restrict w, sp + 108 -> r9 29// ${XINT8_T}*restrict c, sp + 112 -> r11 30// size_t cm_stride, sp + 116 -> (r6) 31// size_t cn_stride, sp + 120 -> (r7) 32// size_t a_offset, sp + 124 -> (r5) 33// const ${XINT8_T}* zero, sp + 128 -> (r7) 34// ${PARAMS_UNION}*params); sp + 132 -> (r5) 35 36// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. 37 38// Register usage 39// A0 r3 d0-d1 q0 40// A1 r12 d2-d3 q1 41// A2 r10 d4-d5 q2 42// A3 r0 d6-d7 q3 43 44// B r9 d8-d9 q4 q5 45 46// C0 r11 d16-d17 q8 d18-d19 q9 47// C1 r4 d20-d21 q10 d22-d23 q11 48// C2 r8 d24-d25 q12 d26-d27 q13 49// C3 r6 d28-d29 q14 d30-d31 q15 50 51// r1,r7 A53 gpr temporary loads 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_a53 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 BLO 3f // less than 8 channels? 186 187 // Main loop - 8 bytes 188 // 64 bytes for weights. 189 // 5 VMOVL = 4 A and 1 B = 5 cycles 190 // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles 191 // 1 blocks with VLD B, VMLA = 9 cycles 192 // total = 84 cycles 193 .p2align 3 1942: 195 // Extend - 5 cycles 196 ${XXTL} q0, d0 197 $if PREFETCH: 198 PLD [r3, 128] 199 $if DATATYPE == "QU8": 200 VSUBL.U8 q4, d8, d14 201 $else: 202 VMOVL.S8 q4, d8 203 $if PREFETCH: 204 PLD [r9, 448] 205 ${XXTL} q1, d2 206 $if PREFETCH: 207 PLD [r12, 128] 208 ${XXTL} q2, d4 209 $if PREFETCH: 210 PLD [r0, 128] 211 ${XXTL} q3, d6 212 $if PREFETCH: 213 PLD [r10, 128] 214 215 // BLOCK 0 - 10 cycles 216 VLD1.8 {d10}, [r9]! // B1 217 VMLAL.S16 q8, d8, d0[0] 218 VMLAL.S16 q9, d9, d0[0] 219 VMLAL.S16 q10, d8, d2[0] 220 VMLAL.S16 q11, d9, d2[0] 221 $if DATATYPE == "QU8": 222 VSUBL.U8 q5, d10, d14 223 $else: 224 VMOVL.S8 q5, d10 225 VMLAL.S16 q12, d8, d4[0] 226 VMLAL.S16 q13, d9, d4[0] 227 VMLAL.S16 q14, d8, d6[0] 228 VMLAL.S16 q15, d9, d6[0] 229 230 // BLOCK 1 - 10 cycles 231 VLD1.8 {d8}, [r9]! // B2 232 VMLAL.S16 q8, d10, d0[1] 233 VMLAL.S16 q9, d11, d0[1] 234 VMLAL.S16 q10, d10, d2[1] 235 VMLAL.S16 q11, d11, d2[1] 236 $if DATATYPE == "QU8": 237 VSUBL.U8 q4, d8, d14 238 $else: 239 VMOVL.S8 q4, d8 240 VMLAL.S16 q12, d10, d4[1] 241 VMLAL.S16 q13, d11, d4[1] 242 VMLAL.S16 q14, d10, d6[1] 243 VMLAL.S16 q15, d11, d6[1] 244 245 // BLOCK 2 - 10 cycles 246 VLD1.8 {d10}, [r9]! // B3 247 VMLAL.S16 q8, d8, d0[2] 248 VMLAL.S16 q9, d9, d0[2] 249 VMLAL.S16 q10, d8, d2[2] 250 VMLAL.S16 q11, d9, d2[2] 251 $if DATATYPE == "QU8": 252 VSUBL.U8 q5, d10, d14 253 $else: 254 VMOVL.S8 q5, d10 255 VMLAL.S16 q12, d8, d4[2] 256 VMLAL.S16 q13, d9, d4[2] 257 VMLAL.S16 q14, d8, d6[2] 258 VMLAL.S16 q15, d9, d6[2] 259 260 // BLOCK 3 - 10 cycles 261 VLD1.8 {d8}, [r9]! // B4 262 VMLAL.S16 q8, d10, d0[3] 263 VMLAL.S16 q9, d11, d0[3] 264 VMLAL.S16 q10, d10, d2[3] 265 VMLAL.S16 q11, d11, d2[3] 266 $if DATATYPE == "QU8": 267 VSUBL.U8 q4, d8, d14 268 $else: 269 VMOVL.S8 q4, d8 270 VMLAL.S16 q12, d10, d4[3] 271 LDR r1, [r3] // A0 low 272 VMLAL.S16 q13, d11, d4[3] 273 LDR r7, [r3, 4] // A0 high 274 VMLAL.S16 q14, d10, d6[3] 275 ADD r3, r3, 8 276 VMLAL.S16 q15, d11, d6[3] 277 278 // BLOCK 4 - 10 cycles 279 VLD1.8 {d10}, [r9]! // B5 280 VMOV d0, r1, r7 // A0 VMOV 281 VMLAL.S16 q8, d8, d1[0] 282 VMLAL.S16 q9, d9, d1[0] 283 VMLAL.S16 q10, d8, d3[0] 284 VMLAL.S16 q11, d9, d3[0] 285 $if DATATYPE == "QU8": 286 VSUBL.U8 q5, d10, d14 287 $else: 288 VMOVL.S8 q5, d10 289 VMLAL.S16 q12, d8, d5[0] 290 LDR r1, [r12] // A1 low 291 VMLAL.S16 q13, d9, d5[0] 292 LDR r7, [r12, 4] // A1 high 293 VMLAL.S16 q14, d8, d7[0] 294 ADD r12, r12, 8 295 VMLAL.S16 q15, d9, d7[0] 296 297 // BLOCK 5 - 10 cycles 298 VLD1.8 {d8}, [r9]! // B6 299 VMOV d2, r1, r7 // A1 VMOV 300 VMLAL.S16 q8, d10, d1[1] 301 VMLAL.S16 q9, d11, d1[1] 302 VMLAL.S16 q10, d10, d3[1] 303 VMLAL.S16 q11, d11, d3[1] 304 $if DATATYPE == "QU8": 305 VSUBL.U8 q4, d8, d14 306 $else: 307 VMOVL.S8 q4, d8 308 VMLAL.S16 q12, d10, d5[1] 309 LDR r1, [r10] // A2 low 310 VMLAL.S16 q13, d11, d5[1] 311 LDR r7, [r10, 4] // A2 high 312 VMLAL.S16 q14, d10, d7[1] 313 ADD r10, r10, 8 314 VMLAL.S16 q15, d11, d7[1] 315 316 // BLOCK 6 - 10 cycles 317 VLD1.8 {d10}, [r9]! // B7 318 VMOV d4, r1, r7 // A2 VMOV 319 VMLAL.S16 q8, d8, d1[2] 320 VMLAL.S16 q9, d9, d1[2] 321 VMLAL.S16 q10, d8, d3[2] 322 VMLAL.S16 q11, d9, d3[2] 323 $if DATATYPE == "QU8": 324 VSUBL.U8 q5, d10, d14 325 $else: 326 VMOVL.S8 q5, d10 327 VMLAL.S16 q12, d8, d5[2] 328 LDR r1, [r0] // A3 low 329 VMLAL.S16 q13, d9, d5[2] 330 LDR r7, [r0, 4] // A3 high 331 VMLAL.S16 q14, d8, d7[2] 332 ADD r0, r0, 8 333 VMLAL.S16 q15, d9, d7[2] 334 335 // BLOCK 7 - 9 cycles 336 VLD1.8 {d8}, [r9]! // B0 337 VMOV d6, r1, r7 // A3 VMOV 338 VMLAL.S16 q8, d10, d1[3] 339 VMLAL.S16 q9, d11, d1[3] 340 VMLAL.S16 q10, d10, d3[3] 341 VMLAL.S16 q11, d11, d3[3] 342 VMLAL.S16 q12, d10, d5[3] 343 VMLAL.S16 q13, d11, d5[3] 344 SUBS r5, r5, 8 345 VMLAL.S16 q14, d10, d7[3] 346 VMLAL.S16 q15, d11, d7[3] 347 BHS 2b 348 349 // Epilogue 350 351 .p2align 3 3523: 353 ${XXTL} q0, d0 354 $if DATATYPE == "QU8": 355 VSUBL.U8 q4, d8, d14 356 $else: 357 VMOVL.S8 q4, d8 358 ${XXTL} q1, d2 359 ${XXTL} q2, d4 360 ${XXTL} q3, d6 361 362 VLD1.8 {d10}, [r9]! // B1 363 VMLAL.S16 q8, d8, d0[0] 364 VMLAL.S16 q9, d9, d0[0] 365 VMLAL.S16 q10, d8, d2[0] 366 VMLAL.S16 q11, d9, d2[0] 367 $if DATATYPE == "QU8": 368 VSUBL.U8 q5, d10, d14 369 $else: 370 VMOVL.S8 q5, d10 371 VMLAL.S16 q12, d8, d4[0] 372 VMLAL.S16 q13, d9, d4[0] 373 VMLAL.S16 q14, d8, d6[0] 374 VMLAL.S16 q15, d9, d6[0] 375 376 VLD1.8 {d8}, [r9]! // B2 377 VMLAL.S16 q8, d10, d0[1] 378 VMLAL.S16 q9, d11, d0[1] 379 VMLAL.S16 q10, d10, d2[1] 380 VMLAL.S16 q11, d11, d2[1] 381 $if DATATYPE == "QU8": 382 VSUBL.U8 q4, d8, d14 383 $else: 384 VMOVL.S8 q4, d8 385 VMLAL.S16 q12, d10, d4[1] 386 VMLAL.S16 q13, d11, d4[1] 387 VMLAL.S16 q14, d10, d6[1] 388 VMLAL.S16 q15, d11, d6[1] 389 390 VLD1.8 {d10}, [r9]! // B3 391 VMLAL.S16 q8, d8, d0[2] 392 VMLAL.S16 q9, d9, d0[2] 393 VMLAL.S16 q10, d8, d2[2] 394 VMLAL.S16 q11, d9, d2[2] 395 $if DATATYPE == "QU8": 396 VSUBL.U8 q5, d10, d14 397 $else: 398 VMOVL.S8 q5, d10 399 VMLAL.S16 q12, d8, d4[2] 400 VMLAL.S16 q13, d9, d4[2] 401 VMLAL.S16 q14, d8, d6[2] 402 VMLAL.S16 q15, d9, d6[2] 403 404 VLD1.8 {d8}, [r9]! // B4 405 VMLAL.S16 q8, d10, d0[3] 406 VMLAL.S16 q9, d11, d0[3] 407 VMLAL.S16 q10, d10, d2[3] 408 VMLAL.S16 q11, d11, d2[3] 409 $if DATATYPE == "QU8": 410 VSUBL.U8 q4, d8, d14 411 $else: 412 VMOVL.S8 q4, d8 413 VMLAL.S16 q12, d10, d4[3] 414 VMLAL.S16 q13, d11, d4[3] 415 VMLAL.S16 q14, d10, d6[3] 416 VMLAL.S16 q15, d11, d6[3] 417 418 VLD1.8 {d10}, [r9]! // B5 419 VMLAL.S16 q8, d8, d1[0] 420 VMLAL.S16 q9, d9, d1[0] 421 VMLAL.S16 q10, d8, d3[0] 422 VMLAL.S16 q11, d9, d3[0] 423 $if DATATYPE == "QU8": 424 VSUBL.U8 q5, d10, d14 425 $else: 426 VMOVL.S8 q5, d10 427 VMLAL.S16 q12, d8, d5[0] 428 VMLAL.S16 q13, d9, d5[0] 429 VMLAL.S16 q14, d8, d7[0] 430 VMLAL.S16 q15, d9, d7[0] 431 432 VLD1.8 {d8}, [r9]! // B6 433 VMLAL.S16 q8, d10, d1[1] 434 VMLAL.S16 q9, d11, d1[1] 435 VMLAL.S16 q10, d10, d3[1] 436 VMLAL.S16 q11, d11, d3[1] 437 $if DATATYPE == "QU8": 438 VSUBL.U8 q4, d8, d14 439 $else: 440 VMOVL.S8 q4, d8 441 VMLAL.S16 q12, d10, d5[1] 442 VMLAL.S16 q13, d11, d5[1] 443 VMLAL.S16 q14, d10, d7[1] 444 VMLAL.S16 q15, d11, d7[1] 445 446 VLD1.8 {d10}, [r9]! // B7 447 VMLAL.S16 q8, d8, d1[2] 448 VMLAL.S16 q9, d9, d1[2] 449 VMLAL.S16 q10, d8, d3[2] 450 VMLAL.S16 q11, d9, d3[2] 451 $if DATATYPE == "QU8": 452 VSUBL.U8 q5, d10, d14 453 $else: 454 VMOVL.S8 q5, d10 455 VMLAL.S16 q12, d8, d5[2] 456 VMLAL.S16 q13, d9, d5[2] 457 VMLAL.S16 q14, d8, d7[2] 458 VMLAL.S16 q15, d9, d7[2] 459 460 VMLAL.S16 q8, d10, d1[3] 461 VMLAL.S16 q9, d11, d1[3] 462 VMLAL.S16 q10, d10, d3[3] 463 VMLAL.S16 q11, d11, d3[3] 464 VMLAL.S16 q12, d10, d5[3] 465 VMLAL.S16 q13, d11, d5[3] 466 ADDS r5, r5, 8 467 VMLAL.S16 q14, d10, d7[3] 468 VMLAL.S16 q15, d11, d7[3] 469 470 # Is there a remainder?- 1-7 bytes of A 471 BNE 6f 472 4734: 474 # ks loop 475 SUBS r14, r14, 16 // ks -= MR * sizeof(void*) 476 BHI 1b 477 478 LDR r7, [sp, 120] // cn_stride 479 LDR r14, [sp, 64] // p = ks 480 481 $if REQUANTIZATION == "RNDNU": 482 # RNDNU quantization 483 VDUP.32 q0, d12[0] // right_pre_shift 484 485 VQSHL.S32 q8, q8, q0 486 VQSHL.S32 q9, q9, q0 487 VQSHL.S32 q10, q10, q0 488 VQSHL.S32 q11, q11, q0 489 VQSHL.S32 q12, q12, q0 490 VQSHL.S32 q13, q13, q0 491 VQSHL.S32 q14, q14, q0 492 VQSHL.S32 q15, q15, q0 493 494 VDUP.32 q2, d13[0] // right_post_shift 495 496 VQDMULH.S32 q8, q8, d12[1] // multiplier 497 VQDMULH.S32 q9, q9, d12[1] 498 VQDMULH.S32 q10, q10, d12[1] 499 VQDMULH.S32 q11, q11, d12[1] 500 VQDMULH.S32 q12, q12, d12[1] 501 VQDMULH.S32 q13, q13, d12[1] 502 VQDMULH.S32 q14, q14, d12[1] 503 VQDMULH.S32 q15, q15, d12[1] 504 505 VRSHL.S32 q8, q8, q2 506 VRSHL.S32 q9, q9, q2 507 VRSHL.S32 q10, q10, q2 508 VRSHL.S32 q11, q11, q2 509 VRSHL.S32 q12, q12, q2 510 VRSHL.S32 q13, q13, q2 511 VRSHL.S32 q14, q14, q2 512 VRSHL.S32 q15, q15, q2 513 $elif DATATYPE == "QC8" and ARMV8: 514 # QC8 FP32 quantization 515 VLD1.8 {q0-q1}, [r9]! 516 517 VCVT.F32.S32 q8, q8 518 VCVT.F32.S32 q9, q9 519 VCVT.F32.S32 q10, q10 520 VCVT.F32.S32 q11, q11 521 VCVT.F32.S32 q12, q12 522 VCVT.F32.S32 q13, q13 523 VCVT.F32.S32 q14, q14 524 VCVT.F32.S32 q15, q15 525 526 VMUL.F32 q8, q8, q0 // multiplier 527 VMUL.F32 q9, q9, q1 528 VMUL.F32 q10, q10, q0 529 VMUL.F32 q11, q11, q1 530 VMUL.F32 q12, q12, q0 531 VMUL.F32 q13, q13, q1 532 VMUL.F32 q14, q14, q0 533 VMUL.F32 q15, q15, q1 534 535 VCVTN.S32.F32 q8, q8 536 VCVTN.S32.F32 q9, q9 537 VCVTN.S32.F32 q10, q10 538 VCVTN.S32.F32 q11, q11 539 VCVTN.S32.F32 q12, q12 540 VCVTN.S32.F32 q13, q13 541 VCVTN.S32.F32 q14, q14 542 VCVTN.S32.F32 q15, q15 543 $elif DATATYPE == "QC8" and not ARMV8: 544 # QC8 FP32 quantization 545 VLD1.8 {q0-q1}, [r9]! 546 547 VDUP.32 q2, d12[0] // magic_bias 548 VDUP.32 q3, d12[1] // magic_bias_less_output_zero_point 549 550 VCVT.F32.S32 q8, q8 551 VCVT.F32.S32 q9, q9 552 VCVT.F32.S32 q10, q10 553 VCVT.F32.S32 q11, q11 554 VCVT.F32.S32 q12, q12 555 VCVT.F32.S32 q13, q13 556 VCVT.F32.S32 q14, q14 557 VCVT.F32.S32 q15, q15 558 559 VMUL.F32 q8, q8, q0 // multiplier 560 VMUL.F32 q9, q9, q1 561 VMUL.F32 q10, q10, q0 562 VMUL.F32 q11, q11, q1 563 VMUL.F32 q12, q12, q0 564 VMUL.F32 q13, q13, q1 565 VMUL.F32 q14, q14, q0 566 VMUL.F32 q15, q15, q1 567 568 VADD.F32 q8, q8, q2 // magic_bias 569 VADD.F32 q9, q9, q2 570 VADD.F32 q10, q10, q2 571 VADD.F32 q11, q11, q2 572 VADD.F32 q12, q12, q2 573 VADD.F32 q13, q13, q2 574 VADD.F32 q14, q14, q2 575 VADD.F32 q15, q15, q2 576 577 VQSUB.S32 q8, q8, q3 // magic_bias_less_output_zero_point 578 VQSUB.S32 q9, q9, q3 579 VQSUB.S32 q10, q10, q3 580 VQSUB.S32 q11, q11, q3 581 VQSUB.S32 q12, q12, q3 582 VQSUB.S32 q13, q13, q3 583 VQSUB.S32 q14, q14, q3 584 VQSUB.S32 q15, q15, q3 585 586 $if DATATYPE != "QC8" or ARMV8: 587 VDUP.16 q0, d13[2] // output_zero_point 588 589 VQMOVN.S32 d16, q8 590 VQMOVN.S32 d17, q9 591 VQMOVN.S32 d18, q10 592 VQMOVN.S32 d19, q11 593 VQMOVN.S32 d20, q12 594 VQMOVN.S32 d21, q13 595 VQMOVN.S32 d22, q14 596 VQMOVN.S32 d23, q15 597 598 $if DATATYPE != "QC8" or ARMV8: 599 VQADD.S16 q8, q8, q0 600 VQADD.S16 q9, q9, q0 601 VQADD.S16 q10, q10, q0 602 VQADD.S16 q11, q11, q0 603 604 LDR r1, [sp, 56] // restore nc 605 VDUP.8 q12, d13[6] // output_min 606 607 ${SQXTXN} d0, q8 608 ${SQXTXN} d1, q9 609 ${SQXTXN} d2, q10 610 ${SQXTXN} d3, q11 611 612 VDUP.8 q13, d13[7] // output_max 613 614 ${XMAX} q0, q0, q12 615 ${XMAX} q1, q1, q12 616 617 SUBS r1, r1, 8 // nc -= 8 618 619 ${XMIN} q0, q0, q13 620 ${XMIN} q1, q1, q13 621 622 # Store full 4 x 8 623 BLO 7f 624 VST1.8 {d3}, [r6], r7 625 VST1.8 {d2}, [r8], r7 626 VST1.8 {d1}, [r4], r7 627 VST1.8 {d0}, [r11], r7 628 SUB r2, r2, r14 // a -= ks 629 BHI 0b 630 631 $if DATATYPE == "QU8": 632 VPOP {d8-d14} 633 ADD sp, sp, 12 // skip r1, r2, r3 634 $else: 635 VPOP {d8-d13} 636 ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 637 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 638 639 # Remainder- 1 to 7 bytes of A 640 .p2align 3 6415: 642 AND r5, r5, 7 // kc remainder 1 to 7 6436: 644 VLD1.8 {d0}, [r3] 645 VLD1.8 {d8}, [r9]! 646 VLD1.8 {d2}, [r12] 647 VLD1.8 {d4}, [r10] 648 VLD1.8 {d6}, [r0] 649 650 ${XXTL} q0, d0 651 $if DATATYPE == "QU8": 652 VSUBL.U8 q4, d8, d14 653 $else: 654 VMOVL.S8 q4, d8 655 ${XXTL} q1, d2 656 ${XXTL} q2, d4 657 ${XXTL} q3, d6 658 VMLAL.S16 q8, d8, d0[0] 659 VMLAL.S16 q9, d9, d0[0] 660 VMLAL.S16 q10, d8, d2[0] 661 VMLAL.S16 q11, d9, d2[0] 662 VMLAL.S16 q12, d8, d4[0] 663 VMLAL.S16 q13, d9, d4[0] 664 VMLAL.S16 q14, d8, d6[0] 665 VMLAL.S16 q15, d9, d6[0] 666 CMP r5, 2 667 BLO 4b 668 669 VLD1.8 {d8}, [r9]! 670 $if DATATYPE == "QU8": 671 VSUBL.U8 q4, d8, d14 672 $else: 673 VMOVL.S8 q4, d8 674 VMLAL.S16 q8, d8, d0[1] 675 VMLAL.S16 q9, d9, d0[1] 676 VMLAL.S16 q10, d8, d2[1] 677 VMLAL.S16 q11, d9, d2[1] 678 VMLAL.S16 q12, d8, d4[1] 679 VMLAL.S16 q13, d9, d4[1] 680 VMLAL.S16 q14, d8, d6[1] 681 VMLAL.S16 q15, d9, d6[1] 682 BEQ 4b 683 684 VLD1.8 {d8}, [r9]! 685 $if DATATYPE == "QU8": 686 VSUBL.U8 q4, d8, d14 687 $else: 688 VMOVL.S8 q4, d8 689 VMLAL.S16 q8, d8, d0[2] 690 VMLAL.S16 q9, d9, d0[2] 691 VMLAL.S16 q10, d8, d2[2] 692 VMLAL.S16 q11, d9, d2[2] 693 VMLAL.S16 q12, d8, d4[2] 694 VMLAL.S16 q13, d9, d4[2] 695 VMLAL.S16 q14, d8, d6[2] 696 VMLAL.S16 q15, d9, d6[2] 697 CMP r5, 4 698 BLO 4b 699 700 VLD1.8 {d8}, [r9]! 701 $if DATATYPE == "QU8": 702 VSUBL.U8 q4, d8, d14 703 $else: 704 VMOVL.S8 q4, d8 705 VMLAL.S16 q8, d8, d0[3] 706 VMLAL.S16 q9, d9, d0[3] 707 VMLAL.S16 q10, d8, d2[3] 708 VMLAL.S16 q11, d9, d2[3] 709 VMLAL.S16 q12, d8, d4[3] 710 VMLAL.S16 q13, d9, d4[3] 711 VMLAL.S16 q14, d8, d6[3] 712 VMLAL.S16 q15, d9, d6[3] 713 BEQ 4b 714 715 VLD1.8 {d8}, [r9]! 716 $if DATATYPE == "QU8": 717 VSUBL.U8 q4, d8, d14 718 $else: 719 VMOVL.S8 q4, d8 720 VMLAL.S16 q8, d8, d1[0] 721 VMLAL.S16 q9, d9, d1[0] 722 VMLAL.S16 q10, d8, d3[0] 723 VMLAL.S16 q11, d9, d3[0] 724 VMLAL.S16 q12, d8, d5[0] 725 VMLAL.S16 q13, d9, d5[0] 726 VMLAL.S16 q14, d8, d7[0] 727 VMLAL.S16 q15, d9, d7[0] 728 CMP r5, 6 729 BLO 4b 730 731 VLD1.8 {d8}, [r9]! 732 $if DATATYPE == "QU8": 733 VSUBL.U8 q4, d8, d14 734 $else: 735 VMOVL.S8 q4, d8 736 VMLAL.S16 q8, d8, d1[1] 737 VMLAL.S16 q9, d9, d1[1] 738 VMLAL.S16 q10, d8, d3[1] 739 VMLAL.S16 q11, d9, d3[1] 740 VMLAL.S16 q12, d8, d5[1] 741 VMLAL.S16 q13, d9, d5[1] 742 VMLAL.S16 q14, d8, d7[1] 743 VMLAL.S16 q15, d9, d7[1] 744 BEQ 4b 745 746 VLD1.8 {d8}, [r9]! 747 $if DATATYPE == "QU8": 748 VSUBL.U8 q4, d8, d14 749 $else: 750 VMOVL.S8 q4, d8 751 VMLAL.S16 q8, d8, d1[2] 752 VMLAL.S16 q9, d9, d1[2] 753 VMLAL.S16 q10, d8, d3[2] 754 VMLAL.S16 q11, d9, d3[2] 755 VMLAL.S16 q12, d8, d5[2] 756 VMLAL.S16 q13, d9, d5[2] 757 VMLAL.S16 q14, d8, d7[2] 758 VMLAL.S16 q15, d9, d7[2] 759 B 4b 760 761 # Store odd width 762 .p2align 3 7637: 764 TST r1, 4 765 BEQ 8f 766 VST1.32 {d3[0]}, [r6]! 767 VST1.32 {d2[0]}, [r8]! 768 VST1.32 {d1[0]}, [r4]! 769 VST1.32 {d0[0]}, [r11]! 770 VEXT.8 q1, q1, q1, 4 771 VEXT.8 q0, q0, q0, 4 7728: 773 TST r1, 2 774 BEQ 9f 775 VST1.16 {d3[0]}, [r6]! 776 VST1.16 {d2[0]}, [r8]! 777 VST1.16 {d1[0]}, [r4]! 778 VST1.16 {d0[0]}, [r11]! 779 VEXT.8 q1, q1, q1, 2 780 VEXT.8 q0, q0, q0, 2 781 7829: 783 TST r1, 1 784 BEQ 10f 785 VST1.8 {d3[0]}, [r6] 786 VST1.8 {d2[0]}, [r8] 787 VST1.8 {d1[0]}, [r4] 788 VST1.8 {d0[0]}, [r11] 789 79010: 791 $if DATATYPE == "QU8": 792 VPOP {d8-d14} 793 ADD sp, sp, 12 // skip r1, r2, r3 794 $else: 795 VPOP {d8-d13} 796 ADD sp, sp, 20 // skip pad of 8, r1, r2, r3 797 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 798 799END_FUNCTION xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_cortex_a53 800 801#ifdef __ELF__ 802.section ".note.GNU-stack","",%progbits 803#endif 804