1// Copyright 2019 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#include <xnnpack/assembly.h> 7 8.syntax unified 9 10# LINT.IfChange 11// void xnn_f32_igemm_minmax_ukernel_4x8__aarch32_neon${"_prfm" if PREFETCH else ""}_cortex_a53( 12// size_t mr, r0 13// size_t nc, r1 14// size_t kc, r2 -> r5 -> sp + 68 15// size_t ks, r3 -> sp + 72 -> r14 16// const float**restrict a, sp + 112 -> (r5) 17// const void*restrict w, sp + 116 -> r9 18// uint8_t*restrict c, sp + 120 -> r11 19// size_t cm_stride, sp + 124 -> (r6) 20// size_t cn_stride, sp + 128 -> (r0) 21// size_t a_offset, sp + 132 -> (r5) 22// const float* zero, sp + 136 -> (r0) 23// minmax_params*params, sp + 140 -> (r5) 24 25// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. 26 27// Register usage 28 29// r0, r2 scratch temporaries for loads 30 31// A0 r3 d0 32// A1 r12 d1 33// A2 r10 d2 34// A3 r7 d3 35 36// B r9 d8, d9, d10, d11 37// B d12, d13, d14, d15 38 39// C0 r11 d16-d17 q8 d18-d19 q9 40// C1 r4 d20-d21 q10 d22-d23 q11 41// C2 r8 d24-d25 q12 d26-d27 q13 42// C3 r6 d28-d29 q14 d30-d31 q15 43 44// Clamp (r5) d4 d5 d6 d7 45 46BEGIN_FUNCTION xnn_f32_igemm_minmax_ukernel_4x8__aarch32_neon${"_prfm" if PREFETCH else ""}_cortex_a53 47 .arm 48#ifndef __APPLE__ 49 .arch armv7-a 50 .fpu neon 51#endif 52 # Push 112 bytes 53 # r2 will be reloaded in outer loop. r3 is ks 54 PUSH {r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +44 55 SUB sp, sp, 4 // 4 56 VPUSH {d8-d15} // +64 = 112 57 58 LDR r11, [sp, 120] // c 59 LDR r6, [sp, 124] // cm_stride 60 LDR r5, [sp, 112] // a 61 LDR r9, [sp, 116] // w 62 MOV r14, r3 // p = ks 63 64 # Clamp C pointers 65 CMP r0, 2 // if mr >= 2 66 ADD r4, r11, r6 // c1 = c0 + cm_stride 67 MOVLO r4, r11 // c1 68 // if mr > 2 69 ADD r8, r4, r6 // c2 = c1 + cm_stride 70 MOVLS r8, r4 // c2 71 CMP r0, 4 // if mr >=4 72 ADD r6, r8, r6 // c3 = c2 + cm_stride 73 MOVLO r6, r8 // c3 74 75 76 .p2align 3 770: 78 # Load initial bias from w into accumulators 79 VLDM r9!, {d16-d19} // Bias 80 81 VMOV q10, q8 82 $if PREFETCH: 83 PLD [r9, 0] // Prefetch B 84 VMOV q11, q9 85 $if PREFETCH: 86 PLD [r9, 64] 87 VMOV q12, q8 88 $if PREFETCH: 89 PLD [r9, 128] 90 VMOV q13, q9 91 $if PREFETCH: 92 PLD [r9, 192] 93 VMOV q14, q8 94 $if PREFETCH: 95 PLD [r9, 256] 96 VMOV q15, q9 97 $if PREFETCH: 98 PLD [r9, 320] 99 1001: 101 # Load next 4 A pointers 102 LDR r3, [r5, 0] 103 LDR r12, [r5, 4] 104 LDR r10, [r5, 8] 105 LDR r7, [r5, 12] 106 ADD r5, r5, 16 107 $if PREFETCH: 108 PLD [r3, 0] // Prefetch A 109 STR r5, [sp, 112] // a 110 $if PREFETCH: 111 PLD [r3, 64] 112 LDR r0, [sp, 136] // zero 113 $if PREFETCH: 114 PLD [r12, 0] 115 LDR r5, [sp, 132] // a_offset 116 $if PREFETCH: 117 PLD [r12, 64] 118 LDR r2, [sp, 68] // kc 119 $if PREFETCH: 120 PLD [r10, 0] 121 $if PREFETCH: 122 PLD [r10, 64] 123 $if PREFETCH: 124 PLD [r7, 0] 125 $if PREFETCH: 126 PLD [r7, 64] 127 128 # Add a_offset 129 CMP r3, r0 // if a0 == zero 130 ADD r3, r3, r5 // a0 += a_offset 131 MOVEQ r3, r0 // a0 = zero, else += a0 + a_offset 132 CMP r12, r0 // if a1 == zero 133 ADD r12, r12, r5 // a1 += a_offset 134 MOVEQ r12, r0 // a1 = zero, else += a1 + a_offset 135 CMP r10, r0 // if a2 == zero 136 ADD r10, r10, r5 // a2 += a_offset 137 MOVEQ r10, r0 // a2 = zero, else += a2 + a_offset 138 CMP r7, r0 // if a3 == zero 139 ADD r7, r7, r5 // a3 += a_offset 140 MOVEQ r7, r0 // a3 = zero, else += a3 + a_offset 141 142 SUBS r5, r2, 16 // kc - 16 143 BLO 5f // less than 4 channels? 144 145 # Prologue 146 VLD1.32 {d0}, [r3]! // A0 147 VLD1.32 {d1}, [r12]! // A1 148 VLD1.32 {d2}, [r10]! // A2 149 VLD1.32 {d3}, [r7]! // A3 150 SUBS r5, r5, 16 151 VLDM r9, {d8-d11} // B0 152 LDR r0, [r9, 56] // B1 low VMOV is in BLOCK 0 153 LDR r2, [r9, 60] // B1 high 154 VLDR d13, [r9, 40] // B1 155 156 BLO 3f // less than 4 channels? skip main loop 157 158 # Main loop - 4 floats of A (16 bytes) 159 # 32 FMA + 8 LD64 A + 8 LDR B 160 .p2align 3 1612: 162 # First group of 16 FMA, Second group loads 163 # BLOCK 0 164 VLD1.32 {d4}, [r3]! // A0 165 VMOV d15, r0, r2 // b1 VMOV b from second group 166 VMLA.F32 q8, q4, d0[0] 167 LDR r0, [r12] // A1 low 168 VMLA.F32 q10, q4, d1[0] 169 LDR r2, [r12, 4] // A1 high 170 VMLA.F32 q12, q4, d2[0] 171 $if PREFETCH: 172 PLD [r3, 128] // Prefetch A0 173 174 # BLOCK 1 175 VLDR d12, [r9, 32] // B1 176 VMOV d5, r0, r2 // a1 VMOV 177 VMLA.F32 q14, q4, d3[0] 178 LDR r0, [r9, 72] // B0 low 179 VMLA.F32 q9, q5, d0[0] 180 LDR r2, [r9, 76] // B0 high 181 VMLA.F32 q11, q5, d1[0] 182 $if PREFETCH: 183 PLD [r12, 128] // Prefetch A1 184 185 # BLOCK 2 186 VLD1.32 {d6}, [r10]! // A2 187 VMOV d9, r0, r2 // b0 VMOV 188 VMLA.F32 q13, q5, d2[0] 189 LDR r0, [r7] // A3 low 190 VMLA.F32 q15, q5, d3[0] 191 LDR r2, [r7, 4] // A3 high 192 VMLA.F32 q8, q6, d0[1] 193 $if PREFETCH: 194 PLD [r10, 128] // Prefetch A2 195 196 # BLOCK 3 197 VLDR d14, [r9, 48] // B1 198 VMOV d7, r0, r2 // a3 VMOV 199 VMLA.F32 q10, q6, d1[1] 200 LDR r0, [r9, 88] // B0 low 201 VMLA.F32 q12, q6, d2[1] 202 LDR r2, [r9, 92] // B0 high 203 VMLA.F32 q14, q6, d3[1] 204 $if PREFETCH: 205 PLD [r7, 128] // Prefetch A3 206 207 # BLOCK 4 208 VLDR d8, [r9, 64] // B0 209 VMOV d11, r0, r2 // B0 VMOV 210 VMLA.F32 q9, q7, d0[1] 211 LDR r0, [r9, 104] // B1 low VMOV is in BLOCK 0 212 VMLA.F32 q11, q7, d1[1] 213 LDR r2, [r9, 108] // B1 high 214 VMLA.F32 q13, q7, d2[1] 215 $if PREFETCH: 216 PLD [r9, 384] // Prefetch B 217 218 # BLOCK 5 219 VLDR d10, [r9, 80] // B0 220 VMOV d13, r0, r2 // b1 VMOV b from second group 221 VMLA.F32 q15, q7, d3[1] 222 LDR r0, [r9, 120] // B1 low VMOV is in BLOCK 0 223 NOP 224 LDR r2, [r9, 124] // B1 high 225 NOP 226 $if PREFETCH: 227 PLD [r9, 448] // Prefetch B 228 229 # Second group of 16 FMA, First group of loads 230 # BLOCK 0 231 VLD1.32 {d0}, [r3]! // A0 232 VMOV d15, r0, r2 // b1 VMOV b from second group 233 VMLA.F32 q8, q4, d4[0] 234 LDR r0, [r12, 8] // A1 low 235 VMLA.F32 q10, q4, d5[0] 236 LDR r2, [r12, 12] // A1 high 237 VMLA.F32 q12, q4, d6[0] 238 # NOP 239 240 # BLOCK 1 241 VLDR d12, [r9, 96] // B1 242 VMOV d1, r0, r2 // a1 VMOV 243 VMLA.F32 q14, q4, d7[0] 244 LDR r0, [r9, 136] // B0 low 245 VMLA.F32 q9, q5, d4[0] 246 LDR r2, [r9, 140] // B0 high 247 VMLA.F32 q11, q5, d5[0] 248 # NOP 249 250 # BLOCK 2 251 VLD1.32 {d2}, [r10]! // A2 252 VMOV d9, r0, r2 // b0 VMOV 253 VMLA.F32 q13, q5, d6[0] 254 LDR r0, [r7, 8] // A3 low 255 VMLA.F32 q15, q5, d7[0] 256 LDR r2, [r7, 12] // A3 high 257 VMLA.F32 q8, q6, d4[1] 258 # NOP 259 260 # BLOCK 3 261 VLDR d14, [r9, 112] // B1 262 VMOV d3, r0, r2 // a3 VMOV 263 VMLA.F32 q10, q6, d5[1] 264 LDR r0, [r9, 152] // B0 low 265 VMLA.F32 q12, q6, d6[1] 266 LDR r2, [r9, 156] // B0 high 267 VMLA.F32 q14, q6, d7[1] 268 ADD r12, r12, 16 // A1++ 269 270 # BLOCK 4 271 VLDR d8, [r9, 128] // B0 272 VMOV d11, r0, r2 // B0 VMOV 273 VMLA.F32 q9, q7, d4[1] 274 LDR r0, [r9, 168] // B1 low 275 VMLA.F32 q11, q7, d5[1] 276 LDR r2, [r9, 172] // B1 high 277 VMLA.F32 q13, q7, d6[1] 278 ADD r7, r7, 16 // A3++ 279 280 # BLOCK 5 281 VLDR d10, [r9, 144] // B0 282 VMOV d13, r0, r2 // b1 VMOV b 283 VMLA.F32 q15, q7, d7[1] 284 LDR r0, [r9, 184] // B1 low VMOV is in BLOCK 0 285 SUBS r5, r5, 16 286 LDR r2, [r9, 188] // B1 high 287 ADD r9, r9, 128 // B++ 288 BHS 2b 289 290 # Epilogue - 4 floats of A (16 bytes) 2913: 292 # First group of 16 FMA, Second group loads 293 # BLOCK 0 294 VLD1.32 {d4}, [r3]! // A0 295 VMOV d15, r0, r2 // b1 VMOV b from second group 296 VMLA.F32 q8, q4, d0[0] 297 LDR r0, [r12] // A1 low 298 VMLA.F32 q10, q4, d1[0] 299 LDR r2, [r12, 4] // A1 high 300 VMLA.F32 q12, q4, d2[0] 301 # NOP 302 303 # BLOCK 1 304 VLDR d12, [r9, 32] // B1 305 VMOV d5, r0, r2 // a1 VMOV 306 VMLA.F32 q14, q4, d3[0] 307 LDR r0, [r9, 72] // B0 low 308 VMLA.F32 q9, q5, d0[0] 309 LDR r2, [r9, 76] // B0 high 310 VMLA.F32 q11, q5, d1[0] 311 # NOP 312 313 # BLOCK 2 314 VLD1.32 {d6}, [r10]! // A2 315 VMOV d9, r0, r2 // b0 VMOV 316 VMLA.F32 q13, q5, d2[0] 317 LDR r0, [r7] // A3 low 318 VMLA.F32 q15, q5, d3[0] 319 LDR r2, [r7, 4] // A3 high 320 VMLA.F32 q8, q6, d0[1] 321 # NOP 322 323 # BLOCK 3 324 VLDR d14, [r9, 48] // B1 325 VMOV d7, r0, r2 // a3 VMOV 326 VMLA.F32 q10, q6, d1[1] 327 LDR r0, [r9, 88] // B0 low 328 VMLA.F32 q12, q6, d2[1] 329 LDR r2, [r9, 92] // B0 high 330 VMLA.F32 q14, q6, d3[1] 331 # NOP 332 333 # BLOCK 4 334 VLDR d8, [r9, 64] // B0 335 VMOV d11, r0, r2 // B0 VMOV 336 VMLA.F32 q9, q7, d0[1] 337 LDR r0, [r9, 104] // B1 low 338 VMLA.F32 q11, q7, d1[1] 339 LDR r2, [r9, 108] // B1 high 340 VMLA.F32 q13, q7, d2[1] 341 # NOP 342 343 # BLOCK 5 344 VLDR d10, [r9, 80] // B0 345 VMOV d13, r0, r2 // b1 VMOV b 346 VMLA.F32 q15, q7, d3[1] 347 LDR r0, [r9, 120] // B1 low VMOV is in BLOCK 0 348 NOP 349 LDR r2, [r9, 124] // B1 high 350 NOP 351 NOP 352 353 # Second group of 16 FMA, First group of loads 354 # BLOCK 0 355 VLDR d12, [r9, 96] // B1 356 VMOV d15, r0, r2 // b1 VMOV b from second group 357 VMLA.F32 q8, q4, d4[0] 358 VMLA.F32 q10, q4, d5[0] 359 VMLA.F32 q12, q4, d6[0] 360 361 # BLOCK 1 362 VLDR d14, [r9, 112] // B1 363 VMLA.F32 q14, q4, d7[0] 364 VMLA.F32 q9, q5, d4[0] 365 VMLA.F32 q11, q5, d5[0] 366 ADD r12, r12, 8 // A1++ 367 368 # BLOCK 2 369 ADD r7, r7, 8 // A3++ VLDR B1 lands here 370 ADD r9, r9, 128 // B++ 371 VMLA.F32 q13, q5, d6[0] 372 VMLA.F32 q15, q5, d7[0] 373 VMLA.F32 q8, q6, d4[1] 374 375 # BLOCK 3 376 VMLA.F32 q10, q6, d5[1] 377 VMLA.F32 q12, q6, d6[1] 378 VMLA.F32 q14, q6, d7[1] 379 TST r5, 15 380 381 # BLOCK 4 382 VMLA.F32 q9, q7, d4[1] 383 VMLA.F32 q11, q7, d5[1] 384 VMLA.F32 q13, q7, d6[1] 385 386 # BLOCK 5 387 VMLA.F32 q15, q7, d7[1] 388 389 # Is there a remainder?- 1 to 3 floats of A (4, 8 or 12 bytes) 390 BNE 5f 391 392 .p2align 3 3934: 394 LDR r5, [sp, 112] // a 395 SUBS r14, r14, 16 // ks -= MR * sizeof(void*) 396 397 # ks loop 398 BHI 1b 399 400 # Load params pointer 401 LDR r0, [sp, 128] // cn_stride 402 LDR r2, [sp, 140] // params 403 LDR r14, [sp, 72] // p = ks 404 SUBS r1, r1, 8 405 406 # Load min/max values 407 VLD1.32 {d4[],d5[]}, [r2]! 408 VLD1.32 {d6[],d7[]}, [r2] 409 410 # Clamp 411 VMAX.F32 q8, q8, q2 412 VMAX.F32 q9, q9, q2 413 VMAX.F32 q10, q10, q2 414 VMAX.F32 q11, q11, q2 415 VMAX.F32 q12, q12, q2 416 VMAX.F32 q13, q13, q2 417 VMAX.F32 q14, q14, q2 418 VMAX.F32 q15, q15, q2 419 VMIN.F32 q8, q8, q3 420 VMIN.F32 q9, q9, q3 421 VMIN.F32 q10, q10, q3 422 VMIN.F32 q11, q11, q3 423 VMIN.F32 q12, q12, q3 424 VMIN.F32 q13, q13, q3 425 VMIN.F32 q14, q14, q3 426 VMIN.F32 q15, q15, q3 427 428 # Store full 4 x 8 429 BLO 7f 430 VST1.32 {d28-d31}, [r6], r0 431 VST1.32 {d24-d27}, [r8], r0 432 VST1.32 {d20-d23}, [r4], r0 433 VST1.32 {d16-d19}, [r11], r0 434 435 SUB r5, r5, r14 // a -= ks 436 437 BHI 0b 438 439 VPOP {d8-d15} 440 ADD sp, sp, 12 // skip pad, r2, r3 441 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 442 443 .p2align 3 4445: 445 # Is there a remainder?- 2 floats of A (8 bytes) 446 TST r5, 8 447 BEQ 6f 448 449 # Remainder - 2 floats of A (8 bytes) 450 VLD1.32 {d0}, [r3]! // A0 451 VLDM r9!, {d8-d11} // B0 452 VLD1.32 {d1}, [r12]! // A1 453 VLD1.32 {d2}, [r10]! // A2 454 VLD1.32 {d3}, [ r7]! // A3 455 456 VMLA.F32 q8, q4, d0[0] 457 VMLA.F32 q9, q5, d0[0] 458 VMLA.F32 q10, q4, d1[0] 459 VMLA.F32 q11, q5, d1[0] 460 VLDM r9!, {d12-d15} // B1 461 VMLA.F32 q12, q4, d2[0] 462 VMLA.F32 q13, q5, d2[0] 463 VMLA.F32 q14, q4, d3[0] 464 VMLA.F32 q15, q5, d3[0] 465 VMLA.F32 q8, q6, d0[1] 466 VMLA.F32 q9, q7, d0[1] 467 VMLA.F32 q10, q6, d1[1] 468 VMLA.F32 q11, q7, d1[1] 469 VMLA.F32 q12, q6, d2[1] 470 VMLA.F32 q13, q7, d2[1] 471 VMLA.F32 q14, q6, d3[1] 472 VMLA.F32 q15, q7, d3[1] 473 474 # Is there a remainder?- 1 float of A (4 bytes) 475 TST r5, 4 476 BEQ 4b 477 4786: 479 # Remainder- 1 float of A (4 bytes) 480 VLDM r3!, {s0} // A0 481 VLDM r9!, {d8-d11} // B0 482 VLDM r12!, {s2} // A1 483 VLDM r10!, {s4} // A2 484 VLDM r7!, {s6} // A3 485 VMLA.F32 q8, q4, d0[0] 486 VMLA.F32 q9, q5, d0[0] 487 VMLA.F32 q10, q4, d1[0] 488 VMLA.F32 q11, q5, d1[0] 489 VMLA.F32 q12, q4, d2[0] 490 VMLA.F32 q13, q5, d2[0] 491 VMLA.F32 q14, q4, d3[0] 492 VMLA.F32 q15, q5, d3[0] 493 B 4b 494 495 # Store odd width 4967: 497 TST r1, 4 498 BEQ 8f 499 VST1.32 {d28-d29}, [r6]! 500 VST1.32 {d24-d25}, [r8]! 501 VMOV q14, q15 502 VMOV q12, q13 503 VST1.32 {d20-d21}, [r4]! 504 VST1.32 {d16-d17}, [r11]! 505 VMOV q10, q11 506 VMOV q8, q9 507 5088: 509 TST r1, 2 510 BEQ 9f 511 VST1.32 {d28}, [r6]! 512 VST1.32 {d24}, [r8]! 513 VMOV d28, d29 514 VMOV d24, d25 515 VST1.32 {d20}, [r4]! 516 VST1.32 {d16}, [r11]! 517 VMOV d20, d21 518 VMOV d16, d17 519 5209: 521 TST r1, 1 522 BEQ 10f 523 VST1.32 {d28[0]}, [r6]! 524 VST1.32 {d24[0]}, [r8]! 525 VST1.32 {d20[0]}, [r4]! 526 VST1.32 {d16[0]}, [r11]! 527 52810: 529 VPOP {d8-d15} 530 ADD sp, sp, 12 // skip pad, r2, r3 531 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 532 533END_FUNCTION xnn_f32_igemm_minmax_ukernel_4x8__aarch32_neon${"_prfm" if PREFETCH else ""}_cortex_a53 534# LINT.ThenChange(4x8-aarch32-neon-cortex-a53.cc) 535 536#ifdef __ELF__ 537.section ".note.GNU-stack","",%progbits 538#endif 539