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